This chapter provides information about simplifying the recompiling and relinking processes using the make command. It is a useful utility that can save you time when managing projects.
The make program is most useful for medium-sized programming projects. It does not solve the problems of maintaining more than one source version and of describing large programs (see sccs command).
The make command assists you in maintaining a set of programs, usually pertaining to a particular software project. It does this by building up-to-date versions of programs.
In any project, you normally link programs from object files and libraries. Then, after modifying a source file, you recompile some of the sources and relink the program as often as required.
The make command simplifies the process of recompiling and relinking programs. It allows you to record, once only, specific relationships among files. You can then use the make command to automatically perform all updating tasks.
Using the make command to maintain programs, you can:
The make command requires a description file, file names, specified rules to tell the make program how to build many standard types of files, and time stamps of all system files.
The make program uses information from a description file that you create to build a file containing the completed program, which is then called a target file. The description file tells the make command how to build the target file, which files are involved, and what their relationships are to the other files in the procedure. The description file contains the following information:
By checking the dates of the parent files, the make program determines which files to create to get an up-to-date copy of the target file. If any parent file was changed more recently than the target file, the make command creates the files affected by the change, including the target file.
If you name the description file makefile or Makefile and are working in the directory containing that description file, enter:
make
to update the first target file and its parent files. Updating occurs regardless of the number of files changed since the last time the make command created the target file. In most cases, the description file is easy to write and does not change often.
To keep many different description files in the same directory, name them differently. Then, enter:
make -f Desc-File
substituting the name of the description file for the Desc-File variable.
The general form of an entry is:
target1 [target2..]:[:] [parent1..][; command]... [(tab) commands]
Items inside brackets are optional. Targets and parents are file names (strings of letters, numbers, periods, and slashes). The make command recognizes wildcard characters such as * (asterisk) and ? (question mark). Each line in the description file that contains a target file name is called a dependency line. Lines that contain commands must begin with a tab character.
Note: The make command uses the $ (dollar sign) to designate a macro. Do not use that symbol in file names of target or parent files, or in commands in the description file unless you are using a predefined make command macro.
Begin comments in the description file with a # (pound sign). The make program ignores the # and all characters that follow it. The make program also ignores blank lines.
Except for comment lines, you can enter lines longer than the line width of the input device. To continue a line on the next line, put a \ (backslash) at the end of the line to be continued.
A command is any string of characters except a # (pound sign) or a new-line character. A command can use a # only if it is in quotes. Commands can appear either after a semicolon on a dependency line or on lines beginning with a tab that immediately follows a dependency line.
When defining the command sequence for a particular target, specify one command sequence for each target in the description file, or else separate command sequences for special sets of dependencies. Do not do both.
To use one command sequence for every use of the target file, use a single : (colon) following the target name on the dependency line. For example:
test: dependency list1... command list... . . . test: dependency list2...
defines a target name, test, with a set of parent files and a set of commands to create the file. The target name, test, can appear in other places in the description file with another dependency list.
However, that name cannot have another command list in the description file. When one of the files that test depends on changes, the make command runs the commands in that one command list to create the target file named test.
To specify more than one set of commands to create a particular target file, enter more than one dependency definition. Each dependency line must have the target name, followed by :: (two colons), a dependency list, and a command list that the make command uses if any of the files in the dependency list changes. For example:
test:: dependency list1... command list1... test:: dependency list2... command list2...
defines two separate processes to create the target file, test. If any of the files in dependency list1 changes, the make command runs command list1. If any of the files in dependency list2 changes, the make command runs command list2. To avoid conflicts, a parent file cannot appear in both dependency list1 and dependency list2.
Note: The make command passes the commands from each command line to a new shell. Be careful when using commands that have meaning only within a single shell process; for example, cd and shell commands. The make program forgets these results before running the commands on the next line.
To group commands together, use the \ (backslash) at the end of a command line. The make program then continues that command line into the next line in the description file. The shell sends both of these lines to a single new shell.
To nest calls to the make program within a make command description file, include the $(MAKE) macro in one of the command lines in the file.
If the -n flag is set when the $(MAKE) macro is found, the new copy of the make command does not execute any of its commands, except another $(MAKE) macro. Use this characteristic to test a set of description files that describe a program. Enter the command:
make -n
The make program does not perform any of the program operations. However, it does write all of the steps needed to build the program, including output from lower-level calls to the make command.
To prevent the make program from writing the commands while it runs, do any of the following:
The make program normally stops if any program returns a nonzero error code. Some programs return status that has no meaning.
To prevent the make command from stopping on errors, do any of the following:
For example, a program named prog is made by compiling and linking three C language files x.c, y.c, and z.c. The x.c and y.c files share some declarations in a file named defs. The z.c file does not share those declarations. The following is an example of a description file, which creates the prog program:
# Make prog from 3 object files prog: x.o y.o z.o # Use the cc program to make prog cc x.o y.o z.o -o prog # Make x.o from 2 other files x.o: x.c defs # Use the cc program to make x.o cc -c x.c # Make y.o from 2 other files y.o: y.c defs # Use the cc program to make y.o cc -c y.c # Make z.o from z.c z.o: z.c # Use the cc program to make z.o cc -c z.c
If this file is called makefile, just enter the command:
make
to update the prog program after making changes to any of the four source files: x.c, y.c, z.c, or defs.
To make this file simpler, use the internal rules of the make program. Based on file-system naming conventions, the make command recognizes three .c files corresponding to the needed .o files. This command can also generate an object from a source file, by issuing a cc -c command.
Based on these internal rules, the description file becomes:
# Make prog from 3 object files prog: x.o y.o z.o # Use the cc program to make prog cc x.o y.o z.o -o prog # Use the file defs and the .c file # when making x.o and y.o x.o y.o: defs
The internal rules for the
make program are in a file that looks like a description
file. When the -r flag is specified, the make
program does not use the internal rules file. You must supply the rules
to create the files in your description file. The internal-rules file
contains a list of file-name suffixes (such as .o, or
.a) that the make command understands, plus rules
that tell the make command how to create a file with one suffix
from a file with another suffix. If you do not change the list,
the make command understands the following suffixes:
The list of suffixes is similar to a dependency list in a description file, and follows the fake target name .SUFFIXES. Because the make command looks at the suffixes list in left-to-right order, the order of the entries is important.
The make program uses the first entry in the list that satisfies the following two requirements:
The make program creates the name of the rule from the two suffixes of the files that the rule defines. For example, the name of the rule to transform a .c file to an .o file is .c.o.
To add more suffixes to the list, add an entry for the fake target name .SUFFIXES in the description file. For a .SUFFIXES line without any suffixes following the target name in the description file, the make command erases the current list. To change the order of the names in the list, erase the current list and then assign a new set of values to .SUFFIXES.
The following example shows a portion of the default rules file:
# Define suffixes that make knows. .SUFFIXES: .o .C .C\~ .c .c~ .f .f~ .y .y~ .l .l~ .s .s~ .sh .sh~ .h .h~ .a #Begin macro definitions for #internal macros YACC=yacc YFLAGS= ASFLAGS= LEX=lex LFLAGS= CC=cc CCC=xlC AS=as CFLAGS= CCFLAGS= # End macro definitions for # internal macros # Create a .o file from a .c # file with the cc program. c.o: $(CC) $(CFLAGS) -c $<
# Create a .o file from # a .s file with the assembler. s.o: $(AS)$(ASFLAGS) -o $@ $<
.y.o: # Use yacc to create an intermediate file $(YACC) $(YFLAGS) $< # Use cc compiler $(CC) $(CFLAGS) -c y.tab.c # Erase the intermediate file rm y.tab.c # Move to target file mv y.tab.o $@. .y.c: # Use yacc to create an intermediate file $(YACC) $(YFLAGS) $< # Move to target file mv y.tab.c $@
The make program has a
set of single-suffix rules to build source files directly into a target file
name that does not have a suffix (command files, for example). The
make program also has rules to change the following source files
with suffix to object files without a suffix:
For example, to maintain the cat program, enter:
make cat
if all of the needed source files are in the current directory.
Use the make command
to build libraries and library files. The make program
recognizes the suffix .a as a library file. The
internal rules for changing source files to library files are:
The make program uses
macro definitions in the rules file. To change these macro definitions,
enter new definitions for each macro on the command line or in the description
file. The make program uses the following macro names to
represent the language processors that it uses:
AS | For the assembler. |
CC | For the C compiler. |
CCC | For the C++ compiler. |
YACC | For the yacc command. |
LEX | For the lex command. |
The make program uses
the following macro names to represent the flags that it uses:
CFLAGS | For C compiler flags. |
CCFLAGS | For C++ compiler flags. |
YFLAGS | For yacc command flags. |
LFLAGS | For lex command flags. |
make "CC=NEWCC"
directs the make command to use the NEWCC program in place of the usual C language compiler. Similarly, the command:
make "CFLAGS=-O"
directs the make command to optimize the final object code produced by the C language compiler.
To review the internal rules that the make command uses, refer to the /usr/ccs/lib/make.cfg file.
When the make command creates a target file but cannot find commands in the description file or internal rules to create a file, it looks at the description file for default conditions. To define the commands that the make command performs in this case, use the .DEFAULT target name in the description file:
.DEFAULT: command command . . .
Because .DEFAULT is not a real target file, it is called a fake target. Use the .DEFAULT fake target name for an error-recovery routine or for a general procedure to create all files in the program that are not defined by an internal rule of the make program.
Include files other than the current description file by using the word include as the first word on any line in the description file. Follow the word with a blank or a tab, and then the file name for the make command to include in the operation.
Note: Only one file is supported per include statement.
For example:
include /home/tom/temp
include /home/tom/sample
directs the make command to read the temp and sample files and the current description file to build the target file.
Do not use more than 16 levels of nesting with the include files feature.
A macro is a name (or label) to use in place of several other names. It is a way of writing the longer string of characters in shorthand. To define a macro:
The macro definition can contain blanks before and after the = (equal sign) without affecting the result. The macro definition cannot contain a : (colon) or a tab before the = (equal sign).
The following are examples of macro definitions:
# Macro -"2" has a value of "xyz" 2 = xyz # Macro "abc" has a value of "-ll -ly" abc = -ll -ly # Macro "LIBES" has a null value LIBES =
A macro that is named, but not defined, has the same value as the null string.
After defining a macro in a description file, use the macro in description file commands by putting a $ (dollar sign) before the name of the macro. If the macro name is longer than one character, put ( ) (parentheses) or { } (braces) around it. The following are examples of using macros:
$(CFLAGS) $2 $(xy) $Z $(Z)
The last two examples in the previous list have the same effect.
The following fragment shows how to define and use some macros:
# OBJECTS is the 3 files x.o, y.o and # z.o (previously compiled) OBJECTS = x.o y.o z.o # LIBES is the standard library LIBES = -lc # prog depends on x.o y.o and z.o prog: $(OBJECTS) # Link and load the 3 files with # the standard library to make prog cc $(OBJECTS) $(LIBES) -o prog
The make program using this description file links and loads the three object files (x.o, y.o, and z.o) with the libc.a library.
A macro definition entered on the command line overrides any duplicate macro definitions in the description file. Therefore, the command:
make "LIBES= -ll"
loads the files with the lex (-11) library.
Note: When entering macros with blanks in them on the command line, put " " (double quotation marks) around the macro. Without the double quotation marks, the shell interprets the blanks as parameter separators and not as part of the macro.
The make command handles up to 10 levels of nested macro expansion. Based on the definitions in the following example:
macro1=value1
macro2=macro1
the expression $($(macro2)) would evaluate to value1.
The evaluation of a macro occurs each time the macro is referenced. It is not evaluated when it is defined. If a macro is defined but never used, it will never be evaluated. This is especially important if the macro is assigned values that will be interpreted by the shell, particularly if the value might change. A variable declaration such as:
OBJS = 'ls *.o'
could change in value if referenced at different times during the process of building or removing object files. It does not hold the value of the ls command at the time the OBJS macro is defined.
The make program has
built-in macro definitions for use in the description file. These
macros help specify variables in the description file. The
make program replaces the macros with one of the following values:
If the $@ macro is in the command sequence in the description file, the make command replaces the symbol with the full name of the current target file before passing the command to the shell to be run. The make program replaces the symbol only when it runs commands from the description file to create the target file.
If the $$@ macro is on the dependency line in a description file, the make command replaces this symbol with the label name that is on the left side of the colon in the dependency line. For example, if the following is included on a dependency line:
cat: $$@.c
the make program translates it to:
cat: cat.c
when the make command evaluates the expression. Use this macro to build a group of files, each of which has only one source file. For example, to maintain a directory of system commands, use a description file like:
# Define macro CMDS as a series # of command names CMDS = cat dd echo date cc cmp comm ar ld chown # Each command depends on a .c file $(CMDS): $$@.c # Create the new command set by compiling the out of # date files ($?) to the target file name ($@) $(CC) -O $? -o $@
The make program changes the $$(@F) macro to the file part of $@ when it runs. For example, use this symbol when maintaining the usr/include directory while using a description file in another directory. That description file is similar to the following:
# Define directory name macro INCDIR INCDIR = /usr/include # Define a group of files in the directory # with the macro name INCLUDES INCLUDES = \ $(INCDIR)/stdio.h \ $(INCDIR)/pwd.h \ $(INCDIR)/dir.h \ $(INCDIR)/a.out.h \ # Each file in the list depends on a file # of the same name in the current directory $(INCLUDES): $$(@F) # Copy the younger files from the current # directory to /usr/include cp $? $@ # Set the target files to read only status chmod 0444 $@
This description file creates a file in the /usr/include directory when the corresponding file in the current directory has been changed.
If the $? macro is in the command sequence in the description file, the make command replaces the symbol with a list of parent files that have been changed since the target file was last changed. The make program replaces the symbol only when it runs commands from the description file to create the target file.
If the $< macro is in the command sequence in the description file, the make command replaces the symbol with the name of the file that started the file creation. The file name is the name of the parent file that was out-of-date with the target file, and therefore caused the make command to create the target file again.
In addition, use a letter (D or F) after the < (less-than sign) to get either the directory name (D) or the file name (F) of the first out-of-date file. For example, if the first out-of-date file is:
/home/linda/sample.c
then the make command gives the following values:
$(<D) = /home/linda $(<F) = sample.c $< = /home/linda/sample.c
The make program replaces this symbol only when the program runs commands from its internal rules or from the .DEFAULT list.
If the $* macro is in the command sequence in the description file, the make command replaces the symbol with the file-name part (without the suffix) of the parent file that the make command is currently using to generate the target file. For example, if the make command is using the file:
test.c
then the $* macro represents the file name test.
In addition, use a letter (D or F) after the * (asterisk) to get either the directory name (D) or the file name (F) of the current file.
For example, the make command uses many files (specified either in the description file or in the internal rules) to create a target file. Only one of those files (the current file) is used at any moment. If that current file is:
/home/tom/sample.c
then the make command gives the following values for the macros:
$(*D) = /home/tom $(*F) = sample $* = /home/tom/sample
The make program replaces this symbol only when running commands from its internal rules (or from the .DEFAULT list), but not when running commands from a description file.
If the $% macro is in a description file, and the target file is an archive library member, the make command replaces the macro symbol with the name of the library member. For example, if the target file is:
lib(file.o)
then the make command replaces the $% macro with the member name, file.o.
When macros in the shell commands are defined in the description file, you can change the values that the make command assigns to the macro. To change the assignment of the macro, put a : (colon) after the macro name, followed by a replacement string. The form is as follows:
$(macro:string1=string2)
When the make command reads the macro and begins to assign the values to the macro based on the macro definition, the command replaces each string1 in the macro definition with a value of string2. For example, if the description file contains the macro definition:
FILES=test.o sample.o form.o defs
you can replace the form.o file with a new file, input.o, by using the macro in the description-file commands, as follows:
cc -o $(FILES:form.o=input.o)
Changing the value of a macro in this manner is useful when maintaining archive libraries. For more information, see the ar command.
The make command creates a file containing the completed program called a target file, using a step-by-step procedure.
The make program:
If the target file or one of the parent files is out-of-date, the make program creates the target file using one of the following:
If all files in the procedure are up-to-date when running the make program, the make command displays a message to indicate that the file is up-to-date, and then stops. If some files have changed, the make command builds only those files that are out-of-date. The command does not rebuild files that are already current.
When the make program runs commands to create a target file, it replaces macros with their values, writes each command line, and then passes the command to a new copy of the shell.
The SCCS command and file system is primarily used to control access to a file, track who altered the file, why it was altered, and what was altered. An SCCS file is any text file controlled with SCCS commands. Using non-SCCS commands to edit SCCS files can damage the SCCS files. See Chapter 23, Source Code Control System (SCCS) to learn more about SCCS.
All SCCS files use the prefix s. to set them apart from regular text files. The make program does not recognize references to prefixes of file names. Therefore, do not refer to SCCS files directly within the make command description file. The make program uses a different suffix, the ~ (tilde), to represent SCCS files. Therefore, .c~.o refers to the rule that transforms an SCCS C language source file into an object file. The internal rule is:
.c~.o: $(GET) $(GFLAGS) -p $< >$*.c $(CC) $(CFLAGS) -c $*.c -rm -f $*.c
The ~ (tilde) added to any suffix changes the file search into an SCCS file-name search, with the actual suffix named by the . (period) and all characters up to (but not including) the ~ (tilde). The GFLAGS macro passes flags to the SCCS to determine which SCCS file version to use.
The make program
recognizes the following SCCS suffixes:
.C\~ | C++ source |
.c~ | c source |
.y~ | yacc source grammar |
.s~ | Assembler source |
.sh~ | Shell |
.h~ | Header |
.f~ | FORTRAN |
.l~ | lex source |
The make program has internal rules for changing the following SCCS files:
If you specify a description file, or a file named makefile or Makefile is in the current directory, the make command does not look for a description file within SCCS. If a description file is not in the current directory and you enter the make command, the make program looks for an SCCS file named either s.makefile or s.Makefile. If either of these files are present, the make command uses a get command to direct SCCS to build the description file from that source file. When the SCCS generates the description file, the make command uses the file as a normal description file. When the make command finishes executing, it removes the created description file from the current directory.
Start the make program from the directory that contains the description file for the file to create. The variable name desc-file represents the name of that description file. Then, enter the command:
make -f desc-file
on the command line. If the name of the description file is makefile or Makefile, you do not have to use the -f flag. Enter macro definitions, flags, description file names, and target file names along with the make command on the command line as follows:
make [flags] [macro definitions] [targets]
The make program then examines the command-line entries to determine what to do. First, it looks at all macro definitions on the command line (entries that are enclosed in quotes and have equal signs in them) and assigns values to them. If the make program finds a definition for a macro on the command line different from the definition for that macro in the description file, it chooses the command-line definition for the macro.
Next, the make program looks at the flags. For more information, see the make command for a list of the flags that it recognizes.
The make program expects the remaining command-line entries to be the names of target files to be created. Any shell commands enclosed in back quotes that generate target names are performed by the make command. Then the make program creates the target files in left-to-right order. Without a target file name, the make program creates the first target file named in the description file that does not begin with a period. With more than one description file specified, the make command searches the first description file for the name of the target file.
Each time the make command runs, it reads the current environment variables and adds them to its defined macros. Using the MAKEFLAGS macro or the MFLAGS macro, the user can specify flags to be passed to the make command. If both are set, the MAKEFLAGS macro overrides the MFLAGS macro. The flags specified using these variables are passed to the make command along with any command-line options. In the case of recursive calls to the make command, using the $(MAKE) macro in the description file, the make command passes all flags with each invocation.
When the make command runs, it assigns macro definitions in the following order:
If the MAKEFLAGS environment variable is not present or null, the make command checks for a non-null value in the MFLAGS environment variable. If one of these variables has a value, the make command assumes that each letter in the value is an input flag. The make program uses these flags (except for the -f, -p, and -d flags, which cannot be set from the MAKEFLAGS or MFLAGS environment variable) to determine its operating conditions.
The following example description file could maintain the make program. The source code for the make command is spread over a number of C language source files and a yacc grammar.
# Description file for the Make program # Macro def: send to be printed P = qprt # Macro def: source filenames used FILES = Makefile version.c defs main.c \ doname.c misc.c files.c \ dosy.c gram.y lex.c gcos.c # Macro def: object filenames used OBJECTS = version.o main.o doname.o \ misc.o files.o dosys.o \ gram.o # Macro def: lint program and flags LINT = lint -p # Macro def: C compiler flags CFLAGS = -O # make depends on the files specified # in the OBJECTS macro definition make: $(OBJECTS) # Build make with the cc program cc $(CFLAGS) $(OBJECTS) -o make # Show the file sizes @size make
# The object files depend on a file # named defs $(OBJECTS): defs # The file gram.o depends on lex.c # uses internal rules to build gram.o gram.o: lex.c # Clean up the intermediate files clean: -rm *.o gram.c -du # Copy the newly created program # to /usr/bin and deletes the program # from the current directory install: @size make /usr/bin/make cp make /usr/bin/make ; rm make # Empty file "print" depends on the # files included in the macro FILES print: $(FILES) # Print the recently changed files pr $? | $P # Change the date on the empty file, # print, to show the date of the last # printing touch print
# Check the date of the old # file against the date # of the newly created file test: make -dp | grep -v TIME >1zap /usr/bin/make -dp | grep -v TIME >2zap diff 1zap 2zap rm 1zap 2zap # The program, lint, depends on the # files that are listed lint: dosys.c doname.c files.c main.c misc.c \ version.c gram.c # Run lint on the files listed # LINT is an internal macro $(LINT) dosys. doname.c files.c main.c \ misc.c version.c gram.c rm gram.c # Archive the files that build make arch: ar uv /sys/source/s2/make.a $(FILES)
The make program usually writes out each command before issuing it.
The following output results from entering the simple make command in a directory containing only the source and description file:
cc -O -c version.c cc -O -c main.c cc -O -c doname.c cc -O -c misc.c cc -O -c files.c cc -O -c dosys.c yacc gram.y mv y.tab.c gram.c cc -O -c gram.c cc version.o main.o doname.o misc.o files.o dosys.o gram.o -o make make: 63620 + 13124 + 764 + 4951 = 82459
None of the source files or grammars are specified in the description file. However, the make command uses its suffix rules to find them and then issues the needed commands. The string of digits on the last line of the previous example results from the size make command. Because the @ (at sign) on the size command in the description file prevented writing of the command, only the sizes are written.
The output can be sent to a different printer or to a file by changing the definition of the P macro on the command line, as follows:
make print "P = print -sp"
OR
make print "P = cat >zap"