Components of
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Notes:
The shell command make or make prog1 invokes the following pattern of execution:
make runs the rule for first target prog1 and figures its dependencies on main.o, file1.o, file2.o, and display.o.make next checks if any of the four object files are listed as targets. If yes, as in the example, it runs the rule for first prerequisite, that is, main.o, to find its dependencies.make checks whether the prerequisites of main.o have further dependencies. If no, as in the example, it checks if main.o is up to date. If not, it runs the command for main.o by compiling main.c to get the object file.make looks at the targets file1.o, file2.o and display.o and compiles these object files in a similar fashion.make returns to first target prog1. As it now has all up-to-date object files for the rule, it executes the command to build prog1.make removes (deletes) prog1 and all object files (see section on Phony Targets).make
make is a "smart" tool in terms of how it interprets its dependency rules. Every file in the chain may not need to be compiled. make looks at the time stamp for each file in the chain and compiles from the point that is required to bring every file in the chain up to date. If any file is missing, it is updated if possible.
If you haven't edited any of your files since the last time you compiled your program, and you haven't deleted your copy of the program, then make assumes that the compiler will create exactly the same program you already have, and the utility doesn't bother doing anything. This can save a tremendous amount of compiling time.
A variable is a name defined in a makefile to represent a string of text. A line of this form:
NAME = value
defines NAME as a variable with the given value. The value can be extended to multiple lines with the '\' continuation character. The variable name consists of a sequence of characters. Special characters such as ':', '#', '=', or leading or trailing white space are not allowed. Variable names are case sensitive.
We simplify the preceding makefile example (Example 1.0) by using variables as shown in the following example (Example 1.1).
|
Note: Refer to the section on Command Echoing.
In the preceding example, we have defined a value for variable objs.
objs = main.o file1.o file2.o display.o
Once the variable is defined, its value can be extracted with the $(variable) or ${variable} operators. However, if the parentheses are left off, then just the first character of the variable name is used. Therefore:
$(objs) gets the value of variable objs.$objs gets the value of variable o (if any).
make has the flexibility to use a variable that has not been defined; the value returned in this case is an empty string. Before setting a variable, make can also check whether it has been already set or not by using shorthand operator '?='. The following example checks whether foo has been already set or not:
foo ?= bar
If not, make will assign it a value.
Two more advanced features of reference variables are substitution reference and computed variable names.
A substitution reference substitutes the value of a variable with alterations that you specify. It has the form '$(var:x=y)' and its meaning is to take the value of the variable var, replace every x at the end of a word with y in that value, and substitute the resulting string. The following example sets bar to a.c b.c c.c:
foo := a.o b.o c.o bar := $(foo:.o=.c)
A variable can be referenced inside the name of another variable; this is called a computed variable name.
Example:
a = b
b = c
x := $($(a))
This example defines x as 'c'. The $(a) expands to 'b', so $($(a)) becomes $(b); and $(b) expands to 'c'.
There are two ways in which a variable can have a value, as recursively expanded variables and simply expanded variables.
|
Often we need to add more text to an existing variable. The shorthand operator '+=' provides this flexibility. In the following example, it takes the value of objs and adds the text file3.o to it:
objs += file3.o
Thus we can set objs to main.o file1.o file2.o display.o file3.o:
objs = main.o file1.o file2.o display.o objs += file3.o
These variables have values computed afresh for each rule that is executed, based on the target and the prerequisites of the rule. In Example 1.2, which follows in the section on Implicit Rules, '$@' is used for the object file name and '$+' for the source file names.
Commonly used automatic variables include the following.
|
This feature defines a different value for the same variable, depending on the target make is building. The values are available locally within the context of the target's command script. It is of the form:
target ... : variable-assignment
The following statement sets CFLAGS to -g in the command script for prog1 and its prerequisites:
prog1 : CFLAGS = -g prog1 : main.o file1.o file2.o display.o
This feature allows you to define a variable for any target that matches the specific pattern. A pattern is represented as '%'. The following example assigns CFLAGS the value of -o for all the targets matching the pattern %.o:
%.o : CFLAGS = -o
(For further information, see section on Additional References.)
Implicit rules tell make how to use customary techniques so that you do not have to specify them in detail when you want to use them. One of the implicit rules updates a '.o' file from a '.c' file using the cc -c command. Using implicit rules, the preceding example with makefile can be written as follows (Example 1.2).
|
Notes:
makefile with different compilers.objs is defined and equated with all the object files.make how to convert *.c file to *.o file.make has a built-in pattern for converting *.h file to dependent *.o file.make deletes prog1 and all object files in the existing directory (see Phony Targets).
The commands build in implicit rules that use certain predefined variables, which fall in two classes: those that are names of programs (like CC) and those that contain arguments for the programs (like CFLAGS).
Some of the variables used as names of programs in built-in rules are as follows.
|
Here are some of the variables whose values are additional arguments for the program.
|
The define directive allows you to set the value of a variable. The define directive is followed on the same line only by the name of the variable. The value appears on the following lines. The endef in the last line marks the end of define. define works just like '=', for example:
define two-lines echo foo echo $(bar) endef
The difference between ordinary variable assignment and the define directive is that the value in an ordinary assignment cannot contain a new line, but the new lines that separate the lines of the value in define become part of the variable's value.
The previous example is functionally equivalent to this:
two-lines = echo foo; echo $(bar)
Two commands separated by a semicolon behave much like two separate shell commands. However, note that using two separate lines means make will invoke the shell twice, running an independent subshell for each line.
The include directive tells make to suspend reading the current makefile and read from each listed file in turn. After finishing, make resumes reading the makefile in which the directive appears. In the form:
include filenames...
filenames can contain shell file name patterns.
For example, if you have three '.mk' files, 'x.mk', 'y.mk', and 'z.mk', and $(bar) expands to bish bash, then the following expression:
include foo *.mk $(bar)
is equivalent to:
include foo x.mk y.mk z.mk bish bash
One use of include directives is to assign a common set of variable definitions for programs handled by individual "makefile" files.
The override directive can set the variable in the makefile, which is already set with a command argument. It is of the form:
override variable = value
To append more text to a variable defined on the command line, use:
override variable += more text
This directive was invented to alter and add to values that the user specifies with command arguments. For example, suppose you always want the -g switch when you run the C compiler, but you would like to allow the user to specify the other switches with a command argument as usual. You could use this override directive:
override CFLAGS += -g
make prints each line before it is executed, called echoing. Echoing can be suppressed by '@' when starting. The echo can also be used to indicate progress through the makefile. For example:
echo prog1 : make complete
With the -n flag, make only echoes commands and does not execute them. Only in this case, even the commands starting with '@' are also printed. To prevent all echoing, the -s flag is used. Output acts as if all commands started with '@'.
The phony target is not a name of a file but the action to be executed with an explicit request. Commands in phony targets do not create any target, but the command will be executed every time the target comes up for remaking. For example:
clean :
rm prog1 *.o
deletes the prog1 and all the object files when the command is explicitly invoked with:
make clean
As the rm command does not create a file named clean, probably no such file will ever exist. But if anything ever does create a file named clean in this directory, the phony target will cease to work. The file clean would inevitably be considered up-to-date as it has no prerequisites, and its command would not be executed.
To avoid this problem, the target is declared as .PHONY. In the following example, make clean will run the commands regardless of whether or not there is a file named clean :
.PHONY : clean clean : rm prog1 *.o
A single file name can specify many files using wildcard characters. make uses wildcard characters '*', '?', and '[...]' in target prerequisites, commands, and variables. Wildcards can be used in the commands of a rule, where they are expanded by the shell. For example, here is a rule to delete all the object files:
clean:
rm *.o
Wildcard expansion does not occur when you define a variable. Thus, if you write this:
objects = *.o
then the value of the variable objects is the actual string '*.o'.
After a command is executed, make checks the exit status to verify successful completion, and sends the next command line to a new shell. If an error is encountered on return, make gives up on the current rule and perhaps on all rules. To ignore errors in a command line, write a '-' at the beginning of the line's text (after the initial tab). The '-' is discarded before the command is passed to the shell for execution. The following example causes rm to continue even if it is unable to remove a file:
clean:
-rm -f *.o
A similar feature can be achieved by combining the -i flag with make; errors are ignored in all commands of all rules.
By default, make executes only one command at a time, waiting for it to finish before executing the next. Combined with '-j' option, make can execute many commands simultaneously. An integer suffix to '-j' option specifies the number of jobs that can be executed at once.
make
Archive files called member are used as subroutine libraries for linking. They are maintained with the program ar. In this form:
archive(member)
an individual member file can be used as a target or prerequisite in make. In the following example, the rule says to create a member foo.o in archive sublib by copying the file foo.o:
sublib(foo.o) : foo.o ar cr sublib foo.o
This document is a starting kit for using makefile, and it covers all the necessary building blocks. make has many features that can be explored in the following references. Experiment with your own new variations on makefile.
makeSuminder Singh Ahuja is a software engineer with four years of experience in high-volume transaction technologies and databases such as TPF, IBM 370/390, VM/CMS, and UNIX. He has worked three years for Galileo, dealing in airline reservation solutions. Feel free to contact him at Suminder: suminder@rediffmail.com
|
| ||||||||||||