I'm new to make. I want a condition inside my Makefile, followed by unconditional steps.
According to the Make manual, this should be possible:
Every conditional must end with an endif. Unconditional makefile text follows.
The following code works, but does not assign the new value
a = something
b =
build:
ifeq ($(a),)
b=yes
else
b=no
endif
$(info what_is_b=$(b))
@echo $(b)
@echo "end of condition"
This code does work, but exits with an error and the two echo
statements are skipped (same goes for info
when a tab is added):
a = something
b =
build:
ifeq ($(a),)
b=yes
else
b=no
endif
$(info what_is_b=$(b))
@echo $(b)
@echo "end of condition"
Results in:
$ make build
what_is_b=no
Makefile:11: *** recipe commences before first target. Stop.
What is wrong here?
Also while searching, I could only find examples where endif
is followed by another if...
but never by unconditional steps.
I'm using GNU Make 4.4
Note: My previous answer was a little lackluster. Rather than performing a rip & replace on it, I deleted it and started fresh.
The following code works, but does not assign the new value
a = something b = build: ifeq ($(a),) b=yes else b=no endif $(info what_is_b=$(b)) @echo $(b) @echo "end of condition"
The contents of a recipe is shell script, and, ordinarily, each line is executed in its own shell. Supposing that the assignments are indented with at least one leading tab, they are shell variable assignments, and
$(b)
expand a make
variable of that name, which would not be the same thing even in the shell where the assignment was performed.So yes, supposing correct tab indentation, which does not carry through in the Stack Overflow medium, I would expect GNU make
to accept that makefile and to be able to build target build
based on it, and in doing so, to report that make
variable b
expands to an empty string.
This code does work, but exits with an error and the two
echo
statements are skipped (same goes forinfo
when a tab is added):a = something b = build: ifeq ($(a),) b=yes else b=no endif $(info what_is_b=$(b)) @echo $(b) @echo "end of condition"
I guess by "does work" you mean that the $(info)
function prints a non-empty value for variable b
, notwithstanding that make
terminates with an error and the two echo
s are not executed.
In the makefile text as modified according to any conditional processing and line-joining, make
recognizes the recipe for a rule as all the zero or more immediately following tab-indented lines. The first line not indented with a tab terminates the recipe. In this version of the makefile, that's whichever of the b=yes
or b=no
emerges from the conditional. And because that's not a recipe line, it is a make
variable assignment, not a shell variable assignment, which your $(info)
can observe.
That also strands the two echo
commands. make
recognizes from their leading tabs that you mean for them to be part of a recipe, but they are not, the recipe for build
having been terminated, with zero lines, by the assignment to b
. make
aborts makefile parsing at that point, without attempting to build any target. However, because the $(info)
appears outside a recipe, it has already been expanded at that point, causing its output to be emitted.
What is wrong here?
Different things in your two examples, as already discussed:
make
variables and shell variablesOverall, I suspect you have a flawed mental model of makefiles and make
's execution. Makefiles are not scripts. make
rules are not functions. You cannot use a rule to set or change make
variables in a way that will be visible after the rule has run.
GNU make
does implement numerous extensions, some of which push the boundaries of the above. The conditionals you are using and the $(info)
function are among them. I generally recommend avoiding GNU extensions, or at least minimizing their use, but it's possible that one or another GNU extension would provide a clean and simple solution to whatever the real problem is that you want to solve.
As for the example code, however, if a make
variable is what you want to use, then you probably want this:
a = something
ifeq ($(a),)
b = yes
else
b = no
endif
build:
$(info what_is_b=$(b))
@echo $(b)
@echo "end of condition"
If you want a shell variable, on the other hand, then it would probably be better to use shell features for the conditional logic too. For example:
a = something
build:
@a='$(a)'; b=$${a:+no}; echo $${b:-yes}
@echo end
(The appearances of $$
represent an escaped $$
to make
. A single $
is passed through to the shell.)
Also while searching, I could only find examples where
endif
is followed by anotherif...
but never by unconditional steps.
The GNU conditionals in your examples are just the context for your issue. They do not play a direct role in it. There is no particular reason unconditional recipe lines could not follow conditional-guarded ones.