Makefile 的 define 指令中的分号终止有什么作用?

What does semicolon-termination inside a Makefile's define directive do?

我想知道以下 makefile 片段中的分号有什么作用:

define Package/xxsim/CopyLocalFiles
    $(call cp, files/Adapter20Sim.h, $(PKG_BUILD_DIR)/xxsim);
    $(call cp, files/Adapter20Sim.cpp, $(PKG_BUILD_DIR));
endef
Hooks/Prepare/Post+=Package/xxsim/CopyLocalFiles

以防万一,makefile 用于 OpenWRT 构建系统中的自定义组件 (xxsim)。

我希望分号是不必要的,例如,这个例子 (source):

define two-lines =
    echo foo
    echo $(bar)
endef

但是,如果我们在没有这些分号的情况下构建,对 cp 的第二次调用将失败(对 cp 的第一次调用隐式成功):

 [CP]        files/Adapter20Sim.h
 [CP]        files/Adapter20Sim.cpp
 cp: target ' [MKDIR]    xxsim docinfo in staging dir' is not a directory

很明显,cp接收到了错误的输入参数。那么,添加分号的实际作用是什么?为什么在存在分号的情况下组件可以正确构建?

更新: 在下面添加了构建系统上下文,这源于 Etan 在评论中的帮助:

Hooks/Prepare/Post 用在 foreach 语句中,其中它的每个值都用在调用函数中:

$(foreach hook,$(Hooks/Prepare/Post),$(call $(hook))$(sep))

foreach的上下文:

 $(STAMP_PREPARED) : export PATH=$$(TARGET_PATH_PKG)
 $(STAMP_PREPARED):| $(PKG_BUILD_DIR) $(STAGING_DIR)/include $(STAGING_DIR)/lib$(LIB_SUFFIX)
    $(foreach hook,$(Hooks/Prepare/Pre),$(call $(hook))$(sep))
    $(Build/Prepare)
    $(foreach hook,$(Hooks/Prepare/Post),$(call $(hook))$(sep))
    $(call touch,$$@,Prepared $(PKG_NAME))

分号在定义函数中没有特殊功能。 $(call MyDefine) 只是扩展定义函数,即 call 函数被 define 函数的内容替换。

call函数实际上还做了一些其他的事情,比如参数扩展,但这似乎对这个主题没有影响。)

在您的情况下,分号是必需的,因为复制命令以同时执行的方式展开。 shell(如 bash)两个命令之间需要分号。

Makefile 语法使用制表符来定义属于目标的命令。你的错误

cp: target ' [MKDIR] xxsim docinfo in staging dir' is not a directory`

建议将您的命令之一(或其他东西..??)作为目标而不是命令处理。如果 define 函数的内容以空格作为缩进,您可能希望用制表符替换它们,因此完整的内容将作为命令而不是目标插入。

另一种解决方案是在每行后添加反斜杠,使内容成为'single-line'。在这种情况下,由于shell单行执行,因此需要分号,因此所有命令都需要用分号分隔。

这里的问题是如何在配方上下文中扩展多行定义(特别是定义的内容)。

在配方上下文中,多行定义被扩展为多行,因此 make 手册中的示例可以正常工作。

因此,您会假设此处讨论的方案也适用,但显然不适用。有两件事共同导致了这个问题。

第一个是定义的值。

你可能会假设给定这个片段:

PKG_BUILD_DIR := build
cp = @echo '[CP] '; cp '' ''

define Package/xxsim/CopyLocalFiles
    $(call cp, files/Adapter20Sim.h, $(PKG_BUILD_DIR)/xxsim)
    $(call cp, files/Adapter20Sim.cpp, $(PKG_BUILD_DIR))
endef

Package/xxsim/CopyLocalFiles 的值将是:

@echo '[CP] files/Adapter20Sim.h'; cp 'files/Adapter20Sim.h' 'build/xxsim'
@echo '[CP] files/Adapter20Sim.cpp'; cp 'files/Adapter20Sim.cpp' 'build'

它是...除了。

可能 假设那是两行和两个换行符,所以实际上是这样的:

@echo '[CP] files/Adapter20Sim.h'; cp 'files/Adapter20Sim.h' 'build/xxsim'\n
@echo '[CP] files/Adapter20Sim.cpp'; cp 'files/Adapter20Sim.cpp' 'build'\n

但事实并非如此。它实际上是这样的:

@echo '[CP] files/Adapter20Sim.h'; cp 'files/Adapter20Sim.h' 'build/xxsim'\n
@echo '[CP] files/Adapter20Sim.cpp'; cp 'files/Adapter20Sim.cpp' 'build'

with no final newline.

现在通常(以及在 makefile 中的示例中)这无关紧要,因为 define 是这样使用的:

tgt:
        $(Package/xxsim/CopyLocalFiles)

定义的最后一个字符紧跟一个换行符(在配方行本身)。

但这不是本例中发生的情况。在这种情况下,定义在 $(foreach) 循环中展开,手册中说的是 foreach:

The multiple expansions of text are concatenated, with spaces between them, to make the result of foreach.

所以当循环展开两个这样的背靠背定义时:

mkdir = @echo '[MKDIR] '; mkdir ''

define Package/xxsim/CopyLocalFiles
    $(call cp, files/Adapter20Sim.h, $(PKG_BUILD_DIR)/xxsim)
    $(call cp, files/Adapter20Sim.cpp, $(PKG_BUILD_DIR))
endef
HOOKS += Package/xxsim/CopyLocalFiles
define Package/xxsim/MkdirStaging
    $(call mkdir, $(PKG_BUILD_DIR)/xxsim/staging)
endef
HOOKS += Package/xxsim/MkdirStaging

tgt:
        $(foreach hook,$(HOOKS),$(call $(hook)))

你得到的扩展输出是这样的:

tgt:
    @echo '[CP] files/Adapter20Sim.h'; cp 'files/Adapter20Sim.h' 'build/xxsim'
    @echo '[CP] files/Adapter20Sim.cpp'; cp 'files/Adapter20Sim.cpp' 'build' @echo '[MKDIR] build/xxsim/staging'; mkdir build/xxsim/staging

导致您看到的错误的原因,包括尾随分号在内的错误得到修复。

在定义中包含一个额外的空行也可以解决问题(如果 make 只在文字定义值的末尾截断一个换行符)。

像这样:

define Package/xxsim/CopyLocalFiles
    $(call cp, files/Adapter20Sim.h, $(PKG_BUILD_DIR)/xxsim)
    $(call cp, files/Adapter20Sim.cpp, $(PKG_BUILD_DIR))

endef