I have a master makefile which has the default values for variables and then a child makefile which includes project specific settings. At the end of the child makefile, I include the master makefile.
I have been using the following code in the master makefile to set default values for a variable
ifndef CC
CC = avr-gcc
endif
And then recently I read that I can also do
CC ?= avr-gcc
So my question is, whether both are same and if yes which one is the recommended way of overriding variables.
The second is broadly understood, easier to read and causes less clutter.
The first way, using ifndef / endif is more for instances where you want to do more than just set a variable, like toggling many things depending on if DEBUG is set, or something else.
If you just want to set a variable if it's not already set, then var ?= value is definitely sufficient.
Related
I recently discovered that
setting a Target-specific Variable
using a conditional assignment (?=)
has the effect of unexporting the global variable using the same name.
For example:
target: CFLAGS ?= -O2
If this statement is anywhere in the Makefile, it has the same impact as unexport CFLAGS for the global variable.
It means that the CFLAGS passed as environment variable to the Makefile will not be passed as environment variable to any sub-makefile, as if it was never set.
Could it be a make bug ?
I couldn't find any mention of this side effect in the documentation.
Example : root Makefile
target:
$(MAKE) -C $(DIR) target
disruptor: CFLAGS ?= -O1
disruptor:
#echo CFLAGS = $(CFLAGS)
and then into $DIR/Makefile:
target:
#echo target CFLAGS = $(CFLAGS)
Now :
make will display target CFLAGS =
make CFLAGS=-Os will display target CFLAGS = -Os
but CFLAGS=-Os make will display target CFLAGS =
after commenting the first disruptor line (CFLAGS ?= -O1), then CFLAGS=-Os make will display target CFLAGS = -Os as expected.
Other mitigations that work :
adding export CFLAGS after the first disruptor line
replacing the ?= assignment by =, := or +=. None of them produce the "implicit unexport" side effect (of course, it also changes the assignment meaning, this is just for test).
I haven't tested with other variable names yet, but I presume it's not specific to CFLAGS.
I reproduce your observed behavior with GNU make 4.0. I concur with your characterization that the effect seems to be as if the variable in question had been unexported, and I confirm that the same effect is observed with other variable names, including names that are without any special significance to make.
This effect is undocumented as far as I can tell, and unexpected. It seems to conflict with the manual, in that the manual describes target-specific variable values as causing a separate instance of the affected variable to be created, so as to avoid affecting the global one, yet we do see the global one being affected.
Could it be a make bug ?
It indeed does look like a bug to me. Evidently to other people, too, as it appears that the issue has already been reported.
I want to set a Global variable through a recipe then reference that variable in another independent recipe
The below code is an example code that sets the variable within the recipe but the variable stays with the initial value if referenced outside the recipe
ACTIVE = a
switch:
ifeq ($(ACTIVE),b)
ACTIVE=$(shell echo 'a')
else
ACTIVE=$(shell echo 'b')
endif
print:
$(info acitve = $(ACTIVE))
I know there are ways to broadcast the value of a target-specific variable to dependent targets, but that's not what I want.
You can use $(eval ...) for this, although it's almost always a bad idea. I have to assume that your real situation is much more complicated because there are many better ways to accomplish what you've actually provided in the sample makefile.
switch:
$(eval ACTIVE=$(if $(filter-out a,$(ACTIVE)),a,b))
i have a Makefile (that is not really under my control and) that defines a number of variables used by implicit rules:
CPPFLAGS := $(CPPFLAGS) -I../../../../modules
CXXFLAGS += -std=c++11
now, I want to add some additional flags to these variables as make variables. Something like:
make CPPFLAGS="-D_FORTIFY_SOURCE=2" CXXFLAGS="-g -O2"
unfortunately this results in overwriting the entire CPPFLAGS/CXXLAGS defined in the Makefile, whereas I would like to accumulate them (actually I would like to append the externally set flags, even though the above code clearly tries to prepend)
For whatever reasons, specifying these variables as environment variables (instead of make variables) works:
CPPFLAGS="-D_FORTIFY_SOURCE=2" CXXFLAGS="-g -O2" make
now for external reasons, i'm having a hard time passing those flags via envvars (and instead need make vars).
So what is the proper way to add compiler flags used by implicit rules? Both overwriting and accumulating variables strike me as a common task for Makefiles; there must be some way to do this...I've searched the make documentation but haven't found anything!
A simplistic approach is obviously to introduce some helper variable:
CXXFLAGS = -std=c++11 $(EXTRA_CXXFLAGS)
and then set this helper variable from outside:
make EXTRA_CXXFLAGS="-g -O2"
But: is there a standard name for such helper variable? (If so, which one? where is that documented??) Even better, is there an other variable that is automatically added to implicit rules (so i don't have to manually append the FLAGS?)
What is the reason why both variants for accumulating variables in my original Makefile work only with envvars, and not with make vars?
Environment variables can be modified within makefile using normal assignments. And it is common to set variables like CFLAGS, CXXFLAGS which can be appended (or modified in some way) in makefile, in the environment:
CPPFLAGS="-D_FORTIFY_SOURCE=2" CXXFLAGS="-g -O2" make
As opposite, variables set in make command line, cannot be modified within makefile using normal assignments. Such way you can set variables which used as some switch within makefile:
make V=1
Example of Makefile:
V=0 # Will be overriden by variable set in `make` command line
ifneq ($(V),0)
# output some debug information
endif
The only way to override variable set in command line is using override directive:
override CPPFLAGS := $(CPPFLAGS) -I../../../../modules # Will append string to variable, even if it set by command line
override CXXFLAGS += -std=c++11 # Similar but in the simpler form
Modifying CXXFLAGS and other *FLAGS variables
Suppose concrete makefile allows user to affect flags (that is, it doesn't hardcode them using direct assignment such CXXFLAGS := -g). And you want to affect on the flags.
Normal way is to set environment variable which will prepend flags set in the makefile itself. These additional flags, set by the makefile, are needed for correct compilation and linking.
However, you can try to override whole flags using variable set in the command line. In that case nobody garantees you don't suddenly broke the compilation, but it may work.
As for appending flags.. Well, it is normally needed for overwrite flags set by makefile (otherwise prepending flags using environment variable is sufficient). Because of that, any garantees will be vanished again. So, why do not use the previos way (setting whole flags via command line variable assignment)? At least, if something will go wrong, you will definitely know that problem is with you flags, not with ones set by makefile.
I have a makefile from Intel in which there is some "?=".
Like
COMPILER ?= $(GCC_PATH)g++
But
EXECUTABLE = run
What is the difference between ?= and = and when do I have to use the first one instead of the second one ?
Thank you very much.
Quoth the fine documentation:
If you'd like a variable to be set to a value only if it's not already set, then you can use the shorthand operator ‘?=’ instead of ‘=’.
?= is for conditional assignment, i.e if it not already defined then only assign the value else leave it. In your example if you give make COMPILER=arm-none-gcc then arm-none-gcc is used as Compiler than the default g++, if you just type make then g++ taken as option for COMPILER. If = is used then COMPILER will be assigned value when and where assignments are encountered. for more on make files you can refer to
Understanding makefile for beginners
I have a Makefile for a C program that has the declaration
CC?=gcc
Changing it to
CC?=g++
does NOT make it compile with g++. Changing it to
CC=g++
DOES make it use g++.
So I wonder what the ?= operator does? My guess is that it looks at a environment variable to decide which compiler to use and if it's not set then use gcc? Anyone who can clear this up?
From http://www.gnu.org/software/make/manual/make.html:
There is another assignment operator
for variables, `?='. This is called a
conditional variable assignment
operator, because it only has an
effect if the variable is not yet
defined. This statement:
FOO ?= bar
is exactly equivalent to this (see The
origin Function):
ifeq ($(origin FOO), undefined)
FOO = bar
endif
Probably CC is already defined as gcc, so CC ?= g++ won't override the existing gcc.
The ?= operator sets the variable only if it isn't already set: info make → * Using Variables → * Setting.
As others mentioned, it is likely already predefined.
On GNU, you can see what is defined with make -p from a directory that does not contain a Makefile.
This is documented at: https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html
CC
Program for compiling C programs; default ‘cc’.
Usually, CC=cc by default. Then on Ubuntu 14.04 for e.g., cc is usually a symlink to gcc.
To disable all variables at once see: Disable make builtin rules and variables from inside the make file Seems currently impossible.
The "?" operator means set if not already set.
So, if CC is already blank CC?= will set it. If CC already contains something, it won't.
Source: http://unix.derkeiler.com/Mailing-Lists/FreeBSD/questions/2007-03/msg02057.html