make: Keeps a Set of Programs Current

 < Day Day Up > 

make: Keeps a Set of Programs Current

tip: This section covers the GNU make program

This section describes the GNU make program. Other make tools (BSN make, GNUStep make, Borland make, and so on) are available as well as similar tools such as ant (the Apache build tool). Makefiles created for GNU make are often incompatible with other make tools, which can be problematic if you are trying to compile code targeted for another platform.


In a large program with many source and header files, the files typically depend on one another in complex ways. When you change a file that other files depend on, you must recompile all dependent files. For example, you might have several source files, all of which use a single header file. When you change the header file, you must recompile each of the source files. The header file might depend on other header files, and so forth. Figure 10-3 shows a simple example of dependency relationships. Each arrow in this figure points from a file to another file that depends on it.

Figure 10-3. Dependency graph for the target form


When you are working on a large program, it can be difficult, time-consuming, and tedious to determine which modules need to be recompiled because of their dependency relationships. The make utility automates this process.

Dependency lines: target files and prerequisite files

At its simplest, make looks at dependency lines in a file named Makefile or makefile in the working directory. The dependency lines indicate relationships among files, specifying a target file that depends on one or more prerequisite files. If you have modified any of the prerequisite files more recently than their target file, make updates the target file based on construction commands that follow the dependency line. The make utility normally stops if it encounters an error during the construction process.

The file containing the updating information for the make utility is called a makefile. (See page 388 for a trivial example.) A simple makefile has the following syntax:

 target: prerequisite-list TAB construction-commands 

The dependency line consists of the target and the prerequisite-list, separated by a colon. Each construction-commands line (you may have more than one) must start with a TAB and must follow the dependency line. Long lines can be continued with a BACKSLASH ( \) as the last character on the line.

The target is the name of the file that depends on the files in the prerequisite-list. The construction-commands are regular shell commands that construct (usually compile and/or link) the target file. The make utility executes the construction-commands when the modification time of one or more files in the prerequisite-list is more recent than that of the target file.

The following example shows the dependency line and construction commands for the file named form in Figure 10-3. The form file depends on the prerequisites size.o and length.o. An appropriate gcc command constructs the target:

 form: size.o length.o TAB    gcc -o form size.o length.o 

Each of the prerequisites on one dependency line can be a target on another dependency line. For example, both size.o and length.o are targets on other dependency lines. Although the example in Figure 10-3 is simple, the nesting of dependency specifications can create a complex hierarchy that dictates relationships among many files.

The following makefile (named Makefile) corresponds to the complete dependency structure shown in Figure 10-3. The executable file form depends on two object files, and the object files each depend on their respective source files and a header file, form.h. In turn, form.h depends on two other header files.

 $ cat Makefile form: size.o length.o     gcc -o form size.o length.o size.o: size.c form.h     gcc -c size.c length.o: length.c form.h     gcc -c length.c form.h: num.h table.h     cat num.h table.h > form.h 

Although the last line would not normally be seen in a makefile, it illustrates the fact that you can put any shell command on a construction line. Because the shell processes makefiles, the command line should be one that you could enter in response to a shell prompt.

The following command builds the default target form if any of its prerequisites are more recent than their corresponding targets or if any of the targets do not exist:

 $ make 

Thus, if the file form has been deleted, make will rebuild it, regardless of the modification dates of its prerequisite files. The first target in a makefile is the default and is built when you call make without any arguments.

If you want make to rebuild a target other than the first in the makefile, you must provide that target as an argument to make. The following command rebuilds only form.h if it does not exist or if its prerequisites are more recent than the target:

 $ make form.h 

Implied Dependencies

You can rely on implied dependencies and construction commands to facilitate the job of writing a makefile. For instance, if you do not include a dependency line for an object file, make assumes that it depends on a compiler or assembler source code file. Thus, if a prerequisite for a target file is xxx.o and no dependency line identifies xxx.o as a target, make looks at the extension to determine how to build the .o file. If it finds an appropriate source file, make provides a default construction command line that calls the proper compiler or the assembler to create the object file. Table 10-1 lists some filename extensions that make recognizes and the type of file that corresponds to each suffix.

Table 10-1. Filename extensions

Filename with extension

Type of file

filename.c

C programming language source code

filename.C, filename.cc, filename.cxx, filename.c++, filename.cpp

C++ programming language source code

filename.f

Fortran programming language source code

filename.h

Header file

filename.l

flex, lex lexical analyzer generator source code

filename.o

Object module

filename.s

Assembler code

filename.sh

Shell script

filename.y

bison, yacc parser generator source code


C and C++ are traditional programming languages that are available with many Linux distributions. The bison and flex tools create command languages.

In the next example a makefile keeps the file named compute up-to-date. The make utility ignores any line that begins with a pound sign (#). Thus the first three lines of the following makefile are comment lines. The first dependency line shows that compute depends on two object files: compute.o and calc.o. The corresponding construction line gives the command make needs to produce compute. The second dependency line shows that compute.o depends not only on its C source file but also on the compute.h header file. The construction line for compute.o uses the C compiler optimizer ( O3 option). The third set of dependency and construction lines is not required. In their absence, make infers that calc.o depends on calc.c and produces the command line needed for the compilation:

 $ cat Makefile # # Makefile for compute # compute: compute.o calc.o         gcc -o compute compute.o calc.o compute.o: compute.c compute.h         gcc -c -O3 compute.c calc.o: calc.c         gcc -c calc.c clean:     rm *.o *core* *~ 

There are no prerequisites for clean, the last target. This target is commonly used to get rid of extraneous files that may be out-of-date or no longer needed, such as .o files.

Following are some sample executions of make based on the previous makefile. As the ls command shows, compute.o, calc.o, and compute are not up-to-date. Consequently the make command runs the construction commands that re-create them.

 $ ls -ltr total 22 -rw-rw----  1 alex  pubs  311 Jun 21 15:56 makefile -rw-rw----  1 alex  pubs  354 Jun 21 16:02 calc.o -rwxrwx---  1 alex  pubs 6337 Jun 21 16:04 compute -rw-rw----  1 alex  pubs   49 Jun 21 16:04 compute.h -rw-rw----  1 alex  pubs  880 Jun 21 16:04 compute.o -rw-rw----  1 alex  pubs  780 Jun 21 18:20 compute.c -rw-rw----  1 alex  pubs  179 Jun 21 18:20 calc.c $ make gcc -c - O3 compute.c gcc -c calc.c gcc -o compute compute.o calc.o 

If you run make once and then run it again without making any changes to the prerequisite files, make indicates that the program is up-to-date and does not execute any commands:

 $ make make: 'compute' is up to date. 

touch

The next example uses the touch utility to change the modification time of a prerequisite file. This simulation shows what happens when you alter the file. The make utility executes only the commands necessary to bring the out-of-date targets up-to-date:

 $ touch calc.c $ make gcc -c calc.c gcc -o compute compute.o calc.o 

In the next example, touch changes the modification time of compute.h. The make utility re-creates compute.o because it depends on compute.h and re-creates the executable because it depends on compute.o:

 $ touch compute.h $ make gcc -c - O3 compute.c gcc -o compute compute.o calc.o 

n

If you want to see what make would do if you ran it, run make with the n (no execute) option. The n option shows the commands that make would execute but it does not execute them.

t

As these examples illustrate, touch is useful when you want to fool make either into recompiling programs or into not recompiling them. You can use touch to update the modification times of all source files so that make considers nothing to be up-to-date; make will then recompile everything. Alternatively, you can use touch or the t option to make to touch all relevant files; make then considers everything to be up-to-date. Using touch in this manner is useful if the modification times of files have changed yet the files remain up-to-date (as can happen when you copy a set of files from one directory to another).

The following example uses make n several times to see what make would do if you gave a make command. The first command shows that the target, compute, is up-to-date. Next touch makes the modification dates on all the *.c files more recent than their targets and make n shows what make would do if you called it without the n option. The make t command then brings all the targets up-to-date. The final make n confirms that compute is up-to-date.

 $ make -n make: 'compute' is up to date. $ touch *.c $ make -n gcc -c -O3 compute.c gcc -c calc.c gcc -o compute compute.o calc.o $ make -t touch compute.o touch calc.o touch compute $ make -n make: 'compute' is up to date. 

j

The j (jobs) option performs a number of tasks in parallel; the numeric argument to j specifies the number of jobs or processes. Most make tasks hit the disk first and then the CPU, resulting in CPU usage dropping between compiles. On a multiprocessor system, you can reduce CPU usage by using make j n, where n is the number of CPUs plus 1. Running tasks in parallel can significantly reduce the build time for a large project.

Once you are satisfied with the program you have created, you can use the makefile to remove extraneous files. It is helpful to keep intermediate files around while you are writing and debugging a program so that you need to rebuild only the ones that change. When you will not be working on the program for a while, you can release the disk space. Using a clean target in a makefile means that you do not have to remember all the little pieces that can safely be deleted. The next example simply removes all object (.o) files:

 $ make clean rm *.o 

optional: Macros

The make utility's macro facility enables you to create and use macros within a makefile. The syntax of a macro definition is

 ID = list 

Replace ID with an identifying name, and replace list with a list of filenames. After this macro definition, $(ID) represents list in the makefile.

With a macro you can compile a program with any of several C compilers, making only a minor change to the makefile. By using the CC macro and replacing all occurrences of gcc in the makefile on page 402 with $(CC), for example, you need to assign a value only to CC to use the compiler of your choice:

 $ cat Makefile # # Makefile for compute # CC=gcc compute: compute.o calc.o         $(CC) -o compute compute.o calc.o compute.o: compute.c compute.h         $(CC) -c - O3 compute.c calc.o: calc.c         $(CC) -c calc.c clean:         rm *.o 

This example assumes that the compiler/loader flags are the same across compilers/loaders. In a more complex situation, you need to create macros for these flags or use the default values. Several commercial, high-performance compilers are available for Linux. You could specify the compiler from the Portland Group, pgcc, by replacing the CC=gcc assignment with CC=pgcc. If you do not assign a value to the CC macro, it defaults to gcc under Linux. The CC macro invokes the C compiler with only the options that you specify.

Several other macro definitions are commonly used. The CFLAGS macro sends arguments to the C compiler, LDFLAGS sends arguments to the linker (ld, or gcc o), and CPPFLAGS sends arguments to the C preprocessor and programs that use it, including gcc. The COMPILE.c macro expands to $(CC) c $(CFLAGS) $(CPPFLAGS). The LINK.c macro expands to $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS).

By default make invokes the C compiler without any options (except the c option when it is appropriate to compile but not to link a file). You can use the CFLAGS macro definition to cause make to call the C compiler with specific options. Replace options with the options you want to use:

 CFLAGS = options 

The following makefile uses macros as well as implied dependencies and constructions:

 # makefile: report, print, printf, printh # CC=gcc CFLAGS = -O3 # comment out the two lines above and uncomment the # two below when you are using the Portland Group's compiler #CC=pgcc #CFLAGS = -fast FILES = in.c out.c ratio.c process.c tally.c OBJECTS = in.o out.o ratio.o process.o tally.o HEADERS = names.h companies.h conventions.h report: $(OBJECTS)         $(LINK.c) -o report $(OBJECTS) ratio.o: $(HEADERS) process.o: $(HEADERS) tally.o: $(HEADERS) print:     pr $(FILES) $(HEADERS) | lpr printf:     pr $(FILES) | lpr printh:     pr $(HEADERS) | lpr 

Following the comment lines in this example, the makefile uses the CFLAGS macro to cause make always to use the optimizer ( O3 option) when it invokes the C compiler as the result of an implied construction. (The CC and CFLAGS definitions for the pgcc C compiler perform the same functions when they are uncommented and you are working with pgcc, except that you use fast with pgcc where you use O3 with gcc.) A construction line in a makefile overrides the corresponding implied construction line, if one exists. If you want to apply a macro to a construction command, you must include the macro in that command; see OBJECTS in the construction command for the report target. Following CFLAGS, the makefile defines the FILES, OBJECTS, and HEADERS macros. Each of these macros defines a list of files.

The first dependency line in the preceding example shows that report depends on the list of files that OBJECTS defines. The corresponding construction line links the OBJECTS and creates an executable file named report.

The next three dependency lines show that three object files depend on the list of files that HEADERS defines. Because there are no construction lines, make looks for a source code file corresponding to each object file and compiles it. These three dependency lines ensure that the object files are recompiled if any header files change.

Finally the LINK.c macro is invoked to link the executable file. If you specify any LDFLAGS, they are used in this step.

You can combine several targets on one dependency line, so these three dependency lines could have been combined into one line as follows:

 ratio.o process.o tally.o: $(HEADERS) 

The three final dependency lines in the preceding example send source and header files to the printer. They have nothing to do with compiling the report file. None of these targets (print, printf, and printh) depends on anything. When you call one of these targets from the command line, make executes the construction line following it. The following command prints all the source files that FILES defines:

 $ make printf 

You can override macros in a makefile by specifying them on the command line. The following command adds debugging symbols to all object files:

 $ make CFLAGS=-g ... 


     < Day Day Up > 


    A Practical Guide to LinuxR Commands, Editors, and Shell Programming
    A Practical Guide to LinuxR Commands, Editors, and Shell Programming
    ISBN: 131478230
    EAN: N/A
    Year: 2005
    Pages: 213

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