Open main menu

CDOT Wiki β

Changes

SBR600 make and Makefiles

4,954 bytes added, 21:04, 10 January 2011
Created page with '{{Chris Tyler Draft}} ''make'' is a specialized scripting language used to build software. Unlike most scripting languages, commands are not executed in linear (start-to-finish) …'
{{Chris Tyler Draft}}
''make'' is a specialized scripting language used to build software. Unlike most scripting languages, commands are not executed in linear (start-to-finish) sequence; instead, command sequences are defined in terms of what input they accept and what output they produce, and ''make'' automatically sequences the commands to produce the required output.

Running the <code>make</code> command by itself will execute the makefile script named <code>Makefile</code> or <code>makefile</code> in the current directory.

= Makefile basics: Targets and Dependencies =

Picture a very simple build, where the file <code>test.c</code> is compiled by ''gcc'' into the executable binary named <code>test</code>.

test.c -> compiled by the command 'gcctest.c -o test' -> test

The binary executable product file, <code>test</code>, is considered the ''target'' -- the object to be built. The file <code>test.c</code> is a dependency - a file that is required in order to produce the target. ''gcc'' is the command that builds the target from the dependency.

In a make script (typically called a ''Makefile''), the syntax looks like this:

target: dependencies
commands

(Note that the ''commands'' must be indented by tabs and not spaces in most versions of ''make'').

The example above could be written:

test: test.c
gcc test.c -o test

Here is the result when this ''Makefile'' is executed:

$ ls -l
total 8
-rw-rw-r--. 1 chris chris 35 Jan 10 19:21 Makefile
-rw-rw-r--. 1 chris chris 40 Jan 10 19:15 test.c
$ cat Makefile
test: test.c
gcc -o test test.c

$ make
gcc -o test test.c

When executed a second time, ''make'' does nothing:

$ make
make: `test' is up to date.

This is because the timestamp on the target (<code>test</code>) is later than the timestamp on the dependency (<code>test.c</code>). If the dependency has been changed since the target was built, though, then ''make'' will rebuild the target.

== Complex Dependencies ==

A more complicated build will involve a number of targets and dependencies. C programs, for example, can be compiled into intermediate files, called object files (.o extension), which can then be combined to produce executables.

Picture this scenario:

* There are three object files:
** double.c, number.h, and sauce.h compile to make: double.o
** half.c, number.h, and sauce.h compile to make: half.o
** sauce.c compiles to make: sauce.o
* There are two binary targets:
** double.o and sauce.o can be linked to produce: double
** half.o and sauce.o can be linked to produce: half

The ''Makefile'' for these relationships may be written like this:

CC=cc
CFLAGS=-O3

all: half double

half: half.o sauce.o
${CC} ${CFLAGS} -o half half.o sauce.o

double: double.o sauce.o
${CC} ${CFLAGS} -o double double.o sauce.o

half.o: half.c number.h
${CC} ${CFLAGS} -c half.c

double.o: double.c number.h
${CC} ${CFLAGS} -c double.c

sauce.o: sauce.c
${CC} ${CFLAGS} -c sauce.c

There are several things worth noting about this ''Makefile'':
# Variables are used for the name of the compiler and the compiler flags. This makes it very easy to change these values -- to use the ''gcc'' compiler, for example, the CC variable could simply be changed to ''gcc''. If variables were not used, you would have to change every line that invoked the compiler.
# ''all'' is a dummy target. Since it appears as the first target in the file, it is executed first. It depends on the ''half'' and ''double'' files, which will be built in order to satisfy the dependency. However, the ''all'' target does not specify any commands, and the file <code>all</code> will never be built.

When ''make'' is executed the first time, five compilations are performed:

$ make
cc -O3 -c half.c
cc -O3 -c sauce.c
cc -O3 -o half half.o sauce.o
cc -O3 -c double.c
cc -O3 -o double double.o sauce.o

Note that the commands are not being executed in the order in which they appear in the file -- instead, they are ordered according to dependencies.

When executed a second time, no compilations are performed:

$ make
make: Nothing to be done for 'all'.

If the file <code>half.c</code> was edited or the datestamp was updated, running ''make'' would execute two compilations:

$ touch half.c
$ make
cc -O3 -c half.c
cc -O3 -o half half.o sauce.o

This reveals the power of ''make'' -- it does the absolute minimum in order to build the specified target.

On a large programming project, a binary may be comprised of hundreds or even thousands of source files, and compiling all of those files may take hours. If a software developer edits just one file, it's a waste of time to rebuild everything, so ''make'' can save a lot of time -- especially when the software is rebuilt many thousand times.