Invoking a Make target in a loop with an argument - makefile

I am designing a simple makefile that defines one target which takes an argument, and I would like to define a second target that invokes the first target in a loop, once per every variable defined in an array variable at the top of the Makefile.
my_loop_var = var1 var2
my_thing:
echo $MY_VAR
all:
invoke my_thing once for each value of my_loop_var
What is the right way to solve this?

Note: $MY_VAR will probably expand as Y_VAR because make will try to expand variable M, which is probably undefined, and concatenate Y_VAR to the result. Use $(VARNAME) to expand make variable VARNAME. It is only with single-character variable names that you can expand with $X.
There is not really a "right way". There are many ways. The most straightforward from your specifications (but the less natural) would be something like:
my_loop_var := var1 var2
my_thing:
echo "$(MY_VAR)"
.PHONY: all my_thing
all:
for v in $(my_loop_var); do $(MAKE) my_thing MY_VAR="$$v"; done
The recipe of all is a shell loop that re-invokes make with the my_thing goal and with each value in my_loop_var passed as the value of make variable MY_VAR.
Note the $$v to escape the first expansion that make always performs before passing the recipe to the shell. With just $v make would expand the recipe as ... MY_VAR="" ... and the result would not be what you expect.
Note also the use of the MAKE make variable instead of directly calling make (have a look at the documentation if you want to understand why it is better like this).
all and my_thing are declared as phony because they are not real file names that their recipes create, and make needs to know this kind of things.
But a much more make-ish way would be something like:
my_loop_var := var1 var2
my_thing_targets := $(addprefix my_thing_,$(my_loop_var))
.PHONY: all $(my_thing_targets)
all: $(my_thing_targets)
$(my_thing_targets): my_thing_%:
echo "$*"
Here we define as many my_thing_something targets as there are something values in my_loop_var. And explain make how to build such targets with a static pattern rule. In the recipe of a pattern rule the $* automatic variable expands as the part that matched the % pattern. So, this static pattern rule says that if we need to build my_thing_something, the recipe is echo "something".
We declare all these my_thing_something targets as prerequisites of all such that if you type make or make all, make will build all the my_thing_something targets.
This second solution is better for at least two reasons. First, make is invoked only once, which is better, at least for performance. Second, make can parallelize the build of the my_thing_something if you use the -j N option (to allow make to run up to N jobs in parallel). This also is better for performance.
But it is also a matter of style. Very frequently if you use shell loops in your recipes, especially to invoke make again, it is the sign that you did not really understand what make is intended for and how it works. The make language is not a scripting language (even if the recipes are written in shell language, which is a scripting language); make is designed to "loop" over all targets to build, without the need for explicit loops.

Related

remove Quotes from String in Windows Terminal

I want to retrieve some Values from AWS and use them afterwards in my Makefile. But for that purpose I need to remove the quotes.
I know different ways how to do that in Linux but not in Windows. The only hint I found is this: https://ss64.com/nt/syntax-dequote.html
But all my attempts to use it with my code have not worked so far.
The Code looks like this:
aws/project_name:
# $(eval PROJECT = $(shell aws ssm get-parameter --name "$(PROJECT_PARAMETER)" --query Parameter.Value))
#set PROJECT = %~1
# echo $(PROJECT)
When I run it the result is:
"MyProject"
Can someone give me hint pls how to strip the Double-quotes?
$(eval ) is a make function, not something which is evaluated as part of a shell script like your snippet suggests. Moreover you need to concatenate recipe shell lines with backslash, otherwise each line is executed in a separate shell process, thereby losing access to any variable instantiated in previous lines.
Another issue is the use of $(PROJECT) without quoting: make will replace this with its internal PROJECT variables' value before it goes to evaluate even the first line (see below) of your recipe. If you want to access the shell variable in this line, quote every use of $ with $$.
To answer your original question for replacement of "": $(patsubst "%",%,$(PROJECT)) would do that - you can wrap the whole $(shell ...) in it. but I suspect your troubles with what you are trying to achieve will not end there.
It is rather complicated to introduce make variable values in a running build in the right order. I'm stressing this again: At least you must keep in mind that make evaluates all of its own syntax (e.g. the $(eval ) call) always and entirely before it executes the recipe. Moreover, the order of evaluation of the rule is also split into two phases, see here: https://www.gnu.org/software/make/manual/html_node/Reading-Makefiles.html#Reading-Makefiles. If you don't understand the execution model of a makefile you will not be able to write controlled advanced scripting code in it.
One can do advanced scripting with make like you are trying, but I recommend a strict architectural approach for such makefiles - ad-hoc writing them will likely lead to chaos.
The current file could be written as:
aws/project_name: ;
# ';' reliably separates recipe lines from target & prerequisites
# we still need to $(eval) PROJECT because it is only accessible for other recipes this way
$(eval PROJECT = $(patsubst "%",%,$(shell aws ssm get-parameter --nam`enter code here`e "$(PROJECT_PARAMETER)" --profile enchomepage --query Parameter.Value)))
#echo $(PROJECT)
later_target: aws/project_name
# this should work now:
#echo $(PROJECT)
...but beware, this is exactly the part where such makefiles become hard to trace, becaus now you need to mentally follow the evaluation order, take care that variables used downstream are really $(eval )'ed upstream (they won't if the recipe isn't executed) thereby having sidestepped the usual contract which other programmers expect from a makefile.

How do you use (GNU) make's "simply expanded" variables in build rules and not get the last definition?

I have a complicated set of rules I need to write to generate a rather large number of "parameterised" output files and thought that, rather than expand them all out by hand, I could repeatedly "include" a template file with sets of rules and use (GNU)make's facility for allowing "simply expanded" variables to avoid the pain.
(In the past I've always been using the "recursively expanded" variable approach, so this is new to me)
As a trivial example of what I thought would work, I tried putting the following in a Makefile
Targ:=A
Param1:=Pa
Param2:=Qa
$(Targ):
#echo expect A, get $(Targ), Target is $#. Params are $(Param1) and $(Param2)
Targ:=B
Param1:=Pb
Param2:=Qb
$(Targ):
#echo expect B, get $(Targ), Target is $#. Params are $(Param1) and $(Param2)
Targ:=C
Param1:=Pc
Param2:=Qc
$(Targ):
#echo expect C, get $(Targ), Target is $#. Params are $(Param1) and $(Param2)
The eventual plan was to replace the rules with an include file containing dozens of different rules, each referencing the various "parameter" variables.
However, what I get is...
prompt> make A
expect A, get C, Target is A. Params are Pc and Qc
prompt> make B
expect B, get C, Target is B. Params are Pc and Qc
Essentially, unlike each rule's target, which is picking up the intended definition, the $(Targ), $(Param1), and $(Param2) in each rule's command is instead being run with the final definition.
Does anyone know how to prevent this, i.e. how do you force the command to use the definition at the time it is encountered in the Makefile?
Simple vs recursive expansion makes no difference here; regardless of which you use you'll see the same behavior. A GNU make variable is global and obviously can have only one value.
You have to understand when variables are expanded. The documentation provides a detailed description of this. Targets and prerequisites are expanded when the makefile is read in, so the value of Targ as the makefile is being parsed is used.
Recipes are expanded when the recipe is to be invoked, which is not until after all makefiles are parsed and make starts to build targets. At that time of course the variable Targ has its last set value.
Without knowing what your makefile really does it's hard to suggest an alternative. One option is to use target-specific variables:
Targ := A
$(Targ): LocalTarg := $(Targ)
$(Targ):
#echo expect A, get $(LocalTarg), Target is $#
Another option is to use constructed variable names:
Targ := A
Targ_$(Targ) := $(Targ)
$(Targ):
#echo expect A, get $(Targ_$#), Target is $#
Apologies for answering my own question, but I now realised it is possible to solve the issue I was having by running make recursively.
E.g. if the parameter variables for the rules are Targ, Param1 and Param2 then
#Set up "default" values for the parameters (As #madscientist points out,
#these will safely be overridden by the defs on the #(make) commands below
Targ=XXXXXXXXXX
Param=XXXXXXXXXX
Param2=XXXXXXXXXX
Recursing=
#
# define N (==3) templated rule(s)
#
$(Targ)%a:
#echo Run Combo_a $(Targ) $(Param1) $(Param2) $#
$(Targ)%b:
#echo Run Combo_b $(Targ) $(Param2) $(Param1) reversed $#
$(Targ)%c:
#echo Run Combo_c $(Param1) $(Targ) $(Param2) mixed again $#
#
#Enumerate "M" (==2) sets of parameters,
# (Except if we are already recursing because unrecognised targets may cause
# it to descend forever)
#
ifneq ($(Recursing), Yes)
Set1%:
#$(MAKE) Targ=Set1 Param1=foo Param2=bar Recursing=Yes $#
Set2%:
#$(MAKE) Targ=Set2 Param1=ray Param2=tracing Recursing=Yes $#
endif
This then allows N*M different combos for N+M typing cost.
eg. (removing messages from make re recursion)
>make Set1.a
Run Combo_a Set1 foo bar Set1.a
>make Set2.c
Run Combo_c ray Set2 tracing mixed again Set2.c

In makefile, how to store multi-line shell output in variable

I have a shell command where it outputs multiple lines. I want to store it in a variable in makefile for later processing in the target.
A simplified example:
I have this file called zfile1
#zfile1
some text
$echo 123
more text
$$$#&^$
more text
The makefile:
a:
#$(eval v1 = $(shell cat zfile1))
# need to process the variable here, example:
#echo "$(v1)"
# I want to prevent expansion of values in the file and print in multi-line
If you have GNU make 4.2 or above you can use the $(file <zfile1) function. See https://www.gnu.org/software/make/manual/html_node/File-Function.html
If you don't have a new-enough version of GNU make, you can't do it. Of course in your example there's no real need to put the contents of the file into a make variable at all: you can just have your recipe use the file itself. But maybe your real use-case isn't so simple.
ETA
You should never use either the make function eval or the make function shell in a recipe [1].
You can just write:
v1 := $(file <zfile1)
.ONESHELL:
a:
#echo "$(v1)"
You must have the .ONESHELL because otherwise each line of the recipe (after it expands into multiple lines) is considered a separate recipe line. Note that .ONESHELL is in effect for the entire make process so could cause other recipes to break if they rely on each line being invoked in a different shell.
Another option is to export the result into the environment, and use a shell variable like this:
export v1 := $(file <zfile1)
a:
#echo "$$v1"
There are probably better ways to do it but since you've only given us this example and not what you really want to do, that's about all we can say.
[1] There are times where it can be useful but if you have a sufficiently sophisticated requirement to need this you'll probably know how to use it.
I think you're making things too complicated.
Start by writing your recipes as proper self-contained shell scripts.
You can then either store the whole script in a file and run it from make, or you can include it directly in your makefile as a single logical line, as in the following:
a:
#v1=$$(< zfile1); \
echo $$v1
Note the need to "escape" the dollar sign by repeating it.
You could also use global make variables, depending on the actual logic of your real-world use.

Makefile expanding variables inside conditionals depends on order of definition

I want to define a variable differently depending on another variables value in a makefile. I thought using conditionals would solve the problem, like this in the makefile:
ifeq ($(BOOT_FLAG),installed)
BOOT_TEST=$(BOOT_FLAG)
else
BOOT_TEST=no
endif
BOOT_DEFINE=$(BOOT_FLAG)
BOOT_FLAG=installed
.PHONY: all
all:
#echo $(BOOT_TEST)
#echo $(BOOT_DEFINE)
I expected the output to be:
installed
installed
but I got this instead:
no
installed
apparently the ifeq does not expand the BOOT_FLAG to installed
but setting of the BOOT_DEFINE variable manages to expand it correctly.
I read in the manual that:
"make evaluates conditionals when it reads a makefile. Consequently, you cannot use automatic variables in the tests of conditionals because they are not defined until commands are run"
but the BOOT_FLAG is not an automatic variable. Also if I move the definition of BOOT_FLAG to before the ifeq, then it works as I want it. However, I want to keep the current order of the definitions (and I don't understand why make does an exception to the order independence of the definitions when using conditions)
The answer is right there in the statement you quoted:
make evaluates conditionals when it reads a makefile.
Since make has evaluated the conditional when it read that line in the makefile, and the variable has not been defined when it read that line, there's no way that variables set after the conditional can take effect.
Just because the documentation lists one consequence of this behavior (the one that most people get confused by) doesn't mean that this is the only consequence of this behavior.
However, I want to keep the current order of the definitions
You can't.
(and I don't understand why make does an exception to the order independence of the definitions when using conditions)
It would be virtually impossible, and even if it could be done the resulting behavior would be almost indecipherable except in the most trivial situations. If you don't believe me, try to write down an algorithm describing how that could work. Remember to consider things like simple variable assignments, nested conditionals, variables used in target and prerequisite lists, variables that are intentionally reset in different parts of makefiles, etc.
ETA You could do it, by putting the ifeq into a define variable then using eval later, after BOOT_FLAG is set, to expand it. Seems gross to me but...
This is because makefile is evaulating the ifeq as it parses the file.
So when it gets to the ifeq..., then BOOT_FLAG is yet not set, so BOOT_TEST = no
Then you set BOOT_FLAG.
Then once all the variables are parsed, makefile will go through and run your rule - so in this case BOOT_DEFINE is evaluated to $(BOOT_FLAG) final value of installed
Try this:
$(info start - BOOT_FLAG=$(BOOT_FLAG))
ifeq ($(BOOT_FLAG),installed)
BOOT_TEST=$(BOOT_FLAG)
else
BOOT_TEST=no
endif
$(info after if - BOOT_FLAG=$(BOOT_FLAG))
BOOT_DEFINE=$(BOOT_FLAG)
BOOT_FLAG=installed
$(info after assignment - BOOT_FLAG=$(BOOT_FLAG))
.PHONY: all
all:
#echo $(BOOT_TEST)
#echo $(BOOT_DEFINE)
You will see various values printed at different times during the makefile parsing. On the first pass it evaluates the variables (and if's) and then on the second pass it can do the target rules.
As others noted the problem is that ifeq is expanded and evaluated in-place.
If you want to postpone the evaluation until some late moment, you must keep the whole expression inside of a recursive variable. Then the conditional could be implemented by $(if ...) function, instead of ifeq (okay, $(eval ifeq...) should also be doable, but... well, gross).
Of course, this is quite an overhead for such simple case, but nonetheless it could be done like this:
BOOT_TEST=$(if $(subst _installed,,_$(BOOT_FLAG)),no,installed)
BOOT_DEFINE=$(BOOT_FLAG)
BOOT_FLAG=installed
.PHONY: all
all:
#echo $(BOOT_TEST)
#echo $(BOOT_DEFINE)

How to find out the definition of the variable MAKE

Haven't been using make for a while. But just got a project from a 10 years old compiler using Ubuntu.
I am looking at the makefile and trying to find out which compiler it is using.
${MAKE} is used in the file.
But where can I find out the definition of MAKE.
Thanks
You could simply use both the info and value built-in functions inside your makefile:
$(info MAKE: $(value MAKE))
This will work if MAKE is a recursively expanded variable, which it is by default. Otherwise, if MAKE were a simply expanded variable, you will see the expansion that was done at the moment of evaluating MAKE's definition (i.e., the same as $(MAKE)).
A better approach, which is independent of the flavour of the variable, would be to run make with the option -p and look at the definition of MAKE, e.g.:
make -p | grep 'MAKE ='
You will probably find out that MAKE is defined as:
MAKE = $(MAKE_COMMAND)
and MAKE_COMMAND, which is another variable (this time, a simply expanded one), may be in turn defined as:
MAKE_COMMAND := make

Resources