I would like to redefine MAKE := $(MAKE) --warn-undefined-variables and this works but only for the first level.
When the first level invokes another makes, this definition is not kept.
How could I do this?
Variable values of the top-level make can be passed to the sub-make through the environment by explicit request.
These variables are defined in the sub-make as defaults, but they do not override variables defined in the makefile used by the sub-make unless you use the ā-eā switch
To pass down, or export, a variable, make adds the variable and its value to the environment for running each line of the recipe. The sub-make, in turn, uses the environment to initialize its table of variable values.
As a convenience, you can define a variable and export it at the same time by doing:
export variable := value
or
variable := value
export variable
https://www.gnu.org/software/make/manual/html_node/Variables_002fRecursion.html
Related
I know that I can pass variables to a Makefile in two ways:
make <target> FOO=bar
export FOO=bar
make <target>
and that both of these will make a variable FOO available in the makefile with value bar.
However, is there a way to require that the variable only comes from the command line? I want the passing of variables to be explicit in order to avoid certain potential overlaps of environment variables, so I want to ensure make only receives the variable if passed from the command line, and to disregard it if it's only set in the environment so that the value it uses must be defined by the user when calling make.
EDIT: I realize after researching it a bit more that environment variables are not actually accessed the way I thought they are, they're actually used within make as ${FOO} so as long as I don't define FOO at any point in the makefile, its only set value will be from the command line (as noted in this answer).
These methods to pass variables to GNU make aren't equivalent.
Variables that come from the environment don't override the assignments in makefile, unless make is invoked with -e option. See variables from the environment. This is because depending on environment variables is poor practice in terms of build reproducibility (someone forgets to set the environment variable and the build is different):
It is not wise for makefiles to depend for their functioning on environment variables set up outside their control, since this would cause different users to get different results from the same makefile. This is against the whole purpose of most makefiles.
Variables that come from make command line do override assignments in makefile, unless override is specified. See the override directive.
Hence, the recommended practice is to explicitly set all your variables to their default values in the makefile unconditionally, so that only the assignments from the command line override them.
As far as I know, there's no difference between ${FOO} and $(FOO), regardless of the way FOO is defined.
If you're using GNU make, there's a function origin that allows you to make the distinction: it will return command line for a variable defined on the command line and environment for a variable exported by the environment (more info in the manual)
with the following Makefile:
foo ?= foo
default:
#echo ${foo}, comes from $(origin foo)
make prints foo, comes from file
make foo=bla prints bla, comes from command line
(export foo=bar; make) prints bar, comes from environment
expanding on #Virgile answer, you could add the following kind of check at the start of the makefile. It is a lot to repeat for each variable you wish to check, although all such checks could reside in a dedicated makefile that is then included from main makefile
foo ?= foo
# check origin
ifdef foo
ifneq "$(origin foo)" "command line"
$(error foo: must come from command line)
endif
else
$(error foo not defined)
endif
default:
#echo ${foo}, comes from $(origin foo)
I can pass a variable to child make using make -C VAR=$(VAR) target (preferring this to export because the variable is only meaningful for a single target). However, it appears that if VAR variable isn't defined in parent, child gets an empty string (it has VAR ?= ..., and the value of VAR is empty). How can I avoid this?
It looks like a variation on Define make variable at rule execution time should work for my specific usecase, but it requires the parent Makefile to know child's default value, which I want to avoid.
You should always use $(MAKE) when invoking sub-makes, never make explicitly. Also you're missing a directory after the -C in your examples.
What about something like:
$(MAKE) $(if $(VAR),VAR=$(VAR))
From the docs:
Target-specific variables have the same priority as any other
makefile variable. Variables provided on the command line (and in the
environment if the '-e' option is in force) will take precedence.
Specifying the 'override' directive will allow the target-specific
variable value to be preferred.
So, a simple makefile, like:
# A pattern-specific variable assignment.
% : foo += file
all : x ;
# Target is a double-colon w/o dependencies, so Make will ALWAYS run its commands.
x ::
#echo '$(foo)'
Running, we get:
# Override makefile-level variables, with a command-line assignment.
$ make foo=cmd
cmd cmd cmd
# Set the value in the environment, And tell Make to prefer it over any makefile-level definitions.
$ foo=env make --environment-overrides
env file file
Returning now, to the quote above, from the documentation:
Variables provided on the command line (and in the environment if the '-e' option is in force) will take precedence.
It seems, that using either:
Command-line assignment.
Environment-set variables, AND using -e (--environment-overrides).
Have both the same effect, i.e. overrides the file-level (makefile) variable.
But, the results differ greatly. Remember that the value given in the command-line was: cmd, and the value given in the environment was: env.
Now, compare the values, given for a command-line override vs. an environment override:
cmd cmd cmd (for command-line override).
env file file (for environment override).
So, whereas for command-line, Make repeats the same value, i.e. cmd, 3 times, for environment-override, the situation is different. That is, Make will "repeat" the environment-level value: env only 1 time, and then repeats - none other - than the overridden file-level value: file.
Now, not only is the situation completely different for an "override" from command-line vs. an "override" from the environment, which is strange by itself, the problem here is much bigger.
Since, Make rules to give "priority" for a command-line (or environment) value, why does it insist to append "other" values (as in the case of environment-override, where Make appends "file file"), or in the case of a command-line override (where Make repeats the same value ***3* times). Seriously?
How does it make sense at all? And what is the justification for these inconsistent and strange results?
I believe the answer here is related to the answer to this other question of yours. (And possibly a bug in the env override version.)
The global variables and the target-specific variables are distinct variables.
The cmd cmd cmd result is because when you write %: foo += file make stores that as an addition to the current value of variable foo of the target-specific value of the variable foo which is file.
However, when you set foo on the command line make overrides the value of the target-specific variable foo to be cmd instead of file. So when make concats the variable each time it gets cmd cmd cmd.
That explanation should, I think, get you env env env then and I'm not sure why it doesn't. This could be a bug or it could be some other detail about how env override variables and target-specific variable values work. I'm not sure.
(Check the output of make -p for both these cases to see what I mean about the target-specific variable's value.)
In my makefile I have a variable FOO:
FOO = /path/to/bar
Is it possible to overwrite this variable during the makefile call? Somthing like the following:
FOO=/path/to/foo make all
Specify them as Var=Value before you specify the target, like make FOO=/path/to/foo all.
$ cat Makefile
Foo = asdf
all:
echo $(Foo)
$ make all
echo asdf
asdf
$ make Foo=bar all
echo bar
bar
The ways that variables get assigned values is specified in the How Variables Get Their Values section of the GNU make Manual.
Variables can get values in several different ways:
You can specify an overriding value when you run make. See Overriding Variables.
You can specify a value in the makefile, either with an assignment (see Setting Variables) or with a verbatim definition (see Defining Multi-Line Variables).
Variables in the environment become make variables. See Variables from the Environment.
Several automatic variables are given new values for each rule. Each of these has a single conventional use. See Automatic Variables.
Several variables have constant initial values. See Variables Used by Implicit Rules.
So, as Colonel Thirty Two indicates, you can override variables set in the makefile on the command line.
You can also, if you expect this to be a variable that you want to set persistently, use the ?= assignment and then environment values for that variable will be used.
I have a makefile that runs some other make target by first setting some variables:
make -C somedir/ LE_VAR=/some/other/stuff LE_ANOTHER_VAR=/and/so/on
Now I need to unset LE_VAR (really unset, not just override the value with "").
Is there any way to do it so on GNU Make 3.81?
Thanks!
Assuming your makefile contains something like this to invoke a sub-make:
submake:
$(MAKE)
You need to modify the magical variable MAKEOVERRIDES, like this:
MAKEOVERRIDES := $(filter-out LE_VAR=%,$(MAKEOVERRIDES))
unexport LE_VAR
submake:
$(MAKE)
Check this out unexport variable.
From gnu manual
export variable
export variable-assignment
unexport variable
Tell make whether or not to export a particular variable to child processes
Refer https://www.gnu.org/software/make/manual/html_node/Quick-Reference.html
Thank you very much for your replies, this was quite tricky.
When executing make, and setting vars in the parameters, like:
make -C le/path install ONEVAR=one OTHERVAR=two
We have both ONEVAR and OTHERVAR on the env and the subtasks ran by the first command. This kind of puzzled me because I added to the task (at le/path) to execute a simple bash script that only did:
echo $ONEVAR
unset ONEVAR
And by my surprise the var $ONEVAR was actually "one" (so it was on the env) and the unset actually cleared it. But, adding an "echo $(ONEVAR)" on the makefile still outputs "one".. This is due to MAKEOVERRIDES, and in fact, as suggested by Communicating Options to a Sub-make:
The command line variable definitions really appear in the variable
MAKEOVERRIDES, and MAKEFLAGS contains a reference to this variable. If
you do want to pass flags down normally, but don't want to pass down
the command line variable definitions, you can reset MAKEOVERRIDES to
empty, like this:
MAKEOVERRIDES =
Or as MadScientist suggested above :)
But this was not enough, since this var was still being passed to the other subtasks below (in this situation some nodejs modules that were being compiled on a local folder, and by bad luck, both a js file from phantomjs and some other makefiles where using a var with the same name (e.g., $ONEVAR).
unexport variable Tell make whether or not to export a particular
variable to child processes.
GNU Make Appendix A Quick Reference
What I did was:
DESTDIR_BUFFER=$(DESTDIR)
MAKEOVERRIDES := $(filter-out DESTDIR=%,$(MAKEOVERRIDES))
unexport DESTDIR
And only then make npm install.
At the end of this task I export DESTDIR with the value at DESTDIR_BUFFER and all the other consequent tasks still work.
Thanks a lot for your help!