Essentially a makefile contains a set of rules used to build an application. The first rule seen by make is used as the default rule . A rule consists of three parts : the target, its prerequisites, and the command(s) to perform: target : prereq 1 prereq 2 commands The target is the file or thing that must be made. The prerequisites or dependents are those files that must exist before the target can be successfully created. And the commands are those shell commands that will create the target from the prerequisites. Here is a rule for compiling a C file, foo.c , into an object file, foo.o : foo.o: foo.c foo.h gcc -c foo.c The target file foo.o appears before the colon . The prerequisites foo.c and foo.h appear after the colon. The command script usually appears on the following lines and is preceded by a tab character. When make is asked to evaluate a rule, it begins by finding the files indicated by the prerequisites and target. If any of the prerequisites has an associated rule, make attempts to update those first. Next, the target file is considered . If any prerequisite is newer than the target, the target is remade by executing the commands. Each command line is passed to the shell and is executed in its own subshell. If any of the commands generates an error, the building of the target is terminated and make exits. One file is considered newer than another if it has been modified more recently. Here is a program to count the number of occurrences of the words "fee," "fie," "foe," and "fum" in its input. It uses a flex scanner driven by a simple main: #include <stdio.h> extern int fee_count, fie_count, foe_count, fum_count; extern int yylex( void ); int main( int argc, char ** argv ) { yylex( ); printf( "%d %d %d %d\n", fee_count, fie_count, foe_count, fum_count ); exit( 0 ); } The scanner is very simple: int fee_count = 0; int fie_count = 0; int foe_count = 0; int fum_count = 0; %% fee fee_count++; fie fie_count++; foe foe_count++; fum fum_count++; The makefile for this program is also quite simple: count_words: count_words.o lexer.o -lfl gcc count_words.o lexer.o -lfl -ocount_words count_words.o: count_words.c gcc -c count_words.c lexer.o: lexer.c gcc -c lexer.c lexer.c: lexer.l flex -t lexer.l > lexer.c When this makefile is executed for the first time, we see: $ make gcc -c count_words.c flex -t lexer.l > lexer.c gcc -c lexer.c gcc count_words.o lexer.o -lfl -ocount_words We now have an executable program. Of course, real programs typically consist of more modules than this. Also, as you will see later, this makefile does not use most of the features of make so it's more verbose than necessary. Nevertheless, this is a functional and useful makefile . For instance, during the writing of this example, I executed the makefile several dozen times while experimenting with the program. As you look at the makefile and sample execution, you may notice that the order in which commands are executed by make are nearly the opposite to the order they occur in the makefile . This top-down style is common in makefile s. Usually the most general form of target is specified first in the makefile and the details are left for later. The make program supports this style in many ways. Chief among these is make 's two-phase execution model and recursive variables . We will discuss these in great detail in later chapters. |