Set variable in a rule and have it available in another rule - makefile

I've the following makefile:
.ONESHELL:
SHELL := /bin/bash
build-%:
#[ $(findstring -, $*) ] && DIR_ENV=$(subst -,/,$*) || DIR_ENV=$*
#echo ${DIR_ENV}
I'm trying to have available the DIR_ENV but without no luck. I know that every command executed is executed in its own shell so no sharing of variavbles. However I've added ONESHELL directive. But it still doesn't work. What am I'm missing?

You are missing the fact the string ${DIR_ENV} is evaluated by make first and the resulting value is a null string. Use this
build-%:
#[ $(findstring -, $*) ] && DIR_ENV=$(subst -,/,$*) || DIR_ENV=$*
#echo $${DIR_ENV}
Also the Make syntax
$(if $(findstring -,$*),$(subst -,/,$*),$*)
Does the if/then/else logic in Make, not the shell

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.

gmake: How to assign global variable from shell command?

I have a makefile that executes some shell command and I want to store the output to a global variable:
GLOBVAR = a
all:
GLOBVAR=$(shell echo 'X')
$(info $(GLOBVAR))
GLOBVAR is empty. What am I doing wrong?
You are mixing up make and shell variables. In GLOBVAR=$(shell echo 'X') it is a shell variable that you assign, while in $(info $(GLOBVAR)) it is a make variable that you expand.
Try this, instead:
GLOBVAR = $(shell echo 'X')
all:
$(info $(GLOBVAR))
But there are several other issues with your Makefile that you should probably consider.
Using $(shell...) in recipes is not recommended because recipes are already shell scripts. So, if you want to assign a shell variable in a recipe, just:
all:
GLOBVAR="$$(echo 'X')"
Note the $$ to escape the expansion that make performs before passing the recipes to the shell.
The different lines of the recipe are executed in different shells. So, if you want to use in a line a shell variable that was assigned in a previous line you must join them:
all:
GLOBVAR="$$(echo 'X')"; echo $$GLOBVAR
(same remark as before about $$). You can use line continuation if you prefer:
all:
GLOBVAR="$$(echo 'X')"; \
echo $$GLOBVAR
And finally, if you want to assign make variables in recipes you can, with the eval make function, but I strongly discourage you to do so until you perfectly understand when make does what:
$ cat Makefile
.PHONY: all lla
all:
$(eval GLOBVAR = $(shell echo 'X'))
#echo all: $(GLOBVAR)
lla:
#echo lla: $(GLOBVAR)
$ make all
all: X
$ make lla
lla:
$ make all lla
all: X
lla: X
$ make lla all
lla:
all: X
And I let you imagine what the results could be with parallel make... In summary, if you start using make functions in recipes you are probably wandering into dangerous areas.

Need to check existence of flag in GNUmakefile

I am checking for existence of flag that is passed by user to GNUmakefile.
Basically, i am checking whether user has passed -j in my makefile. I have added below if condition. But before that i am trying to display MAKEFLAGS where i can see output is empty for that variable.
ifneq (,$(findstring j,$(MAKEFLAGS)))
....
Am i missing anything here?
Sometimes users may also pass --jobs instead of -j , And also i need to check whether the value passed to -j/--jobs is greater than 2
Is there any easy way in GNUmake for doing so in single if condition ?
The answer to your question depends on what version of GNU make you're using.
If you're using GNU make 4.1 or below, then the answer is "no, it's not possible" from within a makefile (of course you can always write a shell script wrapper around make and check the arguments before invoking make).
If you're using GNU make 4.2 or above, then the answer is "yes, it's quite possible". See this entry from the GNU make NEWS file:
Version 4.2 (22 May 2016)
The amount of parallelism can be determined by querying MAKEFLAGS, even when
the job server is enabled (previously MAKEFLAGS would always contain only
"-j", with no number, when job server was enabled).
This is a tricky question because MAKEFLAGS is a very strange make variable. First of all, with GNU make 4.3, -jN, -j N, --jobs N and --jobs=N are all converted to -jN in MAKEFLAGS, which looks interesting. You could thus try something like:
J := $(patsubst -j%,%,$(filter -j%,$(MAKEFLAGS)))
to get the N value passed on the command line or the empty string if -j and --jobs have not been used. But then, if you try the following you will see that it is not the whole story:
$ cat Makefile
.PHONY: all
J := $(patsubst -j%,%,$(filter -j%,$(MAKEFLAGS)))
ifneq ($(J),4)
all:
#echo MAKEFLAGS=$(MAKEFLAGS)
#echo patsubst...=$(patsubst -j%,%,$(filter -j%,$(MAKEFLAGS)))
#echo J=$(J)
else
all:
#echo J=4
endif
$ make -j4
MAKEFLAGS= -j4 -l8 --jobserver-auth=3,4
patsubst...=4
J=
Apparently MAKEFLAGS is not set when the Makefile is parsed (and the J make variable is assigned the empty string) but it is when the recipes are executed. So, using MAKEFLAGS with conditionals does not work. But if you can move your tests in a recipe, something like the following could work:
.PHONY: all
all:
j=$(patsubst -j%,%,$(filter -j%,$(MAKEFLAGS))); \
if [ -n "$$j" ] && [ $$j -gt 2 ]; then \
<do something>; \
else \
<do something else>; \
fi
Or:
.PHONY: all
J = $(patsubst -j%,%,$(filter -j%,$(MAKEFLAGS)))
all:
#if [ -n "$(J)" ] && [ $(J) -gt 2 ]; then \
<do something>; \
else \
<do something else>; \
fi
Note the use of the recursively expanded variable assignment (J = ...) instead of simple assignment (J := ...).

If Else syntax evaluating wrong clause in GNU Makefile

I am trying to get if else syntax working in Makefile
TYPE=src
RTL=src
program_%:
ifeq ($(TYPE),$(RTL))
echo "RTL"
else
echo "Test"
endif
Here is the command line
$make -f test.make prog_src
and I get the following in the output
echo "RTL"
RTL
However, when i change the if statement such that instead of hard-coded variables I do something like
program_%:
ifeq ($(TYPE),$*)
echo "RTL"
else
echo "Test"
endif
and run as follows
$make -f test.make prog_src
I get the wrong clause evaluated
echo "Test"
Test
remove the whitespaces in the condition
ifeq ($(TYPE),$(RTL))
Regarding the second part of the question:
No; this is not possible ifeq is evaluated in the context of the makefile and $* is not expanded there. You can try $(if ...) but checking equality is not directly possible:
program_%:
#echo $(if $(filter ${TYPE},$*),"RTL","Test")
"Context of makefile" means that if* sees only global make variables. Special variables like $* are available in the rule context only.
Complex rules
complex rules are possible by
define complex_rule_A
#echo "complex_rule_A"
#echo "done"
endef
define complex_rule_B
#echo "complex_rule_B"
#echo "failed"
endef
program_%:
$(if $(filter ${TYPE},$*),$(call complex_rule_A),$(call complex_rule_B))
Alternative solutions
When ${TYPE} can be enumerated, a more clean solution might be
program_%: .program_${TYPE}_%
:
.program_RTL_%:
echo else
.program_RTL_RTL:
echo RTL

How does $# work in a make conditional?

I'm using GNU Make 3.80. In my Makefile, I use automatic variable $# to refer to the current target.
#echo current target is ... [$#]
ifeq ($#,sms)
#echo yep, they are equal
else
#echo no, they are not equal
endif
It seems that $# expands to sms, as shown in the output below.
Output is:
current target is ... [sms]
no, they are not equal
My question: since $# (apparently) expands to sms, shouldn't the "true" branch of the ifeq conditional be executed (with the consequence that the output should read yep, they are equal)? [I am at a loss as to why the output is no, they are not equal.]
From the GNU Make manual:
10.5.3 Automatic Variables
...
It's very important that you recognize the
limited scope in which automatic
variable values are available: they
only have values within the recipe.
... there is a special feature of GNU
make, secondary expansion (see
Secondary Expansion), which will allow
automatic variable values to be used
in prerequisite lists.
That is, $# can only be used inside the section containing commands to build the target and, with some restrictions, inside the prerequisites list.
However you can use shell commands to implement conditions inside the list of commands used to build the target:
#echo current target is ... [$#]
if [[ "$#" == "sms" ]]; then \
echo yep, they are equal; \
else \
echo no, they are not equal; \
fi
Also if you want to check what targets were specified on the command line use MAKECMDGOALS variable.

Resources