在 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);
我正在 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);