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
Variable | Meaning | Recommended default |
---|---|---|
CC | C compiler | cc |
CFLAGS | C compile flags | -Wall -O -g |
CXX | C++ compiler | c++ |
CXXFLAGS | C++ compile flags | -Wall -O -g |
CPP | C Preprocessor (not C++ compiler!) | cpp |
CPPFLAGS | C Preprocessor Flags | Not needed |
FC | Fortran compiler | gfortran |
FFLAGS | Fortran compile flags | -Wall -O -g |
LD | Linker | ${CC}, ${FC}, or ${CXX} |
LDFLAGS | Linker flags | -lm if using C math functions |
PREFIX | Directory under which all files are installed | /usr/local |
DESTDIR | Directory for temporary staged install | . |
MKDIR | mkdir command, often provided by package manager as an absolute pathname to avoid aliases and locally installed alternatives | /bin/mkdir or just mkdir |
INSTALL | install command used to install files | install |
RM | rm command, mainly for "clean" target | /bin/rm or just rm |
What is the purpose of make variables?
How do we set a variable in such a way that it can be overridden by make command-line arguments or environment variables?
What variables should be used to specify the C compiler? C compiler flags? The linker? Link flags?
Write a makefile, using standard make variables, that builds the executable "calc" from files "calc.c" and "functions.c".