Makefile在for循环中剪切变量

Makefile在for循环中剪切变量,makefile,gnu-make,Makefile,Gnu Make,我正在为一个高度模块化的项目编写第一个复杂的Makefile 我有不同的子目录,每个子目录都有自己的Makefile,它至少支持all和clean目标 这些子Makefiles工作得很好,但是我对主Makefile有一个问题,它应该从变量COMPONENTS中包含的列表中自动调用所有子Makefiles 我尝试使用以下Makefile: OUTFILE = diskimage.bin export NASM = nasm COMPONENTS = bootloader .PHONY = a

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

我有不同的子目录,每个子目录都有自己的Makefile,它至少支持
all
clean
目标

这些子Makefiles工作得很好,但是我对主Makefile有一个问题,它应该从变量
COMPONENTS
中包含的列表中自动调用所有子Makefiles

我尝试使用以下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
没有任何值(没有这样的变量),它将扩展为零,留下“component”,并传递给she shell,shell抱怨没有这样的目录。(如果您编写了
$(component)
,Make会将其扩展为零,因为Make不知道这样的变量,然后shell会抱怨您根本没有指定目录。)

使用双美元符号,Make将
$$component
展开为
$component
,然后将其传递给shell,shell将其解释为循环变量,一切按计划进行


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

只需将美元符号加倍:

$(COMPONENTS): FORCE
    for component in $(COMPONENTS); do \
  make -C $$component; \
  done
问题在于,对于makefile,Make在执行规则之前会展开
$component
。由于
$c
没有任何值(没有这样的变量),它将扩展为零,留下“component”,并传递给she shell,shell抱怨没有这样的目录。(如果您编写了
$(component)
,Make会将其扩展为零,因为Make不知道这样的变量,然后shell会抱怨您根本没有指定目录。)

使用双美元符号,Make将
$$component
展开为
$component
,然后将其传递给shell,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个命令, 跨越make的所有调用。 如果你有4个CPU就好了

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

只需将
-clean
(比如)添加到每个组件名称, 重复上面的模式

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

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

.PHONY: ${CLEANS}
${CLEANS}: %-clean:
    ${MAKE} -C $* clean
如果你愿意的话,这两个部分可以整理并合并成一个

提示

始终使用
--warn
运行make(或
--warn未定义变量
以给出其全名),以捕获
$c
$component

等问题中的意外扩展

  • .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个命令, 跨越make的所有调用。 如果你有4个CPU就好了

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

只需将
-clean
(比如)添加到每个组件名称, 重复上面的模式

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

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

.PHONY: ${CLEANS}
${CLEANS}: %-clean:
    ${MAKE} -C $* clean
如果你愿意的话,这两个部分可以整理并合并成一个

提示


始终使用
--warn
(或
--warn undefined variables
为其全名)运行make,以捕获
$c
$component

之类的内容中的意外扩展。Ok-我明白了我没有理解的地方:我没有真正理解for循环是由shell而不是make执行的。但现在我想起来这是有道理的,反斜杠肯定是一条重要线索将扩展为$by make,当然,这会产生有效的shell语法。来自Windows环境,我不习惯不同软件之间如此程度的集成。好吧,我明白了我没有理解的地方:我没有真正理解for循环是由shell而不是make执行的。但现在我想起来这是有道理的,反斜杠肯定是一条重要线索将扩展为$by make,当然,这会产生有效的shell语法。我来自Windows环境,不习惯不同软件之间如此程度的集成。