【问题标题】:How to define a Make rule with the whole target being matched by the wildcard (%)?如何使用通配符 (%) 匹配整个目标来定义 Make 规则?
【发布时间】:2014-06-13 18:09:05
【问题描述】:

我知道我可以使用以下语法在make 规则中使用模式匹配:

%.csv : %.tsv
  tsv2csv $< $@

但是,如果应该匹配整个目标,这似乎不起作用:

虽然

%.csv : %.csv.gz
  zcat $< > $@

%.tsv : %.tsv.gz
  zcat $< > $@

工作正常,

% : %.gz
  zcat $< > $@

没有。

我知道对于这个特定的例子我可以使用

%sv : %sv.gz
  zcat $< > $@

但想象一下,我也想使用相同的规则来获取 *.txt 文件的未压缩副本。

这是否有原因(和/或不应该)可能,如果没有,我如何在 (GNU) make 中实现这一目标?

我想这是一个常见的需求(例如将 foo.c 编译为 foo),但我在 SO 和其他地方(网络搜索)都没有找到任何东西。

附录

正如评论中指出的那样/answer 我上面的最小示例工作得很好。

但是,在我较大的 Makefile 中,它没有。

我发现这似乎是由第二级模式匹配触发的。

考虑以下 Makefile:

all : testA testB

testA : testA.txt
  ln -s $< $@

testB : testB.txt
  ln -s $< $@

testA.txt : testA.txt.gz
  zcat $< > $@

testB.txt : testB.txt.gz
  zcat $< > $@

我使用创建了一些虚拟输入文件

touch testA.txt testB.txt
gzip testA.txt testB.txt

如果我运行 make -n 我会得到

zcat testA.txt.gz > testA.txt
ln -s testA.txt testA
zcat testB.txt.gz > testB.txt
ln -s testB.txt testB

使用模式规则,我可以将上面的 Makefile 缩短为

all : testA testB

testA : testA.txt
  ln -s $< $@

testB : testB.txt
  ln -s $< $@

%.txt : %.txt.gz
  zcat $< > $@

在这个版本上使用make -n,我仍然得到和以前一样的输出。

我现在发现我也可以把它缩短为

all : testA testB

testA : testA.txt
  ln -s $< $@

testB : testB.txt
  ln -s $< $@

% : %.gz
  zcat $< > $@

all : testA testB

% : %.txt
  ln -s $< $@

testA.txt : testA.txt.gz
  zcat $< > $@

testB.txt : testB.txt.gz
  zcat $< > $@

所以匹配通配符 (%) 的整个目标不是问题。

但是,我不能将上述简化合并到一个 Makefile 中:

all : testA testB

% : %.txt
  ln -s $< $@

% : %.gz
  zcat $< > $@

此 Makefile 会产生以下 make -n 的输出:

make: *** No rule to make target `testA', needed by `all'.  Stop.

我认为make 可能有两个将通配符作为整个目标的模式规则的问题(尽管我认为这是可能的;至少在这种情况下)。

这就是为什么我也尝试了以下版本:

all : testA testB

% : %.txt
  ln -s $< $@

%.txt : %.txt.gz
  zcat $< > $@

令我惊讶的是,这被make 接受了。

不幸的是,我需要这样的东西:

all : testA.csv testB.csv

testA.csv : testA.txt testA.tsv
  tsv2csv $^ $@

testB.csv : testB.txt testB.tsv
  tsv2csv $^ $@

testA.txt : testA.txt.gz
  zcat $< > $@

testB.txt : testB.txt.gz
  zcat $< > $@

testA.tsv : testA.tsv.gz
  zcat $< > $@

testB.tsv : testB.tsv.gz
  zcat $< > $@

我使用

创建了测试输入
touch testA.txt testB.txt testA.tsv testB.tsv
gzip testA.txt testB.txt testA.tsv testB.tsv

运行make -n 返回

zcat testA.txt.gz > testA.txt
zcat testA.tsv.gz > testA.tsv
tsv2csv testA.txt testA.tsv testA.csv
zcat testB.txt.gz > testB.txt
zcat testB.tsv.gz > testB.tsv
tsv2csv testB.txt testB.tsv testB.csv

综合以上观察,我可以将其缩短为

all : testA.csv testB.csv

%.csv : %.txt %.tsv
  tsv2csv $^ $@

%.txt : %.txt.gz
  zcat $< > $@

%.tsv : %.tsv.gz
  zcat $< > $@

但不是这个:

all : testA.csv testB.csv

%.csv : %.txt %.tsv
  tsv2csv $^ $@

% : %.gz
  zcat $< > $@

make 出现这种行为的原因是什么?有没有办法改变它?

【问题讨论】:

  • 你想做的应该可以。如果不是,您应该提供一个不起作用的实际示例,包括 makefile、您键入的命令以及您得到的结果。
  • @MadScientist:抱歉举了一个不好的例子,希望新信息对您有所帮助。

标签: makefile wildcard gnu-make


【解决方案1】:

我不确定您是如何尝试执行 make 命令的,但以下命令序列对我有用。

生成文件:

% : %.gz
    zcat $< > $@

命令:

touch foo.txt
gzip foo.txt   # Creates foo.txt.gz
make foo.txt   # Produces foo.txt again.

【讨论】:

  • 感谢您的快速回答,很抱歉在发布之前没有测试我的 MWE。我发现它最终太小而无法工作。请查看新信息以重现该问题。
  • @sg-lecram,不寻常的是错误信息是:`No rule to make target testA.csv',而不是抱怨无法制作零件。
  • 关于错误信息,我认为这是由于make 对待基于模式的规则的方式。 AFAIK,使用了第一个可以使用的规则。所以make 会扫描第一条规则,并说明如果有部件或规则可以这样制作testA.csv 的原因。由于make 似乎无法制作零件,这条规则被简单地忽略了,make 继续寻找与testA.csv 匹配的另一条规则,因为既没有文件名testA.csv.gz 也没有规则来制作它,第二条规则也被跳过。由于没有规则,make 得出结论,testA.csv 没有规则
【解决方案2】:

http://www.gnu.org/software/make/manual/make.html#Match_002dAnything-Rules 中所述,GNU make 以不同方式处理目标为% 的规则

您也许可以通过巧妙地放置双冒号让您的规则发挥作用,也许在这条规则上:

% :: %.gz

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-09
    • 1970-01-01
    相关资源
    最近更新 更多