Makefile 在 for 循环中删除变量

Makefile cutting out variables in for loop

我正在为高度模块化的项目编写第一个复杂的 Makefile。

我有各种子目录,每个子目录都有自己的 Makefile,至少支持 allclean 目标。

这些子 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。我不明白为什么会这样,也不知道如何解决。

只需将美元符号加倍:

$(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,后者将其解释为循环变量,一切都按计划进行.

在尝试使用一个命令进行实际工作之前,您真的应该在命令中尝试一个简单的循环。

几个问题。

  • .PHONY应该写成依赖,而不是宏定义
  • 不要写 shell 循环,改用 make 语法
  • 当你递归调用make时,你必须通过${MAKE}宏调用
  • 来完成

通向

OUTFILE = diskimage.bin
export NASM = nasm
COMPONENTS = bootloader

.PHONY: all
all: ${OUTFILE}

.PHONY: ${OUTFILE}
${OUTFILE}: ${COMPONENTS}
    ./make_image

.PHONY: ${COMPONENTS}
${COMPONENTS}:
    ${MAKE} -C $@

这个公式的优点是并行使友好。 始终测试一个好的 Makefile。 这里 make -j5 all 将导致 make 一次保留 5 个命令 运行ning, make 的所有调用。 如果你有 4 个 CPU,那就太好了。

干净呢? (就我个人而言,我讨厌 clean 目标——这是狡猾依赖的标志, 以及源文件夹和目标文件夹的不卫生混合。)

只需在每个组件名称中添加 -clean(比方说), 并重复上面的模式。

CLEANS := $(addsuxffix -clean,${COMPONENTS})

.PHONY: clean
clean: ${CLEANS} ; @echo Clean succesful

.PHONY: ${CLEANS}
${CLEANS}: %-clean:
    ${MAKE} -C $* clean

如果您愿意,可以将这两个部分合并为一个部分。

提示

Always 运行 make with --warn (或 --warn-undefined-variables 使其完整name) 来捕获 $c 在诸如 $component.

中的无意扩展