Полезная информация



TOC
BACK
FORWARD
HOME

UNIX Unleashed, Internet Edition

- 7 -

The make Utility

by Sean Drew

This chapter is intended to be an overview of the UNIX make facility. Most of the salient features of make are covered; however, you should consult your man page for additional information because versions of make tend to differ between different UNIX vendors. Following is a list of the major topics covered in this chapter.

  • Makefiles. This section gives an overview of the contents of a makefile.

  • Target Lines. This section describes how to inform the make utility what items can be built.

  • Shell Command-Lines. This section explains how to encode Unix shell statements in a makefile.

  • Macros. This section tackles how and when to define macros in makefiles.

  • make Directives. This section describes special make commands can be included in a makefile.

  • Command-Line Arguments. This section enumerates what command-line arguments can be passed to the make utility in order to alter make's behavior.

  • Different make Programs. This section reviews a few of the more commonly available versions of make that offer features not found in the "standard" make.

  • make Utilities. This section reviews a few of the more commonly available make utilities.

Introduction to make

The UNIX make utility was originally designed for maintaining C program files in order to prevent unnecessary recompilation. However, make is eminently useful for maintaining any set of files with interdependencies. For example, make can be used to maintain C++ or HTML source code. In fact, NIS (Network Information Service) uses make to maintain user information. The make utility provides a way of codifying the relationships between files as well as which commands need to be generated to bring files up to date when they are changed. The make utility provides a powerful, nonprocedural, template-based way to maintain files. The basic concept of make is akin to logic programming in languages such as Prolog. You tell make what needs to be done and supply some rules, and make figures out the rest.

Makefiles

A makefile is set of make commands. The makefile describes what set of files can be built and how to build those files. Four types of lines are allowable in a makefile: target lines, shell command lines, macro lines, and make directive lines (such as include). Comments in a makefile are denoted by the pound sign (#). When you invoke make, it looks for a file named makefile in your current working directory. If makefile does not exist, then make searches for a file named Makefile. Some UNIX versions also search for additional files, in the following order: s.makefile, SCCS/s.makefile, s.Makefile, and SCCS/s.Makefile. If you don't want to use one of the default names, other files can be used with the -f command-line option. (See the "Command-Line Options" section later in this chapter for more information.) The convention used to identify makefiles not named makefile or Makefile is to use the .mk suffix (for example, foo.mk).

Target Lines

Target lines tell make what can be built. Target lines consist of a list of targets, followed by a colon (:), followed by a list of dependencies. Although the target list can contain multiple targets, typically only one target is listed. The target list cannot be empty; however, the list of dependencies can be empty. Following are some example target lines:

singleTarget:    dependency1 dependency2         #target with 2 dependencies
target1 target2:    dependency1 dependency2         #target list 2 dependencies
target:                        #no dependencies

The dependency list consists of targets that must be older than the current target after make successfully executes. In other words, the target must be newer than any of its dependent targets. If any of the dependent targets is newer than the current target or if the dependent target does not exist, the dependent targets must be made and then the current target must be made. If the list of dependencies is empty, the target is always made.

Filename pattern matching can be used to automatically generate a dependency list for a target. The shell metacharacters asterisk (*), question mark (?), and braces ([]) can be used. For example, if parse.cc, main.cc, and io.cc were all the files ending with .cc in a directory with a makefile, then the following two target lines would be identical.

main:    parse.cc main.cc io.cc        # list all the dependent files manually
main:    *.cc                #use shell metacharacters to generate dependent list

Typically, the list contains dependent items that are used in the construction of the target. For example, the dependent list may be comprised of object files that make up an executable

program:        part.o component.o module.o

or header and source files that an object file depends on

module.o:    module.cc module.hh part.hh

However, dependent targets do not have be components of the target. For example, the dependent targets might be actions you need to perform before a target is built, such as making a backup of the current target before it is rebuilt.

Lines following a target line are usually the commands required to build the target. See the "Shell Command-Lines" section for more detail.

Library Targets

Library targets are a special type of target. A special syntax is supported for library targets (Check the man page on ar for details on using UNIX libraries or to see if your UNIX system has ar.). Library targets allow for the addressing of individual modules within the library. If a target or dependency includes parentheses (()), then the target or dependency is considered to be a library.

libfoo.a(foo.o):    foo.cc foo.hh    #the object file foo.o in library 
libfoo.a depends on foo.hh and foo.cc
libfoo.a:        libfoo(foo.o) libfoo(bar.o)    #libfoo.a depends on the 
two object files foo.o and bar.o

Some make variants allow multiple object files to be listed within the parentheses (see the following line of code). Some make variants, however, allow only one object file to be listed within the parentheses.

libfoo.a:        libfoo.a(foo.o bar.o)    #libfoo.a depends 
on the two object files foo.o and bar.o

Do not use any spaces between the parentheses, unless you can specify multiple object files within the library--in which case the spaces can only appear between the object files.

Sometimes you wish to use different compile options among different dependencies of a library target. However, make only allows a target to be defined once with a single colon. Double colons enable you to define a target multiple times with different lists of dependencies. Suppose there was a particular troublesome module, buggy.o, that needed to be compiled with debugging information, while the more stable object files did not need debugging information. The target lines

libfoo.a::        libfoo.a(foo.o bar.o)    #target list one
libfoo.a::        libfoo.a(buggy.o)        #target list two

would enable the building of foo.o and bar.o without debugging information (typically -g to most UNIX C/C++ compilers) and buggy.o with debugging information included.

Rule Targets

One of the more powerful features of the make utility is its capability to specify generic targets, also known as suffix rules, inference rules, or just simply rules. Rules are a convenient way to tell make only one time how to build certain types of targets. Consider the makefile excerpt

foo.o:    foo.cc
    CC -c foo.cc -I. -I/usr/local/include -DDEBUG +g    #shell command-line
bar.o:    bar.cc
    CC -c bar.cc -I. -I/usr/local/include -DDEBUG +g    #shell command-line
main.o:    main.cc
    CC -c main.cc -I. -I/usr/local/include -DDEBUG +g    #shell command-line
main:    foo.o bar.o main.o
    CC foo.o bar.o main.o

which has a great deal of redundancy. All of the shell command lines are identical, except for the file being compiled. This is a prime candidate for a rule. The following rule tells make how to transform a file ending in .cc to a file ending in .o.

.cc.o:
    CC -c $< -I. -I/usr/local/include -DDEBUG +g

The preceding rule makes use of a special macro, $<, which substitutes the current source file in the body of a rule (see the "Special Built-In Macros" section for more information). Applying the .cc.o rule to the previous makefile simplifies the makefile to

.cc.o:
    CC -c $< -I. -I/usr/local/include -DDEBUG +g
main:    foo.o bar.o main.o
    CC foo.o bar.o main.o

There are many default inference rules supplied by make. It is able to determine which rule to apply by applying the following algorithm:

Search the list of rules--both default and user supplied--for a rule that will build the desired file type. File type is determined by the file extension. The file extension is the set of characters after a dot (.). If a file of the specified type exists, the rule is applied. For example, if a makefile specified only two rules in the following order, a .c.o rule and a .cc.o, and make was asked to create foo.o, the following set of events would occur:

1. make would first try to apply the .c.o rule. The root of the target foo.o (foo) (the root is the filename with the first extension removed (.o)) is used to apply the .c.o rule.

2. The .c.o rule tells make that in order to make a file named root.o, a file named root.c can be employed.

3. If the file root.c exists in the current directory, then apply the rule to root.c. In the example, the file foo.c is checked for existence in the current directory.

4. If foo.c does not exist, then foo.cc is checked for existence.

5. If foo.cc does not exist, then an error is reported that the target cannot be made.

Rules can be chained together by make in order to reach a target goal. (Some versions of make do not perform rule chaining.) For example, say you are working with a CORBA IDL (Common Object Request Broker Architecture Interface Definition Language) compiler. The IDL compiler takes interface definitions as input and creates C++ source as output. For example, if you ask make to create foo.o, given a makefile that has an .idl.cc and a .cc.o rule, make does the following:

1. If foo.cc does not exist, make then looks for foo.idl to create the foo.cc file.

2. After the foo.cc file is created, the .cc.o rule is applied to reach the final goal of foo.o.

This chaining of rules is a very powerful feature of make.

Double Suffix Rules

The rules defined so far are known as double suffix rules because they contain double suffixes, such as .rtf.html. The .rtf.html rule can be used to convert a Rich Text Format file to a HyperText Markup Language File. Double suffix rules describe how to transform root.suffix1 to root.suffix2 (for example, index.rtf to index.html). Following is a list of the more commonly available double suffix rules supplied as defaults by make:

.c.o .c~.o .c~.c .c.a .c~.a .C.o .C~.o .C~.C .C.a .C~.a
.cc.o .cc~.o .cc~.cc .cc.a .cc~.a .h~.h .H~.H
.s.o .s~.o .s~.a .p.o .p~.o .p~.p .p.a .p~.a
.f.o .f~.o .f~.f .f.a .f~.a .r.o .r~.o .r~.r .r.a .r~.a
.y.o .y~.o .y.c .y~.c .l.o .l~.o .l.c

You may redefine any of the default rules supplied by make. The default rules take advantage of standard macros in order to make the default rules more generic. For example, the way a C or C++ file is compiled does not change much, other than some of the flags supplied to the compiler. The most commonly used of these macros are LDFLAGS, CFLAGS, and CXXFLAGS, which are used to parameterize the linker (ld), the C compiler (cc), and the C++ compiler (CC on HP-UX), respectively. The LIBS macro is commonly used but is not incorporated into the default rules. The LIBS macro is used to define which libraries other than the system default libraries are to be used at link time and in what order should the libraries be evaluated.

The tildes (~) in the double suffix rules refer to an SCCS (Source Code Control System) file. SCCS files have a prefix prepended to a filename, which is in direct conflict with make because make bases all of its algorithms on suffixes. For example, the file foo.cc becomes s.foo.cc when using SCCS. The tilde signals make to treat the file as an SCCS file, so that .cc~.o can be used to transform s.foo.cc to foo.o with the command make foo.o, which is preferable to the command make s.foo.o && mv s.foo.o foo.o.

Single Suffix Rules

Single suffix rules describe how to transform root.suffix1 to root (for example, cat.c to cat). The second suffix is in effect null in a single suffix rule. Single suffix rules are useful for creating programs that are composed of a single source file. In fact, if you have the CFLAGS and LDFLAGS environment variables defined, you don't need a makefile to effectively use the .c rule, as the .c rule is part of the default set of rules. Assuming that a Bourne-compatible shell and the source files cat.c, echo.c, cmp.c, and chown.c are in the current directory, the following commands build the targets cat, echo, cmp, and chown without a makefile:

% export CFLAGS="-I. -DDEBUG +g" LDFLAGS="-lfoo -lbar"
% make cat echo cmp chown

Following is a list of the more commonly available single suffix rules supplied as defaults by make:

.c .c~ .C .C~ .cc .cc~ .sh .sh~ .p .p~ .f .f~ .r .r~

Built-In Targets

Make supplies several built-in targets for modifying the behavior of make. Some of the built-in targets accept dependencies, but the dependencies are really arguments to the built-in target. For example, the arguments to .PRECIOUS are file suffixes. Table 7.1 lists the build-in targets.

Table 7.1. Built-in targets for make.

Target Target Description
.IGNORE The .IGNORE default target causes make to ignore nonzero error codes returned from the command-lines specified to build the target. The default make behavior is to cease all processing and exit when a command-line returns a nonzero status. The -i make command-line option can be used to achieve the same behavior.
.SILENT The .SILENT default target tells make to echo any of the command-lines it is executing. By default, make echoes any command-line that is not preceded by the at sign (@). The -s make command-line option can be used to achieve the same behavior.
.DEFAULT make executes any commands associated with the .DEFAULT target if no other rule can be applied. The purpose is similar to the default: case in C/C++ and C shell switch statements.
.PRECIOUS make deletes all the files it has built when it receives a signal or encounters a non-zero return code from a shell command. There are certain types of files that you do not want make to delete even if there are errors. These precious files are arguments to the .PRECIOUS target. The .PRECIOUS target may appear multiple times in a makefile, with each occurrence appending to the list of precious files. .PRECIOUS is useful for commands that generate source (lex, yacc, IDL compilers) or fetch source (RCS, SCCS, and so on).
.SUFFIXES The .SUFFIXES default target supplies make with the list of known file types that make will process. A blank .SUFFIXES target will reset the suffix list to an empty state. The .SUFFIXES target may appear multiple times in a makefile, with each occurrence appending to the list of known suffixes. The order of the suffixes is important, as that is the order rules are tried. To rearrange the order, use a blank .SUFFIX target to clear the current suffix list, then subsequent .SUFFIX targets can specify a new order. Common default suffixes are: .o .c .c~ .C .C~ .cc .cc~ .y .y~ .l .l~ .s .s~ .sh .sh~ .h .h~ .H .H~ .p .p~ .f .f~ .r .r~ .

Common Targets

By convention, there are some common targets in makefiles. These common targets are usually not files, and are known as dummy targets. One of the most common of the dummy targets is clean. The command make clean generally removes all of the built files, which are typically programs and object files. Clobber is a more severe target, which removes all files and associated directories. The command make clobber is often used to uninstall software. For makefiles that build and install software, install is often a target. The command make install usually creates the programs, installs the man pages, and copies the program to its intended location. Another common target is all. When a makefile builds several targets, make all typically builds all the targets.

Shell Command-Lines

Shell command-lines, also known more simply as commands, define the actions that are used to build a target. Any text following a semi-colon (;) on a target line is considered a command. All subsequent lines after a target that begin with a tab are also commands for the target. The comment character of a pound sign (#) is allowed within a target's command definition. For example, the following makefile excerpt shows a comment embedded in a command definition:

foo.html: foo.rtf
    rtftohtml -hx $<
#place current date in file, $$$$ expands to $$ in shell
    sed s/__DATE__/"'date'"/ $@ > foo.$$$$ && mv foo.$$$$ $@

The first line that is not a comment or does not start with a tab ends the list of commands associated with the target. Long command-lines can be continued on the next line using the backslash (\) newline sequence.

foo.html: foo.rtf
    rtftohtml -hx $<
    sed     -e "s/PAGE_DATE/'date'/"         \
        -e "s/PAGE_TITLE/$(DOCTITLE)/"         \
        -e "s/PAGE_NAME/$(HOMETITLE)/"  $@    \
    > foo.$$$$ && mv foo.$$$$ $@

Any macros embedded in the command are evaluated, and the proper value is substituted by make; as a result, the shell sees only the values of the macros.

Normally, the commands are echoed to the standard output, unless the command is preceded by the at sign (@). Use of the @ directive provides a finer grain of output control than the .SILENT target or the -s command-line option--both of which turn off all command output. The @ directive is particularly useful when issuing an echo command. The command echo Build complete at 'date' without the @ directive would produce the output

echo Build complete at 'date'
Build complete at Mon May 12 02:32:37 MST 1997

while using the @ results in the cleaner output of

Build complete at Mon May 12 02:32:37 MST 1997

Note that the @ directive, the -s option, and the .SILENT target only suppress the echoing of the command; the output of the command is still shown. The output of the following makefile snippet

target:
#echo this command to standard out
    echo echoed to standard out
#do not echo this command to standard out
    @echo not echoed to standard out

for target is

% make target
echo echoed to standard out
echoed to standard out
not echoed to standard out

The comment character can appear after shell commands; however, the comment is not a make comment. The # sign and all the following text are passed to the shell. The good news in this case is that the # sign is a comment in just about any shell, and the net effect is the same.

Macros

Macros serve the following four purposes in a makefile:

1. Macros can save you a great deal of typing. By specifying lists of information as macros, the list of information can be referenced simply by using the macro. The following makefile snippet shows a list of object files used to build program in bold:
program:    oh.o dot.o polka.o disor.o o.o whoa.o doe.o
    $(CPLUSPLUS) oh.o dot.o polka.o disor.o o.o whoa.o doe.o $(LIBS) $(LDFLAGS) -o $@
The following makefile snippet introduces a macro, OBJECTS, for the list of object files and has the macro reference bolded:
OBJECTS = oh.o dot.o polka.o disor.o o.o whoa.o doe.o
program:    $(OBJECTS)
    $(CPLUSPLUS) $(OBJECTS) $(LIBS) $(LDFLAGS) -o $@
Notice how much more succinct the second example makefile is.

2. Macros increase maintainability by allowing information to reside in only one place within the makefile. When information is in only one spot, that information needs to be changed in only one spot. For example, the list of object files needed to create a program needs to appear in the makefile in the dependency list and in the link command, as in the makefile example in step one. If a new object file, yo.o, is added to the executable, you must remember to update at least two places: the dependency list and the link command. By placing the object files in a macro, only the macro definition must be changed. The ease of updating macros is coupled with the side benefit of error reduction; if more than one part of the makefile needs to be updated, odds are that one of the parts will not be updated.

3. Macros provide a way to introduce variability into a makefile by parameterizing what is likely to change. For example, whether or not a program should be built with debugging information included can be determined through a macro. By changing the value of the macro, which can be done via the make command-line or an environment variable, the desired behavior can be achieved without modifying the makefile.

4. Macros improve the readability of makefiles. A long list of object files distorts what is really being done in a makefile. The macro not only provides a convenient shorthand, but documentation as well. Consider the example makefiles presented in step one; the second version is easier to read.

Macro definitions, in order of preference, can come from four places: make internal defaults, environment variables, the makefile(s), and the command-line. The precedence order can be changed via the -e make command-line option to have environment variables override makefile macro definitions. See the "Command-Line Options" section for a discussion of make command-line options.

Macro Syntax

See the "Command-Line Macro Definition" for information on how to define macros on the command-line. The basic syntax for defining macros within a makefile is

name = valueList

The name may consist of any combination of uppercase (A-Z) and lowercase (a-z) letters, digits (0-9), and underlines (_). Macro names are all uppercase by convention. Depending on your version of make, certain punctuation characters are allowed in a macro name, such as the caret (^) or at sign (@). Unless strange compulsions force you to name macros ^foo*@, such punctuation usage is strongly discouraged; it seldom helps readability or portability.

The equal sign (=) can migrate rather freely about the macro assignment expression because blanks and tabs surrounding the equal sign are removed. As a result of the white space removal behavior, all of the following assignments produce the same result, the string VALUE is assigned to the name NAME:

NAME=VALUE
NAME = VALUE
NAME=    VALUE
NAME    =VALUE

The valueList may contain zero, one, or more entries, as demonstrated in the following:

BLANK        =
ONE_VALUE    =    one
LIST_VALUE    =    one two three

The valueList can be quite long and the backslash (\) newline escape may be used to continue a definition on another line. If the line is continued, the newline is translated to a space by make and all subsequent white space (blanks, tabs, and newlines) are removed. Thus, the makefile

BAR=one\
                \
        space

X:
        echo $(BAR)

would produce the following output if target X were made:

echo one space
one space

Other than the white space translations mentioned previously, white space in a macro definition is preserved.

Macro definitions can use other macros. Nested definitions cannot be recursive, or make will complain.

RECURSIVE    = $(BAD)        #don't do this
BAD        = $(RECURSIVE)        #don't do this
FIRST_HALF     = first
SECOND_HALF    = second
NESTED    = $(FIRST_HALF) $(SECOND_HALF)
NESTED_AGAIN    = zero.$(FIRST_HALF).$(SECOND_HALF)

Ordering is not important when defining macros. In the preceding example, the macro NESTED could have been defined before FIRST_HALF and SECOND_HALF. A macro does need to be defined before it is used in any target line as a dependency. If a macro is defined multiple times, the last value is used. This means that a macro cannot have one value for part of the makefile and a different value for another part of the makefile. If the value of a macro needs to be changed, a recursive call to make is needed with the new value passed in the command-line, as in the following example:

MACRO_NAME=oldValue
target:
    $(MAKE) MACRO_NAME=newValue target

A macro is dereferenced by applying the dollar ($) operator and either parenthesis (()) or curly braces ({}). For example, the macro MAY could be dereferenced as $(MAY) or ${MAY}. However, in the case of single character macros, just the $ suffices, so the macro Z could be dereferenced as $Z, $(Z), or ${Z}. However, in the case of single character macros, the use of () or {} is encouraged. Single character names are not good to use in general; a more descriptive name will be appreciated by the next person to read the makefile.

If a macro is undefined or is assigned a blank value, the null string is substituted for its value. The makefile

BLANK=
X:; echo foo$(BLANK)$(UNDEFINED)bar

produces the following output for target X:

echo foobar
foobar

If you need to use a dollar sign ($) in make, it needs to be escaped with another dollar sign. Multiple consecutive occurrences of the dollar sign are allowed:

#echo environment variable $LOGNAME and process id $$
foo:
    echo $$LOGNAME $$$$

Macro Substitution

The make utility supports a simple text substitution function for macros. The syntax is :oldString=newString, which is appended immediately following the macro name in macro reference. For example, if the macro OBJS were defined as

OBJS = fuggles.o perle.o cascade.o saaz.o

the .o extension could be replaced by the .cc extension by using the macro reference, $(OBJS:.o=.cc), which would evaluate to fuggles.cc perle.cc cascade.cc saaz.cc. The macro string substitution is somewhat limited in capability, but works well for maintaining files that differ only in suffix or trailing characters.

Special Built-In Macros

The make utility provides several special built-in macros in order to allow rules to be generic. If the rules were not generic, then the filenames would be hard coded into the rule, and thus, not really a rule, because one rule would be required for every filename. The built-in macros can be referenced without parenthesis. For example, the @ macro can be referred to as $@ instead of $(@). The built-in macros may not have a value, depending at what state make is in when the macro is evaluated. Some macros are only valid during suffix rule evaluation, while other macros are only valid during regular rule evaluation. Table 7.2 lists the built-in macros.

Table 7.2. Built-in macros for make.

Macro Macro Description
$@ The value of the entire current target name is substituted for $@. However, in the case of a library target, the value is the name of the library, not the name of the archive member to be placed in the library. $@ can be used in target and suffix rules.
$% The value of the current archive member is substituted for $%, so this macro is only valid when the current target is a library. Remember that a library target has the form of lib(object.o) or lib((kernel_entry)). $% is needed because $@ evaluates to the library name for library targets. $% can be used in target and suffix rules.
$? The list of dependents that are out of date for the current target is substituted for $?. $? can be used in target and suffix rules. However, $? evaluates to possibly many names in a target rule, but only evaluates to one name in a suffix rule.
$< The current source file is substituted for $<. The current source file is the file that is out of date with respect to the current target, based on the implicit rule that is being invoked. While that sounds very complicated, it really boils down to which file is currently being manipulated to build the target. For example, in a .cc.o rule, $< would be whichever .cc file is being compiled. $< is only valid in a suffix rule or in the .DEFAULT rule.
$* The root of the current target name is substituted for $*. For example, if the target were foo.o, the root would have the suffix .o deleted for an end result of foo. $* is valid only during evaluation of inference rules.

The preceding built-in macros can have special modifiers appended to the end of the macro to return the filename or directory name portion. Use F to retrieve the filename, and D to retrieve the directory name. Note that only uppercase F and D will work. The shortcut method without parenthesis may not be used when a modifier is applied. For example, if $< evaluated to /users/dylan/foo.cc, $(<F) would return foo.cc and $(<D) would return /users/dylan. Some versions of make return a trailing slash appended to directory names, so using the previous example, $(<D) would return /users/dylan/. If the macro evaluates to multiple values, as does the $? macro, the F or D modifier is applied to each of the multiple values in turn. (Some versions of make do not support the F and D modifiers.)

In addition to the five macros previously discussed, there is the dynamic dependency macro $$@. The macro is called dynamic because it is evaluated at the time the dependency is processed. $$@ can only be used on dependency lines. The $$@ macro evaluates to the current target just as $@ does, but $$@ is allowed on the dependency line, whereas $@ is not. The $$@ macro is useful for building executables made up of only one source file, as the following makefile snippet demonstrates:

COMMANDS = cat dog say sed test true false more ar less
$(COMMANDS) : $$@.c
    $(CC) $? -o $@

The macros previously discussed have values that are supplied by make and are not modifiable by you. There are other macros that make uses but in which you can modify the default value supplied by make. The macros VPATH and SHELL fall into this category. Some versions of make have additional macros of this type; however, VPATH and SHELL are the most widely available.

VPATH is a path where make can search for dependent files. The current directory is searched first, then each of the VPATH elements is searched. VPATH uses colons (:) to delimit the list elements. For example, if

VPATH = source:../moreSource:/the/rest/of/the/source

appeared in a makefile, make would first search for dependents in the current directory, then in a subdirectory named source, followed by a sibling directory named moreSource, and then the absolute directory of /the/rest/of/the/source.

The SHELL macro tells make which shell to use when processing the command-line portions of a target. Most versions of make default to the Bourne or POSIX shell (/bin/sh). To maximize portability, it is best to write shell commands in the POSIX shell syntax and to set the SHELL variable to /bin/sh, for example, SHELL = /bin/sh. Some versions of make will only allow the use of Bourne shell syntax.

make Directives

A makefile is mostly composed of macro, command, and target lines. A fourth type of line found in makefiles is lines that are directives to make. make directives are one of the more nonstandardized areas of make. You are likely to find many incompatibilities in this area. If portability is of concern to you, you may want to avoid using make directive features.

The most common of the make directives is the include directive. The include directive enables common definitions to be written once and included. The include directive must be the first item on a line followed by a filename, as in the following example.

include /project/global.mk
include /users/sdrew/myGlobal.mk
#rest of makefile

If your version of make does not have include directives, you can fake the same behavior using multiple -f options (see the "Command-Line Options" section for a description of the -f option). The following command effectively emulates the previous example makefile:

% make -f /project/global.mk -f /users/sdrew/myGlobal.mk

If you grow tired of typing the -f command-line option, some shell trickery should relieve the drudgery. You can write an alias or shell script to automatically supply the include file options to make.


WARNING: For you C/C++ programmers, when using the include directive, do not place a pound sign (#) in front of the include directive (for example, #include foo.mk). The include line will then be interpreted as a comment by make and the file will not be included.

The comment (#) is a directive to make to ignore the line if it is the first nonwhitespace character on a line. Comments can appear after other make lines was well, as shown in the following:

foo=bar                #assignment
target:;echo $(FOO)        #target
#this whole line is a comment

Note that the comment directive is supported by all versions of make.

Command-Line Arguments

While make has methods of configuration from within a makefile, the make command-line options provide a convenient way to configure make on-the-fly. The typical sequence of make command-line arguments is shown here, although arguments may appear in any order:

make [-f makefile] [options] [macro definitions] [targets]

Note that optional items are enclosed in braces ([]).

Command-Line Options

Command-line options are indicated with a dash (-) and then the option, for example, make -e. If multiple options are needed, the options may be preceded by only one dash, make -kr, or by using a dash per option, make -k -r. Mixing option specification methods is allowed, make -e -kr. Table 7.3 lists the command-line options for make.

Table 7.3. Command-line options for make.

Option Option Description
-b Turns on compatibility mode for makefiles written before the current versions of make. The -b option is usually on by default.
-d Turns on debug mode. Debug mode is exceedingly verbose and is generally only used as a last resort when debugging a makefile. Information about file dates, internal flags, and variables are printed to the standard out.
-e Environment variables override assignments made in a makefile.
-f filename Denotes the name of a file to be used as a makefile. Multiple -f options may be used; the files are processed in the order they appear on the command line. A hyphen (-) may be used to indicate that make should read commands from the standard input. Multiple -f - options are not allowed. The -f option is the only option that requires an argument. A space must appear between the -f argument and the filename that appears afterward. The -f option can be used as a "poor man's" include directive if your version of make does not support the include directive.
-p Prints all the macro definitions, suffix rules, suffixes, and explicit description file entries to the standard output. The -p option is useful for interrogating make's set of default rules.
-i Places make into ignore mode. When make is in ignore mode, non-zero error codes returned by commands will no longer cause make to terminate the building of all targets. The ignore mode can be entered by placing the .IGNORE target in a makefile.
-k Instructs make kill work on the current target only if a non-zero error code is returned by a command. Work on the other targets may continue. This is the opposite of the -S mode. If both -k and -S are supplied, the last one specified is used. This overriding behavior provides a way to override the presence of a -S in the MAKEFLAGS environment variable.
-n Places make into no execute mode. When in no execute mode, make will just print the commands rather than execute the commands. Lines beginning with the at sign (@), which are not normally printed, will be printed. Lines that have the string $(MAKE) or ${MAKE} are executed so that all the commands can be seen.
-q Places make into question mode. make will return a zero status code if all the targets are up to date, and a non-zero status code if any one of the targets is out of date.
-r Removes built-in suffix list and built-in rules. This will put make in a pristine state, such that only user-specified rules and suffixes are used.
-s Places make in silent mode. Normally commands are executed to the standard output, unless the commands are preceded with the at (@) symbol. The -s option has the same effect as including the .SILENT target in the makefile.
-S Places make in standard error handling mode. The -S option will have make terminate building all targets if any command returns a non-zero status. If both -k and -S are supplied, the last one specified is used. This overriding behavior provides a way to override the presence of a -k in the MAKEFLAGS environment variable. -S is the default mode.
-t Places make in touch mode. When in touch mode, make will not issue the commands associated with a rule, but will simply touch the files. (Consult your man page for description of the UNIX command touch.)

Command-Line Macro Definition

Macros can be defined on the command line using the name=value syntax. Zero or more macro definitions may be supplied on the command line. Command-line macros have the highest precedence and override macros defined internally by make, macros from the current environment, and macros specified in the makefile. Command-line macros provide a convenient way of temporarily overriding current settings without changing them in the environment or in the makefile. For scripting, command-line macros help ensure consistent execution from run to run.

Command-Line Target Specification

Zero or more targets can be specified on the make command line. If no target is provided on the command line, make searches for the first nonrule target in the first makefile, and then each subsequent makefile, and tries to update the target if one is found. Targets specified on the command line should be listed in one of the makefiles currently being used by make. Each of the targets specified is updated by make in the order the arguments appeared on the command line.

Different Make Programs

While make is a very powerful tool, the "standard" versions of make have some rather gaping feature holes (for example, no conditional statements such as if). As a result, there are other versions of make available that try to fill some of the feature gaps. In addition to extra features, other make offerings offer a portability solution. You can either try to write a makefile that matches the lowest common denominator feature set while exercising the fewest bugs for various UNIX platforms, or use the same make offering on all platforms. If you are not distributing your makefile for public consumption, the latter choice is much more palatable. Some of the more commonly available make offerings are covered in this following sections.

GNU make

If you intend to use make extensively for software development, you need GNU's version of make. This is especially true if you develop software on multiple UNIX platforms. GNU processes "standard" makefiles better than most UNIX platforms "standard" offering. If after reading this section you feel you absolutely must have GNU make, then look up http://www.gnu.org in your WWW browser. You can also use your favorite Internet search engine and search for gmake or GNU make.

Conditionals

GNU make provides a full complement of if statements for conditional processing within a makefile.

Calling Shell Using $(shell)

GNU make has the handy ability to substitute the output of any shell command as though it were a macro. When used in conjunction with if statements, $(shell) is very handy for parameterizing a makefile automatically. For example,

HOSTTYPE = $(shell uname)
ifeq "$(HOSTTYPE" "HP-UX"
# config host environment
endif

Pattern Rules

Pattern rules are a powerful extension to suffix rules. The pattern rule has the form of targetPattern: dependencyPattern, which is the opposite order of a suffix rule. For example, .cc.o expressed as a pattern rule would be %.o: %.cc. The % in the pattern rule operates as the asterisk (*) wildcard operates in the shell, which allows more than just a suffix to specify a rule. For example, if you have a directory of .gif files that you wish to enlarge with a command-line utility for users with larger screens, the following makefile with a pattern rule would do the job nicely:

%_big.gif:      %.gif
        giftran -x 125 $< > $*_big.gif

GIFS     = $(shell ls *.gif)
BIG_GIFS:       $(GIFS:.gif=_big.gif)

Other Nifty Features

GNU make is loaded with far too many nifty features to list here, but some of the more commonly used are: := operator, simplified library syntax - libfoo.a(one.o two.o), and extra text processing functions.

imake

Include Make, or imake, is a pre-processor for the make utility. The C pre-processor provides functionality that make does not offer: include directives, if directives, macro functions. imake is used in the X distribution. imake works by providing a template file that is then processed to create a file for use by make. imake can be a bit of a pain to use because the documentation is somewhat poor and the extra level of indirection can be cumbersome. Because of the template ability, large project trees can be generated automatically, which can offset some of the pain of use.

If you want to get a copy of imake, try ftp://ftp.primate.wisc.edu/pub/imake-book/itools.tar.Z or ftp://ftp.primate.wisc.edu/pub/imake-book/itools.tar.gz. The tar file contains, among other files, the following: imake, makedepend, xmkmf, mkdirhier, imboot, msub, imdent. Another good place to look for imake is ftp://ftp.x.org which contains the X11 R5 distribution directory structure, so you can retrieve imake without having to pull the entire X11 distribution. You can also consult your favorite Internet search engine using the keyword imake.

nmake

Developed by AT&T, nmake has been tailored to help meet the demands of large scale C development. nmake plugs many of the standard holes of make, and has a few new twists of its own. nmake is the only make that stores compile options for use in subsequent runs of make. That enables the ability to ask make to build any file that was not built with a certain macro definition or compiler switch. Another good feature of nmake is its capability to include multiple shell lines in a single shell without your bending over backward using semicolons and backslash newline.

make Utilities

Several utilities are available to enhance the usability of make. A few of the more common utilities are briefly covered in the following sections.

makedepend

A makefile is intended to document the relationships between files as well as the rules for building those files. makedepend provides a way of automating the documentation of the file relationships by generating a dependency list for your source files. makedepend takes into account #if directives for proper dependency generation.

mkmf

mkmf is short for make makefile. mkmf examines all the source in the current directory, searches the source for dependencies, and then generate a makefile based on templates.

Summary

By now you should be familiar with the contents of a makefile and know how to use make to manage your source files. We covered how to specify targets for make and how to encode the instructions for building files into your makefile. You may want to consult the chapters on UNIX shells to help you with shell syntax. We also looked at efficiency of expression using make macros and shell metacharacters. You may wish to read Chapter 6, "The C and C++ Programming Languages" and see how make can help you manage your C and C++ source files.

make is a powerful tool for maintaining sets of files with interdependencies. Whether you are writing software or maintaining Web pages, make can make your job easier (pun intended). Investing a little time learning make can save you a lot of work.

TOCBACKFORWARDHOME


©Copyright, Macmillan Computer Publishing. All rights reserved.