我正在为一个高度模块化的项目编写我的第一个复杂的Makefile。
我有各种子目录,每个子目录都有自己的Makefile,至少支持all
和clean
目标。
这些子Makefile工作得很好,但是主Makefile有问题,应该从变量COMPONENTS
中包含的列表中自动调用所有子Makefile。
我尝试使用以下Makefile:
OUTFILE = diskimage.bin
export NASM = nasm
COMPONENTS = bootloader
.PHONY = all clean FORCE $(OUTFILE) $(COMPONENTS)
all: $(OUTFILE)
$(OUTFILE): $(COMPONENTS)
./make_image
$(COMPONENTS): FORCE
for component in $(COMPONENTS); do \
make -C $component; \
done
FORCE:
clean:
for component in $(COMPONENTS); do \
make -C $component clean; \
done
这会导致以下错误消息:
for component in bootloader; do \
make -C omponent; \
done
make: *** omponent: No such file or directory. Stop.
make: *** [bootloader] Error 2
好像$component
表达式只被解析为$c
。我不明白为什么会发生这种情况以及如何解决它。
答案 0 :(得分:1)
只需加倍美元符号:
$(COMPONENTS): FORCE
for component in $(COMPONENTS); do \
make -C $$component; \
done
问题在于,使用makefile,Make会在执行规则之前展开$component
。而且由于$c
没有价值(没有这样的变量),它会扩展为空,留下" omponent",它传递给她的shell,抱怨没有这样的目录。 (如果你写了$(component)
,Make会把它扩展为空,因为Make知道没有这样的变量,然后shell会抱怨你根本没有指定目录。)
使用双美元符号,Make将$$component
扩展为$component
,然后将其传递给shell,后者将其解释为循环变量,并且所有内容都按计划进行。
在尝试使用一个循环之前,你真的应该在命令中使用一个简单的循环。
答案 1 :(得分:0)
有几个问题。
.PHONY
应该写为依赖项,而不是宏定义${MAKE}
宏调用导致
OUTFILE = diskimage.bin
export NASM = nasm
COMPONENTS = bootloader
.PHONY: all
all: ${OUTFILE}
.PHONY: ${OUTFILE}
${OUTFILE}: ${COMPONENTS}
./make_image
.PHONY: ${COMPONENTS}
${COMPONENTS}:
${MAKE} -C $@
这种表述的优点是它平行 make 友好。
总是测试一个好的Makefile。
这里make -j5 all
会导致 make 一次运行5个命令,
跨 make 的所有调用。
如果您有4个CPU,那就太好了。
clean 怎么样? (我个人讨厌干净的目标 - 它是狡猾的依赖关系的标志, 以及源和目标文件夹的不卫生混合。)
只需将-clean
(比方说)添加到每个组件名称中,
并重复上面的模式。
CLEANS := $(addsuxffix -clean,${COMPONENTS})
.PHONY: clean
clean: ${CLEANS} ; @echo Clean succesful
.PHONY: ${CLEANS}
${CLEANS}: %-clean:
${MAKE} -C $* clean
如果您有这种倾向,这两部分可以整理并合并为一部分。
提示强>
始终运行 make 并使用--warn
(或--warn-undefined-variables
给它全名)来捕捉$c
的无意扩展像$component
这样的东西。