When writing large programs, compiling can be quite time consuming. Suppose you have to make a change to one line in one function in your code; despite the fact that you made a small change you must wait to recompile the entire program. Would it not be nice to be able to break the program up into several pieces and recompile just those that you have changed? It would be even more convenient if there were an automated way to have the pieces that you have changed recompiled and the pieces that had not changed left alone.
Make is a facility for automated maintenance of programs. Make uses a file called a makefile that specifies the dependencies between component files, and the commands that will bring all component files up to date. Suppose you have a program that is in 3 different files and you modify one of these files. If one of the other unmodified files depends on something in the modified file, then the unmodified file should be recompiled as well as the one that was modified. When there are many source files, and the dependencies between them are complex, it would be very helpful if there were a way to specify these dependencies automatically. This is the role of the make facility. Make also serves to document how the components fit together.
The makefile provides the following information:
Note that if file B depends on file A and file A is changed, then file B must be recompiled even though it was not changed. This is the type of dependency that is specified in the makefile. The makefile must be in the current working directory; make assumes that the makefile is named makefile or Makefile unless told otherwise. To run make, type:
make argumentwhere argument is one of the targets given to the left of a ":" in a dependency rule (see below). If no argument is specified, make will start with the first dependency rule in the makefile. To tell make that the makefile has a name other than the default , use the -f flag to pass the name of the makefile. If the makefile were called dactmake, the command:
make -f dactmake dactwill run make with the desired makefile, starting with the dependency rule that begins with dact.
A typical entry for a "description file" is of the form
target : components list TAB command1 TAB command2where TAB means the tab character, not the word TAB; it must come first on a command line. The target is the name of the file to be created; the components list is a list of files that are used to create the target file. This line is called a dependence rule. If make determines that the target needs to be updated, the commands following the dependence rule will be executed. Make terminates if any of the commands is unsuccessful. An example is:
dact: main.o init.o process.o process2.o gcc -ansi -o dact main.o init.o process.o process2.oThe first line tells make that dact needs to be remade if any of the files to the right of the ":" are changed. Before checking the time the files were last changed, make will look for any dependence rule lines that start with each of components (main.o, init.o, process.o, and process2.o). If it finds any such lines, it will check if those components are up to date. Make determines which files have been changed by examining the time of last modification for each of the files. After checking that all the component files are up to date, and remaking any that were not, make brings dact up to date. If any of the files to the right of the ":" ( main.o, init.o, process.o, or process2.o), were modified after the file to the left of the ":" (dact), then that file needs to be remade.
The second line tells make how it should remake the target dact, instructing it to link together the four object files.
If the component list has 0 elements then the commands given on the following lines will be executed only if you type
make targetMake will execute only those commands and then exit; for example, see the target clean in the example section below.
A complete example description file follows.
# makefile for dact # the sharp sign means the rest or the line is a comment dact: main.o init.o process.o process2.o gcc -ansi main.o init.o process.o process2.o -o dact # the next line says main.o depends on main.c # the line after it says to create main.o with the command # gcc -ansi -c main.c main.o: main.c gcc -ansi -c main.c # process.o depends on both process.c and process.h # and is created with the command gcc -ansi -c process.c process.o: process.c process.h gcc -ansi -c process.c process2.o: process2.c gcc -ansi -c process2.c # clean is a target with no components; when you make it, the # files main.o, init.o, process.o, and process2.o are removed clean: rm main.o init.o process.o process2.oRemember that will only continue if no errors are reported by the commands it executes. If nothing needs to be updated or remade, make will report that the target is up to date:
make dact dact is up to dateThe above example does not fully exploit the capabilities of the make facility. Three of the four types of statements in a description file have been discussed above. The fourth and final type of statement is a macro definition. This statement has the form:
NAME = valueThe value of a macro is accessed by ${NAME}. Macros are simply parameters used in the makefile. Lines 3-4 in the example above could be replaced with:
OBJS = mail.o init.o process.o process2.o dact : $(OBJS) gcc -ansi $(OBJS) -o main.oand the last line of the makefile could be replaced with
rm $(OBJS)
The command touch(1) updates the modification date of a file to the current time and can be used to force the remaking of a target. Just type
touch filenameand the modification date of the file filename will be changed. In the above example, even if no files had actually been modified, the command
touch process.hwould cause process.o to be remade, which would in turn require dact to be remade.
This document was originally written by Kendrick Mock, and modified by Matt Bishop.
ECS 30A, Introduction to Programming Spring Quarter 2002 Email: cs30a@cs.ucdavis.edu |