Chapter 22. Building with Make

Table of Contents

22.. Overview
Practice
22.. Building a Program
Practice
22.. Make Variables
Practice
22.. Phony Targets
Practice
22.. Building Libraries
Practice
22.. Mixing Tool Chains
22.. Mixing C, C++, and Fortran: A Brief Overview
Data formats
Libraries
Examples
Practice
22.. Makefile Generators
Practice

Overview

Now that you know how to use subprograms, you have the ability to create programs from multiple source files.

Most real-world programs contain several thousand or tens of thousands of lines of code, and are broken into many separate source files. A single source file of 50,000 lines of code would take a very long time to recompile every time we make a small change. Using many smaller source files allows us to recompile only a small fraction of the program following each change. Only the source files that have changed need to be recompiled.

The make utility is a standard Unix program that automatically rebuilds a designated set of target files when the source files from which they are built have changed. For example, an object file or executable is a target file where the source files are C source code. The PDF form of this document is a target file built from hundreds of DocBook XML source files.

The relationships between the files are spelled out in a Makefile, which is usually simply called Makefile (note the capital M). The Makefile indicates which source files are needed to build each target file, and contains the commands for performing the builds. A Makefile consists of a set of build rules in the following form, where "target-file" begins in column 1 and each command is indented with a TAB character.

target-file: source-file1 source-file2 ...
        command1
        command2
    ...
    

Each rule is interpreted as "if any source file is newer than the target file, or the target file does not exist, then execute the commands". This is made possible by the fact that Unix records the last modification time of every file on the system. When you edit a source file, it becomes newer than the target that was previously built from it. When you rebuild the target (probably using make), it becomes newer than the sources.

Before executing any rule, make automatically checks to see if any of the sources are targets in another rule. This ensures that all targets are rebuilt in the proper order.

# The Makefile
#
# Before executing this rule, make checks for other rules where
# program.o is the target
program: program.o
        cc -o program program.o -lm

# This rule will be executed before any rule where program.o is a source
# no matter where it is located in the Makefile
program.o: program.c program.h
        cc -c program.c
    

If we edit program.c or program.h, Unix records the modification time upon saving the file. When we run make, it first sees that program depends on program.o. It then searches the Makefile to see if program.o is built from other files and finds that it depends on program.c and program.h. It then sees that the edited source file is newer than program.o and executes the command cc -c program.c. It then returns to the rule to build program and sees that program.o is now newer, so it runs the command cc -o program program.o -lm.

What we will see:

shell-prompt: vi program.h      # Make some changes and save
shell-prompt: make
cc -c program.c
cc -o program program.o -lm
    

There is really nothing more to it than this. The make command knows nothing about the target or source files. It simply compares time stamps on the files and runs the commands in the Makefile. You specify the targets, the sources, and the commands, and make blindly follows your instructions. You can make it as simple or as complicated as you wish.

Caution

One of the quirky things about make is that every command must be preceded by a TAB character. We cannot substitute spaces.

Note that not all editors insert a TAB character when you press the TAB key. Many use soft tabs, where some number of spaces are inserted instead. The APE editor uses soft tabs and indents only 4 columns when the TAB key is pressed by default. However, it will save Makefiles with TAB characters for lines that are indented 8 columns (i.e. start in column 9 or later). Just be sure to indent commands at least to column 9, e.g. by pressing TAB twice.

Make can be used to generate any kind of file from any other files, but is most commonly used to build an executable program from a group of source files written in a compiled language. Once we have a proper Makefile, we can edit any or all of the source files, and then simply run make to rebuild the executable. The make command will figure out the necessary compile commands based on the rules.

shell-prompt: make
    

By default, make looks for a file called Makefile and if present, executes the rules in it. A Makefile with a different name can be specified following -f. The traditional filename for a Makefile other than Makefile is ".mk".

shell-prompt: make -f myprog.mk
    
Practice

Note

Be sure to thoroughly review the instructions in Section 2, “Practice Problem Instructions” before doing the practice problems below.
  1. What does make do?

  2. How is each rule in a makefile interpreted?

  3. How does make know what is a target file and what is a command?

  4. What is make most commonly used for?