How do computed variables passed to included make files' functions get expanded? - makefile

To reduce the amount of repeated boiler plate in a top level makefile, I created an included make file that uses computed variable names. Where I'm having difficulty is in the excerpt from the included makefile: ftp-files.mk:
...
$($(FNMPFX)_FTP_CFG): $(CFG_MAKE_FILE) | $($(FNMPFX)_FTP_CFG_DIR)
$(call ftp_helper, $#, $($(FNMPFX)_FTP_DIR), $($(FNMPFX)_CACHE_DIR), $($(FNMPFX)_FTP_NAME))
...
In the main makefile, I was hoping to do something akin to:
CFG_MAKE_FILE := Makefile
define ftp_helper
echo "quote USER anonymous" > $(1)
echo "quote PASS" >> $(1)
echo "cd $(2)" >> $(1)
echo "lcd $(3)" >> $(1)
echo "binary" >> $(1)
echo "get $(4)" >> $(1)
echo "quit" >> $(1)
endef
FNMPFX := FILE_A
include ftp-files.mk
...
FNMPFX := FILE_Z
include ftp-files.mk
...
The trouble is that the order only prerequisite (also tried it as a normal prerequisite) expands to the last ... instanciation (?) of the included file.
What appears to be happening is a first expansion of the two targets that behave as though they were written:
# Point of confusion V
# |
$(FILE_A_FTP_CFG): $(CFG_MAKE_FILE) | $(FILE_Z_FTP_CFG_DIR)
$(call ftp_helper, $#, $(FILE_A_FTP_DIR), $(FILE_A_CACHE_DIR), $(FILE_A_FTP_NAME))
$(FILE_Z_FTP_CFG): $(CFG_MAKE_FILE) | $(FILE_Z_FTP_CFG_DIR)
$(call ftp_helper, $#, $(FILE_Z_FTP_DIR), $(FILE_Z_CACHE_DIR), $(FILE_Z_FTP_NAME))
Is this possible?
My work around was to include that simple target rule in the main Makefile (ftp-file.mk is reasonably wordy at 100 lines), so having those two lines (repeated) throughout the main Makefile isn't too burdensome.
Can somebody suggest a working alternative?

In general you can't read a whole Makefile like a shell script. It's read in multiple phases, and certain things are done with the entire contents of the Makefile before proceeding to the next step. The rules are rather complex, but suffice it to say that it looks like the last assigned value is used:
$ cat Makefile
variable := original
first:
echo $(variable)
variable := other
second:
echo $(variable)
$ make first
echo other
other
$ make second
echo other
other

Related

Make $* check for string

I am trying to check if $* matches hello . But the following is not working
build: build-hello
build-%:
ifeq ($*, hello)
echo Hello
else
echo World
endif
The conditions in the ifeq's are processed at makefile read time -- when $* is still blank. There's a couple of workarounds to this: First, you could do a build-hello: rule, which would override the build-% rule for build-hello. If, on the other hand you wanted to minimize rules, you could use the $(if) function as so:
build-%:
#echo $(if $(filter $*,hello),Hello,World)
Or, you could just use shell logic to accomplish this as well.

Makefile evaluation question - foreach list doesnt evaluate before execution

Here's a snippet from a Makefile which in my environment is recursive and it appears this piece of code has a n issue and I don't understand why foreach doesn't evaluate. The variables TOOLVERSION and TOOLSDIR are assigned values based on running a Ruby script. These provide the include paths for the C_FLAGS. The target test2 produces the right result where every element of C_FLAGS seperated by space appears on a new line which is the desired result. However test1 does not evaluate TOOLVERSION and TOOLSDIR so produces garbage. If I use $(shell getenvval.rb version) for assigning TOOLVERSION and TOOLSDIR the foreach work but the value of running the script is incorrect. I thought this was because something defined in the Makefile environment doesn't get to the shell so I used export but it didn't make a difference.
So the question comes down to why does the following foreach loop not work:
#$(foreach flag, $(C_FLAGS), `echo $(flag) >> $(FILE_C1_LIST)`)
while this works:
#echo $(C_FLAGS) >> $(FILE_C2_LIST)
Appreciate any help in understanding the evaluation.
Snippet from Makefile:
export
TOOLVERSION:= `getenvval.rb version`
TOOLSDIR:= `getenvval.rb directory`
FILE_C1_LIST := test_c1.f
FILE_C2_LIST := test_c2.f
C_FILES =\
./a.c \
./b.c
C_FLAGS := \
-I$(TOOLSDIR)/$(TOOLVERSION)/tools/include \
-I./aa/include \
-I./bb/editline \
-g -DDEBUG -DPLISIM -DINCLUDE_EDITLINE -DSYS_UNIX
$(FILE_C1_LIST): $(C_FILES)
rm -f $(FILE_C1_LIST)
touch $(FILE_C1_LIST)
#(echo $(C_FLAGS) )
#$(foreach flag, $(C_FLAGS), `echo $(flag) >> $(FILE_C1_LIST)`)
#$(foreach file, $(C_FILES), `echo $(file) >> $(FILE_C1_LIST)` )
$(FILE_C2_LIST): $(C_FILES)
#rm -f $(FILE_C2_LIST)
#touch $(FILE_C2_LIST)
#echo $(C_FLAGS) >> $(FILE_C2_LIST)
#$(foreach file, $(C_FILES), `echo $(file) >> $(FILE_C2_LIST)` )
test1: $(FILE_C1_LIST)
test2: $(FILE_C2_LIST)
Your expectations are odd. You appear to try to use shell syntax in constructs which are not going to be evaluated by a shell; and even if they were, the expressions would not be syntactically valid.
Without delving too much into the details, try this instead.
printf '%s\n' $(C_FLAGS) >> $(FILE_C1_LIST)
In some more detail, your loop would create the text
`echo one >>file` `echo two >>file` `echo three >>file`
where the backticks imply that the output from echo should be the text of a command which you want the shell to execute; but of course, because of the redirection, there is no output to standard output from any of these commands. (The superficial problem of having all the commands on a single line could be worked around with a semicolon after each.)
As a further aside, there is no need to rm or touch a file you are going to overwrite. Just write it. Your recipes can be substantially simplified.
$(FILE_C1_LIST): $(C_FILES)
printf '%s\n' $(C_FLAGS) $(C_FILES) >$#
$(FILE_C2_LIST): $(C_FILES)
echo $(C_FLAGS) >$#
printf '%s\n' $(C_FILES) >>$#
The parentheses you had around the first echo would needlessly run that in a separate subshell. I'm guessing you had that purely to see what you were doing; removing the # on your recipes does that much better. You can run make silently witth make -s once you have everything debugged and sorted.

how to avoid variable expansion for a bash for loop in define makefile

Usually, I use foreach call in a makefile. Notwithstanding, I wanted to try different approaches.
For that, I have created the code bellow.
I am using GNU Make 3.81
I have replaced the eval by info to understand what is happening.
I cannot avoid both the call and the eval expansion to keep the $file
I mean, when I open the file I am getting:
"for file in *.v; do echo ; echo ; done; > trial.sh;"
define create_area
$(1): $(2)
mkdir -p $$(#D); \
cp -nLr -v $(2)/* $$(#D); \
cd $$(#D); \
for file in *.v; do echo $$(file); \
done; \
" > trial.sh;
$(foreach a,$(A1) $(A2),\
$(foreach b,$(B1),\
$(eval $(call create_area,$(a),$(b)))))
Thank you in advance,
You're almost there.
If you run the loop in a shell, you must precede the variable name with '$' so that it will be expanded.
If you put that command in a makefile recipe, to prevent Make from expanding the variable name too soon (when the variable has not yet been given a value) you must precede ("escape") that '$' with another '$'.
If you put that recipe in a definition which you will expand with call, you must double each of them:
for file in *.v; do echo $$$$file; done

makefile use variable defined in target [duplicate]

This question already has answers here:
Define make variable at rule execution time
(4 answers)
Closed 4 years ago.
How can one use the variable defined inside a make target
.PHONY: foo
VAR_GLOBAL=$(shell cat /tmp/global)
foo:
echo "local" > /tmp/local
VAR_LOCAL=$(shell cat /tmp/local)
echo ${VAR_GLOBAL}
echo ${VAR_LOCAL}
here is the execution output:
$ echo global > /tmp/global
$ make foo
echo "local" > /tmp/local
VAR_LOCAL=local
echo global
global
echo
As #KelvinSherlock pointed out this is a duplicate of another question
here is the specific solution for my question:
.PHONY: foo
VAR_GLOBAL=$(shell cat /tmp/global)
foo:
echo "local" > /tmp/local
$(eval VAR_LOCAL := $(shell cat /tmp/local))
echo ${VAR_GLOBAL}
echo ${VAR_LOCAL}
You probably want to use the override directive in a target-specific variable assignment, so try:
foo: override LS_LOCAL=$(shell ls /var | tail -1)
echo ${LS_GLOBAL}
echo ${LS_LOCAL}
If LS_LOCAL is never defined (even by builtin-rules) you might not need the override keyword.
BTW, you might avoid $(shell ls /var | tail -1) by using the wildcard function combined with the lastword function (perhaps combined with notdir function), so you might code $(lastword $(wildcard /var/*)) or $(notdir $(lastword $(wildcard /var/*))) instead . However, beware of the order of expansion, and of filenames with spaces. At last the shell function probably uses your $PATH variable (so strange things could happen if some weird ls program appears there before /bin/ls). Perhaps using $(shell /bin/ls /var | /usr/bin/tail -1) might be better.
Look also into Guile-extended make; consider perhaps some other build-automation tool like ninja and/or generating your Makefile (or other build configuration) with something like a configure script generated via autoconf or cmake.
Notice also that a command in recipe can be made of several physical backslashed lines (hence executed in the same shell). Maybe you might consider something like
export MY_VAR=$$(ls /var | tail); \
dosomething; \
use $$MY_VAR
inside some recipe.

Makefile target-based dependencies with a wildcard (compact)

I would like to apply the following code in a more compact way:
hello1:
#echo "Hello1"
hello2:
#echo "Hello2"
hello3:
#echo "Hello3"
hello_all: hello1 hello2 hello3
Is there a way of coding hello_all dependencies with a wildcard, e.g. hello* or something?
If your targets are very similar, go with #TimF's solution as it avoids repetition. If they are different and can't be generalized, you can do this with a small helper:
hello-add = $(eval HELLO_TARGETS += $1)$1
$(call hello-add,hello1):
#echo "Hello1"
$(call hello-add,hello2):
#echo "Hello2"
$(call hello-add,hello3):
#echo "Hello3"
hello_all: $(HELLO_TARGETS)
hello-add accepts an argument, adds it to the HELLO_TARGETS variable, and expands to exactly that argument (because eval expands to empty). It behaves like:
HELLO_TARGETS += hello1
hello1:
...
But avoids having to write the target name twice.
I don't think there's a native way to do it. Find below a solution specific to your usecase. I think however that it cannot easily be used in a generic way.
# The number list that will be used to generate the targets
NUMBERS = 1 2 3 4
# Function that will expand to a custom helloX target, with X the number given as parameter
# Note the strip that removes the spaces in the parameter
define createHelloTargets
HELLO_TARGETS += hello$(strip $(1))
hello$(strip $(1)):
#echo Hello$(strip $(1))
endef
# Generate one Hello target for each number in NUMBERS
$(foreach nb, $(NUMBERS), $(eval $(call createHelloTargets, $(nb))))
all: $(HELLO_TARGETS)
This will output :
Hello1
Hello2
Hello3
Hello4
The advantage is that you don't have to write each target, just fill the NUMBERS var and it does the trick.
Basically, this Makefile will create for each number X in NUMBERS a target that looks like this :
helloX:
#echo HelloX
It will also add helloX to HELLO_TARGETS which is a list of all the existing helloX targets. This list is expanded in the all target prerequisites.

Resources