Skip to content

← Back to Make Directory

Variables and Environment

The Hierarchy of Variable Precedence

  1. The override Directive (Inside Makefile)

    Syntax: override CFLAGS += -g

  2. Command-Line Arguments

    Syntax: make CFLAGS = -O2

  3. Makefile Assignments (Normal)

    Syntax: CFLAGS = -O2

  4. Environment Variables:

    Variables exported in your shell before running make.

    Make imports these automatically but lets the Makefile overwrite them.

    Syntax: export CFLAGS=-O2

  5. The conditional Directive (?=)

    Only assigns a value if the variable is currently undefined.

    Syntax: CFLAGS ?= -g

  6. Implicit / Built-in Variables

    Default values baked into Make (e.g., CC defaults to cc)


Automatic Variables

Nuances about $?

$? lists prerequisites that are strictly newer than the target.

Gotcha: If the target doesn't exist yet, Make considers its timestamp to be "infinitely old." Therefore, Make assumes every single prerequisite is "newer" than the non-existent target, and $? will list all of them.


Behaviours that are different between Shell and Make

Referencing Variables

  1. Make: Without parentheses or braces, Make only reads the very next character.

  2. Shell:

    The shell sees $ then keeps reading until it hits a character that cannot be part of a variable name.

    When do you need them in Shell?

    They are only strictly required when you need to disambiguate the variable name from the surrounding text. (${path}_v1)

    NOTICE: Only {} works in shell for this purpose, () doesn't mean this!!

Make Variables vs Shell Variables

  1. Make Variables

    Defined at the start of a line (no indentation, Top-level).

    Spaces around the assignment operator = are allowed.

  2. Shell Variables

    Inside a recipe (Indented with Tab). (NO SPACES around =)


If you need a literal $(var) to appear in a file generated by a shell command inside a Makefile, you usually need \$$ (Backslash + Double Dollar).

Remember you are passing a string through three different parsers, and each one wants to eat your dollar signs.


About spaces

Since Make automatically strips away the leading spaces in assignment, to create a variable that contains leading spaces, you will need to use some tricks.

The steps are as follows:

  1. Create a space character using an empty nullstring variable.

  2. Create variables that have leading spaces using the space character created.

  3. When trying to print out these variables, even if the variables created in Step 2 do contain spaces, Make will expand the variable and hand this command to the shell, which eats the spaces.

    By default, the shell treats any amount of unquoted whitespace as a delimiter separating arguments.

    To force the shell to respect the whitespace exactly as Make expands it, you need to wrap the variable in double quotes inside your echo command.

    For example:

    all:
       @echo "${var}"
    

Spaces are allowed in the following specific places.

1. Around the = sign in assignments:

# Valid. Make trims spaces around the operator.
CC       = gcc
CFLAGS   = -O2

2. Between Targets and Prerequisites:

# Valid.
main.o : main.c defs.h

3. Inside Line Continuations:

# Valid. The backslash escapes the newline, and the indentation 
# on the next line is usually ignored (unless it's in a recipe).
SRCS = main.c \
       utils.c \
       api.c

The += Operator

The += operator won't cause the problems which normally appending to recursive variables cause.