Can't override target-specific variable - makefile

I have such Makefile:
print: var1=inside
print:
#echo $(var1)
When I run var1=outside make -e print. I expected to see:
outside
Because of:
Variables provided on the command line (and in the environment if the ‘-e’ option is in force) will take precedence.
But I get:
inside
Is it a correct output?

It is expected; whether or not it's correct I guess depends on who you ask.
The -e option is in effect for "global" variables in makefiles, but target-specific variables take precedence over globally-scoped variables.
Personally I think -e was a mistake and never should have existed. But, you know, POSIX and all. Even though it exists I recommend that it be avoided. It's far, far too easy to completely mess up your build in confusing and surprising ways.
ETA Well, maybe it's not expected. Maybe it is a bug. Where did you get that quote you provided? With just that sentence from a 225-page document it's hard to say... a link or at least a section name would be helpful.

Related

Let make fail, if Makefile encounters undefined variable

I had a mistake in my Makefile:
verify-prettier:
$(PRETTIER) --check **/*.yaml **/*.yml
The var PRETTIER was not set.
The result:
check **/*.yaml **/*.yml
bash: line 1: check: command not found
make: [Makefile:156: verify-prettier] Error 127 (ignored)
I would like to get an error (which does not get ignored), if I accidentaly have an undefined variable.
Is there a way to configure make accordingly?
Version: GNU Make 4.3
We don't need to support other make implementations.
I know this, but I want it to fail, not just a warning.
MAKEFLAGS=--warn-undefined-variables
Update, since this seem to be unavailable up to now, I created a feature request: https://savannah.gnu.org/bugs/?63737
As said in comments there is no way as of current (4.3) GNU Make versions. And any addition is likely to take forever before reaching the general public (WSL still stuck at 4.2.1!!!).
I can think of a few crutches that might work depending on circumstances:
Pass --warn-undefined-variables to make, then pipe output to e.g. grep testing absence of the warning message. Suitable for build automation where command line length and complexity doesn't matter, less so for interactive use.
Have blocks of ifndef and error at the end of the makefile. You'll need a specific block for each variable, however this isn't too far off how unit tests work and those are generally accepted :-)
Default initialize to something that will fail. In the case of $(PRETTIER) you could initialize it to false, silently eating any arguments and returning failure. Feasibility depends on how the variable is used. Works well for a variable used as a command, might get trickier for arguments though I'm sure some hack ought to work (because how hard can it be to intentionally write crashing code).

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)

debugging Makefile how to suppress silence

Is there a way to suppress the silence '#' in the rules?
I want to see what the Makefile does but all rules from a given Makefile are defined with the '#' in front like:
go:
#do something here...
I search for an option to see the output as # was not there.
You can run make with the -n flag. You will be able to see which commands are executed. Without actually executing them though!
From GNU make manual:
When make is given the flag ‘-n’ or ‘--just-print’ it only echoes most
recipes, without executing them. See Summary of Options. In this case
even the recipe lines starting with ‘#’ are printed. This flag is
useful for finding out which recipes make thinks are necessary without
actually doing them.
Aside from using -n as suggested you could (if the makefile is under your control to edit) look at using something like my automake-inspired silent make rules stuff.
Which lets you override the silencing at make run time dynamically.
You might be willing to do this:
At the top of the makefile, put silence ?= #
Change all the #-prefixes to $(silence).
Then make will observe the silences by default and make silence= will
be verbose.
If you don't want to do that, there's a pretty good chance that:
cat Makefile | sed 's/\t#/\t/g' | make -f -
will do the trick.

GNU Makefile "preprocessor"?

Is there an option to output the "preprocessed" makefile, something equivalent to the GCC's -E option?
I have a project comprised of an hierarchy of dozens of modules, each with its makefile. The build is invoked from a master makefile. That master makefile contains includes, variable definitions, command line option dependent variables, etc.
So, essentially, I am looking for the processed makefile, including all substitutions.
Not that I'm aware of. The closest thing you can get to this is the output from make -qp (or similar) which will dump the make database out at you.
Part of the problem with this request is that many of the substitutions/etc. happen as targets are processed and the list of targets isn't necessarily known without actually attempting a build (at least to an extent) so it isn't necessarily possible to fully expand/etc. a makefile in-place.
The make -d output is also useful for certain incidental information related to how make has processed the makefiles but doesn't contain makefile contents directly.
Remake might also be able to provide some extra useful information.
If you are looking for the computed value of some assembled/etc. global make variable then this blog post by Eric Melski is likely to be very helpful.
tl;dr It adds a target like this to the Makefile (though there's more magic in the blog post so I suggest reading it).
print-%:
#echo '$*=$($*)'
#echo ' origin = $(origin $*)'
#echo ' flavor = $(flavor $*)'
#echo ' value = $(value $*)'
Though in personal use I replaced that first line with something more like this
#echo '$*=$(subst ','\'',$($*))'
to keep the quoting of the result correct.

How to set default to -j2 in Makefile?

I would like to have make option "-j2" as the default.
Can I modify Makefile for that?
Looking at the GNU Make manual (3.82), there is nothing I can see that allows that.
You might be able to set environment variable MAKEFLAGS (to either '-j 2' or perhaps 'j 2'), but otherwise, it appears you cannot.
As mentioned previously one can set the environment variable MAKEFLAGS. But this apparently works even inside a makefile (at least with GNU make). If you add a line
MAKEFLAGS=-j 2
at the top of the makefile this should give you the desired results. I have not tested this thoroughly and maybe it does only work with recursive invocations, but that could be easily worked around with a wrapper target.
I have used this to prevent make from printing the "Entering directory"/"Leaving directory" messages in recursive executions by setting MAKEFLAGS=-s.

Resources