Learn Make
Swe03
Introduction
This series of exercises introduces you to the
make build tool.
An example project is provided without a makefile. It is up to you to
write one.
The example program is an interpreter for a very simple
lambda-calculus-like programming language, written in C. It reads
in lambda-terms from standard input such as:
(\x . \y . x) ((\x . x) 1000) 2000
(where
\ denotes lambda-abstraction) and reduces the
term to normal form using a lazy rewriting strategy, e.g. yielding:
1000
The actual purpose of the program is not very important, though. More
important are the following facts about the implementation:
- The program consists of several C source files and header files such as
main.c, abstract.h, etc.
- The parser is implemented using the Bison parser generator which translates a Bison parser specificition ---
parser.y --- into a C-language parser corresponding to the specified grammar.
- Likewise, the lexical analyser (lexer) is implemented using the Flex lexer generator. The lexer is specified in
lexer.l.
A shell script
buildme.sh is provided that builds the program.
Exercises
- You can get the code for these exercises at http://www.cs.uu.nl/~eelco/interpreter-0.1.tgz. You can unpack this file as follows:
gunzip < interpreter-0.1.tgz | tar xvf -
-
Build the program by running
buildme.sh. Try if the
resulting program works, e.g.:
$ ./interpreter < test-03.lam
This should print out 1000.
The problem with buildme.sh is that it is inefficient: it
recompiles everything. This is not a problem for such a small
program, but for real-world software this approach becomes
intractable. In addition, the order of statements in
buildme.sh is important; for example, bison must be run
before main.c is compiled (why is this?).
-
Write a simple makefile.
Note: in makefiles, commands have to be prefixed with a tab character
(not 8 spaces!).
-
What about dependencies? What files depend on what other files?
-
Use pattern rules to make the makefile shorter.
-
Add a
clean target to delete derived files.
-
Add a
install target to copy interpreter to some
other location, e.g. /usr/bin.
-
Rather than writing header file dependencies by hand, they
should be generated automatically. You can use
gcc -MM
to let the C compiler generate a list of dependencies for a C file.
-
Unit testing: add a
check target that runs
interpreter on every input file (*.lam) and checks
that the output equals the corresponding output file
(*.out). Pattern rules are useful here. You can use the
cmp or diff Unix commands to check whether files are
equal
Information