COP4610: Operating Systems & Concurrent Programming | up ↑ |
The main purpose of the Unix make utility is is to determine automatically which pieces of a large program need to be recompiled, and issue the commands to recompile them. However, it is a very powerful tool that can also be adapted for other purposes.
These notes are not intended to fully explain the make utility. To learn more about make, you can start with the man pages, but beware they may be slightly different on each system you use. This is because there are different versions of make on different systems, with various special features. If you want to write portable code you should use only the standard features that are documented in the UNIX standard. (See the on-line UNIX 98 standard for detailed explanation of make.)
To prepare to use make, you must write a file called a makefile that describes the relationships among files in your program and the commands for updating each file. Typically the makefile describes the source files, how to compile them to produce object files, and how to link the object files to produce one or more programs. By default, the make utility looks for a makefile file called "makefile" or "Makefile", though the user may specify another name for the makefile.
Once a suitable makefile exists, each time you change some source files, the simple shell command make suffices to perform all necessary recompilations. The make program uses the information in the makefile and the last-modification times of the files to decide which of the files need to be updated.
# an example of a very simple makefile PROGRAMS=prog1 prog2 CCOPTS=-Wall -ansi -pedantic all: $(PROGRAMS) prog1: shared.h prog1.c gcc -Wall -ansi -pedantic -o prog1 prog1.c prog2: shared.h funcs.o prog2.c gcc -Wall -ansi -pedantic -o prog2 funcs.o prog1.c clean: rm -f $(PROGRAMS) *~ core #*# |
Observe:
The syntax of a makefile is not the same as a shell script, although there are some similarities and shell commands may be embedded in the construction rules of the makefile. In particular, in a makefile it is OK to have spaces in symbol definitions, such as CC = gcc, but if you leave spaces like that in a shell script the shell would try to execute gcc. Another difference is that to refer to the value of a make variable you use the syntax $(variablename) but to refer to the value of a shell variable you use the syntax ${variablename}.
# a makefile that uses a suffix-based pattern PROGRAMS=prog1 prog2 ... include Config .c: $(CC) $(CFLAGS) $(LDFLAGS) -o $@ mylib.o $< all: $(PROGRAMS) |
Observe:
the suffix-based general rule for compiling C programs:
.c: $(CC) $(CFLAGS) $(LDFLAGS) -o $@ myobject.o $
This rule specifies that the make utility should try to build any target file filename, if there is a corresponding file filename.c, by executing the command pattern
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ myobject.o $<
The symbols in this command will be expanded out by the make utility before it executes the command, replacing $@ by the current target. For example, if CC has been defiend to be "gcc", CFLAGS has been defined to be "-ansi -g", and LDFLAGS has been defined to be "-lsocket -lnsl", the would expand out to
gcc -ansi -g -o filename -lsocket -lnsl myobject.o filename.c
This particular rule is shown for pedagogical reasons only. These particular libraries and the object file myobject.o have no special significance.
the use of the line include Config to include configurable text from the file Config
For example, the file Config might contain the following:
CFLAGS=-ansi -g CC=gcc LDFLAGS=-lnsl -lsocket
the use of variables CC, CFLAGS, and LDFLAGS to specify compiler options
The example above could be simplified still further, if we did not want to always link in the special object file myobject.o. By default, make has certain built-in rules. One of these is:
.c: $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
T. P. Baker. ($Id) |