1

In GNU Makefiles, there are often interactions between environment variables and parameters/variables/macros. There are also several different assignment operators, there are "override" variables that can be provided on the command line, and there is an override directive.

What are the precedence rules for these interactions?

Consider this Makefile:

# Use := to avoid infinite recursion of macro expansion
CFLAGS := -g -O2 $(CFLAGS)
hello: src/hello.c
    cc $(CFLAGS) -o $@ $<

If you invoke CFLAGS=-pipe make hello, you get cc -g -O2 -pipe -o hello src/hello.c. If you invoke make CFLAGS=-pipe hello, you get cc -pipe -o hello src/hello.c. What's the difference?

And if you change the Makefile to:

CFLAGS ?= -g -O2 $(CFLAGS)
hello: src/hello.c
    cc $(CFLAGS) -o $@ $<

You get cc -pipe -o hello src/hello.c in both cases.

shadowtalker
  • 12,529
  • 3
  • 53
  • 96
  • Maybe a better title would be something like "When do Makefile variables get their values from the environment, command line or Makefile itself?". Also, another popular tag for `make` is `Makefile`. – Victor Sergienko Aug 18 '21 at 00:57

1 Answers1

2

This answer is based on information in the comments of https://stackoverflow.com/a/63187052/2954547, as well as the GNU Make manual.

The precedence is, in order of strongest to weakest, is:

  1. The override directive
  2. Command-line override
  3. =, :=
  4. Environment variable
  5. ?=

There are several individual rules that interact to create this precedence:

  • A macro is created for every environment variable. If e.g. HOME is set, there will be a $(HOME) macro.

  • If a macro is already defined, = and := overwrite the previous macro definition. This includes environment variables, which are (conceptually) created before the rest of the Makefile is interpreted. So = or := will overwrite an environment variable. But if the environment variable is defined, it can still be referenced on the right-hand side of the assignment:

    CFLAGS := -g -O2 $(CFLAGS)
    

    Note that you had to use := in this case, because if you used = you would get a macro that tried to expand itself recursively, forever, at run time.

  • As per section 6.5 of the manual, VAR ?= val is equivalent to:

    ifeq ($(origin VARIABLE), undefined)
      VAR = val
    endif
    

    Environment variables have their origin set to 'environment', so ?= will always be overridden by an environment variable. Unlike = and :=, if the environment variable is set, its corresponding assignment with ?= will never even be invoked.

  • Command-line "override variables" override all variables. This means that make VAR=val overrides the environment variable VAR, and the assignments VAR = val, VAR := val, and VAR ?= val.

  • The override directive overrides everything, including command-line overrides.

Note that GNU Make has the -e/--environment-overrides option, which causes environment variables to override = and :=. This causes them to behave the same as command-line override variables with respect to macro definition , but (I think) the latter will still take precedence.

shadowtalker
  • 12,529
  • 3
  • 53
  • 96
  • 1
    I would mention that "macro" and "variable" are used interchangeably here. 2. I would add a a documentation reference to `override` directive, and define or add a reference for "command-line override". Not everyone would know that "command-line override" means "variables set in `make` command line". – Victor Sergienko Aug 18 '21 at 00:54
  • 1
    In "Command-line "override variables" override all variables", I believe, you mean "... override all assignments, except for `override`". Also, add a documentation reference to `override`. – Victor Sergienko Aug 18 '21 at 00:55
  • 1
    Also `?=`, not `=?`. – Victor Sergienko Aug 18 '21 at 00:58