Makefile Tutorial Robert R. Snapp Department of Computer Science University of Vermont Burlington, Vermont 05405 (snapp@cs.uvm.edu) http://www.emba.uvm.edu/~snapp/maketutorial/make.html Introduction to the make program make is a UNIX program that helps programmers efficiently build projects. The goal of each project is usually one or more executable programs. Each executable program is called a target. Thus, to create and executable program called pizza, a user would type make pizza in a UNIX shell. If all of the object code exists, and is up-to-date, the make program would invoke the appropriate linker, which would then produce the executable. However, if the object code is out-of-date, or does not even exist, the make program will invoke the appropriate compiler (making up-to-date object code from the project's source code) followed by a call to the linker. Targets can also refer to other file operations. For example, it is common practice to invoke make clean to delete object files, executable programs, and core dumps from a project directory. (Usually you want to do this before you distribute (or submit) a project.) Similarly, one might use make all to compile every library and link every executable within a complex project. More formal projects might use make install to copy (or move) every executable, compiled library, and header file to an appropriate subdirectory in the file system. (The make program should also compile and link as needed. Users can interact with the make program with a number of command line arguments. For example, make -d pizza will print lots of debugging information as it tries to make the pizza program. More information about these arguments can obtained from the make man page: Just type man make Introduction to makefiles Unfortunately, the make program is not very intelligent, and thus needs instructions from the developer on how each target is created. By default, make will first look for a special file in the current directory called either makefile or Makefile. A makefile is a description file that details how each target should be processed. Let's look at a sample makefile for my pizza project, which defines five targets: # Robert's pizza project. # (Characters from the first "#" to the end of the line are ignored.) pizza.o: pizza.c pizza.h # Line 4 gcc -c pizza.c # Line 5 pizza: pizza.o # Line 7 gcc -o pizza pizza.o # Line 8 all: pizza # Line 10 clean: # Line 12 rm pizza pizza.o # Line 13 install: pizza # Line 15 mv pizza /usr/local/bin # Line 16 cp pizza.h /usr/local/include # Line 17 echo "Your pizza is ready!" # Line 18 Note how simple the syntax is. The name of each target begins a new line, and is followed by a colon. After the colon is a list of file names that the target depends on. (These are often called prerequisites. It is important to emphasize that each continuation line, that is lines 5, 8, 13, 16, 17, and 18, begin with a TAB character. Suppose our directory contained only the following files makefile pizza.c pizza.h After typing make all, the make program would perform the following recursive tasks: First make will try to create the file named pizza, because pizza is a prerequisite to the target all on line 10. In order to create pizza, make will parse line 7. Since pizza.o is a prerequisite to pizza, make will needs to create pizza.o before it can create pizza. The target pizza.o has two prerequisites: pizza.c and pizza.h. (If these did not exist, make would fail, as neither of these file names is specified as a target in the makefile.) Since both of these files exist in the current directory, line 5 is executed. Assuming the syntax of the source files is correct (and gcc exists), gcc will create the object file pizza.o. Returning to Line 7, make can proceed with the task of creating file pizza. Line 8 is executed, and gcc will link pizza.o to create our pizza program. Returning to Line 10, make will now attempt to execute line 11. However, this line is blank, so make will exit. Makefile macros The make program supports many other features that allow one to create more versitile description files. One of the most useful of these are macros. A macro is symbol that can be defined within a makefile to represent a list of other symbols. For example, we might wish to create a symbol CC that exapnds to "gcc." We can accomplish this with the single line CC = gcc We could then write our pizza makefile as # Robert's pizza project. # (Characters from the first "#" to the end of the line are ignored.) CC = gcc # Line 4 pizza.o: pizza.c pizza.h # Line 6 ${CC} -c pizza.c # Line 7 pizza: pizza.o # Line 9 ${CC} -o pizza pizza.o # Line 10 all: pizza # Line 12 clean: # Line 14 rm pizza pizza.o # Line 15 install: pizza # Line 17 mv pizza /usr/local/bin # Line 18 cp pizza.h /usr/local/include # Line 19 echo "Your pizza is ready!" # Line 20 The macro CC is expanded by typing ${CC}, as shown on lines 7 and 10. (One can use parentheses instead of brackets.) Note that $CC (without brackets) would be parsed as ${C}C. Thus, the new makefile would function as it did before. However, if you wanted to use a different compiler for your project, you would now only have to edit line 4. Once a macro has been defined, you can overide its definition in the command line. For example, if we want to build our project with the compiler /usr/bin/cc, we could invoke the command make all "CC = /usr/bin/cc" It is also possible to initialize a macro variable using a UNIX shell environment variable. For csh (C-shell) or tcsh, setenv CC /usr/bin/cc make -e all (The -e option gives environment definitions precedence over macro definitions in the description file.) Suffix Rules Large projects use many source files, hence they require many object (.o) files for the build. It is cumbersome to write a pair of lines like lines 6 and 7 above, for each individual object file. To alleviate this chore, the make program will assume a set of standard rules, called suffix rules, that describe how each object file should be generated from the corresponding source. First, it's important to specify which suffixes are important to your project. These are stored in the .SUFFIXES macro, which should be placed in the description file. For example, .SUFFIXES: .o .c The default rule for generating anyfile.o from anyfile.c is written as .c.o: $(CC) $(CFLAGS) -c $< The first line specifies that a filename with a .c extension is a prerequisite to the corresponding filename with a .o extension. (This is syntactically different from before: here, no characters should appear after the colon.) In the second line, the macro $< evaluates to the prerequisite filename. Thus if we were to replace lines 6 and 7 by the above suffix rule, all would be well. We also introduced the symbol CFLAGS which can be used to modify the manner in which the source is compiled. For example, if you wanted to use an interactive source debugger, such as gdb, you might invoke make by touch *.c make all CLFAGS=-g If instead we wanted to optimize the program, we would enter touch *.c make all CLFAGS=-O (The touch command sets the modification time of each listed filename to the current instant. Why is it necessary here?) Most implementations of make have built-in, or default, suffix rules. If you want to know what they are, type make -p -f/dev/null Suffix Replacement Macros Some implementations of make support the following string substition macro. Suppose the macro SOURCE was defined to expand to a list of file names, e.g., SOURCE = pizza.c pepperoni.c cheese.c mushrooms.c anchovies.c Then the statement OBJECTS = $(SOURCE:.c=.0) would be equivalent to OBJECTS = pizza.o pepperoni.o cheese.o mushrooms.o anchovies.o Thus, every occurence of the string ".c" in SOURCE, is replaced by the string ".o". Putting things all together Here's a more sophisticated version of our original makefile # Robert's pizza project. # Line 1 SOURCE = pizza.c pepperoni.c cheese.c \ mushrooms.c anchovies.c # Line 2 HEADERS = pizza.h topping.h # Line 3 OBJECTS = ${SOURCE:.c=.o} # Line 4 .PREFIXES = .c .o # Line 6 CC = gcc CFLAGS = -O -I${HOME}/include # Line 8 RM = /usr/bin/rm -f # Line 9 MV = /usr/bin/mv -f # Line 10 CP = /usr/bin/cp -f # Line 11 .c.o: # Line 13 ${CC} -c ${CFLAGS} $< # Line 14 pizza: ${OBJECTS} # Line 16 ${CC} -o $@ ${OBJECTS} -lm # Line 17 all: pizza # Line 19 clean: # Line 21 -${RM} pizza *.o core # Line 22 install: pizza # Line 24 ${MV} pizza /usr/local/bin # Line 25 ${CP} ${HEADERS} /usr/local/include # Line 26 @echo "Your pizza is ready!" # Line 27 Internal macro symbols $? The list of prerequisites that are younger than than the target. (Cannot be used in a suffix rule.) $@ Name of the current target. (If the target is a library, the this expands to the library name.) $$@ Name of the current target. (Can only be used in a prerequisite list.) $< The prerequisite file name in a suffix rule. $* In a suffix rule, this expands to the root name of the file. $% Expands to a .o file if the target is a library. References Andrew Oram and Steve Talbott, Managing Projects with Make, O'Reilly, Sebastapol, CA, 1991.