在我看来,Makefile规则可以粗略地分为“正面”和“负面”规则:“正面”规则创建缺失或更新过时文件,而“负面”规则删除文件。
编写“肯定”规则的先决条件非常简单:如果目标和先决条件是文件名,默认情况下make
会在目标丢失或过时时运行配方(在此上下文中可能缺少文件)被视为无限旧文件。)
但是,请考虑“否定”规则,例如目标clean
。写它的通常方法似乎是这样的:
clean:
rm -f *.log *.synctex.gz *.aux *.out *.toc
这显然不是最好的方法:
rm
,
需要使用-f
标志来抑制其错误消息和退出状态,该标志具有其他(可能不合需要的)效果,并且
与目标clean
无关的事实不会向用户报告,这与“正面”目标的正常情况不同。
我的问题是:如何编写一个Makefile规则,只有在某些文件存在时才由make
处理? (就像对make clean
有用的那样。)
答案 0 :(得分:3)
如何编写Makefile规则,只有在某些文件存在时才由make处理? (就像清洁一样有用。)
你可以这样做:
filenames := a b c
files := $(strip $(foreach f,$(filenames),$(wildcard $(f))))
all: $(filenames)
$(filenames):
touch $@
clean:
ifneq ($(files),)
rm -f $(files)
endif
示例会话:
$ make
touch a
touch b
touch c
$ make clean
rm -f a b c
$ make clean
make: Nothing to be done for 'clean'.
可能出于某些目的而有用,但它对我来说是make clean
的紧张改进。
答案 1 :(得分:2)
这很容易解决:
clean:
for file in *.log *.synctex.gz *.aux *.out *.toc; do \
if [ -e "$file" ]; then \
rm "$$file" || exit 1; \
else \
printf 'No such file: %s\n' "$file" \
fi \
done
除非您的shell支持并已启用nullglob
或类似内容,否则if
语句是必需的。
如果您的printf
支持%q
,则应使用该%s
代替AGPBI: {"kind":"simple","text":"Uncaught translation error: com.android.dex.util.ExceptionWithContext","sources":[{}]}
AGPBI: {"kind":"simple","text":"1 error; aborting","sources":[{}]}
,以避免在打印奇怪的文件名时可能损坏您的终端。
答案 2 :(得分:-1)
一个元答案是:你确定要这样做吗?
其他答案告诉我,治愈方法比疾病更糟糕,因为一个涉及到POSIX make(ifneq
)的扩展,另一个使用扩展到七行的复合命令。这两者有时都是必要的权宜之计 - 我不是批评任何一个答案 - 但如果可以的话,这两件事都是我在Makefile中避免的事情。如果我发现自己想要在clean
规则中执行此操作,也许是因为您在对@MikeKinghans的回答中提及的原因,我会非常努力地更改Makefile的其余部分以避免需要它。
依次反思你的三个原点:
rm即使无所事事也会被执行:那又怎样?例如,替代方案仍然需要扩展*.log *.synctex.gz ...
,因此避免rm
只能获得极小的效率提升。 Make
是一种高级工具,通常不关心效率。
需要使用-f标志来抑制其错误消息和退出状态:-f
标志通常不会抑制错误和退出状态,它仅表示rm
不认为不存在或未经许可的文件是错误。
没有任何关于目标清理的事实没有报告给用户:用户真的应该关心吗?
最后一点是最有趣的。人们在Stackoverflow和其他地方询问make
,有时通过尝试将其用作过程语言来使自己变得困难 - make
不是Python或Fortran。相反,它是一个目标编程语言(如果我们想要了解它):你编写规则片段来实现子目标,以便用户(你,后来)不必关心细节或目录的当前状态,但可以简单地指出目标,程序会做任何必要的事情来实现目标。因此,无论是否有任何事情要做,用户都不应该“关心”。
我认为这个答案的简短版本是:保持make
规则尽可能简单(因此可读性和健壮性)是惯用的,即使是以一点点粗暴或重复为代价。