how to make 'make' append additional standard FLAGS - makefile

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.

Related

Allowing users to override CFLAGS, CXXFLAGS and friends

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

GNU Make: requested target

Is there any GNU Make special variable for knowing which target was the one sent by the user in the command line call? To achieve something like (it is just an example):
// makefile
ifeq ($(USER_TARGET),"plain")
CXXFLAGS += -O0
else
CXXFLAGS += -O3
endif
plain:
// do something using CXXFLAGS
all:
// other rule using CXXFLAGS
It's important that, the USER_TARGET variable (which can be an special GNU Make variable, or a custom variable which value was set by some special GNU Make function returning the user target), contains the original target specified by the user in a call like:
$ make plain
and not the value in any step of the dependency tree. In other words, the USER_TARGET must not change during the building dependence chain, because, if I'm not wrong, the value of USER_TARGET will be evaluated when it is used by the first time, and than can happen in any point of the document, so, I want to be sure the current step of building chain don't change the value of USER_TARGET.
I don't know if I have been clear enough or if it is was overexplained.
If there is no built-in mechanism for that in GNU Make, any $(shell command) trick is welcome as well.
You want the MAKECMDGOALS variable, described here.

Why doesn't gnu make's "override" pass through to sub-makes?

Note: This question was originally posted as a rant by a now-deleted user, but there was a valid question behind the rant; this is my attempt to provide an answer.
Given the Makefile:
ifeq "$(MAKELEVEL)" "0"
# Override the command-line specification of "foo".
override foo=replaced
export foo
all::
#echo outer: foo is "$(foo)"
#$(MAKE)
else
# Variable 'foo' was "exported" from the top-level Makefile.
all::
#echo inner: foo is "$(foo)"
endif
The expectation is that export foo will cause make to export the value defined in the override declaration. But it doesn't:
$ make -s foo=original
outer: foo is replaced
inner: foo is original
The expectation is probably reasonable, but it turns out that this is not the way Gnu make works. It could well be that the make documentation could be improved to clarify the process, but the hints all seem to be there.
How variables get their values
A variable can be set by the programmer in three ways:
On the command line with a var=value command-line argument
Explicitly in the make file
From the environment
The above list is the normal priority order; the first definition found in the list "wins". However, you can use the override directive to swap the priorities of the first two methods. (You can also use the -e flag to make to swap the priorities of the last two methods. The -e flag is required by Posix, but its use is discouraged and it does not interact will with override.)
How variables are passed to sub-makes
make is very similar to a shell in that the environment is used to pass variable values. If a variable is marked as exported, then its value is placed into the environment for any processes initiated by make, including sub-makes. As with the shell, a variable is marked as exported if its definition came from the environment or if it is explicitly marked as exported with the export directive. Variables are also exported if they were set on the command line.
However, there is another mechanism by which variables on the command-line are passed to subprocesses: the MAKEFLAGS exported variable.. MAKEFLAGS contains (most) command-line options as well as all of the command-line variable overrides. If make finds MAKEFLAGS in the environment, it merges the settings in that variable with the ones actually specified on its command line. That means that command-line variable settings in a make will also take priority in a sub-make.
Since command-line variable settings are passed through the MAKEFLAGS variable, they are not subject to any changes in the makefile. A makefile can unexport or override a variable set on the command-line, but that will only affect the value (or presence) of the variable in the environment; it does not remove or change the value from MAKEFLAGS.
Resolution
So if the intent is to override (or modify) a command-line variable both in the make itself and in the environment of sub-makes, it is necessary to use both an override and an explicit modification of MAKEFLAGS. (As explained in the make manual, MAKEFLAGS is actually recursively composed using the MAKEOVERRIDES variable, so we actually modify that variable.)
ifeq "$(MAKELEVEL)" "0"
# Override the command-line specification of "foo".
override foo=replaced
MAKEOVERRIDES += foo=replaced
all::
#echo outer: foo is "$(foo)"
#$(MAKE) -s
else
# Variable 'foo' was "exported" from the top-level Makefile.
all::
#echo inner: foo is "$(foo)"
endif
And now we get the expected result:
$ make -s foo=original
outer: foo is replaced
inner: foo is replaced
Real-life application: dealing with whitespace
The primary intention of overrides is to allow the makefile to append words to a variable possibly provided on the command line. The example provided in the gnu make manual is insisting that CFLAGS always includes the -g flag, even if it were specified on the make command line:
override CFLAGS += -g
Passing the append through to a sub-make needs a little caution; in particular, the obvious:
MAKEOVERRIDES += CFLAGS=$(CFLAGS) # Don't do this
won't work because the whitespace inside the CFLAGS variable will not be escaped when it is added to MAKEFLAGS; the result will be that MAKEFLAGS will look something like this:
-- CFLAGS=-O3 CFLAGS=-O3 -g
instead of the desired
-- CFLAGS=-O3 CFLAGS=-O3\ -g
If the value assigned to CFLAGS on the command line included whitespace, the whitespace is escaped in MAKEFLAGS. The particular escaping mechanism used is not documented, and Posix only requires that there be some mechanism; apparently, Gnu make uses backslash. It would be possible to manually backslash escape the whitespace, resulting in something like this:
# Don't do this either
MAKEOVERRIDES += CFLAGS=$(subst $(space),\ ,$(CFLAGS))
(The definition and use of space is based on an example in the gnu make manual.)
But it is actually easier to just use an append assignment in MAKEOVERRIDES, which is undocumented but appears to work. It works on the command line, too.
override CFLAGS+=-g
MAKEOVERRIDES += CFLAGS+=-g
Important Note as of make v4.1: A bit of testing revealed that the above stanza will only work if CFLAGS (or some other variable) is actually set on the command-line. I reported this bug as Savannah issue 46013, with a very simple fix in the bug report. In the meantime, if you really want to do this, use the following workaround:
override CFLAGS+=-g
MAKEOVERRIDES += CFLAGS+=-g
# This line is necessary in case there were no command-line overrides.
# In effect, it produces a command-line override, although that value
# will not be passed on to sub-makes.
MAKEFLAGS += dummy=dummy
Update May 19, 2019: Today I was informed that a fix for the bug referenced above has been committed, so it should be fixed in the next gmake release.
First of all, I want to point out that your suggestion to add to MAKEOVERRIDES, is dangerous!
And SHOULD NEVER BE DONE!!
You simply turn a recursive variable into a simple one, you will always get false results, if recursive expansion is done.
I can not believe that you got up-voted for this clearly wrong "suggestion".
And note this:
You can not even fix it with a quoted assignment, like MAKEOVERRIDES += foo=$$(bar)!!!
But, let me return to the main point of your post.
And, with which, I couldn't disagree more.
One simple example would be, if you run the very same makefile, that you have:
This one is copied verbatim, from your post:
ifeq "$(MAKELEVEL)" "0"
# Override the command-line specification of "foo".
override foo=replaced
export foo
all::
#echo outer: foo is "$(foo)"
#$(MAKE) -s
else
# Variable 'foo' was "exported" from the top-level Makefile.
all::
#echo inner: foo is "$(foo)"
endif
And running in any modern version, 4.0 and up:
# Sub-make does NOT get the value from the root-Make's command-line.
# Instead, it "inherits" the value from the root-Make's definition in the Makefile.
$ make -s foo=original -e
outer: foo is replaced
inner: foo is replaced
Now, given your assertion above:
However, there is another mechanism by which variables on the command-line are passed to subprocesses: the [MAKEFLAGS exported variable.][3]. MAKEFLAGS contains (most) command-line options as well as all of the command-line variable overrides. If make finds MAKEFLAGS in the environment, it merges the settings in that variable with the ones actually specified on its command line. That means that command-line variable settings in a make will also take priority in a sub-make.
Since command-line variable settings are passed through the MAKEFLAGS variable, they are not subject to any changes in the makefile. A makefile can unexport or override a variable set on the command-line, but that will only affect the value (or presence) of the variable in the environment; it does not remove or change the value from MAKEFLAGS.
You should get:
outer: foo is replaced
inner: foo is original
In other words, we should get for the sub-make, the value defined on the command-line (original)!
Because, you said yourself:
A makefile can't unexport or override a variable set on the command-line.
So, here, when we empower the environment over the makefile, which means that the makefile has less "power" in the total scheme of things. Right?
Sure, for such a case, you assertion will hold even stronger.

Overriding a makefile variable

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.

Passing additional variables from command line to make

Can I pass variables to a GNU Makefile as command line arguments? In other words, I want to pass some arguments which will eventually become variables in the Makefile.
You have several options to set up variables from outside your makefile:
From environment - each environment variable is transformed into a makefile variable with the same name and value.
You may also want to set -e option (aka --environments-override) on, and your environment variables will override assignments made into makefile (unless these assignments themselves use the override directive . However, it's not recommended, and it's much better and flexible to use ?= assignment (the conditional variable assignment operator, it only has an effect if the variable is not yet defined):
FOO?=default_value_if_not_set_in_environment
Note that certain variables are not inherited from environment:
MAKE is gotten from name of the script
SHELL is either set within a makefile, or defaults to /bin/sh (rationale: commands are specified within the makefile, and they're shell-specific).
From command line - make can take variable assignments as part of his command line, mingled with targets:
make target FOO=bar
But then all assignments to FOO variable within the makefile will be ignored unless you use the override directive in assignment. (The effect is the same as with -e option for environment variables).
Exporting from the parent Make - if you call Make from a Makefile, you usually shouldn't explicitly write variable assignments like this:
# Don't do this!
target:
$(MAKE) -C target CC=$(CC) CFLAGS=$(CFLAGS)
Instead, better solution might be to export these variables. Exporting a variable makes it into the environment of every shell invocation, and Make calls from these commands pick these environment variable as specified above.
# Do like this
CFLAGS=-g
export CFLAGS
target:
$(MAKE) -C target
You can also export all variables by using export without arguments.
The simplest way is:
make foo=bar target
Then in your makefile you can refer to $(foo). Note that this won't propagate to sub-makes automatically.
If you are using sub-makes, see this article: Communicating Variables to a Sub-make
Say you have a makefile like this:
action:
echo argument is $(argument)
You would then call it make action argument=something
From the manual:
Variables in make can come from the environment in which make is run. Every environment variable that make sees when it starts up is transformed into a make variable with the same name and value. However, an explicit assignment in the makefile, or with a command argument, overrides the environment.
So you can do (from bash):
FOOBAR=1 make
resulting in a variable FOOBAR in your Makefile.
It seems command args overwrite environment variable.
Makefile:
send:
echo $(MESSAGE1) $(MESSAGE2)
Example run:
$ MESSAGE1=YES MESSAGE2=NG make send MESSAGE2=OK
echo YES OK
YES OK
There's another option not cited here which is included in the GNU Make book by Stallman and McGrath (see http://www.chemie.fu-berlin.de/chemnet/use/info/make/make_7.html). It provides the example:
archive.a: ...
ifneq (,$(findstring t,$(MAKEFLAGS)))
+touch archive.a
+ranlib -t archive.a
else
ranlib archive.a
endif
It involves verifying if a given parameter appears in MAKEFLAGS. For example .. suppose that you're studying about threads in c++11 and you've divided your study across multiple files (class01, ... , classNM) and you want to: compile then all and run individually or compile one at a time and run it if a flag is specified (-r, for instance). So, you could come up with the following Makefile:
CXX=clang++-3.5
CXXFLAGS = -Wall -Werror -std=c++11
LDLIBS = -lpthread
SOURCES = class01 class02 class03
%: %.cxx
$(CXX) $(CXXFLAGS) -o $#.out $^ $(LDLIBS)
ifneq (,$(findstring r, $(MAKEFLAGS)))
./$#.out
endif
all: $(SOURCES)
.PHONY: clean
clean:
find . -name "*.out" -delete
Having that, you'd:
build and run a file w/ make -r class02;
build all w/ make or make all;
build and run all w/ make -r (suppose that all of them contain some certain kind of assert stuff and you just want to test them all)
If you make a file called Makefile and add a variable like this $(unittest)
then you will be able to use this variable inside the Makefile even with wildcards
example :
make unittest=*
I use BOOST_TEST and by giving a wildcard to parameter --run_test=$(unittest)
then I will be able to use regular expression to filter out the test I want my Makefile
to run
export ROOT_DIR=<path/value>
Then use the variable, $(ROOT_DIR) in the Makefile.

Resources