Program sources by themselves don't make an application. The way
you put them together and package them for distribution matters,
too. Unix provides a tool for semi-automating these processes;
make(1).
Make is covered in most introductory Unix
books. For a really thorough reference, you can consult
Managing Projects with Make [Oram-Talbot]. If you're using
GNU make (the most advanced make, and the
one normally shipped with open-source Unixes) the treatment in
Programming with GNU Software [Loukides-Oram] may be better in some
respects. Most Unixes that carry GNU make
will also support GNU Emacs; if yours does you will probably find a
complete make manual on-line through Emacs's
info documentation system.
If you're developing in C or C++, an important part of the recipe for
building your application will be the collection of compilation and
linkage commands needed to get from your sources to working
binaries. Entering these commands is a lot of tedious detail work, and
most modern development environments include a way to put them in
command files or databases that can automatically be re-executed to
build your application.
Unix's
make(1)
program, the original of all these facilities, was designed
specifically to help C programmers manage these recipes. It lets you
write down the dependencies between files in a project in one or more
‘makefiles’. Each makefile consists of a series of
productions
; each one tells
make that some given target file depends on
some set of source files, and says what to do if any of the sources
are newer than the target. You don't actually have to write down all
dependencies, as the make program can
deduce a lot of the obvious ones from filenames and
extensions.
For example: You might put in a makefile that the binary
myprog depends on three object files
myprog.o, helper.o, and
stuff.o. If you have source files
myprog.c, helper.c, and
stuff.c, make will
know without being told that each .o file depends
on the corresponding .c file, and supply its own
standard recipe for building a .o file from a .c
file.
|
Make originated with a visit from Steve Johnson (author of
yacc, etc.), storming into my office, cursing
the Fates that had caused him to waste a morning debugging a correct
program (bug had been fixed, file hadn't been compiled, cc
*.o was therefore unaffected). As I had spent a part of the
previous evening coping with the same disaster on a project I was
working on, the idea of a tool to solve it came up. It began with an
elaborate idea of a dependency analyzer, boiled down to something much
simpler, and turned into Make that weekend. Use of tools that were
still wet was part of the culture. Makefiles were text files, not
magically encoded binaries, because that was the Unix ethos:
printable, debuggable, understandable stuff.
|
|
| --
Stuart Feldman
|
|
When you run make in a project directory, the
make program looks at all productions and
timestamps and does the minimum amount of work necessary to make sure
derived files are up to date.
You can read a good example of a moderately complex makefile in
the sources for
fetchmail
.
In the subsections below we'll refer to it again.
Very complex makefiles, especially when they call subsidiary
makefiles, can become a source of complications rather than
simplifying the build process. A now-classic warning is issued
in Recursive Make Considered
Harmful.[136] The argument in this paper has
become widely accepted since it was written in 1997, and has come
near to reversing previous community practice.
No discussion of
make(1)
would be complete without an acknowledgement that it includes one of
the worst design botches in the history of Unix. The use of tab
characters as a required leader for command lines associated with a
production means that the interpretation of a makefile can change
drastically on the basis of invisible differences in whitespace.
|
Why the tab in column 1? Yacc was new, Lex was brand new. I
hadn't tried either, so I figured this would be a good excuse to
learn. After getting myself snarled up with my first stab at Lex, I
just did something simple with the pattern newline-tab. It
worked, it stayed. And then a few weeks later I had a user population
of about a dozen, most of them friends, and I didn't want to screw up
my embedded base. The rest, sadly, is history.
|
|
| --
Stuart Feldman
|
|