In any programming project, even a small one, there is a considerable amount of typing involved in building a final executable file from a collection of source code files: Each source code file must be compiled into an object file. The object files must be linked with system libraries into an executable file. The compilation commands may have a considerable number of options. The linking command may contain several system libraries. And if changes are made to any source code file, which files need to recompiled? Or should all of them be recompiled and linked?
The make program was designed to build and maintain programming projects while recompiling only the parts that are necessary.
Like most UNIX commands, make has many options. The most useful of these are
target-list: dependency-list command 1 command 2 command 3 . . .
The target-list in a makefile rule is usually the names of the files that are created when the rule is run. This is most commonly an executable file or an object code file. But it doesn't have to be a file (see Pseudo-Targets, below). The target-list must be separated from the dependency-list with a colon. The first target name should start in the first column position on the line.
The dependency-list is the list of files which must all exist and be up-to-date in order to create the target. By up-to-date we mean that the modify date on the file must be later than the modify date on all the files needed to build that file.
The files in the dependency-list must be separated by spaces and placed on one line. If the line becomes too long for the editor (a very real possibility for the dependency lists in a large project), then the line may be logically extended by typing a \ immediately followed by a new line. Anything on the following line is considered to be logically part of the line extended with the \. If the target is an executable file the dependency list is probably a list of object code files. For example
raytrace: trace.o light.o input.o sphere.o polygon.o ray.o bound.o triangle.o quad.o (a command goes here to build the executable)Other common dependency lists are made of source code and header files if the target is an object code file.
trace.o: trace.cc trace.h rt_attrib.h light.h jitter.h (a command goes here to make the object file)
pxform: pxform.o similar.o affine.o g++ -o pxform pxform.o similar.o affine.o -lmAn extremely important point about commands in makefile rules is that they must be indented with a tab character. Yes that's right... tabs. They must not be indented with any spaces. To use spaces can produce odd errors that are hard to track down.
Astoundingly this has been a severe design flaw in the make program for decades! By visual inspection it is impossible to tell if spaces or tabs are being used. The only way to tell is by using an editor and moving the cursor around or by using a global substitution command to reveal the location of stray blanks or using a substitution command such as tr to substitute a printable character for blanks.
A command in a makefile rule is run only if the target is out of date with respect to the dependencies. This is determined by examining the timestamps on the target and the dependencies. If any of the dependencies are newer than the target then the target is regenerated by executing the commands. As part of checking the dependencies of any given rule, the make program will verify that the dependency is not the target of some other rule. If it is, then that new rule is evaluated first. Only when all of the dependencies are known to be up-to-date is the comparison made with the target of the current rule.
For example, a common and useful rule in any project has the pseudo target of "clean".
clean: -rm *.o my_executable_files_hereThis rule, when invoked, will remove all of the object files and any executable files that are listed as part of the command. This rule is invoked by typing make clean at the command line prompt.
Many helper rules such as all for rebuild everything and tar for archiving the entire project can be added. By convention, rules of this form are placed towards the end of the makefile, after the other rules that actually build the project.
Another thing to note in the above example is the hyphen in front of the rm command. When placed immediately in front of the command to be executed, a hyphen will cause the make program to ignore any errors associated with the command. For example,
% make clean rm *.o .... A file editing session takes place here % make clean rm: cannot remove *.o : No such file or directory make: [clean] Error 1 (ignored)
Another use of a pseudo target is to create a rule for projects with multiple executables. This rule has a target and a dependency list, but no executables:
project: program1 prog2 mystuff3 program1: program1.o class2.o otherthings.o g++ -o program1 program1.o class2.o otherthings.o -lm . . .In this case, the pseudo target
project
depends on multiple
executable files. Typing make project or placing this rule at
the top of the rule list for default application will cause
make to create all of the executables.
-g
) to all
of the compiling commands. And after the problem is solved, you must
go back and restore those options to their original form.
To help this situation, makefiles allow the use of variables. A makefile variable is assigned a string value in a similar to an assignment in a programming language:
CC = g++The symbol CC is the variable and its value is
g++
.
Makefile variable names are case sensitive. By convention, they are
all capital letters. Variables are typically set once, at the
beginning of the makefile.
There are some standard variable names that are used for common purposes. The
name CC
is used to hold the name of the compiler. The variable
CCFLAGS
holds C++ compiler options and
CFLAGS
holds C compiler options.
Unlike most programming languages, using the value of makefile
variable does not consist of simply giving the variable name.
To use a makefile variable it is necessary to put the name in
parantheses and place a dollar sign in front. For example, the
variable CC
is given a value as described above. To use
the variable, it is necessary to write the expression
$(CC)
. To use the variable CCFLAGS
, the
expression $(CCFLAGS)
must be used:
CC = g++ CCFLAGS = -O -Wall ... twister: twister.o rotate.o $(CC) $(CCFLAGS) -o twister twister.o rotate.o -lm
PROGNAME=fm CC=g++ CCFLAGS=-O CFLAGS=$(CFLAGS) LDFLAGS=-lm LIB=walshlib.a SRCS=\ bitutils.cpp\ main.cpp\ mathlib.cpp\ linkage.cpp HDRS=\ constants.h\ bitutils.h\ main.h\ mathlib.h\ linkage.h ALLFILES=$(HDRS) $(SRCS) makefile LIBOBJS=\ bitutils.o\ main.o\ mathlib.o\ linkage.o main: main.o $(LIB) $(CC) -o $(PROGNAME) $(CCFLAGS) main.o $(LIB) $(LDFLAGS) all: touch $(SRCS) make $(LIB): $(LIBOBJS) echo Build libs ar vr $@ $? size: $(HDRS) $(SRCS) wc $? srcs: $(HDRS) $(SRCS) echo $(HDRS) $(SRCS) allfiles: $(ALLFILES) echo $(ALLFILES) clean: rm -f *.o core clobber: rm -f *.o *.a core shar: shar -Z $(ALLFILES) > main.shar backup: $(FILES) mv Backup/backup Backup/backup.old shar $(ALLFILES) | gzip > Backup/backup tar: tar -cvf ~/fm.tar $(ALLFILES)
BIN = calc CC = gcc CFLAGS = -g # CCFLAGS = -DCPLUSPLUS -g # for use with C++ if file ext is .cc # CFLAGS = -DCPLUSPLUS -g # for use with C++ if file ext is .c SRCS = $(BIN).y $(BIN).l OBJS = lex.yy.o $(BIN).tab.o LIBS = -lfl -lm $(BIN): $(OBJS) $(CC) $(CCFLAGS) $(OBJS) $(LIBS) -o $(BIN) $(BIN).tab.h $(BIN).tab.c: $(BIN).y bison -v -t -d $(BIN).y lex.yy.c: $(BIN).l $(BIN).tab.h flex -d $(BIN).l # -d debug all: touch $(SRCS) make clean: rm -f $(OBJS) $(BIN) lex.yy.c $(BIN).tab.h $(BIN).tab.c $(BIN).tar tar: tar -cvf $(BIN).tar $(SRCS) makefile
Use | Macro | Default Value |
---|---|---|
Library | AR | ar |
Archives | ARFLAGS | rv |
Assembler | AS | as |
Commands | ASFLAGS | |
COMPILE.s | $(AS) $(ASFLAGS) | |
COMPILE.S | $(CC) $(ASFLAGS) $(CPPFLAGS) -c | |
C | CC | cc |
Compiler | CFLAGS | |
Commands | CPPFLAGS | |
COMPILE.c | $(CC) $(CFLAGS) $(CPPFLAGS) -c | |
LINK.c | $(CC) $(CFLAGS) $(CPPFLAGS) | |
$(LDFLAGS) | ||
C++ | CCC | CC |
Compiler | CCFLAGS | CFLAGS |
Commands | CPPFLAGS | |
COMPILE.cc | $(CCC) $(CCFLAGS) $(CPPFLAGS) -c | |
LINK.cc | $(CCC) $(CCFLAGS) $(CPPFLAGS) | |
$(LDFLAGS) | ||
COMPILE.C | $(CCC) $(CCFLAGS) $(CPPFLAGS) -c | |
LINK.C | $(CCC) $(CCFLAGS) $(CPPFLAGS) | |
$(LDFLAGS) | ||
FORTRAN 77 | FC | f77 |
Compiler | FFLAGS | |
Commands | COMPILE.f | $(FC) $(FFLAGS) -c |
LINK.f | $(FC) $(FFLAGS) $(LDFLAGS) | |
COMPILE.F | $(FC) $(FFLAGS) $(CPPFLAGS) -c | |
LINK.F | $(FC) $(FFLAGS) $(CPPFLAGS) | |
$(LDFLAGS) | ||
FORTRAN 90 | FC | f90 |
Compiler | F90FLAGS | |
Commands | COMPILE.f90 | $(F90C) $(F90FLAGS) -c |
LINK.f90 | $(F90C) $(F90FLAGS) $(LDFLAGS) | |
COMPILE.ftn | $(F90C) $(F90FLAGS) $(CPPFLAGS) -c | |
LINK.ftn | $(F90C) $(F90FLAGS) $(CPPFLAGS) | |
$(LDFLAGS) | ||
Link Editor | LD | ld |
Command | LDFLAGS | |
lex | LEX | lex |
Command | LFLAGS | |
LEX.l | $(LEX) $(LFLAGS) -t | |
lint | LINT | lint |
Command | LINTFLAGS | |
LINT.c | $(LINT) $(LINTFLAGS) $(CPPFLAGS) | |
Modula 2 | M2C | m2c |
Commands | M2FLAGS | |
MODFLAGS | ||
DEFFLAGS | ||
COMPILE.def | $(M2C) $(M2FLAGS) $(DEFFLAGS) | |
COMPILE.mod | $(M2C) $(M2FLAGS) $(MODFLAGS) | |
Pascal | PC | pc |
Compiler | PFLAGS | |
Commands | COMPILE.p | $(PC) $(PFLAGS) $(CPPFLAGS) -c |
LINK.p | $(PC) $(PFLAGS) $(CPPFLAGS) | |
$(LDFLAGS) | ||
Ratfor | RFLAGS | |
Compilation | COMPILE.r | $(FC) $(FFLAGS) $(RFLAGS) -c |
Commands | LINK.r | $(FC) $(FFLAGS) $(RFLAGS) |
$(LDFLAGS) | ||
rm Command | RM | rm -f |
sccs | SCCSFLAGS | |
Command | SCCSGETFLAGS | -s |
yacc | YACC | yacc |
Command | YFLAGS | |
YACC.y | $(YACC) $(YFLAGS) | |
Suffixes List | SUFFIXES | .o .c .c~ .cc .cc~ .y .y~ .l .l~ .s .s~ .sh .sh~ .S .S~ .ln .h .h~ .f .f~ .F .F~ .mod .mod~ .sym .def .def~ .p .p~ .r .r~ .cps .cps~ .C .C~ .Y .Y~ .L .L .f90 .f90~ .ftn .ftn~ |
Robert Heckendorn | Last updated: |