Make调用函数中的Shell命令和参数扩展

时间:2017-10-30 19:43:43

标签: gnu-make

我有以下Makefile:

dependencies=$(1) style.sty $(shell sed -rne 's/^.*\\input\{([^\}]+)\}.*$//\1.tex/p' $(1))

%.pdf: $(call dependencies,%.tex)
    @echo $^
    @mkdir -p build
    @pdflatex -output-directory build $<
    @cp build/$@ .

这里非常令人不安的是%.texdependencies内使用两次,第一次正确扩展,但不是第二次,导致:sed: can't read %.tex: No such file or directory

make conventions.tex的输出是:

sed: can't read %.tex: No such file or directory
conventions.tex style.sty
This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Arch 
Linux) (preloaded format=pdflatex)
[...]

正如您所看到的,%.texconventions.tex的第一次出现时已扩展为$(1),但在$(shell)命令内则无效。为什么呢?

我想到的一个答案是$(shell)%.tex之前得到扩展,这听起来很可能。如果是这样,我怎样才能先强制%.tex扩展?我尝试了以下方法:

define dependencies
   temp=$(1)
   echo $(termp) style.sty $(shell sed -rne 's/^.*\\input\{([^\}]+)\}.*$//\1.tex/p' $(temp))
endef

然后它会挂起,直到Ctrl-D被击中,就好像sed被调用,最后一个参数为空......

2 个答案:

答案 0 :(得分:1)

实际上非常接近。

您希望扩展某种$(shell)以提供具有更多依赖关系的 make 。 这是一种模式:

.SECONDEXPANSION:
%.pdf: %.tex $${tex-dependencies}
    commands-that-make-$@

这里发生了什么?

  • 在阅读makefile时, make 首先展开依赖行,然后看到
    %.pdf: %.tex ${tex-dependencies}
    (注意$$已成为$
  • Make 现在有一个声称能够的模式规则 将.tex文件转换为.pdf
  • 您要求 make f.pdf说(假设 f.tex已经存在!)
    $ make f.pdf
  • 制作使用上述模式规则。
    • %匹配f
    • 由于.SECONDEXPANSION的存在, make 第二次展开依赖行。 的 危重 下, 这一次$*会扩展为与规则中的%匹配的任何文字。
    • 因此,您可以在$*的定义中使用$tex-dependencies。 (顺便说一句,所有其他自动变量也可用,$@$<等等。)

类似的东西:

.SECONDEXPANSION:

tex-dependencies = $(shell grep -Po '\\input{\K[^\\}]+' $*.tex)

%.pdf: %.tex $${tex-dependencies}
    mkdir -p build
    pdflatex -output-directory build $<
    cp build/$@ .

不要使用$(call),因为我们只会传入$*(甚至是$<$@),而这只是此时是一个全局变量。

注意用于从\input{}表达式中提取文件名的 grep 模式完全未经测试。 它虽然在正确的球场: - )

答案 1 :(得分:1)

您的示例中有两个不同的事情:首先运行整个makefile,在$(call dependencies,%.tex)的先决条件中遇到%.pdf并立即重写该规则。详细说明:它首先将字面扩展为

%.tex $(shell sed -rne 's/^.*\\input\{([^\}]+)\}.*/\1.tex/p' %.tex)

就make而言,这是一个需要进一步扩展的字符串 - 但不是%.tex部分。使语法仅需要扩展$ - 转义符号,在此阶段它不关心文件系统及其通配符。您可以通过将字符串提供给info函数而不是shell来查看此内容:

dependencies = $(info sed -rne 's/^.*\\input\{([^\}]+)\}.*$//\1.tex/p' $(1)) $(1) style.sty $(shell sed -rne 's/^.*\\input\{([^\}]+)\}.*$//\1.tex/p' $(1))

执行此操作:

    user@dogbert:~$ make
    %.tex sed -rne 's/^.*\\input\{([^\}]+)\}.*/\1.tex/p' %.tex

首先注意make甚至在考虑任何目标之前运行此函数(没有用实际目标名称替换%)。在扫描makefile时,它遇到了dependencies的调用并执行它而不知道哪些文件会进入此规则。 从技术上讲,它只是为它看到的每个%.tex插入了$(1),另外,作为不必要的副作用,它还替换了$/作为空字符串,因为它将$/作为变量name(如果你想通过一个需要扩展的make字符串传递一个$,你需要引用另一个$ - &gt; $(shell sed -rne 's/^.*\\input\{([^\}]+)\}.*$$//\1.tex/p' $(1)))。在第一次扩展之后,make转向扩展$(shell )部分,这意味着执行您命令的shell调用。请注意,我们仍在dependencies函数内。传递给shell的字符串现在看起来像这样:

sed -rne 's/^.*\\input\{([^\}]+)\}.*/\1.tex/p' %.tex

现在sed无法找到%.tex文件,因为%是make的通配符,而不是shell通配符,并且将它提供给shell将真正搜索{{1}而不是以%.tex结尾的所有文件。 Sed找不到它,返回一条错误消息,显然会被make忽略,并继续将.tex的现在完全扩展返回到调用表达式,即模式规则的先决条件列表,现在显示为:

dependencies

正如您所看到的,函数的%.pdf: %.tex style.sty 部分根本没有返回任何内容,而shell的其余部分返回了应该的值。 这里的关键是没有发生任何文件名扩展 - 这仍然是make内部计算而不从文件系统询问的部分。

在先决条件扩展中从dependencies踢回来后,这一切都与我对字符串扩展和shell所说的相反:在先决条件列表中,dependencies是通配符目标名称和词干(与%匹配的字符)将传输到先决条件列表%以生成其符号。仍然没有涉及文件系统,因为make可以从它到目前为止找到的字符串中推断出所有必需的符号。现在知道每个% - 结束目标将取决于.pdf - 具有相同名称的结束先决条件加上共同先决条件.tex

现在更正将style.sty更改为%.tex的错误:原始意图肯定是扫描特定的先决条件文件以获取其他依赖项(即,扫描规则中的sed for foo.tex)并且不给foo.pdf通配符模式sed来扫描它在当前目录中找到的每个*.tex文件。因此,我们必须将.tex函数的执行推迟到实例化此模式规则的单个目标并生成其特定先决条件列表的时间。虽然make中没有特殊语法可以实现此目的,但我们可以使用specail目标shell来实现此目的。 .SECONDEXPANSION:之后的每条规则都将受到其先决条件列表的另一次扩展。特别是make允许在先决条件列表中使用特殊变量,如.SECONDEXPANSION,这在第一个扩展中是未定义的。这导致我们进行以下更正:

$@

越优雅(恕我直言)

dependencies=$(@:.pdf=.tex) style.sty $(shell sed -rne 's/^.*\\input\{([^\}]+)\}.*$$//\1.tex/p' $(@:.pdf=.tex))

.SECONDEXPANSION:
%.pdf: $$(dependencies)
    @echo $^
    @mkdir -p build
    @pdflatex -output-directory build $<
    @cp build/$@ .

在我的make 4.0上没有效果,因为它插入目标而不是文档所说的dependencies = style.sty $(shell sed -rne 's/^.*\\input\{([^\}]+)\}.*$$//\1.tex/p' $<) .SECONDEXPANSION: %.pdf: %.tex $$(dependencies) @echo $^ @mkdir -p build @pdflatex -output-directory build $< @cp build/$@ . 的第一个先决条件。