Reputation: 11
Given the following part of a Makefile:
$(call pkg-source,$(1)):
$(call unpack,$(call tarball,$(1)))
$(foreach pch, $(sort $(wildcard src/$(notdir $(ver_$(1))).patch*)), patch -d $(1)* -p1 < $(pch))
$(foreach var, $(shell find $(1)* -type f -iname abc.xyz), cp my_abc.xyz $(var))
touch $$@
noopstop
The noopstop is there to force make to terminate with "command not found".
First run after make clean
, external source is unpacked, then (the first foreach
) patched (if patches are present).
Then (the second foreach
) a file of a known name that came with the source has to be overwritten by my own version. This always fails on the first run, yet the file is there.
Removing the file created by touch $$@
, and running make
again, the file is now replaced.
Starting make
with the -j1
option does not make a difference; the -d
option does not help to find the problem either.
The GNU Make pages state: "Normally, make will execute only one recipe at a time, waiting for it to finish before executing the next." So after the first foreach
(for patching), why doesn't the second see the file?
This is on Fedora_35, x86_64, GNU make 4.3
Thanks, Mat
Upvotes: 1
Views: 544
Reputation: 100936
It's very hard to understand your example because you haven't provided it in the context of an actual rule. But I'll assume that the entirety of the content you provide above is in some define
variable that you are using with call
. But, the details of how this is used are critical and you don't provide them.
The problem is that you're doing all this work with make variables and functions, not shell commands. The way make works is that all lines of the entire recipe are expanded first (so all make variables and functions are expanded), and only after that is complete will make invoke a shell with each line of the recipe one at a time.
A good rule of thumb is that if you ever find yourself running the make shell
function in a recipe, you're probably doing something wrong. A recipe is already run in a shell, so there's little to gain except confusion by using make's shell
function.
In addition, your operations will not work correctly if they ever find more than one file.
For example:
$(foreach pch, $(sort $(wildcard src/$(notdir $(ver_$(1))).patch*)), patch -d $(1)* -p1 < $(pch))
Suppose the wildcard
here expands to two files, src/xyz.patch-01
and src/xyz.patch-02
. Now the result of expanding this will be something like:
patch -d foo* -p1 < src/xyz.patch-01 patch -d foo* -p1 < src/xyz.patch-02
which is clearly wrong. If you want to have a make foreach
loop expand to multiple shell commands, you have to remember to add a semicolon or &&
to separate the commands:
$(foreach pch, ..., patch -d $(1)* -p1 < $(pch) &&) true
You'll have the same problem with the other command, except you don't want to do this with a make foreach
and shell
anyway as I discussed above. You should be using shell constructs here, not make constructs:
for pch in $$(ls -1 src/$(notdir $(ver_$(1))).patch*); do \
patch -d $(1)* -p1 < $$pch; \
done
for var in $$(find $(1)* -type f -iname abc.xyz); do \
cp my_abc.xyz $$var; \
done
Upvotes: 1