CS 372 Spring 2016  >  Notes for February 22, 2016


CS 372 Spring 2016
Notes
for February 22, 2016

Build Tools

What They Are

A build tool, is a software package that organizes and oversees the building of a project. Every IDE includes a build tool of some kind. Build tools are also available as stand-alone programs. Another name for build tools is build automation software.

Build tools originated in the field of software development, and this remains their primary application. However, they are not restricted to this area. For example, I use a build tool in my writing, to create and view PDF files from LaTeX source.

The basic functionality of a build tool revolves around two areas.

Make

Introduction

The granddaddy of all build tools is make, created in 1976 as a framework for automating software builds under the Unix operating system.

Make is still heavily used, but its limitations, along with criticisms of its syntax and other annoyances have led to a number of other build tools being written. Some of these are based on Make. The most popular of these is probably GNU make, available under a FLOSS license from the GNU Project. Many other build tools are not based on Make, but all of them were influenced by it.

We will look at how Make is used. Other build tools will differ in interface and syntax, but all generally offer the same basic functionality.

Makefiles & Basic Syntax

Makefiles—The operation of Make is driven by script files called makefiles. By default, the filename of a makefile is “Makefile”.

Simply executing Make does a build using the default makefile. Other makefiles can be specified.

A makefile is written using a simple programming language, with a very restricted set of operations. The contents of a makefile consist of three things: rules, comments, and macro definitions.

Rules—A rule tells Make the information it needs to determine whether a file needs to be built, and if so, how to do it. Here is an example of a rule.

[Makefile]

main: main.cpp main.h
    g++ main.cpp -o main

In the above rule, abc is the target: the file to be built.

The target is followed by a colon and a blank-separated list of dependencies. In the above rule, the dependencies are abc.cpp and abc.h. These are the files that the target is built from.

To determine whether a target needs to be built, Make first checks whether the target exists. If it does not, then it is built. If the target exists, then the modification times of the target and all of its dependencies are examined. If any dependency has a modification time more recent than the target, then the target is built; otherwise it is not.

To build the target, the code in the following lines is executed. Each of these lines must begin with a tab character—indicated by a gray box above. (The number one criticism of Make involves this requirement.)

So, for example, using the rule above, if abc does not exist, or if either abc.cpp or abc.h has a more recent modification time, then the following is executed.

[*ix Command Line]

g++ main.cpp -o main

What if a dependency does not exist? Then Make looks for a rule to build the dependency. If no appropriate rule is found, then Make fails with an error message. If a rule is found, then the dependency is built. The dependency will then generally have a very recent modification time, and so the original target will be built as well.

Here is an example of a rule whose dependencies have their own rules.

[Makefile]

all: prog

prog.o: prog.cpp prog.h lib1.h lib2.h
    g++ prog.cpp -c

lib1.o: lib1.cpp lib1.h
    g++ lib1.cpp -c

lib2.o: lib2.cpp lib2.h
    g++ lib2.cpp -c

prog: prog.o lib1.o lib2.o
    g++ prog.o lib1.o lib2.o -o prog

Above, all is the default target: the one that is built if no target is specified. Target all is an example of a phantom target: a target that does not correspond to a file. Note that there are no commands associated with target all; the rule simply says that, if no target is specified, then we make target prog.

Another common example of a phantom target is clean. Typically, making target clean deletes temporary files and possibly other files that have their own rules, and thus can easily be rebuilt.

Comments—A comment in a makefile begins with a pound sign (“#”) and continues to the end of the line. Make ignores comments in makefiles.

Macro DefinitionsMake allows macros (think “variables”) to be defined and then later used in rules. Basic macro definitions are nearly self-explanatory.

[Makefile]

# Macro definitions for C++ compilation
CPP = g++
CPPFLAGS = -ansi -pedantic -Wall -std=c++11

To use a macro, enclose its name in parentheses, and precede all of this with a dollar sign. This will be replaced with the value of the macro.

[Makefile]

# Our primary executable
main: main.cpp main.h
    $(CPP) main.cpp -o main $(CPPFLAGS)

Other Makefile Syntax

Special Macros

Make includes a number of special macros that can be used to make rules more concise or more general. These include the following.

$@;
The current target
$<
The first dependency
$?
A list of all dependencies whose modification times are more recent than the target.

So, for example, we could rewrite our rule for main as follows.

[Makefile]

# Our primary executable
main: main.cpp main.h
    $(CPP) $< -o $@ $(CPPFLAGS)

Suffix Rules

A suffix rule tells how to build a file with a particular filename suffix from another file with the same basename, but a different filename suffix. Here is an example for C++.

[Makefile]

# From C++ source to object file
.cpp.o:
    $(CPP) $< -o $@ $(CPPFLAGS)

If a makefile has the above rule, then we can use it to build any C++ object file that is based only on a single source file. Object files that are based on more than one file can have their own rules; these override the suffix rule.

Pattern Rules

Make can do simple pattern matching, using the % character. Here is the above suffix rule rewritten as a pattern rule.

[Makefile]

# From C++ source to object file
%.o: %.cpp
    $(CPP) $< -o $@ $(CPPFLAGS)

Pattern rules are more general than suffix rules, since they allow for more than one dependency.

CMake

Another noteworthy build tool is CMake, which is aimed at system-independent/cross-platform development.

A CMake script is written in a relatively full-featured platform-independent programming language. CMake can generate scripts for a number of common build tools, including Make.

CMake is distributed under a FLOSS license.