Setting a variable in Makefiles through the command line [duplicate] - bash

I am trying to do a simple thing:
TMPDIR ?= /tmp
test:
#echo $(TMPDIR)
This works if I run:
$ make test
/tmp
It also works if I run:
$ make test -e TMPDIR=~/tmp
/home/user/tmp
What can I do to also have it works for:
$ TMPDIR=~/tmp make test
/home/user/tmp

To follow up on my comments above, here's an example:
T ?= foo
all:
: '$(T)'
Now if I run the Makefile in various ways, it behaves as we expect (I get foo only if I don't set T either on the command line or environment):
$ make
: 'foo'
$ make T=bar
: 'bar'
$ T=bar make
: 'bar'

Variables specified on make command line override the values assigned in makefile:
TMPDIR := "/tmp"
test:
#echo $(TMPDIR)
And then:
make TMPDIR=whatever
whatever
It is generally considered a bad practice for makefiles to depend on environment variables because that may lead to non-reproducible builds. This is why passing variable overrides in make command line explicitly is recommended.

Here is a simple solution:
SHELL := env TMPDIR=$(TMPDIR) $(SHELL)
TMPDIR ?= "/tmp"
all:
#echo $(TMPDIR)
which works for both scenarios: TMPDIR=new/path make and make TMPDIR=new/path.

One of the thing you could do is:
TMPDIR := "/tmp"
ifdef $$TMPDIR
TMPDIR := $$TMPDIR
endif
test:
echo $(TMPDIR)

Related

Makefile - Function to check empty environment variables

I am trying to write a function to check if multiple environment variables are set. In this example, I've just tried to use a function which does not works probably because call opens up a subshell which does not has my exported variable.
What is a neat way to check multiple environment variables? I am trying to avoid multiple ifndef statements in my Makefile.
Makefile
define func_test
ifndef ${1}
$(error ${1} is not set - does not works)
endif
endef
test:
#$(call func_test, account_name)
ifndef account_name
$(error account_name is not set - works)
endif
Logs
~ $ export account_name=somename
~ $ make test
Makefile:8: *** account_name is not set - does not works. Stop.
~ $
Check if variable is empty
$(if $(some_var),,$(error some_var is not defined))

Export variable for a target in POSIX Make

In GNU Make you can export a variable for a target:
foo: export X=42
foo:
echo $$X
# Call several more commands that use $X.
Is there a way to do this in portable POSIX Make? So far, I've found two ways. The first is to basically merge all commands into one:
foo:
export X=42; \
echo $$X; \
# Call several more commands that use $X.
This is bad because now everything is bundled together. The second is to call $(MAKE):
foo:
$(MAKE) foo_ X=42
foo_:
echo $$X
# Call several more commands that use $X.
But this has an extra call to make again. Is there a better way?
The simplest solution is probably to set the variable on the command line by invoking make with:
make X=42
This way:
The make X variable is defined and set to 42, even if it is set to another value in the Makefile.
The shell environment variable X is defined and set to 42 for all recipes.
If you cannot use this (for instance because it is make that computes the value) the recursive make solution is probably the best option:
ifeq ($(X),)
X := <some-make-magic>
all:
$(MAKE) X=$(X) all
else
all:
<recipe-that-uses-X-environment-variable>
endif

Define a Makefile variable using a ENV variable or a default value

I am trying to do a simple thing:
TMPDIR ?= /tmp
test:
#echo $(TMPDIR)
This works if I run:
$ make test
/tmp
It also works if I run:
$ make test -e TMPDIR=~/tmp
/home/user/tmp
What can I do to also have it works for:
$ TMPDIR=~/tmp make test
/home/user/tmp
To follow up on my comments above, here's an example:
T ?= foo
all:
: '$(T)'
Now if I run the Makefile in various ways, it behaves as we expect (I get foo only if I don't set T either on the command line or environment):
$ make
: 'foo'
$ make T=bar
: 'bar'
$ T=bar make
: 'bar'
Variables specified on make command line override the values assigned in makefile:
TMPDIR := "/tmp"
test:
#echo $(TMPDIR)
And then:
make TMPDIR=whatever
whatever
It is generally considered a bad practice for makefiles to depend on environment variables because that may lead to non-reproducible builds. This is why passing variable overrides in make command line explicitly is recommended.
Here is a simple solution:
SHELL := env TMPDIR=$(TMPDIR) $(SHELL)
TMPDIR ?= "/tmp"
all:
#echo $(TMPDIR)
which works for both scenarios: TMPDIR=new/path make and make TMPDIR=new/path.
One of the thing you could do is:
TMPDIR := "/tmp"
ifdef $$TMPDIR
TMPDIR := $$TMPDIR
endif
test:
echo $(TMPDIR)

Conditional in makefile that redefines variable?

I have a variable in my makefile I would like to either unset or redefine, so that targets a, b and c use the correct value of MY_VARIABLE (or none at all) within their respective makefiles.
If I run make foo with the following:
export MY_VARIABLE := 'false'
foo: prep_foo dist
prep_foo:
$(shell export MY_VARIABLE='true')
echo ${MY_VARIABLE}
dist: a b c
a:
make -C a/src
b:
make -C b/src
c:
make -C c/src
I get this output:
export MY_VARIABLE='true'
echo 'false'
false
...
If, instead, I run make foo with the following makefile:
export MY_VARIABLE := 'false'
foo: prep_foo dist
prep_foo:
$(shell unset MY_VARIABLE)
echo ${MY_VARIABLE}
dist: a b c
a:
make -C a/src
b:
make -C b/src
c:
make -C c/src
I get the following output:
make: unset: No such file or directory
echo 'false'
false
...
How can I unset or redefine MY_VARIABLE when specifying a target (like foo, in this case)?
EDIT
Here is the situation I would like to avoid:
dist: a b c
foo: a_foo b_foo c_foo
a:
make -C a/src
...
a_foo
make -C a_foo/src
I just want the a target to use a different value for my particular variable, so that compilation is handled differently in that target's makefile.
Also, it doesn't look like I can export or unset variables within a target. For example:
dist: a b c
foo: a_foo b_foo c_foo
a:
make -C a/src
...
a_foo:
export MY_VARIABLE='true'; make -C a/src
If I try to do so, I get something similar to the following error on the export MY_VARIABLE='true' line (and similarly if I try to use unset):
Makefile:16: *** unterminated variable reference. Stop.
Does this help clarify what I'm trying to do?
EDIT 2
I tried a target which touch-es a file and tries to run the child target's makefile (which checks for the file's existence):
foo: prep_foo
prep_foo:
touch a/src/.foo
make -C a/src
When I try to run this via make foo, I get the following error:
Makefile:14: *** commands commence before first target. Stop.
If I remove the make statement from prep_foo, then I can touch the file without getting the error message, but I cannot trigger making the a target, so this doesn't seem to help.
The following yields the same commands commence before first target error message:
foo: prep_foo a
prep_foo:
touch a/src/.foo
Is there an example of using touch to communicate state to child targets?
The common way to communicate between targets in a Makefile is through files. Just touch a file in one target and check it in the other. Can this help you solve your problem?
Don't forget that every single shell execution in make is done in a new process. Trying to fiddle with the environment variables of recipes like this does not really make much sense -- the environment you have modified in any given line is gone after that shell dies, before the next line is executed.
You may be able to do the job like this:
a:
unset MY_VARIABLE; $MAKE -C a/src
or
a:
$MAKE MY_VARIABLE=foo -C a/src

start other makefile within makefile

Since i am not so experienced with the building process / makefiles on linux i ran in follow problem:
the Setup:
i have an makefile A, which needs some enviroment variables set before running, this is done by running . ./set_A_vars.sh (set_A_vars.sh contains many export lines) before running make -f A
now i need to make project A within a makefile B.
i tried the following setup for makefile B:
all: debug release
A_debug:
. ./set_A_vars.sh && make -f A DEBUG=1
A_release:
. ./set_A_vars.sh && make -f A DEBUG=0
debug: A_debug some_B_stuff_debug
release: A_release some_B_stuff_debug
however i get lots of errors, which sound like the enviroment variables in set_A_vars.sh have not been set for make -f A ... in B.
How can i call makefile A from makefile B with the enviroment variables in set_A_vars.sh set in makefile B ??
Any help appreciated.
Your makefile looks good with these provisos:
When you call make from a makefile, please use the macro invocation ${MAKE} rather than plain make. (This ensures parallel make works, and also means it still works even if your make has another name (GNUmake say).)
If your targets do not correspond to actual files, then mark them with .PHONY (see below).
Does some_B_stuff_debug require A to be built first? Then you must tell make this.
some_B_stuff_debug: A_debug
some_B_stuff_debug: A_release
This is clearly wrong. One way is to enforce the ordering via the shell.
Try something like this:
.PHONY: debug
debug:
. ./set_A_vars.sh && ${MAKE} -f A DEBUG=1
${MAKE} some_B_stuff_debug
.PHONY: release
release:
. ./set_A_vars.sh && ${MAKE} -f A DEBUG=0
${MAKE} some_B_stuff_debug
.PHONY: some_B_stuff_debug
∶
Your makefiles should work. I suggest you try the following:
Try running set_A_vars.sh from the command line.
Verify that the variables you wanted set are set.
make -f MakefileA, to verify that MakefileA really does work nicely with these variables set.
Try a rule in MakefileB that will test one of the variables, say FOO:
test_var:
#echo FOO is $(FOO)
This should work if you have just run set_vars.sh. If it doesn't, then there are a couple of things that could be wrong...
Now clear the variables (including FOO) and try this rule in MakefileB:
set_vars_and_test_them:
./set_A_vars.sh && echo FOO is $(FOO)
Now put it together:
A_debug:
./set_A_vars.sh && make -f MakefileA DEBUG=1
(I recommend against calling a makefile "A".)

Resources