Makefile 如何在通配符(%)匹配整个目标的情况下定义生成规则?

Makefile 如何在通配符(%)匹配整个目标的情况下定义生成规则?,makefile,wildcard,gnu-make,Makefile,Wildcard,Gnu Make,我知道我可以使用以下语法在make规则中使用模式匹配: %.csv:%.tsv tsv2csv$

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

%.csv:%.tsv
tsv2csv$<$@
但是,如果要匹配整个目标,这似乎不起作用:

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

%.tsv:%.tsv.gz
zcat$<>$@
干得好

%:%.gz
zcat$<>$@
没有

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

%sv:%sv.gz
zcat$<>$@
但是想象一下,我也想使用相同的规则来获取
*.txt
文件的未压缩副本

有没有理由不可能(和/或不应该)这样做?如果没有,我如何在(
GNU
make
中实现这一点

我想这是一个常见的需求(例如编译
foo.c
foo
),但我没有在上面或其他地方找到任何东西(web搜索)

附录

正如上面的评论/我的最小示例中所指出的那样,效果很好

然而,在我更大的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:**没有规则将目标设为'testA',这是'all'所需要的。停止
我认为,
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$<>$@
我使用

触摸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
的这种行为的原因是什么?有没有办法改变这种行为?

我不确定您是如何执行
make
命令的,但是下面的命令序列对我有效

生成文件:

% : %.gz
    zcat $< > $@

GNU make对目标为
%
的规则的处理方式不同,如中所述

您可能可以通过巧妙地放置双冒号来使用您的规则,可能是基于以下规则:

% :: %.gz

你想做的应该行得通。如果没有,你应该提供一个实际的不起作用的例子,包括makefile、你键入的命令和你得到的结果。@MadScientist:很抱歉这个坏例子,我希望新的信息是有用的。谢谢你的快速回答,也很抱歉在发布之前没有测试我的MWE。我发现它最终太小而无法工作。请查看新信息以重现问题。@sg lecram,不寻常的是错误消息是:`没有规则生成目标
testA.csv'
,而不是抱怨无法生成部件。关于错误消息,我认为这是由于
make
处理基于模式的规则的方式造成的。好了,第一条可以使用的规则被使用了。因此,
make
扫描了第一条规则,以及如果有零件或规则来制作这些零件,
testA.csv
可以这样做的原因。由于
make
似乎无法生成零件,因此该规则被忽略,并且
make
继续寻找另一个与
testA.csv
匹配的规则,因为既没有文件名
testA.csv.gz
也没有生成该规则,第二条规则也被跳过。由于没有留下任何规则,
make
得出结论,因此没有规则
% :: %.gz