带链接规则的Makefile在每次运行时不必要地运行最后一个命令

时间:2016-01-17 09:11:16

标签: makefile

我使用TikZ为我的文档绘制各种矢量图形。它相当慢,每个绘图使主LaTeX文档的编译时间更长。因此,我将所有数据作为独立文件。它们是独立编译的,PDF文件包含在主文档中。这允许根据需要并行运行LaTeX。

该过程如下:

  • 实际的TikZ代码位于Figures/fig.tex的LaTeX代码段文件中。 Python脚本(tikzpicture_wrap.py)会将代码段包装到包含前导码的独立文档中。这些文件进入build/page/fig.tex

  • lualatex在文件build/page/fig.tex上运行,生成build/page/fig.pdf

  • 由于我使用与同一文档相同的文档类(scrartcl),因此该图形设置在A4纸上,因此需要在将其包含在文档中之前进行裁剪。为此,我使用pdfcrop作为最后一步。结果将放入build/fig.pdf

我的完整makefile如下所示:

# Copyright © 2015-2016 Martin Ueding <dev@martin-ueding.de>

.PRECIOUS: %.tex %.pdf build/page/%.pdf

document_tex := $(wildcard physics*.tex)
document_pdf := $(document_tex:%.tex=%.pdf)

figures_tex := $(wildcard Figures/*.tex)
figures_pdf := $(figures_tex:Figures/%.tex=build/%.pdf)

all: $(figures_pdf)
#all: $(document_pdf)  # Disabled to only typeset figures right now.

test:
    @echo "document:    $(document_pdf)"
    @echo "figures_tex: $(figures_tex)"
    @echo "figures_pdf: $(figures_pdf)"

$(document_pdf): $(figures_pdf)

$(figures_pdf): build

build:
    mkdir -p build/page

build/page/%.tex: Figures/%.tex
    ../build-system/tikzpicture_wrap.py $< $@

build/%.pdf: build/page/%.pdf
    pdfcrop $< $@
    touch $@  # Added in an attempt to work around the problem, does not make any difference, though.

%.pdf: %.tex
    cd $$(dirname $@) && lualatex --halt-on-error $$(basename $<)

clean:
    $(RM) *-blx.bib
    $(RM) *.aux
    $(RM) *.log
    $(RM) *.run.xml
    $(RM) *.out
    $(RM) *.svg
    $(RM) *.pdf
    $(RM) -r build

它确实有效,它会对所有数字进行排版,最终会出现在build/*.pdf。问题是pdfcrop步骤一次又一次地运行,即使没有其他事可做。在输出中,您可以看到以下内容:

pdfcrop build/page/propagator.pdf build/propagator.pdf
PDFCROP 1.38, 2012/11/02 - Copyright (c) 2002-2012 by Heiko Oberdiek.
==> 1 page written on `build/propagator.pdf'.
touch build/propagator.pdf

对于我Figures目录中的每一个数字,都会重复此操作。

我认为这可能是chained rules的一个问题,并将中间文件添加到.PRECIOUS目标中,以便保留它。现在,pdfcrop使用的文件不会在中途删除。

接下来我认为文件上的时间戳可能有问题。如果源比目标更新,make将运行它。因此,我添加了touch以确保目标比源更新。这不是问题,因为我今天早上跑完后可以看到这个问题。从昨天开始,我没有改变该图中的任何内容。

源文件。 stat Figures/propagator.tex

  File: 'Figures/propagator.tex'
  Size: 102             Blocks: 8          IO Block: 4096   regular file
Device: fd03h/64771d    Inode: 17432618    Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/      mu)   Gid: ( 1000/      mu)
Context: unconfined_u:object_r:user_home_t:s0
Access: 2016-01-16 10:58:34.384515470 +0100
Modify: 2016-01-16 10:58:34.369515566 +0100
Change: 2016-01-16 10:58:34.373515540 +0100
 Birth: -

排版PDF文档。 stat build/page/propagator.pdf

  File: 'build/page/propagator.pdf'
  Size: 6265            Blocks: 16         IO Block: 4096   regular file
Device: fd03h/64771d    Inode: 17432636    Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/      mu)   Gid: ( 1000/      mu)
Context: unconfined_u:object_r:user_home_t:s0
Access: 2016-01-16 10:59:04.317323576 +0100
Modify: 2016-01-16 10:59:04.261323935 +0100
Change: 2016-01-16 10:59:04.261323935 +0100
 Birth: -

裁剪的最终文件。 stat build/propagator.pdf

  File: 'build/propagator.pdf'
  Size: 6612            Blocks: 16         IO Block: 4096   regular file
Device: fd03h/64771d    Inode: 17301550    Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/      mu)   Gid: ( 1000/      mu)
Context: unconfined_u:object_r:user_home_t:s0
Access: 2016-01-17 09:54:32.102511429 +0100
Modify: 2016-01-17 09:54:30.943517396 +0100
Change: 2016-01-17 09:54:30.943517396 +0100
 Birth: -

它仍然一次又一次地执行所有pdfcrop操作。我不懂为什么。与此同时,我添加了以下kludge,以便在没有任何操作时使编译过程更快完成:

build/%.pdf: build/page/%.pdf
    if [ $< -nt $@ ]; then \
        pdfcrop $< $@; \
        fi

这里的实际问题是什么?如何解决?

1 个答案:

答案 0 :(得分:2)

@Tsyvarev走在正确的轨道上。这确实是问题所在:

$(figures_pdf): build

但是,这不是因为 build与特定文件无关build 确实存在:它是使用规则创建的目录:

build:
        mkdir -p build/page

这构建了子目录build/page,这意味着build存在(作为目录),因此在后续构建中,当检查它是否存在时,它将确定是,它确实存在。当列为目标时,不要将目录与任何其他文件区别开来。

但是,先决条件的存在并不是唯一可以使用的测试:它还会检查先决条件是否比目标更新。这是目录和文件行为不同的地方,以及为什么你几乎从不(除了在某些特殊情况下)想要使用目录作为目标的正常先决条件。

目录有一个&#34;时间最后修改&#34;就像文件一样,目录的TLM值就像文件一样更新:当目录被修改时。什么构成&#34;修改&#34;一个目录?好吧,添加新文件,删除现有文件或重命名文件都会修改目录并导致其更改TLM。

您现在可以看到问题:每次makefile添加,删除或重命名build目录中的文件时,其修改时间都会更新。这意味着目录build总是比其中的几乎所有文件都新,这意味着它们每次都会重建。

有两种方法可以解决这个问题。一种是始终将目录创建为副作用;从先决条件列表中删除build并删除上面的规则,而只是在解析makefile时强制通过make强制创建目录:

__dummy := $(shell mkdir -p build/page)

第二种方式,如果你有一个足够新的GNU make版本,就是使用order-only prerequisites

$(figures_pdf): | build