2.3 Finding Files with VPATH and vpath

     

Our examples so far have been simple enough that the makefile and sources all lived in a single directory. Real world programs are more complex (when's the last time you worked on a single directory project?). Let's refactor our example and create a more realistic file layout. We can modify our word counting program by refactoring main into a function called counter .

 #include <lexer.h> #include <counter.h> void counter( int counts[4] ) {     while ( yylex( ) )         ;     counts[0] = fee_count;     counts[1] = fie_count;     counts[2] = foe_count;     counts[3] = fum_count; } 

A reusable library function should have a declaration in a header file, so let's create counter.h containing our declaration:

 #ifdef COUNTER_H_ #define COUNTER_H_ extern void counter( int counts[4] ); #endif 

We can also place the declarations for our lexer.l symbols in lexer.h :

 #ifndef LEXER_H_ #define LEXER_H_ extern int fee_count, fie_count, foe_count, fum_count; extern int yylex( void ); #endif 

In a traditional source tree layout the header files are placed in an include directory and the source is placed in a src directory. We'll do this and put our makefile in the parent directory. Our example program now has the layout shown in Figure 2-1.

Figure 2-1. Example source tree layout
figs/mke3_0201.gif

Since our source files now include header files, these new dependencies should be recorded in our makefile so that when a header file is modified, the corresponding object file will be updated.

 count_words: count_words.o counter.o lexer.o -lfl         gcc $^ -o $@ count_words.o: count_words.c include/counter.h         gcc -c $< counter.o: counter.c include/counter.h include/lexer.h         gcc -c $< lexer.o: lexer.c include/lexer.h         gcc -c $< lexer.c: lexer.l         flex -t $< > $@ 

Now when we run our makefile , we get:

 $  make  make: *** No rule to make target `count_words.c', needed by `count_words.o'.  Stop. 

Oops, what happened ? The makefile is trying to update count_words.c , but that's a source file! Let's "play make ." Our first prerequisite is count_words.o . We see the file is missing and look for a rule to create it. The explicit rule for creating count_words.o references count_words.c . But why can't make find the source file? Because the source file is in the src directory not the current directory. Unless you direct it otherwise , make will look in the current directory for its targets and prerequisites. How do we get make to look in the src directory for source files? Or, more generally , how do we tell make where our source code is?

You can tell make to look in different directories for its source files using the VPATH and vpath features. To fix our immediate problem, we can add a VPATH assignment to the makefile :

 VPATH = src 

This indicates that make should look in the directory src if the files it wants are not in the current directory. Now when we run our makefile , we get:

 $  make  gcc -c src/count_words.c -o count_words.o src/count_words.c:2:21: counter.h: No such file or directory make: *** [count_words.o] Error 1 

Notice that make now successfully tries to compile the first file, filling in correctly the relative path to the source. This is another reason to use automatic variables : make cannot use the appropriate path to the source if you hardcode the filename. Unfortunately, the compilation dies because gcc can't find the include file. We can fix this latest problem by "customizing" the implicit compilation rule with the appropriate -I option:

 CPPFLAGS = -I include 

Now the build succeeds:

 $  make  gcc -I include -c src/count_words.c -o count_words.o gcc -I include -c src/counter.c -o counter.o flex -t src/lexer.l > lexer.c gcc -I include -c lexer.c -o lexer.o gcc count_words.o counter.o lexer.o /lib/libfl.a -o count_words 

The VPATH variable consists of a list of directories to search when make needs a file. The list will be searched for targets as well as prerequisites, but not for files mentioned in command scripts. The list of directories can be separated by spaces or colons on Unix and separated by spaces or semicolons on Windows. I prefer to use spaces since that works on all systems and we can avoid the whole colon /semicolon imbroglio. Also, the directories are easier to read when separated by spaces.

The VPATH variable is good because it solved our searching problem above, but it is a rather large hammer . make will search each directory for any file it needs. If a file of the same name exists in multiple places in the VPATH list, make grabs the first one. Sometimes this can be a problem.

The vpath directive is a more precise way to achieve our goals. The syntax of this directive is:

 vpath   pattern directory-list   

So our previous VPATH use can be rewritten as:

 vpath %.c src vpath %.h include 

Now we've told make that it should search for .c files in the src directory and we've also added a line to search for .h files in the include directory (so we can remove the include/ from our header file prerequisites). In more complex applications, this control can save a lot of headache and debugging time.

Here we used vpath to handle the problem of finding source that is distributed among several directories. There is a related but different problem of how to build an application so that the object files are written into a "binary tree" while the source files live in a separate "source tree." Proper use of vpath can also help to solve this new problem, but the task quickly becomes complex and vpath alone is not sufficient. We'll discuss this problem in detail in later sections.



Managing Projects with GNU make
Managing Projects with GNU Make (Nutshell Handbooks)
ISBN: 0596006101
EAN: 2147483647
Year: 2003
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net