Getting exit code of shell execution inside define - shell

I have something like this in my makefile:
exit_code := $(shell some_script.py; echo $$?)
ifneq ($(exit_code),0)
$(error Error occured)
endif
and it works properly - the echo $$? returns exit code of python script
I need to put that code into define like that:
define run-python-script
exit_code := $(shell some_script.py; echo $$?)
ifneq ($(exit_code),0)
$(error Error occured)
endif
endef
$(call run-python-script)
but then exit_code does not contain the exit code. And $(error Error occured) is always executed.
How to make work the version with define?

I need to put that code into define like that:
The if()/endif are processed and evaluated when the Makefile is parsed. You can't use them inside a variable definition.
The exit_code := ... in first snippet is a definition of a make variable, while in the second it is just a string, part of the make's variable called run-python-script.
You can try this (your script is replaced with false for test purposes):
eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
the-command = false; echo $$?
run-python-script = $(if $(call eq,0,$(shell $(the-command))),,$(error Error occured))
$(run-python-script)
(The $(call) is redundant in that case.)

Related

Makefile conditional variable assignment

I wish to make the following target:
ifeq ("$(VARENV1)", "true")
VARENV3=$(VARENV2)
endif
dummy:
echo $(VARENV3)
I am then calling my makefile with
VARENV1=true VARENV2=test make dummy
expecting to get a display of "test".
But this fail as VARENV3 is empty.
However, if I use the following makefile:
ANYVARNAME=ANYVALUE
ifeq ("$(VARENV1)", "true")
VARENV3=$(VARENV2)
endif
dummy:
echo $(VARENV3)
Purpose is to override VARENV3 with VARENV2 in some condition.
Then I will correctly display "test".
I am bit confused at why, any clue?

Makefile expanding variable inside define

define func1
include $(shell pwd)/test/$(strip $1)/component.mk
$(info :::::::${NAME} ::::::::::::::: )
endef
INCLUDES := a b c
$(foreach dir, $(INCLUDES), $(eval $(call func1, $(dir)) ))
all : $(objs)
Contents of each makefile:
cat test/a/component.mk
NAME := AA
cat test/b/component.mk
NAME := BB
cat test/c/component.mk
NAME := CC
Output is
::::::: :::::::::::::::
:::::::AA :::::::::::::::
:::::::BB :::::::::::::::
It looks like first time NAME is empty.
Let's look at the expansion of $(foreach dir, ${INCLUDES}, $(eval $(call func1, ${dir}) )) in painful detail.
${INCLUDES} is expanded, giving $(foreach dir,a b c,$(eval $(call func1,${dir})))
Next dir is set to a
$(call func1,a) is expanded
1 is set to a
func1 is expanded:
include $(shell pwd)/test/$(strip $1)/component.mk
$(info :::::::${NAME} ::::::::::::::: )
$(shell pwd) becomes HERE, say (N.B. Use ${CURDIR} instead)
$(strip $1) becomes $(strip a) becomes a
${NAME} expands to nothing
$(info ::::::: ::::::::::::::: ) expands to nothing
As a side effect ::::::: ::::::::::::::: appears on stdout
$(eval $(call func1,a)) expands to $(eval include HERE/test/a/component.mk), expands to nothing
As a side effect, the include is processed by make
Presumably HERE/test/a/component.mk exists and contains valid make syntax,
and the variable NAME gets a value.
1 is set to b. Lather, rinse, repeat.
Tip
To get a hint of problems in code like this, always run make with --warn:
$ make --warn -Rr
Makefile:8: warning: undefined variable 'NAME'
::::::: :::::::::::::::
⋮
Fix
To get some insight, replace the $(eval stuff) with $(error [stuff])
$ make
::::::: :::::::::::::::
Makefile:8: *** [ include /cygdrive/c/Users/somewhere/a/component.mk
]. Stop.
Here we see the $(info …) has disappeared even before it has got to the eval.
The naive fix is pretty horrible.
define func1
include $(shell pwd)/test/$(strip $1)/component.mk
$$(info :::::::$${NAME} ::::::::::::::: )
endef
Running this with the $(error …) in place gives
$ make
Makefile:8: *** [ include /cygdrive/c/Users/somewhere/a/component.mk
$(info :::::::${NAME} ::::::::::::::: )]. Stop.
That stuff between the [ and ] is valid make syntax.
Tidied up it looks like:
include /cygdrive/c/Users/somewhere/a/component.mk
$(info :::::::${NAME} ::::::::::::::: )
Job done. There are cleaner ways, but you need to understand the pain first!

GNU make: ifeq inside a call function

I want to write a call function something like this
define run
#echo "running" > $1
ifeq ("$(var)" "var1")
#echo "var1" > $1
else
#echo "var2" > $1
endif
endef
Problem is I am not able to figure out from make documents how to use ifeq inside a call function, or if there is any better solution than using ifeq.
Putting a conditional in a canned sequence of commands is a pain. It's much easier to resolve the conditional outside:
define run
#echo "running" > $(log)
#echo $(var) > $(log)
endef
targ: log := 23B.log
targ: var := var1
targ:
$(run)

conditional execution in makefile

I have the following makefile:
C_FILE=""
cfg:
## C to CFG ####
# echo $(C_FILE)
ifndef C_FILE
$(error variable C_FILE not set)
endif
$(eval CFG_FILE := ./outputs/temp/$(shell basename $(C_FILE) .c).cfg)
gcc -fdump-tree-cfg=$(CFG_FILE) $(C_FILE)
When I run the command make cfg C_FILE="./inputs/Fib.c" it always
terminates saying variable C_FILE not set.
Lines beginning with a tab character (by default) aren't parsed by make (other than for variable expansion), they're sent directly to the shell, get rid of the indents on the lines with the make conditionals
C_FILE=""
cfg:
## C to CFG ####
# echo $(C_FILE)
ifndef C_FILE
$(error variable C_FILE not set)
endif
$(eval CFG_FILE := ./outputs/temp/$(shell basename $(C_FILE) .c).cfg)
gcc -fdump-tree-cfg=$(CFG_FILE) $(C_FILE)
I'd like to add some comments to user657267's answer.
ifndef C_FILE is always false. C_FILE is defined on the first line, or from command line. Consider using ifeq "" "$(C_FILE)".
Quote (") is normal character in makefile. Define empty variable this way:
C_FILE=
instead of using $(shell ) function, use makefile built-ins:
$(basename $(notdir $(C_FILE)))
avoid using $(eval ) if not really needed. Extract relevant code outside recipe.
My proposal is:
C_FILE=
ifeq "" "$(C_FILE)"
$(error variable C_FILE not set)
endif
CFG_FILE=./outputs/temp/$(basename $(notdir $(C_FILE))).cfg
cfg:
## C to CFG ####
# echo $(C_FILE)
gcc -fdump-tree-cfg=$(CFG_FILE) $(C_FILE)

Test if a list of variables are defined in a Makefile

My Makefile is based on multiple variables defined in a configuration file or ENV vars. My current solution is to test all of them manually:
NOGOAL = help clean distclean mrproper
ifeq ($(strip $(filter $(NOGOAL), $(MAKECMDGOALS))),)
VAR1 ?= $(error VAR1 undefined)
VAR2 ?= $(error VAR2 undefined)
VAR3 ?= $(error VAR3 undefined)
...
VARn ?= $(error VARn undefined)
endif
I would like to use a foreach loop instead:
ifeq ($(strip $(filter $(NOGOAL), $(MAKECMDGOALS))),)
TESTVAR = TEST1 TEST2 TEST3
$(foreach v, $(TESTVAR), $(eval $v ?= $$(warning Error: $v undefined)))
endif
Unfortunately eval doesn't work as I expected. Did I miss something?
Here a full test of my tests with 2 implementations of the tests. Even if TEST3 is not defined I don't get any error
TEST1 = 1
TEST2 = 1
#TEST3 = 1 # NOT DEFINED
TESTVAR := TEST1 TEST2 TEST3
# First implementation
$(foreach v, $(TESTVAR), $(eval $v ?= $$(warning Error: $v undefined)))
# Second implementation
$(foreach v, $(TESTVAR), $(eval $(call TESTER,$v)))
define TESTER
ifndef $1
$(warning $1 not defined)
endif
endef
# Dummy rule
all:
#echo Hello World
However, my first implementation works if I use $(TEST3) somewhere.
EDIT
Here I get no error but TEST3 is not defined:
~$ cat Makefile
TEST1 = 1
TEST2 = 1
#TEST3 = 1 # NOT DEFINED
TESTVAR := TEST1 TEST2 TEST3
# First implementation
$(foreach v, $(TESTVAR), $(eval $v ?= $$(warning Error: $v undefined)))
# Dummy rule
all:
#echo Hello World
~$ make
Hello World
Well, I guess I don't get it. Your original version, that you say works the way you want it, will not print any warnings unless you USE one of the variables which is not defined. Your first alternative with foreach works the same way: it will print a warning but only when you use the variable that's undefined.
If you want it that way, then testing for clean, etc. doesn't really make much sense since presumably those rules won't use the variables that are not defined so you won't get any errors (and if they did use the variables that weren't defined, presumably you'd want those rules to fail as well).
But in your second edit, you say that you want the make to fail immediately if the variables are not defined, regardless of whether or not they're used (in your last example you don't define TEST3, but you don't use TEST3 for anything either so no warning is printed). If that's what you want I don't see why you are assigning values to the variables with ?= at all, or using eval. Just write something like:
ifeq ($(strip $(filter $(NOGOAL), $(MAKECMDGOALS))),)
$(foreach v,$(TESTVAR),$(if $($v),,$(error Error: $v undefined))
endif
(In this version you do need to check MAKECMDGOALS since it fails immediately on an unset variable).

Resources