为何Makefile中*通配符正常生效,%通配符却无法匹配文件?
*.s生效但%.s作为依赖会报错? 这个问题其实是没搞清楚Makefile里*和%这两个通配符的本质区别,我给你拆解明白:
1. 为什么default: *.s能正常工作?
当你写default: *.s时,Make在处理这条规则的依赖时,会调用系统Shell来展开*.s这个通配符——就像你在终端里敲ls *.s一样,Shell会把它替换成当前目录下所有后缀为.s的文件(也就是你的smf.s)。
这条规则最终会被解析成default: smf.s,依赖是实际存在的文件,所以Make能正常执行后续命令,$<指向第一个依赖smf.s,经过basename处理后自然能输出正确结果。
2. 为什么default: %.s会报错?
%是Make专属的模式匹配符,它的用法和Shell的*完全不同,不能直接用来“匹配所有.s文件”。
当你写default: %.s时,Make不会把%展开成所有可能的文件名前缀,反而会把%.s当成一个字面意义上的文件名——也就是它会去寻找一个名叫%.s的文件,而你的目录里显然没有这个文件,所以Make就会报错:make: *** No rule to make target '%.s', needed by 'default'. Stop.
3. %的正确用法是什么?
%的核心作用是在模式规则里关联目标和依赖的同名部分,用来批量处理同类型的文件。比如你想把所有.s汇编文件编译成.o目标文件,就可以这么写:
%.o: %.s gcc -c $< -o $@
这里的%会匹配文件名的前缀,当Make需要生成smf.o时,它会自动找到对应的依赖smf.s,这时候%才会发挥它的匹配作用——它是用来建立“目标-依赖”的对应关系,而不是直接匹配一堆文件作为某个目标的依赖。
小技巧:更稳妥的依赖所有.s文件的写法
如果想让default依赖所有.s文件,除了用*.s,更稳妥的方式是使用Make的wildcard函数,它会在Make解析阶段就直接展开成所有存在的.s文件:
default: $(wildcard *.s) echo $(basename $<)
这个写法能避免当目录里没有.s文件时,*.s被当作字面量依赖的问题。
内容的提问来源于stack exchange,提问作者Herdsman

