An Introduction to Makefile
Content
Preliminaries
Before the introduction of Makefile, it is better to understand the working flow of executable file building, especially for the multi-files process part.
In general, the working flow goes like
source code –> compile (Object file) –> link (exe file)
compile : check grammar
link : sometimes it need to package some files, in Windows, it’s called Library File (.lib); in Unix, it’s called Archive File (.a).
You will find that make
is a capable tool to automate short interdependent workflows and to build small projects. But for larger projects, you will probably soon run against some of it limitations. Usually, make
is therefore not used alone but combined with other tools to generate the Makefile completely or in parts.
Website for further introduction and discussion.
Working process
- step 1 : Preprocessing
- step 2 : Compilation
- step 3 : Assembly
- step 4 : Linking
The end result, the executable program, contains the compiled source code and various auxiliary routines that make it work. It also contains references to so-called dynamic run-time libraries (in Windows: DLLs, in Linux: shared objects or shared libraries). Without these run-time libraries the program will not start.
Use ldd
to know what libraries are required.
Library
Using libraries you can build very large programs without having to resort to extremely long command lines.
Static library
Libraries contain any number of object files in a compact form, so that the command-line becomes far shorter than to include all the object files. For example, supportLib.a
is a collection of one, two or many object files, all compiled and then put into a library. The extension “.a” is used by Linux and Linux-like platforms. On Windows the extension “.lib” is used.
Static libraries (or at least parts of their contents) become an integral part of the executable program. The only way to change the routines incorporated in the program is by rebuilding the program with a new version of the library.
ar r supportLib.a file1.o file2.o # build static library
The command ar
with the option r either creates the library (the name appears after the option) or adds new object files to the library (or replaces any existing ones). Libraries are often sought in directories indicated by an option -L
or /LIBPATH
. This saves you from having to specify the exact path for every library.
Dynamic library
A flexible alternative is to use the so-called dynamic libraries. These libraries remain outside the executable program and as a consequence can be replaced without rebuilding the entire program. Compilers and indeed the operating system itself rely heavily on such dynamic libraries. You could consider dynamic libraries as a sort of executable programs that need a bit of help to be run.
Building dynamic libraries works slightly differently from building static libraries: you use the compiler/linker instead of a tool like ar
or lib
.
On Linux: for example
gfortran -fpic -c file1.f90 file2.f90
gfortran -fpic -c file3.f90 ...
gfortran -shared -o supportlib.so file1.o file2.o file3.o ...
The differences are that:
- You need to specify a compile option on Linux, for
gfortran
that is-fpic
, because the object code is slightly different. - You need to tell in the link step that you want a dynamic library (on Linux: a shared object/library, hence the extension
.so
; on Windows: a dynamic link library,dll
)
One of the reasons why we use dynamic library (or shared object) is that this file is frequently used in many software and to prepare a copy for each of these softwares will waste some space.
Usage of library in makefile
To figure out the position of particular library, you can use full command path/to/lib/libName
, for example, ${FFTW_DIR}/lib/libfftw3.a
, which is treated as part of the dependent files of the target.
You can also use short command -lname
, where name
in the command should be replaced by a concrete name of the library. When a prerequisite’s name has the form -lname
, make handles it specially by searching for the file libname.so
, and, if it is not found, searching for the file libname.a
in the current directory, in directories specified by matching vpath search paths and the VPATH search path, and then in the directories /lib, /usr/lib, and prefix/lib (normally /usr/local/lib, but MS-DOS/MS-Windows versions of make behave as if prefix is defined to be the root of the DJGPP installation tree).
For example, -lmkl_core
is equivalent to /usr/lib/libmkl_core.a
if there is a library in corresponding directory.
Finally, to figure out where should we look for libraries to link with, use the option -L
, for example,
LDFLAGs = -L. \ # current folder
-L$(LIB_DIR)`.
In conclusion, you can also figure out many libraries in one command if they are in the same folder, for example,
FFTW_LIB = -L${FFTW_DIR}/lib -lfftw3_mpi -lfftw3 -lm
For dynamic library, the case is a little complicated since they are shared object, you have to write the path to dynamic library into the target so that it know where to link those .so
files. If the path have been part of the environment variables LD_LIBRARY_PATH
, you can use -L
and -l
as above. However, if the path are not in the environment variables LD_LIBRARY_PATH
, you have to figure out the path for complier, e.g., gcc in the following way
BLASPP_LIB = -lblaspp -L${BLASPP_DIR} -Wl,-rpath=${BLASPP_DIR}
where ${BLASPP_DIR}
is the path to position of libblaspp.so
etc.
Linux command ldd name_of_exe
is useful to list the dynamic link libraries.
GCC/G++ command
Use man gcc
to see the help document of gcc
All the gcc
may need to be replaced by g++
or cc
, etc, the command parameters can combine together, multi files are the same
gcc -c hello.c # preprocessing, compilation and assembly, generate object file named *.o
gcc -s hello.c # preprocessing, compilation, generate assembly file named *.s
gcc -E hello.c > hello.txt # preprocessing only, not generate file, but you can redirect it to a output file
gcc hello.c # generate executable file named a.out
gcc -o outputFileName hello.c # preprocessing, compilation, assembly and link, specify the output file name
gcc -c hello.c -o outputName # preprocessing, compilation and assembly, generate object file ans specify it name as outputName
gcc -g hello.c # generate executable file used for debug, which can run in gdb
Some other commands
gcc -Wall hello.c # turn on all warning
gcc -W hello.c # open some extra warning
gcc hello.c -I /xxx/xxx/file # equivalent to include<file>
gcc hello.c -L /xxx/xxx/lib # specify the folder of lib
Introduction of Makefile
“Makefile” means use software “make” to run the text document to make some files. The Makefile gathers some command tell how to make the file you want.
You can type make
in command line to make the first target of the Makefile or type make fileName
to make one of the target files described in the Makefile. To make multi files, you can add an extra target listing all the target files.
Most commands are Shell-like with a little difference. If there are many Makefile or the Makefile not named Makefile, you can figure out the filename as a parameter for command make
like make -f makefile_name
or make --file=fileName
Basic
A template of Makefile always be
target ... : prerequisites ...
command ...
target
relies on files in prerequisites
whose generated rules defines in command
, which could be shell command. Command should start with a tab.
An example
result.txt: source.txt
cp source.txt result.txt
source.txt:
echo "This is the source" > source.txt
For multi files, add all: file1 file2 file3
at the first line and type make all
or make
Target
target can be file name or files’ name and some operation, which is called “phony target”.
.PHONY: clean # for distinguishing with file named "clean"
clean:
rm *.o # -rm means skip problem when cleaning
To clean all the file, just type make clean
.
Phony target can also be used to print some information
.PHONY: build_msg
build_msg:
@printf "#\n# Building \n#\n"
You can use &&
to do the two target in a command line, just like make targetA && make targetB
Comment
Comment line starts with #
, but it will print the comment line in Makefile, add @#
to make it not to print
Echo
@echo TEST # @ is used for not print the echo command, but it will not work in for loop
Line breaker
Use \
at the end of the line. For those complex command, like for loop, use \
to make them work.
NOTE: extra blank space after \
will cause error.
Pattern rule
A pattern rule contains the character %
(exactly one of them) in the target. The target is a pattern for matching file names, the %
matches any nonempty substring, while other characters match only themselves.
For example, %.c
as a pattern matches any file name that ends in .c
, s.%.c
as a pattern matches any file name that starts with s.
and ends in .c
and at least five characters long. (There must be at least one character to match the %
.)
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
Static mode
<targets ...> : <target-pattern> : <prereq-patterns ...>
<commands>
...
For example,
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
Auto-generate dependency
There maybe some header files in our prerequisites files, like
main.o : main.c defs.h
It’s really something if there are a lot of header files and the command line will be long and hard to manage. Since all the include relation have been written in cpp file and we can use the option of compiler -M
or -MM
to find the header file listed in source code. However, to connect this option of compiler and Makefile, we need some more effort since -M
option just prints some texts about the dependency of files and you have to update .d
file every time they changed.
GNU suggests that to save the dependency generated automatically by compiler into a file, i.e., generate a Makefile name.d
for every name.c
, .d
file saves the dependency of .c
file.
This could be done by the following pattern rule code, which generates .d
files.
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
For the first line, set
is a linux shell command. The -e
flag to the shell causes it to exit immediately if the $(CC)
command (or any other command) fails (exits with a nonzero status). rm -f $@
means to delete all the target .d
file.
The second line means to generate dependency file for every prerequisites file $<
, i.e., .c
file using the -M
option of compiler. $@
is %.d
file, for name.c
file, %
will be name
and $$$$
is a random number. For example, the second line may generate a file name.d.12345
.
The third line use sed
command to substitute from $@.$$$$
to $@
, in sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g'
, ,
is delimiter (you can also use other type of delimiter). This line adds .d
file target file, i.e., from
main.o : main.c defs.h
to
main.o main.d : main.c defs.h
The final line delete temporary files.
After the command above, .d
file will be auto-generated and auto-updated. Finally you need to add those rule from .d
file into our real Makefile, you can use the include
command just like
sources = foo.c bar.c
include $(source:.c=.d)
Variable
VARIABLE = value # assign when running, dynamic extension
VARIABLE := value # assign when defining, static extension
VARIABLE ?= value # assign when it is empty
VARIABLE += value # append the value to the end of the VARIABLE
When calling the variable, put it inside $()
.
For Shell variables, like $HOME
or $variable_name
, use an extra $
to call them, i.e., $$HOME
, $$variable
.
Replace the same part of the variable, like the prefix or postfix, use command bar:=$(var:a=b)
, for example, foo:.o=.c
or use command foo:%.o=%.c
.
Automatic Variables
$@ # target file
$< # the first file of the prerequisites list
$? # all the prerequisites files newer than the target
$^ # all the prerequisites files
$* # the pattern part described by %
$(@D) # directory of the target $@
$(@F) # file name of target $@
$(<D) # directory of the first prerequisites
$(<F) # file name of the first prerequisites
Implicit Variables
For example, $(CC)
refers to the current complier
Grammar
The same as Shell.
ifeq ($(CC), gcc)
command
else
command
endif
for i in $(LIST); do \
echo $$i; \
done
Function
To call a function, use command $(function_name argument1,argument2)
. Use blank space to separate function name and argument, use ,
to separate arguments.
Special functions
$(subst from,to,text)
: substitute function
$(patsubst pattern,replacement,text)
$(strip string)
$(filter pattern1 pattern2,text)
$(filter-out pattern,text)
Include file
include of Makefile
include foo.make
include ../foo.make
When make is running and it meets -I
or --include-dir
, it will then search under the directory these parameters figure out.
If you want make to simply ignore a makefile which does not exist or cannot be remade, with no error message, use the -include
directive instead of include
.
include of header files
So what if we want to start putting our .h files in an include directory, our source code in a src directory, and some local libraries in a lib directory? Also, can we somehow hide those annoying .o files that hang around all over the place? The answer, of course, is yes.
gfortran -c tabulate.f90 -I ../sub
Just like the example above, we can use -I directory
to figure out where the files tp be included and the rule has been written in the .cpp
or .f90
files.
Path problem
In some large projects, there will be a lot of source files. It is common to sort out these files and put them into different folders. You can use vpath
to tell Makefile where to find all the source files. For example, we can use
vpath %.cpp . dir1 dir2
SRC = file1.cpp file2.cpp file3.cpp
where file1.cpp, file2.cpp, file3.cpp
may come from different directories dir1, dir2
.
Finally, you may use vpath <pattern>
to clear the rule you have already set up for a pattern file and use vpath
to clear all the rules.
Nested execution
For module compilation, we may write a Makefile for each of the directories to make them much clearer. For example, we have a sub directory named subdir, then we can write in the main Makefile
subsystem:
cd subdir && $(MAKE)
As for passing variables between different level Makefile, use command
export variable1 = value
An single export
means passing all the variables.
Samples of Makefile
Sample 1
Usually there are several Makefile of a large package and they should be connected by some shell script.
The sample below gives an illustration for such file structure.
- compile.sh (clean and make file)
cd directory # there will be a Makefile in each directory make cleanall && make -j # option job means to run jobs simultaneously
make.inc Put those general parameters and complier rules in this file. For each Makefile in different folders, just include make.inc
- Makefile
# NOTE: This Makefile does NOT support auto-dependency for the .h files.
# If the header files are changed, do "make clean" first.
include ../make.inc
SRCS = pwdft.cpp dgdft.cpp
OBJS = ${SRCS:.cpp=.o}
DEPS = ${SRCS:.cpp=.d}
EXES = ${SRCS:.cpp=}
pwdft: pwdft.o ${DGDFT_LIB}
($(LOADER) -o $@ pwdft.o $(LOADOPTS) )
dgdft: dgdft.o ${DGDFT_LIB}
($(LOADER) -o $@ dgdft.o $(LOADOPTS) )
-include ${DEPS}
${DGDFT_LIB}:
(cd ${DGDFT_DIR}/src; make all)
cleanlib:
(${RM} -f ${DGDFT_LIB})
cleanall:
rm -f ${EXES} ${OBJS} ${DEPS} *.d.o
Sample 2
##############################################################################
#
# Sample Makefile for C++ applications
# Works for single and multiple file programs.
# written by Robert Duvall
# modified by Owen Astrachan
# and by Garrett Mitchener
#
##############################################################################
##############################################################################
# Application-specific variables
# EXEC is the name of the executable file
# SRC_FILES is a list of all source code files that must be linked
# to create the executable
##############################################################################
EXEC = usepix
SRC_FILES = application.cc displaycommand.cc filelister.cc menu.cc \
menuitem.cc pixmap.cc quitcommand.cc readcommand.cc \
usepix.cc
##############################################################################
# Where to find course related files
# COURSE_DIR is where various header files (.h) and library files (.so and .a)
# are found. LIB_DIR is where other libraries not specific to the course
# are kept.
# for CS machines
# COURSE_DIR = /usr/project/courses/cps108/lib
# LIB_DIR = /usr/local/lib
# for acpub machines
COURSE_DIR = /afs/acpub/users/o/l/ola/cps108/lib
LIB_DIR = /afs/acpub/project/cps/lib
##############################################################################
# Compiler specifications
# These match the variable names given in /usr/share/lib/make/make.rules
# so that make's generic rules work to compile our files.
# gmake prefers CXX and CXXFLAGS for c++ programs
##############################################################################
# Which compiler should be used
CXX = g++
CC = $(CXX)
# What flags should be passed to the compiler
DEBUG_LEVEL = -g
EXTRA_CCFLAGS = -Wall
CXXFLAGS = $(DEBUG_LEVEL) $(EXTRA_CCFLAGS)
CCFLAGS = $(CXXFLAGS)
# What flags should be passed to the C pre-processor
# In other words, where should we look for files to include - note,
# you should never need to include compiler specific directories here
# because each compiler already knows where to look for its system
# files (unless you want to override the defaults)
CPPFLAGS = -I. \
-I$(COURSE_DIR)
# What flags should be passed to the linker
# In other words, where should we look for libraries to link with - note,
# you should never need to include compiler specific directories here
# because each compiler already knows where to look for its system files.
LDFLAGS = -L. \
-L$(COURSE_DIR) \
-R $(LIB_DIR):$(COURSE_DIR)
# What libraries should be linked with.
# For example, -lm links with libm.so, the math library.
# If you make a library of your own, say, libscandir.a, you have to link it
# in by adding -lscandir here.
LDLIBS = -lscandir
# All source files have associated object files.
# This line sets `OFILES' to have the same value as `SRC_FILES' but
# with all the .cc's changed into .o's.
O_FILES = $(SRC_FILES:%.cc=%.o)
###########################################################################
# Additional rules make should know about in order to compile our files
###########################################################################
# all is the default rule
all: $(EXEC)
# exec depends on the object files
# It is made automagically using the LDFLAGS and LOADLIBES variables.
# The .o files are made automagically using the CXXFLAGS variable.
$(EXEC): $(O_FILES)
# to use `makedepend', the target is `depend'
depend:
-> makedepend -- $(CXXFLAGS) -- -Y $(SRC_FILES)
# clean up after you're done
clean:
-> $(RM) $(O_FILES) core *.rpo
very-clean: clean
-> $(RM) $(EXEC)
Note that the arrows ->
represent places where you should put tab characters, not eights spaces. It hath been decreed that this shalt be a tab character. Emacs and XEmacs have a makefile mode, which you can get into by typing M-x makefile-mode if it doesn’t come up automatically. In makefile mode, pressing the tab key inserts a real tab. Alternatively, the keystrokes C-q C-i or C-q tab will enter a tab character in any mode. (C-q is emacs for quote next key unprocessed.)