我有一个包含测试输入和输出的目录。为了方便起见,我希望make
在构建后针对该目录自动测试我的程序。因此,我需要以某种方式强制test
的{{1}}目标依赖于整个测试目录(之所以称为Makefile
,是因为它包含程序的有效输入和输出)
我阅读了这个问题,可接受的答案以及该答案下有关已删除文件的评论:Makefile rule that depends on all files under a directory (including within subdirectories),并结合了该答案和评论的建议,我得出了这样的结论:
good
出于MCVE的考虑,my@comp:~/wtfdir$ cat Makefile
test : test.sh $(shell find good)
./test.sh
my@comp:~/wtfdir$
非常简单:
test.sh
但是,我注意到,这表现得非常出乎意料:
my@comp:~/wtfdir$ cat test.sh
echo "blah"
my@comp:~/wtfdir$
为什么(删节删除)确实修改了my@comp:~/wtfdir$ ls good
test1 test1.out
my@comp:~/wtfdir$ make
./test.sh
blah
my@comp:~/wtfdir$ touch good/test1
my@comp:~/wtfdir$ make
cp good/test1 good/test1.out
./test.sh
blah
my@comp:~/wtfdir$
,导致test1
用make
覆盖了test1.out
?您知道我不是数据丢失的忠实拥护者。
这是怎么回事?
答案 0 :(得分:1)
您的Make似乎是GNU Make。这就是为什么发生这种情况。您的食谱:
test : test.sh $(shell find good)
./test.sh
将列出的每个文件和目录添加到test
的先决条件中
在当前目录中的find good
中,恰好是:
good
good/test1
good/test1.out
因此要确定目标test
,请首先确定是否指定了目标
或内置食谱要求它重建任何先决条件:
test.sh good good/test1 good/test1.out
在其内置的食谱中,它发现:
%.out: %
# recipe to execute (built-in):
@rm -f $@
cp $< $@
您可以通过运行以下命令进行验证:
$ make --print-data-base | grep -A4 '%.out'
此食谱的规则与以下项匹配:
good/test1.out: good/test1
并这样做:
$ touch good/test1
您已经使good/test1.out
相对于good/test1
过期了。
因此make执行配方:
@rm -f good/test1.out
cp good/test1 good/test1.out
您所观察到的可见输出
cp good/test1 good/test1.out
然后继续处理test
的配方:
./test.sh
blah
如果您盲目编写一个makefile,则总是存在此类陷阱的风险 在运行时生成一些您事先不知道的前提条件或目标。
您可以通过明确删除违规行为来避免这种情况 通过以下方式在makefile中使用隐式模式规则:
%.out: %
没有配方。而且,您可以通过禁用 all 来避免所有可能的此类诱杀装置 内置食谱,其中包括:
$ make --no-builtin-rules ...
但是,这将需要您为自己编写任何内置食谱, makefile依赖。
最适合您的解决方案可能是如下修改makefile:
PREREQS := $(shell find good)
test : test.sh $(PREREQS)
./test.sh
$(PREREQS): ;
然后最后一行明确指定一个empty recipe
对于每个$(PREREQS)
,并且Make不会参考目标的任何模式规则
有明确的食谱。
您还应该将test
设为phony target:
.PHONY: test
避免在某些情况下会在构建目录中创建名为test
的文件的陷阱。