Make Variables

We can render this makefile more flexible and readable by using variables to eliminate redundant hard-coded commands and filenames, just as we do in scripts and programs.

To reference variables in the makefile, we enclose them in ${}. We can also use $(), but this is easily confused with Bourne shell output capture, which converts the output of a process to a string expression that can be used by the shell.

BIN     = myprog
OBJS    = myprog.o math.o
CC      = cc
CFLAGS  = -Wall -O -g
LD      = ${CC}
LDFLAGS += -lm          # Add -lm to existing LDFLAGS

${BIN}: ${OBJS}
        ${LD} -o ${BIN} ${OBJS} ${LDFLAGS}

myprog.o: myprog.c Makefile
        ${CC} -c myprog.c

math.o: math.c
        ${CC} -c math.c
    
# Output capture in a Unix shell command, using the output of the
# "date" command as a string
printf "Today's date is %s.\n" $(date)
    
# We could also use the following, but since both make variables and
# shell output capture can be used in a Makefile, this could cause
# some confusion.

$(BIN): $(OBJS)
        $(LD) -o $(BIN) $(OBJS) -lm

...
    

If we want to allow the user to override a variable, we can use the conditional ?= assignment operator instead of =. This tells make to perform this assignment only if the variable was not set in the make command or the environment.

The += operator appends text to a variable rather than overwriting it. This is often useful for adding important flags to commands.

# Values that the user cannot override are set using '='

BIN     = myprog
OBJS    = myprog.o math.o

# Set only if the user (or package manager) has not provided a value
# -Wall:    Issue all possible compiler warnings
# -g:       Compile with debug info to help locate crashes, etc.

CC      ?= cc
CFLAGS  ?= -Wall -O -g
LD      = ${CC}
LDFLAGS += -lm          # Add -lm to existing LDFLAGS

${BIN}: ${OBJS}
        ${LD} -o ${BIN} ${OBJS} ${LDFLAGS}

myprog.o: myprog.c Makefile
        ${CC} -c myprog.c

math.o: math.c
        ${CC} -c math.c
    

If Makefile contains the conditional assignments above, then make will use cc -Wall -O -g to compile the code unless CC or CFLAGS is defined in the make command or as an environment variable. Any of the following will override the defaults:

# Set CC and CFLAGS as make variables
shell-prompt: make CC=icc CFLAGS='-O -g'

# Set CC and CFLAGS as environment variables
shell-prompt: env CC=icc CFLAGS='-O -g' make

# Set CC and CFLAGS as environment variables (Bourne shell family)
export CC=icc
export CFLAGS='-O -g'
shell-prompt: make

# Set CC and CFLAGS as environment variables (C shell family)
setenv CC icc
setenv CFLAGS '-O -g'
shell-prompt: make
    

Some variables used in makefiles, such as CC, FC, LD, CFLAGS, FFLAGS, and LDFLAGS, are standardized. They have special meaning to make and to package managers. Hence, you should always use these variable names to indicate compilers, linkers, and compile/link flags. Most package managers will set these variables in the environment or make arguments, so the makefile should respect the values provided by using ?= to only set defaults, rather than override what the package manager provides.

Table 22.1. Standard Make Variables

VariableMeaningRecommended default
CCC compilercc
CFLAGSC compile flags-Wall -O -g
CXXC++ compilerc++
CXXFLAGSC++ compile flags-Wall -O -g
CPPC Preprocessor (not C++ compiler!)cpp
CPPFLAGSC Preprocessor FlagsNot needed
FCFortran compilergfortran
FFLAGSFortran compile flags-Wall -O -g
LDLinker${CC}, ${FC}, or ${CXX}
LDFLAGSLinker flags-lm if using C math functions
PREFIXDirectory under which all files are installed/usr/local
DESTDIRDirectory for temporary staged install.
MKDIRmkdir command, often provided by package manager as an absolute pathname to avoid aliases and locally installed alternatives/bin/mkdir or just mkdir
INSTALLinstall command used to install filesinstall
RMrm command, mainly for "clean" target/bin/rm or just rm

Practice

Note

Be sure to thoroughly review the instructions in Section 2, “Practice Problem Instructions” before doing the practice problems below.
  1. What is the purpose of make variables?

  2. How do we set a variable in such a way that it can be overridden by make command-line arguments or environment variables?

  3. What variables should be used to specify the C compiler? C compiler flags? The linker? Link flags?

  4. Write a makefile, using standard make variables, that builds the executable "calc" from files "calc.c" and "functions.c".