Automated Compilation
You can access the slides 🖼️ for this lecture. All the code samples given here can be found online, alongside instructions on how to bring up the proper environment to build and execute them here. You can download the entire set of slides and lecture notes in PDF from the home page.
Here we introduce a tool called make
, that allows to automate the compilation of programs composed of multiple and potentially many source files.
Incremental Build
When we have a program composed of several modules, there is no need to rebuild the entire program when just one or a few source files are changed.
Let's take the example from the following lecture in which we build our program from 3 source files, network.c
, parser.c
and main.c
.
A possible scenario is:
# Initial build:
$ gcc -c network.c -o network.o
$ gcc -c parser.c -o parser.o
$ gcc -c main.c -o main.o
$ gcc main.o network.o parser.o -o prog
# Assume we update parser.c, to rebuild we don't need to recompile everything:
$ gcc -c parser.c -o parser.o
$ gcc main.o network.o parser.o -o prog
# Now we update parser.h, remember it's included in both parser.c and main.c, so:
$ gcc -c parser.c -o parser.o
$ gcc -c main.c -o main.o
$ gcc main.o network.o parser.o -o prog
We have an initial build in which we compile each file and link the object files into the program.
Now assume we update parser.c
, to rebuild the entire program we only need to recompile that source file and relink all object files.
We don't need to recompile network.c
and main.c
.
Same thing if we modified parser.h
, we only need to recompile the files including that header, parser.c
and main.c
, then relink.
We compile and link the program as follows:
And the dependencies between source/object/executable files can be represented as follows:
Keeping track of all these dependencies manually to avoid rebuilding the entire program each time we modify a subset of the source files is difficult though. Thankfully there exists a tool that can automatically manage all that process.
Makefiles: Automated Build and Dependency Management
This tool is called make
.
It relies on configuration files named Makefiles.
They live alongside the sources, describing the build and its dependencies, following a particular format.
Let's consider the build dependencies for our server application example:
- The final executable
prog
is produced by the link stage that requires the 3 object files,network.o
,parser.o
, andmain.o
. network.o
is the result of compilingnetwork.c
, which also includenetwork.h
.parser.o
is the result of compilingparser.c
that also includesnetwork.h
andparser.h
.- Finally,
main.o
is the result of compilingmain.c
, which includes both headers.
Now that we have reasoned about the dependencies we know what needs to be done when a given file is modified.
For example, if parser.h
is modified, then main.o
and parser.o
need to be rebuilt, and prog
needs to be relinked.
An Example of Makefile
We describe these dependencies in a file named Makefile
:
# The first rule is executed when the
# command make is typed in the local folder:
all: prog
# executable deps and command to build it:
prog: main.o network.o parser.o
gcc main.o network.o parser.o -o prog
# network.o deps and command to build it:
network.o: network.c network.h
gcc -c network.c -o network.o
parser.o: parser.c parser.h network.h
gcc -c parser.c -o parser.o
main.o: main.c network.h parser.h
gcc -c main.c -o main.o
# Special rule to remove all build files
clean:
rm -rf *.o prog
It contains rules describing dependencies and actions with the following format:
- We have the target file on the left, followed by
:
, followed by the file the target depends on. For examplemain.o
depends onmain.c
,network.h
and parser.h
; The final executableprog
depends onmain.o
,network.o
andparser.o
; etc. - Below each target and list of dependencies, we have the command used to rebuild the target.
For example
main.o
is created by compilingmain.c
, andprog
is created by linking the 3 object files. We also have a special ruleall
that will be executed when we typemake
in the local directory. And another special ruleclean
that removes all build file.
After this file is properly set up, typing make
in the local directory will trigger the rebuild of only what is needed to rebuild the target referenced by all
.