Programming Utilities Guide

Description Files and Substitutions

The following section explains the most commonly used elements of the description file.

Comments

The # symbol marks the beginning of a command, and all characters on the same line after it are ignored. Blank lines and lines beginning with # are ignored.

Continuation Lines

If a noncomment line is too long, the line can be continued by using the symbol \, which must be the last character on the line. If the last character of a line is \, then it, the new line, and all following blanks and tabs are replaced by a single blank.

Macro Definitions

A macro definition is an identifier followed by the symbol =. The identifier must not be preceded by a colon (:) or a tab. The name (string of letters and digits) to the left of the = (trailing blanks and tabs are stripped) is assigned the string of characters following the =+ (leading blanks and tabs are stripped). The following are valid macro definitions:

2 = xyz 
abc = -ll -ly -lm 
LIBES =

The last definition assigns LIBES the null string. A macro that is never explicitly defined has the null string as its value. Remember, however, that some macros are explicitly defined in make's own rules. (See "Internal Rules ".)

General Form

The general form of an entry in a description file is:

target1 [target2 ...] :[:] [dependent1 ...] [; commands] 
[# ...] [ \t commands] [# ...] 
 .  .  .  

Items inside brackets can be omitted and targets and dependents are strings of letters, digits, periods, and slashes. Shell metacharacters such as * and ? are expanded when the commands are evaluated. Commands can appear either after a semicolon on a dependency line or on lines beginning with a tab (denoted above as \t) immediately following a dependency line. A command is any string of characters not including #, except when # is in quotes.

Dependency Information

A dependency line can have either a single or a double colon. A target name can appear on more than one dependency line, but all of those lines must be of the same (single or double colon) type. For the more common single colon case, a command sequence can be associated with at most one dependency line. If the target is out of date with any of the dependents on any of the lines and a command sequence is specified (even a null one following a semicolon or tab), it is executed; otherwise, a default rule can be invoked. In the double colon case, a command sequence can be associated with more than one dependency line. If the target is out of date with any of the files on a particular line, the associated commands are executed. A built-in rule can also be executed. The double-colon form is particularly useful in updating archive-type files, where the target is the archive library itself. (An example is included in the "Archive Libraries " section.)

Executable Commands

If a target must be created, the sequence of commands is executed. Normally, each command line is printed and then passed to a separate invocation of the shell after substituting for macros. The printing is suppressed in the silent mode (-s option of the make command) or if the command line in the description file begins with an @ sign. make normally stops if any command signals an error by returning a nonzero error code. Errors are ignored if the -i flag has been specified on the make command line, if the fake target name .IGNORE appears in the description file, or if the command string in the description file begins with a hyphen (-). If a program is known to return a meaningless status, a hyphen in front of the command that invokes it is appropriate. Because each command line is passed to a separate invocation of the shell, care must be taken with certain commands (cd and shell control commands, for instance) that have meaning only within a single shell process. These results are forgotten before the next line is executed.

Before issuing any command, certain internally maintained macros are set. The $@ macro is set to the full target name of the current target. The $@ macro is evaluated only for explicitly named dependencies. The $? macro is set to the string of names that were found to be younger than the target. The $? macro is evaluated when explicit rules from the makefile are evaluated. If the command was generated by an implicit rule, the $< macro is the name of the related file that caused the action; and the $* macro is the prefix shared by the current and the dependent filenames. If a file must be made but there are no explicit commands or relevant built-in rules, the commands associated with the name .DEFAULT are used. If there is no such name, make prints a message and stops.

In addition, a description file can also use the following related macros: $(@D), $(@F), $(*D), $(*F), $(<D), and $(<F).

Extensions of $*, $@, and $<

The internally generated macros $*, $@, and $< are useful generic terms for current targets and out-of-date relatives. To this list is added the following related macros: $(@D), $(@F), $(*D), $(*F), $(<D), and $(<F). The D refers to the directory part of the single character macro. The F refers to the filename part of the single character macro. These additions are useful when building hierarchical makefiles. They allow access to directory names for purposes of using the cd command of the shell. Thus, a command can be:

cd $(<D); $(MAKE) $(<F)

Output Translations

The values of macros are replaced when evaluated. The general form, where brackets indicate that the enclosed sequence is optional, is as follows:

$(macro[:string1=[string2]])

The parentheses are optional if there is no substitution specification and the macro name is a single character. If a substitution sequence is present, the value of the macro is considered to be a sequence of ``words'' separated by sequences of blanks, tabs, and new-line characters. Then, for each such word that ends with string1, string1 is replaced with string2 (or no characters if string2 is not present).

This particular substitution capability was chosen because make is sensitive to suffixes. The usefulness of this type of translation occurs when maintaining archive libraries. Now, all that is necessary is to accumulate the out-of-date members and write a shell script that can handle all the C language programs (that is, files ending in .c). The following fragment optimizes the executions of make for maintaining an archive library:

$(LIB): $(LIB)(a.o) $(LIB)(b.o) $(LIB)(c.o) 
        	$(CC) -c $(CFLAGS) $(?:.o=.c) 
        	$(AR) $(ARFLAGS) $(LIB) $? 
        	rm $?

A dependency of the preceding form is necessary for each of the different types of source files (suffixes) that define the archive library. These translations are added to offer more general use of the wealth of information that make generates.

Recursive Makefiles

Another feature of make concerns the environment and recursive invocations. If the sequence $(MAKE) appears anywhere in a shell command line, the line is executed even if the -n flag is set. Since the -n flag is exported across invocations of make (through the MAKEFLAGS variable), the only thing that is executed is the make command itself. This feature is useful when a hierarchy of makefiles describes a set of software subsystems. For testing purposes, make -n can be executed and everything that would have been done is printed, including output from lower-level invocations of make.

Suffixes and Transformation Rules

make uses an internal table of rules to learn how to transform a file with one suffix into a file with another suffix. If the -r flag is used on the make command line, the internal table is not used.

The list of suffixes is actually the dependency list for the name .SUFFIXES. make searches for a file with any of the suffixes on the list. If it finds one, make transforms it into a file with another suffix. Transformation rule names are the concatenation of the before and after suffixes. The name of the rule to transform a .r file to a .o file is thus .r.o. If the rule is present and no explicit command sequence has been given in the user's description files, the command sequence for the rule .r.o is used. If a command is generated by using one of these suffixing rules, the macro $* is given the value of the stem (everything but the suffix) of the name of the file to be made; and the macro $< is the full name of the dependent that caused the action.

The order of the suffix list is significant because the list is scanned from left to right. The first name formed that has both a file and a rule associated with it is used. If new names are to be appended, the user can add an entry for .SUFFIXES in the description file. The dependencies are added to the usual list.

A .SUFFIXES line without any dependencies deletes the current list. It is necessary to clear the current list if the order of names is to be changed.

Implicit Rules

make uses a table of suffixes and a set of transformation rules to supply default dependency information and implied commands. The default suffix list (in order) is as follows:

.o

Object file

.c

C source file

.c~

SCCS C source file

.y

yacc C source grammar

.y~

SCCS yacc C source grammar

.l

lex C source grammar

.l~

SCCS lex C source grammar

.s

Assembler source file

.s~

SCCS assembler source file

.sh

Shell file

.sh~

SCCS shel file

.h

Header file

.h~

SCCS header file

.f

FORTRAN source file

.f~

SCCS FORTRAN source file

.C

C++ source file

.C~

SCCS C++ source file

.Y

yacc C++ source grammar

.Y~

SCCS yacc C++ source grammar

.L

lex C++ source grammar

.L~

SCCS lex C++ source grammar

Figure A-1 summarizes the default transformation paths. If two paths connect a pair of suffixes, the longer one is used only if the intermediate file exists or is named in the description.

Figure A-1 Summary of Default Transformation Path

Graphic

If the file x.o is needed and an x.c is found in the description or directory, the x.o file would be compiled. If there is also an x.l, that source file is run through lex before compiling the result. However, if there is no x.c but there is an x.l, make discards the intermediate C language file and uses the direct link, as shown in Figure A-1.

It is possible to change the names of some of the compilers used in the default or the flag arguments with which they are invoked by knowing the macro names used. The compiler names are the macros AS, CC, C++C, F77, YACC, and LEX. The following command causes the newcc command to be used instead of the usual C language compiler.

$ make CC=newcc

The macros CFLAGS, YFLAGS, LFLAGS, ASFLAGS, FFLAGS, and C++FLAGS can be set to cause these commands to be issued with optional flags. Thus

$ make CFLAGS=-g

causes the cc command to include debugging information.

Archive Libraries

The make program has an interface to archive libraries. A user can name a member of a library in the following manner:

projlib(object.o)

or

projlib((entry_pt))

where the second method actually refers to an entry point of an object file within the library. (make looks through the library, locates the entry point, and translates it to the correct object filename.)

To use this procedure to maintain an archive library, the following type of makefile is required:

projlib:: projlib(pfile1.o) 
        	$(CC) -c $(CFLAGS) pfile1.c 
        	$(AR) $(ARFLAGS) projlib pfile1.o 
        	rm pfile1.o 
projlib:: projlib(pfile2.o) 
        	$(CC) -c $(CFLAGS) pfile2.c 
        	$(AR) $(ARFLAGS) projlib pfile2.o 
        	rm pfile2.o

and so on for each object. This is tedious and prone to error. Obviously, the command sequences for adding a C language file to a library are the same for each invocation; the filename being the only difference each time. This is true in most cases.

The make command also gives the user access to a rule for building libraries. The handle for the rule is the .a suffix. Thus, a .c.a rule is the rule for compiling a C language source file, adding it to the library, and removing the .o file. Similarly, the .y.a, the .s.a, and the .l.a rules rebuild yacc, assembler, and lex files. The archive rules defined internally are .c.a, .c~.a, .f.a, .f~.a, and .s~.a. (The tilde (~) syntax will be described shortly.) The user can define other needed rules in the description file.

The above two-member library is then maintained with the following shorter makefile:

projlib: projlib(pfile1.o) projlib(pfile2.o) 
        	@echo projlib up-to-date.

The internal rules are already defined to complete the preceding library maintenance. The actual .c.a rule is as follows:

c.a:
        	$(CC) -c $(CFLAGS) $< 
        	$(AR) $(ARFLAGS) $@ $*.o 
        	rm -f $*.o

Thus, the $@ macro is the .a target (projlib); the $< and $* macros are set to the out-of-date C language file, and the filename minus the suffix, (pfile1.c and pfile1). The $< macro (in the preceding rule) could have been changed to $*.c.

It is useful to go into some detail about exactly what make does when it executes the construction

projlib: projlib(pfile1.o) 
        	@echo projlib up-to-date

Assume the object in the library is out of date with respect to pfile1.c. Also, there is no pfile1.o file.

  1. make projlib.

  2. Before using make projlib, check each dependent of projlib.

  3. projlib(pfile1.o) is a dependent of projlib and needs to be generated.

  4. Before generating projlib(pfile1.o), check each dependent of projlib(pfile1.o). (There are none.)

  5. Use internal rules to try to create projlib(pfile1.o). (There is no explicit rule.) Note that projlib(pfile1.o) has a parenthesis in the name to identify the target suffix as .a. This is the key. There is no explicit .a at the end of the projlib library name. The parenthesis implies the .a suffix. In this sense, the .a is hard-wired into make.

  6. Breakup the name projlib(pfile1.o) into projlib and pfile1.o. Define two macros, $@ (projlib) and $* (pfile1).

  7. Look for a rule .X.a and a file $*.X. The first .X (in the .SUFFIXES list) that fulfills these conditions is .c so the rule is .c.a, and the file is pfile1.c. Set $< to be pfile1.c and execute the rule. In fact, make must then compile pfile1.c.

  8. The library has been updated. Execute the command associated with the projlib: dependency, namely

    @echo projlib up-to-date

To enable pfile1.o to have dependencies, the following syntax is required:

projlib(pfile1.o): $(INCDIR)/stdio.h pfile1.c

There is also a macro for referencing the archive member name when this form is used. The $% macro is evaluated each time $@ is evaluated. If there is no current archive member, $% is null. If an archive member exists, then $% evaluates to the expression between the parenthesis.

Source Code Control System (SCCS) Filenames

The syntax of make does not directly permit referencing of prefixes. For most types of files on operating operating system machines, this is acceptable since nearly everyone uses a suffix to distinguish different types of files. SCCS files are the exception. Here, s. precedes the filename part of the complete path name.

To allow make easy access to the prefix s., the symbol ~ is used as an identifier of SCCS files. Hence, .c~.o refers to the rule which transforms an SCCS C language source file into an object file. Specifically, the internal rule is $(GET) $(GFLAGS) $< $(CC) $(CFLAGS) -c $*.c rm -f $*.c .

Thus, ~ appended to any suffix transforms the file search into an SCCS filename search with the actual suffix named by the dot and all characters up to (but not including the tilde symbol.

The following SCCS suffixes are internally defined:

.c~.sh~.C~
.y~.h~.Y~
.l~.f~.L~
.s~

The following rules involving SCCS transformations are internally defined:

.c~:.s~.s:.c~:
.c~.c:.s~.a:.C~.C:
.c~.a:.s~.o:.C~.a:
.c~.o:.sh~:.C~.o:
.y~.c:.sh~.sh:.Y~.C:
.y~.o:.h~.h:.Y~.o:
.y~.y:.f~:.Y~.Y:
.l~.c.f~.f:.L~.C:
.l~.o:.f~.a:.L~.o:
.l~.l:.f~.o:.L~.L:
.s,:

Obviously, the user can define other rules and suffixes that can prove useful. The ~ provides a handle on the SCCS filename format so that this is possible.

The Null Suffix

Many programs consist of a single source file. make handles this case by the null suffix rule. To maintain the operating system program cat, a rule in the makefile of the following form is needed:

$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $< 

In fact, this .c: rule is internally defined so no makefile is necessary at all. The user only needs to enter $ make cat dd echo date (these are all operating system single-file programs) and all four C language source files are passed through the above shell command line associated with the .c: rule. The internally defined single suffix rules are:

.c:.sh:.f,:
.c,:.sh,:.C:
.s:.f:.C,:
.s,:

Others can be added in the makefile by the user.

Included Files

The make program has a capability similar to the #include directive of the C preprocessor. If the string include appears as the first seven letters of a line in a makefile and is followed by a blank or a tab, the rest of the line is assumed to be a filename, which the current invocation of make reads. Macros can be used in filenames. The file descriptors are stacked for reading include files so that no more than 16 levels of nested includes are supported.

SCCS Makefiles

Makefiles under SCCS control are accessible to make. That is, if make is typed and only a file named s.makefile or s.Makefile exists, make performs a get on the file, then reads and removes the file.

Dynamic-Dependency Parameters

A dynamic-dependency parameter has meaning only on the dependency line in a makefile. The $$@ refers to the current ``thing'' to the left of the : symbol (which is $@). Also the form $$(@F) exists, which allows access to the file part of $@. Thus, in the following example:

cat: $$@.c

the dependency is translated at execution time to the string cat.c. This is useful for building a large number of executable files, each of which has only one source file. For instance, the operating system software command directory could have a makefile such as:

CMDS = cat dd echo date cmp comm chown 

$(CMDS): $$@.c 
        	$(CC) $(CFLAGS) $? -o $@

Obviously, this is a subset of all the single file programs. For multiple file programs, a directory is usually allocated and a separate makefile is made. For any particular file that has a peculiar compilation procedure, a specific entry must exist in the makefile.

The second useful form of the dependency parameter is $$(@F). It represents the filename part of $$@. Again, it is evaluated at execution time. Its usefulness becomes evident when trying to maintain the /usr/include directory from makefile in the /usr/src/head directory. Thus, the /usr/src/head/makefile would look like:

INCDIR = /usr/include 

INCLUDES = \ 
        	$(INCDIR)/stdio.h \ 
        	$(INCDIR)/pwd.h \ 
        	$(INCDIR)/dir.h \ 
        	$(INCDIR)/a.out.h 

$(INCLUDES): $$(@F) 
        	cp $? $@ 
        	chmod 0444 $@

This would completely maintain the /usr/include directory whenever one of the above files in /usr/src/head was updated.