4

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.

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • And then there's the big Question if `CFLAGS` and cousins belong to the dependency hierarchy. How do you want it? – Vroomfondel Jul 31 '18 at 12:02
  • @Vroomfondel - what do you mean by dependency hierachy? – BeeOnRope Jul 31 '18 at 15:48
  • 1
    Generally, people create two different variables: one containing all the "required" flags and the other set to something like `-O2 -g` that users can override with what they want. This is, for example, how automake makefiles work. This does mean you need to create your own implicit rules, you can't use the built-in rules. Others do things the opposite way and ask users to use a non-standard variable to override things on the command line. – MadScientist Jul 31 '18 at 16:05
  • Does something like a command line belong to the input data set, i.e. are C files dependent on the compiler switches the same way they are on header files, or do you prefer to see this as a separate information space, which lives outside of the dependency hierarchy of `make`? If the former, the most logical thing to do is lay them down in a file which effects recompilation if changed, if the latter, you have to live with `make clean` as the patch which hides this logical inconsistency. – Vroomfondel Jul 31 '18 at 20:33
  • @Vroomfondel - well the first one yeah a bit, I mean some files might need some switches to compile (e.g., `-std=c++11` or similar), but some are optional. Rebuilding when the flags change is interesting, but not really my question here (in fact I do usually set up my Makefiles to regenerate everything when they change by using a dummy file touched by the `Makefile: ` target). The latter, I'm still not really understanding. – BeeOnRope Jul 31 '18 at 23:15
  • When you compile with `-O3` instead of `-O1` then nearly all object files will change. This is the proof that `.o`files depend on compiler switches as much as they do on source files, no? If you leave that piece of information out, you will have to jump in as a human factor in your build process, issueing `make clean` etc. just at the right time, because that piece of the dependency tree just exists inside your head. Thats what I meant by logical inconsistency. – Vroomfondel Aug 01 '18 at 13:18
  • 1
    I use `CPPFLAGS_INTERNAL := -Iinclude -Isubdir/anotherinclude $(CPPFLAGS)`. We can append to it but not override unless we use `make CPPFLAGS_INTERNAL=...` directly. I find it easier because my target might include some non-standard/obvious include directories, for example, and this would force us to write (and know) them when calling make. – Daniel Nov 29 '18 at 16:09

4 Answers4

3

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.

Danila Vershinin
  • 8,725
  • 2
  • 29
  • 35
  • How does that work? In the case CFLAGS is set as an env car, it will be concatenated with CFLAGS set in the Makefile? Seems unlikely unless the Makefile is written specially. – BeeOnRope Jun 15 '19 at 21:10
  • @BeeOnRope it just does. Unfortunately, I have no documentation link to back my statement. It seems like common knowledge or axiom not written anywhere :D. Here's confirmation through my testing on a couple of packages today, [link](https://github.com/GetPageSpeed/wrk-rpm/commit/e2f06bf50a83f9137166d490d205feaf6f263ee0). You see I was trying to just change `Makefile` with `sed`, but the elegant solution which worked was with the `CFLAGS='-g' make` approach. – Danila Vershinin Jun 15 '19 at 21:52
  • @BeeOnRope If I did `make CFLAGS='-g' ` instead then this will overwrite CFLAGS completely and the package doesn't build at all. If I don't include anything, then debug symbols are not included which means environment variable works when used. So overall: it works, and doesn't seem to apply any requirements on the `Makefile`. Then again, that surely works for "adding" something. Enough for my use case of adding `-g` which isn't present in original `Makefile` – Danila Vershinin Jun 15 '19 at 21:54
  • @BeeOnRope just to add, here is [the `Makefile` being used](https://github.com/wg/wrk/blob/master/Makefile). So `CFLAGS += ...` in Makefile. Other package where it worked as well been using `CFLAGS =` construct in Makefile. – Danila Vershinin Jun 15 '19 at 22:03
  • It doesn't work with `CFLAGS = ...` in the `Makefile`. You can test this easily using [this makefile](https://gist.github.com/travisdowns/62ad97039c9f79b6e5d3c45704687012). Run it with `CFLAGS=bar make` and you get `CFLAGS=foo` as the output. Yes, `+=` works, because it doesn't overwrite the flags, it happens them. You could do the same for flags specified as an argument to make (like `make CFLAGS=bar`) with `override CFLAGS += ...`. – BeeOnRope Jun 16 '19 at 01:43
  • @BeeOnRope you're right. Thanks for correcting. That is something good to know about making sure Makefile with `CFLAGS += ...` will do but `CFLAGS = ...` won't. – Danila Vershinin Jun 16 '19 at 02:39
2

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
user7023624
  • 571
  • 5
  • 14
1

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

  1. Define these variables to their needs
  2. Check their defaults, preferably at the top of the makefile
  3. 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.

lubgr
  • 37,368
  • 3
  • 66
  • 117
  • Often it is not so simple to replicate the entire `CFLAGS` or whatever since it may be composed of several variables, conditional makefile directives and so on. I would expect a user to carefully trace through the makefile to rebuild the default `CFLAGS` in order to modify it (at least based on many examples I've seen where people casually modify `CLFLAGS`). In the example you should though it would work fine: my concern is with clobbering flags that will make the code not compile, e.g, a bunch of `-I` directives, etc. – BeeOnRope Jul 31 '18 at 07:41
  • @BeeOnRope Ok, that's a good point. I was thinking of rather smallish projects, where the defaults can simply be set in one or two lines. I'll add this restricition to the answer. – lubgr Jul 31 '18 at 07:46
0

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
Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
  • I've used `override` but it doesn't seem to work well here. For example, if the user specifies `CFLAGS=-O2` and you have `override CFLAGS += -O1` in your makefile, the `-O1` will take priority. In general, `override` seems awkward for the variables that should be overriden if specified by the user. – BeeOnRope Jul 31 '18 at 07:29
  • 1
    Absolutely, override does not help in situations where the command line flags are not compatible with the Makefile. This is the reason why I prefer having two different variables, as you suggested yourself, `cflags` defined in the Makefile and `CFLAGS` that can be overridden on the command line. – Renaud Pacalet Jul 31 '18 at 07:37