Makefiles


make is a tool that helps you compile and track dependencies for projects with many source files. To use make, you must specify your program’s dependencies and compile options in a file. By default, make looks for a file called makefile or Makefile in the current directory (You can use a different filename by passing make the -f argument along with the filename.)

make allows you to compile your project with a single short command, and saves you time by recompiling only the source files that have changed since your last compile. make will also recompile any files that you told it depend on changed files. make determines whether or not a file should be recompiled by comparing a file’s modification date with the modification date of the files on which it depends. While there are multiple versions of the make utility, this section focuses on GNU make.

A Short Makefile

Here is an example of a makefile for a program containing a single C file and a single header:

 # makefile for a single C file and a single header SOURCES=hello.c INCLUDE=include/hello.h PRODUCT=$(HOME)/bin/hello CC=gcc CFLAGS=-g -o # Running the command "make" will use this rule $ (PRODUCT): $ (SOURCES) $ (INCLUDE)        $(CC) $ (CFLAGS) -o $ (PRODUCT) $ (SOURCES) # Running the command "make clean" will use this rule .PHONY: clean clean:       rm -f *.o $ (PRODUCT)

Running make on this makefile will use gcc with debugging symbols and optimization to compile the file hello.c and create a binary called ~/bin/hello.

If you save this file as makefile in the same directory as a file called hello.c, place a header file called hello.h in the subdirectory include, and run the command make,

 $ make gcc -g -o -o /home/nate/bin/hello hello.c

it will display and execute a line like the one shown and generate ~/bin/hello. If you run the make command a second time, then you will get a response like

 $ make make: '/home/nate/bin/hello' is up to date.

because your executable will be up to date. If you execute the command

 $ make clean rm -f *.o /home/nate/bin/hello

then it will delete any object files in this directory as well as the generated program file.

While generating a makefile is a lot more work than just typing in a command to compile hello.c, the time investment really pays off as your project grows. You can also reuse your makefiles for new projects, just by changing the file names.

Makefile Syntax

Makefile syntax is similar to that of shell scripts. Makefiles can contain comments, variables, dependencies, and commands.

Comments

You can insert comments into a makefile with a # (pound sign). Everything on the line after the # is ignored by make.

Variables

The make program allows you to define named variables similar to those used in the shell. For example, if you define SOURCES=hello.c, the value of that variable, $ (SOURCES), is hello.c.

You can also do pattern replacement in variable assignments. The assignment

 OBJECTS=${SOURCES:.cpp=.o}

will take a list of files from the variable SOURCES and will assign it to OBJECTS with the .cpp extensions replaced with .o.

The make program has some built-in knowledge about program development. You can get a listing of the built-in rules and variable values by running make -p. make knows that files ending in a .c suffix should be built as C source files, those ending in .cpp, .cc, or .C are C++ source files, those ending in .o are object files, and those ending in .a are assembler files. Although make allows you to choose your own variable names, it will use default values for variables such as CC and CFLAGS if they are not defined.

Dependencies

After assigning variables, our example specifies dependencies. You specify dependencies by placing a target filename on the left, followed by a colon, and then a list of the filenames on which the target file depends. In our example,

 $ (PRODUCT): $ (SOURCES) $ (INCLUDE)

the “PRODUCT” variable depends on the “SOURCES” variable and on the “INCLUDE” variable. After substituting the variables, this says that the file $HOME/bin/hello depends on the files hello.c and include/hello.h. If the target file doesn’t exist or is older than a file that it depends on, then make will attempt to rebuild the target.

It’s also possible to create target names that don’t generate a file. These targets are called phony. If you mark a target as .PHONY, then it will run without checking dependencies. For example,

 .PHONY: clean

will cause the clean target to run even if there is a file named clean in the directory that is up to date.

Dependencies combined with commands, which are described in the section that follows, form rules.

Commands

The dependency line can be followed by one or more shell command lines that are executed if any of the dependencies have changed. These commands are often used to rebuild the target. For example,

 gcc -g -o -o hello hello.c

is a command to build hello.

Command lines must be indented at least one tab stop from the left margin. (Tabs are required; the equivalent number of spaces won’t work.) Indenting these lines with spaces will result in an error message:

 Makefile:8: *** missing separator (did you mean TAB instead of 8 spaces?). Stop.

A rule consists of a dependency line and the commands that follow it. They are often used to remake a target file, but they can also perform an arbitrary command. For example,

 clean:       rm -f *.o $ (PRODUCT)

executes the command rm when make clean is run.

Makefiles with Multiple Dependencies

If you have multiple source files, you could extend the preceding example by adding the extra .c files to the end of the SOURCES line. Unfortunately, this approach would force a full recompile whenever any of the source files are changed, even if you only modify a single source file. It is more efficient to instead make the program target depend on object files (.o), so that make can reuse objects if their corresponding sources have not changed.

If you leave out a rule to explain how to get an object file (.o) from your source C or C++ files, then make will use a built-in implicit rule to automatically build your object files. For C files, the built-in command would look like

 $ (CC) $ (CFLAGS) $ (CPPFLAGS) $ (TARGET_ARCH) -c

and for C++ files, the line would look like

 $ (CXX) $ (CXXFLAGS) $ (CPPFLAGS) $ (TARGET_ARCH) -c

If you want to specify how to build the object files (.o), you could manually type in a rule for each object file/source file pair, or you could include in your makefile an explicit rule for building all files of that type. For C and C++ files, the explicit rules would look something like

 .C. O:       $ (CC) -c $ (CFLAGS) -o $@ $< .cpp.O:       $ (CXX) -c $ (CXXFLAGS) -o $@ $<

The .c.o and .cpp.o dependency lines are suffix rules that tell make that their rule applies to object files (.o) generated from .c and .cpp source files respectively $@ and $< are automatic variables. The $@ variable stands for the filename of the target, and the $< variable stands for the name of the out-of-date dependency file. So, for example, if make were compiling the file hello.cpp, it would use the second rule, and $@ would be hello.o and $< would be hello.cpp.

Table 24–2 lists some of the automatic variables used inside of makefiles.

Table 24–2: Makefile Automatic Variables

Variable

Description

$@

The target filename.

$<

The name of the dependent file that is out of date.

$*

The stem of the dependent filename without the pattern matching elements.

$?

The list of all out-of-date dependent files. (All those that must be recompiled.)

$^

The list of all unique dependent files

$+

The list of all dependent files with duplicates in the order they were given.

$%

Only applies to library archives. The target member name when the target is an archive.

Regardless of whether you use an implicit or an explicit rule to generate your object files (.o), you will still need to explicitly list header file dependencies. To tell make that an object file must be rebuilt if a specific header file changes, you just add another dependency line without a trailing command. For example, the line

 hello.o: headerfile1.h headerfile2.h

will tell make that hello.o should be rebuilt if either headerfile1.h or headerfile2.h have changed. Since manually entering header file dependencies is time consuming and error prone, there are utilities available such as makedepend, which will parse your source files and recursively follow #include directives to generate the header file dependency lines for your makefile.

A Complex C++ Makefile

What follows is an example of a more complex C++ makefile that generates a program called Executable. The program contains two source files, main.cpp and rest.cpp; a header file, private.h, which is in a subdirectory called include; and a library, libRoutines.a, with source files routine1.cpp and routine2.cpp.

 # A more complicated makefile to combine c++ sources # private header files, and libraries. HEADERS=include/private.h SOURCES=main.cpp rest.cpp OBJECTS=${SOURCES:.cpp=.o} PRODUCT=Executable LIB=libRoutines.a LIBSOURCES=routinel.cpp routine2.cpp LIBOBJECTS=${LIBSOURCES:.cpp=.o} INCLUDE=include CXX=g++ CXXFLAGS=-g -Wall -o all: $ (PRODUCT) $(PRODUCT): $ (OBJECTS) $ (LIB)         $(CXX) $(CXXFLAGS) -o $(PRODUCT) $(OBJECTS) $(LIB)  .cpp.O:         $(CXX) -c $(CXXFLAGS) -I$(INCLUDE) $< $(LIB): $(LIBOBJECTS)         ar crs $(LIB) $^ $(OBJECTS): $(HEADERS) .PHONY: clean clean:         rm -f *.o $ (PRODUCT) $ (LIB)

If you have all of these files and run make, then the output would be

 $ make g++ -c -g -Wall -o -Iinclude main.cpp g++ -c -g -Wall -o -Iinclude rest.cpp g++ -c -g -Wall -o -Iinclude routine1.cpp g++ -c -g -Wall -o -Iinclude routine2.cpp ar crs libRoutines.a routine1.o routine2.o g++ -g -Wall -o -o Executable main.o rest.o libRoutines.a

Non-Programming Makefiles

The make command can be used to update other types of projects that have internal dependencies, such as documentation. Here’s a sample makefile that shows the basic structure to use make in a text writing project:

 # Makefile for book version PRINTER = lp FILES = intro chap1 chap2 chap3 chap4 chap5 chap6 appendix glossary book:     troff -Tpost -mm $ (FILES) | $ (PRINTER) draft: $ (FILES)     nl -bt -p $? | pr -d $ (PRINTER)

To print the current version of the complete document, you would type make book or make draft.




UNIX. The Complete Reference
UNIX: The Complete Reference, Second Edition (Complete Reference Series)
ISBN: 0072263369
EAN: 2147483647
Year: 2006
Pages: 316

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