Programming Utilities Guide

Using make to Compile Programs

In previous examples you have seen how to compile a simple C program from a single source file, using both explicit target entries and implicit rules. Most C programs, however, are compiled from several source files. Many include library routines, either from one of the standard system libraries or from a user-supplied library.

Although it might be easier to recompile and link a single-source program using a single cc command, it is usually more convenient to compile programs with multiple sources in stages--first, by compiling each source file into a separate object (.o) file, and then by linking the object files to form an executable (a.out) file. This method requires more disk space, but subsequent (repetitive) recompilations need to be performed only on those object files for which the sources have changed, which saves time.

Simple Makefile Example

The following makefile is not elegant, but it does the job.

Table 4-4 Simple Makefile for Compiling C Sources: Everything Explicit
# Simple makefile for compiling a program from
# two C source files.  

.KEEP_STATE

functions: main.o data.o 
         cc -O -o functions main.o data.o 
main.o: main.c 
        	cc -O -c main.c 
data.o: data.c 
         cc -O -c data.c 
clean: 
        	rm functions main.o data.o

In this example, make produces the object files main.o and data.o, and the executable file functions:

$ make 
cc -o functions main.o data.o 
cc -O -c main.c 
cc -O -c data.c 

Using make's Predefined Macros

The next example performs exactly the same function, but demonstrates the use of make's predefined macros for the indicated compilation commands. Using predefined macros eliminates the need to edit makefiles when the underlying compilation environment changes. Macros also provide access to the CFLAGS macro (and other FLAGS macros) for supplying compiler options from the command line. Predefined macros are also used extensively within make's implicit rules. The predefined macros in the following makefile are listed below. [Predefined macros are used more extensively than in earlier versions of make. Not all of the predefined macros shown here are available with earlier versions. ] They are generally useful for compiling C programs.

COMPILE.C

The cc command line; composed of the values of CC, CFLAGS, and CPPFLAGS, as follows, along with the -c option.

COMPILE.c=$(CC) $(CFLAGS) $(CPPFLAGS) -c

The root of the macro name, COMPILE, is a convention used to indicate that the macro stands for a compilation command line (to generate an object, or .o file). The .c suffix is a mnemonic device to indicate that the command line applies to .c (C source) files.


Note -

Macro names that end in the string FLAGS pass options to a related compiler-command macro. It is good practice to use these macros for consistency and portability. It is also good practice to note the desired default values for them in the makefile. The complete list of all predefined macros is shown in Table 4-9.


LINK.c

The basic cc command line to link object files, such as COMPILE.c, but without the -c option and with a reference to the LDFLAGS macro:

LINK.c=$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)
CC

The value cc. (You can redefine the value to be the path name of an alternate C compiler.)

CFLAGS

Options for the cc command; none by default.

CPPFLAGS

Options for cpp; none by default.

LDFLAGS

Options for the link editor, ld; none by default.

Table 4-5 Makefile for Compiling C Sources Using Predefined Macros
# Makefile for compiling two C sources 
CFLAGS= -O 
.KEEP_STATE:

functions: main.o data.o 
          $(LINK.c) -o functions main.o data.o 
main.o: main.c 
          $(COMPILE.c) main.c 
data.o: data.c 
          $(COMPILE.c) data.c 
clean: 
         	rm functions main.o data.o

Using Implicit Rules to Simplify a Makefile: Suffix Rules

Since the command lines for compiling main.o and data.o from their .c files are now functionally equivalent to the .c.o suffix rule, their target entries are redundant; make performs the same compilation whether they appear in the makefile or not. This next version of the makefile eliminates them, relying on the .c.o rule to compile the individual object files.

Table 4-6 Makefile for Compiling C Sources Using Suffix Rules
# Makefile for a program from two C sources 
# using suffix rules.  
CFLAGS= -O 

.KEEP_STATE:

functions: main.o data.o 
         $(LINK.c) -o functions main.o data.o 
clean: 
         rm functions main.o data.o


Note -

A complete list of suffix rules appears in Table 4-8.


As make processes the dependencies main.o and data.o, it finds no target entries for them. It checks for an appropriate implicit rule to apply. In this case, make selects the .c.o rule for building a .o file from a dependency file that has the same base name and a .c suffix.


Note -

make uses the order of appearance in the suffixes list to determine which dependency file and suffix rule to use. For instance, if there are both main.c and main.s files in the directory, make uses the .c.o rule, since .c is ahead of .s in the list.


First, make scans its suffixes list to see if the suffix for the target file appears. In the case of main.o, .o appears in the list. Next, make checks for a suffix rule to build it with, and a dependency file to build it from. The dependency file has the same base name as the target, but a different suffix. In this case, while checking the .c.o rule, make finds a dependency file named main.c, so it uses that rule.

The suffixes list is a special-function target named .SUFFIXES. The various suffixes are included in the definition for the SUFFIXES macro; the dependency list for .SUFFIXES is given as a reference to this macro:

Table 4-7 The Standard Suffixes List
SUFFIXES= .o .c .c~ .cc .cc~ .C .C~ .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~ .Y .Y~ .L .L~ .SUFFIXES: $(SUFFIXES)

The following example shows a makefile for compiling a whole set of executable programs, each having just one source file. Each executable is to be built from a source file that has the same basename, and the .c suffix appended. For instance demo_1 is built from demo_1.c.


Note -

Like clean, all is a target name used by convention. It builds "all" the targets in its dependency list. Normally, make and make all are usually equivalent.


# Makefile for a set of C programs, one source 
# per program.  The source file names have ".c" 
# appended.  
CFLAGS= -O 
.KEEP_STATE:

all: demo_1 demo_2 demo_3 demo_4 demo_5

In this case, make does not find a suffix match for any of the targets (through demo_5). So, it treats each as if it had a null suffix. It then searches for a suffix rule and dependency file with a valid suffix. In the case of demo_2, it would find a file named demo_2.c. Since there is a target entry for a .c rule, along with a corresponding .c file, make uses that rule to build demo_2 from demo_2.c.

To prevent ambiguity when a target with a null suffix has an explicit dependency, make does not build it using a suffix rule. This makefile

program: zap 
zap:

produces no output:

$ make program 
$

When to Use Explicit Target Entries vs. Implicit Rules

Whenever you build a target from multiple dependency files, you must provide make with an explicit target entry that contains a rule for doing so. When building a target from a single dependency file, it is often convenient to use an implicit rule.

As the previous examples show, make readily compiles a single source file into a corresponding object file or executable. However, it has no built-in knowledge about how to link a list of object files into an executable program. Also, make compiles only those object files that it encounters in its dependency scan. It needs a starting point--a target for which each object file in the list (and ultimately, each source file) is a dependency.

So, for a target built from multiple dependency files, make needs an explicit rule that provides a collating order, along with a dependency list that accounts for its dependency files. If each of those dependency files is built from just one source, you can rely on implicit rules for them.

Implicit Rules and Dynamic Macros

make maintains a set of macros dynamically, on a target-by-target basis. These macros are used quite extensively, especially in the definitions of implicit rules. It is important to understand what they mean.


Note -

Because they are not explicitly defined in a makefile, the convention is to document dynamic macros with the $-sign prefix attached (in other words, by showing the macro reference).


They are:

$@

The name of the current target.

$?

The list of dependencies newer than the target.

$<

The name of the dependency file, as if selected by make for use with an implicit rule.

$*

The base name of the current target (the target name stripped of its suffix).

$%

For libraries, the name of the member being processed. See "Building Object Libraries " for more information.

Implicit rules use these dynamic macros in order to supply the name of a target or dependency file to a command line within the rule itself. For instance, in the .c.o rule, shown in the next example:

.c.o:
         $(COMPILE.c) $< $(OUTPUT_OPTION)

$< is replaced by the name of the dependency file (in this case the .c file) for the current target.


Note -

The macro OUTPUT_OPTION has an empty value by default. While similar to CFLAGS in function, it is provided as a separate macro intended for passing an argument to the -o compiler option to force compiler output to a given file name.


In the .c rule:

.c:
         $(LINK.c) $< -o $@

$@ is replaced with the name of the current target.

Because values for both the $< and $* macros depend upon the order of suffixes in the suffixes list, you might get surprising results when you use them in an explicit target entry. See "Suffix Replacement in Macro References " for a strictly deterministic method for deriving a file name from a related file name.

Dynamic Macro Modifiers

Dynamic macros can be modified by including F and D in the reference. If the target being processed is in the form of a pathname, $(@F) indicates the file name part, while $(@D) indicates the directory part. If there are no / characters in the target name, then $(@D) is assigned the dot character (.) as its value. For example, with the target named /tmp/test, $(@D) has the value /tmp; $(@F) has the value test.

Dynamic Macros and the Dependency List: Delayed Macro References

Dynamic macros are assigned while processing any and all targets. They can be used within the target rule as is, or in the dependency list by prepending an additional $ character to the reference. A reference beginning with $$ is called a delayed reference to a macro. For instance, the entry:

x.o y.o z.o: $$@.BAK 
         cp $@.BAK $@

could be used to derive x.o from x.o.BAK, and so forth for y.o and z.o.

Dependency List Read Twice

This technique works because make reads the dependency list twice, once as part of its initial reading of the entire makefile, and again as it processes target dependencies. In each pass through the list, it performs macro expansion. Since the dynamic macros aren't defined in the initial reading, unless references to them are delayed until the second pass, they are expanded to null strings.

The string $$ is a reference to the predefined macro `$'. This macro, conveniently enough, has the value `$'; when make resolves it in the initial reading, the string $$@ is resolved to $@. In dependency scan, when the resulting $@ macro reference has a value dynamically assigned to it, make resolves the reference to that value.

Notice that make only evaluates the target-name portion of a target entry in the first pass. A delayed macro reference as a target name produces incorrect results. The makefile:

NONE= none 
all: $(NONE) 

$$(NONE): 
         @: this target's name isn't `none'

produces the following results.

$ make 
make: Fatal error: Don't know how to make target `none'

Rules Evaluated Once

make evaluates the rule portion of a target entry only once per application of that command, at the time that the rule is executed. Here again, a delayed reference to a make macro produces incorrect results.

No Transitive Closure for Suffix Rules

There is no transitive closure for suffix rules. If you had a suffix rule for building, say, a .Y file from a .X file, and another for building a .Z file from a .Y file, make would not combine their rules to build a .Z file from a .X file. You must specify the intermediate steps as targets, although their entries can have null rules:

trans.Z: 
trans.Y:

In this example trans.Z is built from trans.Y if it exists. Without the appearance of trans.Y as a target entry, make might fail with a "don't know how to build" error, since there would be no dependency file to use. The target entry for trans.Y guarantees that make will attempt to build it when it is out of date or missing. Since no rule is supplied in the makefile, make will use the appropriate implicit rule, which in this case would be the .X.Y rule. If trans.X exists (or can be retrieved from SCCS), make rebuilds both trans.Y and trans.Z as needed.

Adding Suffix Rules

Although make supplies you with a number of useful suffix rules, you can also add new ones of your own. However, pattern-matching rules are to be preferred when adding new implicit rules (see "Pattern-Matching Rules: An Alternative to Suffix Rules ". Unless you need to write implicit rules that are compatible with earlier versions of make, you can skip the remainder of this section, which describes the traditional method of adding implicit rules to makefiles. (The procedure for adding implicit rules is given here for compatibility with previous versions of make.)

Adding a suffix rule is a two-step process. First, you must add the suffixes of both target and dependency file to the suffixes list by providing them as dependencies to the .SUFFIXES special target. Because dependency lists accumulate, you can add suffixes to the list by adding another entry for this target, for example:

.SUFFIXES:  .ms .tr

Second, you must add a target entry for the suffix rule:

ms.tr:
         troff -t -ms $< > $@

A makefile with these entries can be used to format document source files containing ms macros (.ms files) into troff output files (.tr files):

$ make doc.tr 
troff -t -ms doc.ms > doc.tr

Entries in the suffixes list are contained in the SUFFIXES macro. To insert suffixes at the head of the list, first clear its value by supplying an entry for the .SUFFIXES target that has no dependencies. This is an exception to the rule that dependency lists accumulate. You can clear a previous definition for this target by supplying a target entry with no dependencies and no rule like this:

.SUFFIXES:

You can then add another entry containing the new suffixes, followed by a reference to the SUFFIXES macro, as shown below.

.SUFFIXES:
.SUFFIXES: .ms .tr $(SUFFIXES)

Pattern-Matching Rules: An Alternative to Suffix Rules

A pattern-matching rule is similar to an implicit rule in function. Pattern-matching rules are easier to write, and more powerful, because you can specify a relationship between a target and a dependency based on prefixes (including path names) and suffixes, or both. A pattern-matching rule is a target entry of the form:

tp%ts: dp%ds
     rule

where tp and ts are the optional prefix and suffix in the target name, dp and ds are the (optional) prefix and suffix in the dependency name, and % is a wild card that stands for a base name common to both.


Note -

make checks for pattern-matching rules ahead of suffix rules. While this allows you to override the standard implicit rules, it is not recommended.


If there is no rule for building a target, make searches for a pattern-matching rule, before checking for a suffix rule. If make can use a pattern-matching rule, it does so.

If the target entry for a pattern-matching rule contains no rule, make processes the target file as if it had an explicit target entry with no rule; make therefore searches for a suffix rule, attempts to retrieve a version of the target file from SCCS, and finally, treats the target as having a null rule (flagging that target as updated in the current run).

A pattern-matching rule for formatting a troff source file into a troff output file looks like:

%.tr: %.ms 
     troff -t -ms $< > $@

make's Default Suffix Rules and Predefined Macros

The following tables show the standard set of suffix rules and predefined macros supplied to make in the default makefile, /usr/share/lib/make/make.rules.

Table 4-8 Standard Suffix Rules

Use 

Suffix Rule Name 

Command Line(s)  

Assembly Files 

.s.o$(COMPILE.s) -o $@ $<
.s$(COMPILE.s) -o $@ $<
.s.a$(COMPILE.s) -o $% $<
$(AR) $(ARFLAGS) $@ $%
$(RM) $%
.S.o$(COMPILE.S) -o $@ $<
.S.a$(COMPILE.S) -o $% $
$(AR) $(ARFLAGS) $@ $%
$(RM) $%

C Files (.c Rules) 

.c$(LINK.c) -o $@ $< $(LDLIBS)
.c.ln$(LINT.c) $(OUTPUT_OPTION) -i $<
.c.o$(COMPILE.c) $(OUTPUT_OPTION) $<
.c.a$(COMPILE.c) -o $% $<
$(AR) $(ARFLAGS) $@ $%
$(RM) $%

C++ Files 

.cc$(LINK.cc) -o $@ $< $(LDLIBS)
.cc.o$(COMPILE.cc) $(OUTPUT_OPTION) $<
.cc.a$(COMPILE.cc) -o $% $<
$(AR) $(ARFLAGS) $@ $%
$(RM) $%

C++ Files (SVr4 style) 

.C$(LINK.C) -o $@ $< $(LDFLAGS) $*.c
.C.o$(COMPILE.C) $<
.C.a$(COMPILE.C) $<
$(AR) $(ARFLAGS) $@ $*.o
$(RM) -f $*.o

FORTRAN 77 Files 

.cc.o$(LINK.f) -o $@ $< $(LDLIBS)
.cc.a$(COMPILE.f) $(OUTPUT_OPTION) $<
$(COMPILE.f) -o $% $<
$(AR) $(ARFLAGS) $@ $%

 

$(RM) $%
.F$(LINK.F) -o $@ $< $(LDLIBS)
.F.o$(COMPILE.F) $(OUTPUT_OPTION) $<
.F.a$(COMPILE.F) -o $% $<
$(AR) $(ARFLAGS) $@ $%
$(RM) $%

 

lex Files 

.l$(RM) $*.c
$(LEX.l) $< > $*.c
$(LINK.c) -o $@ $*.c $(LDLIBS)
$(RM) $*.c
.l.c$(RM) $@
$(LEX.l) $< > $@
.l.ln$(RM) $*.c
$(LEX.l) $< > $*.c
$(LINT.c) -o $@ -i $*.c
$(RM) $*.c
.l.o$(RM) $*.c
$(LEX.l) $< > $*.c
$(COMPILE.c) -o $@ $*.c
$(RM) $*.c
.L.C$(LEX) $(LFLAGS) $<
.L.o$(LEX)(LFLAGS) $<
$(COMPILE.C) lex.yy.c
.L.orm -f lex.yy.c
mv lex.yy.o $@

Modula 2 Files 

.mod$(COMPILE.mod) -o $@ -e $@ $<
.mod.o$(COMPILE.mod) -o $@ $<
.def.sym$(COMPILE.def) -o $@ $<

NeWS 

.cps.h$(CPS) $(CPSFLAGS) $*.cps

Pascal Files 

.p$(LINK.p) -o $@ $< $(LDLIBS)
.p.o$(COMPILE.p) $(OUTPUT_OPTION) $<

Ratfor Files 

.r$(LINK.r) -o $@ $< $(LDLIBS)
.r.o$(COMPILE.r) $(OUTPUT_OPTION) $<
.r.a$(COMPILE.r) -o $% $<
$(AR) $(ARFLAGS) $@ $%
$(RM) $%

Shell Scripts 

.sh $(RM) $@
cat $< >$@
chmod +x $@

yacc Files (.yc Rules) 

.y$(YACC.y) $<
$(LINK.c) -o $@ y.tab.c $(LDLIBS)
$(RM) y.tab.c
.y.c$(YACC.y) $<
mv y.tab.c $@
.y.ln$(YACC.y) $<
$(LINT.c) -o $@ -i y.tab.c
$(RM) y.tab.c
.y.o$(YACC.y) $<
$(COMPILE.c) -o $@ y.tab.c
$(RM) y.tab.c

yacc Files (SVr4) 

.Y.C$(YACC) $(YFLAGS) $<
mv y.tab.c $@
.Y.o$(YACC) $(YFLAGS) $<
$(COMPILE.c) y.tab.c
rm -f y.tab.c
mv y.tab.o $@

Table 4-9 Predefined and Dynamic Macros

Use 

Macro 

Default Value  

Library Archive 

ARar
ARFLAGSrv

Assembler Commands 

ASas
ASFLAGS
COMPILE.s$(AS) $(ASFLAGS)
COMPILE.S$(CC) $(ASFLAGS) $(CPPFLAGS) -target -c

C Compiler Commands 

CCcc
CFLAGS
CPPFLAGS
COMPILE.c$(CC) $(CFLAGS) $(CPPFLAGS) -c
LINK.c $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)

C++ Compiler Commands [For backward compatibility, the C++ macros have alternate forms. For C++C, you can instead use CCC; instead of C++FLAGS, you can use CCFLAGS; for COMPILE.C, you can use COMPILE.cc; and LINK.cc can be substituted for LINK.C. Note that these alternate forms will disappear for future releases.]

CCCCC
CCFLAGS
COMPILE.cc$(CCC) $(CCFLAGS) $(CPPFLAGS) -c
LINK.cc$(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS)

C++ SVr4 Compiler Commands 

(C++C)CC
(C++FLAGS)-O
COMPILE.C$(C++C) $(C++FLAGS) $(CPPFLAGS) -c
LINK.C$(C++C) $(C++FLAGS) $(CPPFLAGS) $(LDFLAGS) -target

FORTRAN 77 Compiler Commands 

FC in SVr4f77
FFLAGS
COMPILE.f$(FC) $(FFLAGS) -c

LINK.f  

$(FC) $(FFLAGS) $(LDFLAGS)
COMPILE.F$(FC) $(FFLAGS) $(CPPFLAGS) -c
LINK.F $(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS)

Link Editor Command 

LDld
LDFLAGS

lex Command 

LEXlex
LFLAGS
LEX.l $(LEX) $(LFLAGS) -t

lint Command 

LINTlint
LINTFLAGS
LINT.c$(LINT) $(LINTFLAGS) $(CPPFLAGS)

Modula 2 Commands 

M2Cm2c
M2FLAGS
MODFLAGS
DEFFLAGS
COMPILE.def$(M2C) $(M2FLAGS) $(DEFFLAGS)
COMPILE.mod$(M2C) $(M2FLAGS) $(MODFLAGS)

NeWS  

CPScps
CPSFLAGS

Pascal Compiler Commands 

PCpc
PFLAGS
COMPILE.p$(PC) $(PFLAGS) $(CPPFLAGS) -c
LINK.p$(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS)

Ratfor Compilation Commands 

RFLAGS
COMPILE.r$(FC) $(FFLAGS) $(RFLAGS) -c
LINK.r$(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS)

rm Command 

RMrm -f

yacc Command 

YACCyacc
YFLAGS
YACC.y$(YACC) $(YFLAGS)

Suffixes List 

SUFFIXES.o .c .c~ .cc .cc~ .C .C~ .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~ .Y .Y~ .L .L~
 

SCCS get Command 

.SCCS_GETsccs $(SCCSFLAGS) get $(SCCSGETFLAGS) $@ -G$@
SCCSGETFLAGS-s