I'm wondering what the semicolons in the following makefile snippet do:
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
In case it matters, the makefile is for a custom component (xxsim) in the OpenWRT buildsystem.
I would expect that the semicolons are unnecessary, per, e.g., this example (source):
define two-lines =
echo foo
echo $(bar)
endef
However, if we build without those semicolons, the second call to cp
fails (the first call to cp
is implicitly successful):
[CP] files/Adapter20Sim.h
[CP] files/Adapter20Sim.cpp
cp: target ' [MKDIR] xxsim docinfo in staging dir' is not a directory
Clearly, cp receives incorrect input parameters. So, what does adding the semicolons actually do, why does the component build correctly with semicolons present?
Update: added buildsystem context below, resulting from Etan's help in the comments:
The Hooks/Prepare/Post
is used in a foreach statement, where each of its values is used in a call function:
$(foreach hook,$(Hooks/Prepare/Post),$(call $(hook))$(sep))
The context of the 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))
The issue here is how a multi-line define is expanded in a recipe context (and specifically what the contents of the define are).
In a recipe context a multi-line define is expanded as multiple lines so the example from the make manual works correctly.
You would assume, as such, that the scenario under discussion here would work as well which it clearly doesn't. Two things combine to cause this problem.
The first of which is what the value of the define is exactly.
You might assume that given this snippet:
PKG_BUILD_DIR := build
cp = @echo '[CP] $1'; cp '$1' '$2'
define Package/xxsim/CopyLocalFiles
$(call cp, files/Adapter20Sim.h, $(PKG_BUILD_DIR)/xxsim)
$(call cp, files/Adapter20Sim.cpp, $(PKG_BUILD_DIR))
endef
that the value of Package/xxsim/CopyLocalFiles
would be:
@echo '[CP] files/Adapter20Sim.h'; cp 'files/Adapter20Sim.h' 'build/xxsim'
@echo '[CP] files/Adapter20Sim.cpp'; cp 'files/Adapter20Sim.cpp' 'build'
and it is... except.
You might assume that that was two lines with two newlines so really this:
@echo '[CP] files/Adapter20Sim.h'; cp 'files/Adapter20Sim.h' 'build/xxsim'\n
@echo '[CP] files/Adapter20Sim.cpp'; cp 'files/Adapter20Sim.cpp' 'build'\n
but it isn't. What it actually is is this:
@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.
Now normally (and in the example in the makefile) that doesn't matter because the define is used something like this:
tgt:
$(Package/xxsim/CopyLocalFiles)
and the final character of the define is followed immediately by a newline (on the recipe line itself).
But that isn't what is happening in this case. In this case the define is being expanded in a $(foreach)
loop and the manual says this about foreach
:
The multiple expansions of text are concatenated, with spaces between them, to make the result of foreach.
So when the loop expands two such defines back-to-back:
mkdir = @echo '[MKDIR] $1'; mkdir '$1'
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)))
what you get as the expanded output is this:
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
which causes the error you are seeing and which including the trailing semi-colons fixes.
Including an additional blank line in the define might also solve the problem (if make only chomps a single newline off the end of the literal define value).
Like this:
define Package/xxsim/CopyLocalFiles
$(call cp, files/Adapter20Sim.h, $(PKG_BUILD_DIR)/xxsim)
$(call cp, files/Adapter20Sim.cpp, $(PKG_BUILD_DIR))
endef