当我编译 C 文件并将生成的目标文件输出到它们自己的目录中时,我需要确保该目录存在。 我见过两种方法:
obj/%.o: src/%.c
@mkdir -p $(@D)
$(CC) ...
obj/
目录作为先决条件
obj/%.o: src/%.c | obj/
$(CC) ...
obj/:
mkdir -p $@
来自
1.
我喜欢它使用 $(@D)
来避免重复并使其更加健壮,来自 2.
我喜欢 mkdir
只被调用一次,我觉得它在概念上更有意义。
当我尝试像这样结合两者时
obj/%.o: src/%.c | $(@D)
$(CC) ...
obj/:
mkdir -p $@
然后编译器会抱怨
obj/
目录不存在。
因为这就是 make 的工作原理;来自手册:
认识到自动变量值可用的有限范围非常重要:它们仅在配方中具有值。特别是,您不能在规则的目标列表中的任何位置使用它们;它们在那里没有任何价值,并将扩展为空字符串。此外,无法在规则的先决条件列表中直接访问它们。一个常见的错误是尝试在先决条件列表中使用 $@;这是行不通的。然而,GNU make 有一个特殊功能,即二次扩展(请参阅二次扩展),这将允许在先决条件列表中使用自动变量值。
如果你问,为什么 make 会这样工作,那是因为 make 总是在解析 makefile 时扩展目标和先决条件,而不是在匹配规则时扩展。 否则,这样的事情就无法工作:
OBJ = foo
$(OBJ)/%.o : %.c | $(OBJ) ;
OBJ = bar
$(OBJ)/%.o : %.cpp | $(OBJ) ;
尝试特殊情况,某些变量或函数立即扩展,而另一些则推迟到以后,会很混乱,甚至更难以解释和理解。 当然,在规则实际匹配之前,我们不知道
$(@D)
应该是什么,因为 %
可能包含一个目录。