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.
Related
Given the following GNU Makefile:
OBJS = a.o b.o
LIB = libX.a
$(LIB): $(OBJS)
$(AR) cr $# $^
$(LIB): CPPFLAGS = $(shell P)
When I build $(LIB), I can see that external program P is called twice, once each to build a.o and b.o (I have it just printing getpid() to stderr).
In my case, P will always produce the same result, so it's wasted cycles/time having P called for creation of every .o . LIB could be made of MANY .o's of course, and the problem worse.
Is there a way to get target-specific variables to only be evaluated once, i.e evaluated for the target $(LIB) and that VALUE, verbatim, be passed to the prerequisite recipes (.o from .c)? Or am I misunderstanding their usage (I suspect that I am!)
Tried various variable assignment syntaxes, e.g. = and :=, even ::=, all by trial and error. Read the manual over and over.
Is there a way to get target-specific variables to only be evaluated once, i.e evaluated for the target $(LIB) and that VALUE, verbatim, be passed to the prerequisite recipes (.o from .c)? Or am I misunderstanding their usage (I suspect that I am!)
The docs don't seem to specify the exact semantics of when and how target-specific variables are set for building the affected target's prerequisites. Your best bet for the behavior you want was to use simple variable assignment (:= or ::=), but you say that doesn't work. make seems to be behaving as if the variable assignment were included, separately, in the rule for each prerequisite, and that makes sense because in general, there is no guarantee that the prerequisites will all be built one after another or immediately before the main target, and where they aren't all built one right after another, the variable must resume its global value between.
Really, I'd like to encourage you to minimize your use of features specific to GNU make. Every one of them is a convenience, not a necessity, though occasionally, some of them are very convenient indeed. You might consider instead deploying Autoconf +/- Automake to (semi-)dynamically insert flags into your makefile.
But if you must use $(shell) in your makefile, and you want to be certain that the command is executed only once per make run, then your best bet is probably to run it outside any rule. If you don't want to modify the global CPPFLAGS then store the result instead in some other variable:
OBJS = a.o b.o
LIB = libX.a
X_CPPFLAGS := $(shell P)
$(LIB): $(OBJS)
$(AR) cr $# $^
$(LIB): CPPFLAGS = $(X_CPPFLAGS)
Typical makefiles often use the built-in variables CFLAGS, CXXFLAGS, CPPFLAGS and so on1 to set the flags passed to the C, C++ or other compilers/tools. In principle, this sometimes even lets you avoid writing a compilation recipe entirely since the various built-in rules use these flags.
In general, a makefile might add things to the FLAGS variables that are required for the code to compile, such as include directories, arguments indicating which language standard to use and so on. The variables might also include "optional" or "default" arguments, such as optimization level, warning level and other settings that might validly be altered or removed.
Since CFLAGS and fields are "well known" variables, they are also apparently a configuration point for end users. For example, if a project compiles without debug information by default, it is expected that CFLAGS=-g on the make command line causes -g to be added to the $(CC) compiler command line and hence cause debug info to be produced. Similarly for other options the end user might want to control, such as the optimization level, the -march setting on gcc, and so on.
However, these two uses seem incompatible to me. If the user overrides $(CFLAGS) they will obliterate any internal "required" flags as described above, and the project either may not compile or may compile incorrectly.
Is there a best practice for handling this? The same problem doesn't really arise for "single value" variables like $(CC) since they generally have exactly one value: in this example, the C compiler to use. If the user overrides it, you use their value. Things like $(CFLAGS) are in principle a list of values, some of which are internal and shouldn't be overridden, an others which a user may want to override.
Intuitively, a solution seems to be to leave $(CFLAGS) and friends empty and unused in your makefile, preferring say CFLAGS_INTERNAL for in-makefile arguments, and then put both on the command line. I'm curious, however, if there is a best practice around this or if I'm missing something obvious.
1 For the rest of this question I will often simply refer to $(CFLAGS) with the understanding that this is simply a convenient representative of the whole family of well known compiler flag variables such as $(CPPFLAGS), $(CXXFLAGS) and so on.
I am faced with the same problem. For the time being my solution is to provide "non-standard" flags such as OPTIMS, WARNINGS, MODENV which will be appended to the "standard" CXXFLAGS internally.
If the user defines CXXFLAGS from the command-line it is assumed that he wants to override it, and if that's what he wants, that's what he should get: an override. Ironically this means I'm not using override CXXFLAGS += ... in the Makefile.
I don't want advanced users to pull their hairs out because I insist on appending/prepending my stuff to their flags, so in my opinion the final situation is like this:
GOOD: require advanced users to pass intricate custom flags
BAD: require advanced users to patch the Makefile
Just stumbled upon the same question while building an RPM with debuginfo package.
The requirement for debuginfo generation is to pass -g in CFLAGS while preserving whatever CFLAGS the software has in its Makefile.
So if you want to add some extra bits to CFLAGS, without overwriting the ones present in Makefile, you can simply use CFLAGS as an environment variable. But only as long as the Makefile in question uses CFLAGS += ... notation.
Example, suppose that you have software with Makefile having:
CFLAGS += $(ARCH) -O3 -std=gnu11 -Wall ...
To have it build with all those flags and -g, you will do:
CFLAGS='-g' make
Note that passing it as an argument to make won't work, as in: make CFLAGS='-g' is wrong, because it will overwrite internal CFLAGS.
More on the solution to pass -g for building debuginfo packages properly
Here's reference on make: appending to variables.
The approach I prefer is to provide sensible default values to these common variables, but let users provide their own - overriding the default values.
include $(wildcard makefile.in Makefile.in)
BUILD ?= build
CFLAGS ?= -O2 -fPIC -pedantic -Wall -Wextra -Wconversion
This can be done by either environment variables, command line parameters like make CFLAGS=-g or persistently in a makefile.in.
I am aware that this doesn't exactly pick up the issue you described in the questions, but I found use cases in which users want to compile a project with non-default flags should be able to
Define these variables to their needs
Check their defaults, preferably at the top of the makefile
Maybe adjust the definitions in accordance to the defaults
If someone wants to build with some special flags and is incapable of these steps, there will be some more serious problems anyhow.
This approach will not scale well when the build becomes more involved and the defaults are set across a larger makefile and dependent on other conditions.
The override directive may be what you are looking for:
$ cat Makefile
override CFLAGS += -foobar
all:
$(info CFLAGS = $(CFLAGS))
$ make
CFLAGS = -foobar
make: 'all' is up to date.
$ make CFLAGS=-g
CFLAGS = -g -foobar
make: 'all' is up to date.
Note that you can also use:
$ make CFLAGS+=-g
on the command line but it behaves just like:
$ make CFLAGS=-g
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 read documents about CPPFLAGS. Shortly, I understand that CPPFLAGS used for pass parameter to compiler. Sample usage CPPFLAGS in makefile is below.
gcc $(CPPFLAGS) main.c -o main.o
Execute make
make CPPFLAGS=-I../header
What is special CPPFLAGS text? It can be interchangable any other text like "FOO". What is the differences between FOO variable and CPPFLAGS variable? Replace all CPPFLAGS text with FOO text build is success again, nothing changes.
Main problem that actually need to solve. There are lots of makefiles. There is no include CPPFLAGS variable in these makefiles. Is there a way to pass compiler options without change makefiles.
Thanks.
What is special CPPFLAGS text? It can be interchangable any other text like "FOO"
Three things are special about CPPFLAGS:
It is a convention that many tools follow. Most notably GNU autoconf/automake.
GNU Make provides implicit rules to build many target types. These implicit rules use CPPFLAGS variable when compiling .o from C and C++ sources. These rules can be replaced with one's own rules if necessary.
When you use CPPFLAGS for preprocessor flags you follow the principle of least astonishment.
There is no include CPPFLAGS variable in these makefiles
If you meant that there are no occurrences of CPPFLAGS in the makefiles that may be because the implicit rules are used which I mentioned above.
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