在 Makefile $(foreach ) 循环中插入一个新行

Insert a new-line in a Makefile $(foreach ) loop

我正在 Makefile 中编译大量文件。

my.list : ${deps}
    rm -f $@
    $(foreach F,$^,echo "${F}" >> $@;)

但是 ${deps} 可能很大,而且生成的命令行对于一个 SHELL 调用来说可能太大。是否可以用换行符 '\n' 替换 ';'

您可以像这样定义一个扩展为换行符的变量:

define NEWLINE

endef

现在您可以使用 $(NEWLINE) 扩展到换行符。

虽然这不会改变任何东西,foreach 仍然会生成一个在语句之间嵌入换行符的命令,而不是在语句之​​间生成一个带有 ; 的命令。

正在替换“;”通过 carriage-return 将产生相同大小的字符串,但会遇到相同的问题。

"foreach" 只是一个字符串扩展。如果要对每个项目执行单独的命令,可以使用for循环。

my.list : ${deps}
    rm -f $@
    (for F in $^; do echo $$F >> $@ ; done)

正如 Jonathan Wakely 的回答中已经提到的,直截了当的答案是

define newline


endef

有趣的是,对于除换行符之外的所有字符,在 shell 的帮助下,有一种更简单的方法可以将不可打印的字符放入变量中,例如:

tab := $(shell printf '1')

不过,它在这里不起作用,因为内置的 shell 函数会用空格替换所有换行符。

上面的版本仍然有点脆弱,特别是当与 automake 结合使用时,它会自动从输入文件中删除第二个连续的换行符,将 newline 变成一个空变量。要强制它保留换行符,我们可以扩展代码段:

blank :=
define newline

$(blank)
endef

最后,要为每个依赖项实际获得一个单独的 shell 命令,您需要 运行 通过 eval:

生成的字符串
define generate-rule =
my.list : $(1)
    rm -f $$@
    $(foreach F,$$^,$(newline)$(tab)echo "${F}" >> $@)

endef

$(eval $(call generate-rule,$(deps)))

编辑 -- 经过一些修改,我原来的唯一问题似乎不是因为白色space,而是因为 MAKE_O。我已经在我的版本中修复了它,但我会在下面删除它们。

顺便说一下原文 post 的写法,我不确定我的解决方案是否相关。但是,我发现自己处于 define 的中间,而 define 已经在 foreach 中,无法弄清楚如何使用给定的任何其他答案插入换行符。

解决方案:

define BR
$(1)

endef

define MAKE_O
$(1): $(wildcard $(1:obj/%.o=src/%.cpp)); \
        $(CXX) ... -c $$< $(call BR)-o  ... \
                $(call BR)&& touch $$@
endef
$(foreach N,main.o,$(eval $(call MAKE_O,$(N))))

所需的输出(编译从 auto-dependency generation 截断,因此 touch):

> make obj/main.o
g++ ... -c src/main.cpp \
-o obj/main.o ... \
&& touch obj/main.o

我更改了 BR 以执行缩进,但将行尾留给您:

define BR
$(1)
$(1:%= )        #<remove this comment
endef
define MAKE_O
$(1): $(wildcard $(1:obj/%.o=src/%.cpp)); \
        $(CXX) ... -c $$< $(call BR,\)-o  ... \
                $(call BR,\)&& touch $$@
endef
$(foreach N,main.o,$(eval $(call MAKE_O,$(N))))

标记无助于显示这一点,但 BR 的第 2 行是 $(1:%=_space_)_tab_(不允许注释本身。)结果:

> make -n obj/main.o
g++ obj/main.o -c \
        -o obj/main.o && \
        echo statement on new line

我用了$(call BR,\)这样换行就不会被解析为换行的转义,而$(1:%=space)tab 以便强制制表符(许多类似的规则已经定义,例如 SPACE:=$(SPACE) $(SPACE) 没有先前的值。) white[=59= 左边的变量] 的计算结果必须为 something。明确地说,删除调用前后的白色 space:...lastword$(call BR,\)firstword... 会产生 ...lastword\n\tfirstword...,或者写成

[^]...lastword \[$]
[^]$(call BR,\)firstword...[$]

...产量...

[^]...lastword \[$]
[^]        firstword...[$]

实现相同(使用 ^,$ 表示行的开头和结尾。其他人可能会更好地了解如何 format/annotate。)

我的语法荧光笔显然对 'escaped' 括号和尾随白色 space 没有印象,但结果还不错。

可能最直接的答案是使用:

@printf "\n"

您也可以像我通常那样使用它来在每个新的 make 目标执行时显示描述。示例目标:

.PHONY: clean-version
clean-version:    ## Reset version back to committed version number
    @printf "\n## Reset version to last committed version number ##\n\n"; \
    $(GIT) checkout $(VERSION_FILE);