3

我正在尝试创建一个执行以下操作的makefile(GNU make):

  1. 脚本会生成一堆文件——文件名事先不知道。
  2. 这些文件中的每一个都转换为不同的文件。
  3. 全部转换后,所有这些文件都合并为一个输出文件。

如何使用“钟形曲线”模式的依赖图创建生成文件,其中中间源文件和目标文件事先不知道?

从概念上讲,我正在执行以下操作:

combined.pdf: $(filter combined.pdf, $(wildcard *.pdf))
    cat *.pdf > combined.pdf

%.pdf: %.svg
    cp $^ $@

$(wildcard *.svg):
    # recipe is for simple example
    # actually the *.svg files are not known in advance
    echo a > a.svg
    echo b > b.svg
    echo c > c.svg

.PHONY: clean
clean:
    ${RM} *.svg *.pdf *.d

当然这不起作用:Make 在运行实际创建 svg 的目标之前评估目标和源。此外,无法确保所有 svg 在合并之前都已转换。

我意识到我可以创建依赖项并将它们包含到 makefile 中,但是我也很难让它工作:

.PHONY: clean

include deps.d

combined.pdf: deps.d
    cat *.pdf > combined.pdf

%.pdf: %.svg
    cp $^ $@


deps.d:
    @# recipe is for simple example
    @# actually the *.svg files are not known in advance
    echo a > a.svg
    echo b > b.svg
    echo c > c.svg
    @# we know what files exist now, so we can establish dependencies
    @# "a.pdf : a.svg"
    echo *.svg : > deps.d
    @# combined.pdf: a.pdf b.pdf c.pdf
    ls *.svg \
        | awk '{targetfn=$$0; sub(/\.svg$$/, ".pdf", targetfn); print targetfn, ":", $$0;}' \
        >> deps.d
    @# combined.pdf: a.pdf b.pdf c.pdf
    echo combined.pdf : $$(echo *.svg | sed -e 's/\.svg/\.pdf/g') >> deps.d

clean:
    ${RM} *.pdf *.svg *.d

但是,这仍然没有正确连接依赖图。当我运行它时,退出如下:

Makefile:3: deps.d: No such file or directory
echo a > a.svg
echo b > b.svg
echo c > c.svg
echo *.svg : > deps.d
ls *.svg \
        | awk '{targetfn=$0; sub(/\.svg$/, ".pdf", targetfn); print targetfn, ":", $0;}' \
        >> deps.d
echo combined.pdf : $(echo *.svg | sed -e 's/\.svg/\.pdf/g') >> deps.d
make: Nothing to be done for `a.svg'.

我似乎仍然有 make 不知道 deps.d 中的规则的问题。

此外,这仍然不能解决构建所有依赖项的问题。我想过使用这样的标记文件:

%.pdf: %.svg
    cp $^ $@
    @# if all svgs are converted, touch a target allpdfs
    if [ $(ls -1 *.svg | wc -l) -eq $(ls -1 *.pdf | grep -v combined\.pdf | wc -l) ]; touch allpdfs; fi

但是没有办法告诉 make 这个规则可以创建“allpdfs”。

4

3 回答 3

3

我很惊讶移动include指令会有所作为(您使用的是什么版本的 Make?),但有一种更简单的方法。您的使用deps.d实际上是对 Make 的递归使用——Make 正在安排第二次执行自身——所以我们不妨将其正式化:

combined.pdf: ALL_SVGS
    $(MAKE) ALL_PDFS
    rm -f $@ # just in case it exists already
    cat *.pdf > $@

.PHONY: ALL_SVGS
ALL_SVGS:
    # recipe is for simple example                                              
    # actually the *.svg files are not known in advance                         
    echo a > a.svg
    echo b > b.svg
    echo c > c.svg

# These variables will be empty in the first execution of Make
SVGS = $(wildcard *.svg)
PDFS = $(patsubst %.svg,%.pdf,$(SVGS))

.PHONY: ALL_PDFS
ALL_PDFS: $(PDFS))

%.pdf: %.svg
    cp $^ $@
于 2012-07-06T13:47:34.787 回答
1

这不是一个确切的答案,因为我不知道为什么会这样,但我发现如果我将include指令移到创建包含文件的目标之后,一切正常。

即这样做:

deps.d:
    ....

include deps.d

因为我的 deps.d 包含足够的依赖信息,所以不需要中间目标allpdfs文件。一切正常,即使使用make -j.

但是,我不知道为什么会这样。包含文档并没有启发我。

更新

我在 make 手册的最底部注意到了以下注释,讨论了 Automatic Prerequisites :

请注意,“.d”文件包含目标定义;您应该确保将 include 指令放在您的 makefile 中的第一个默认目标之后,或者冒着让随机目标文件成为默认目标的风险。请参阅Make 的工作原理

所以发生的事情是生成的 deps.d中的第一条规则成为默认目标,导致构建的神秘提前完成。因此,解决方案只是确保include指令不在您的预期默认目标之前。

于 2012-07-06T10:01:29.323 回答
1

我只是在稍微不同的环境中解决这个确切的问题。这是一个干净的解决方案 - 不需要递归等(如果你愿意,你可以调整 sed):

include deps.d

combined.pdf:
    cat *.pdf > combined.pdf

%.pdf: %.svg
    cp $^ $@

deps.d:
    echo a > a.svg
    echo b > b.svg
    echo c > c.svg
    echo 'combined.pdf:' *.svg | sed 's/\.svg/\.pdf/g' > deps.d

享受!

于 2012-07-12T20:43:57.667 回答