A makefile is a set of commands that tell the compiler the build and link order as to generate an executable (or similar).It is usually parsed within a unix Shell, or similar command-line interpreter.

It indicates (in explicit detail), what compiles when, to what file name and type, and when to link to libraries or other resources. It is also a handy place to put feedback, so that the developer can see how the process is going. see nmake.

Makefiles are actually fairly generic: they consist of rules specifying commands to be executed when a file (target) is older than certain other files (its dependencies). The commands are arbitrary, but they are expected to rebuild the target from the dependencies.

make(1) usually comes with a very large default Makefile that defines default behaviour for common situations.

The syntax of a Makefile rule is this:

target1 target2: dep1 dep2 dep3
	command -flag argument | another > output
	yet | another | command
	# etc.

This syntax was designed before it was common sense that whitespace should always be arbitrary. Command lines are defined as lines that start with a TAB character! Countless Makefile writers have wrestled with this problem, and make doesn't exactly produce a clear error message on it, either.

Another syntactical quirk is in variables: in a Makefile, $ABC means the value of the variable named A followed by the string BC, and you have to use $(ABC) or ${ABC} to refer to the variable named ABC.

But worse problems of Makefiles are in their semantics. For any nontrivial project you want generic rules: rules that do not name names of individual files, but instead, determine how to make *any* *.o out of the corresponding *c, or *any* executable out of the *.o object files it depends on, or *any* PDF file from the corresponding LaTeX source. The original make allows wildcards, so you can write a rule header like this:

*.o: *.c
	# (some rule to compile a *.o file)
myexecutable: *.o
	# (some rule to compile myexecutable)
It also has special variables to refer to 'the target currently being made' and 'the dependencies newer than the current target'. But many variables are missing. What's worse, the wildcards are resolved on the filesystem, which causes chains to be broken: if all *.o files are missing, the rule for myexecutable has no dependencies so it won't be triggered, even though there is a rule to make *.o files.

Suffix rule exist to solve this. They look like this:

.c.o:
	# (some rule to compile a *.o file from a *.c file)
The semantics is different from normal rules, because it supports "chaining": I can have a sequence of rules like this
.tex.dvi:
	latex $? && bibtex $? latex $?  # often good enough
.dvi.ps:
	dvips -o $@ $?
.ps.pdf:
	ps2pdf $?
and even if foo.dvi and/or foo.ps do not exist, foo.pdf will be created from foo.tex.

But this syntax is restrictive: what if my files do not have a suffix at all? And what to do if rules of both types match for the same target?

From here, things have grown really hairy. The existing Makefiles weren't powerful enough in many situations, but by the time something was done about it, Unix had already fork()ed in various directions. Consequently, we have BSD make, Sun make, pmake, nmake, GNU make, SysV make, and other variants that all provide a different set of extensions. If you see confusing error messages from 'make', you may have to try a different version of 'make'. Try GNU make first, especially if the software you're trying to compile was developed on Linux.

Solaris make, and GNU make, for instance, have pattern rules that look like this:

%.dvi: %.tex
	latex $? && bibtex $& latex $?  # often good enough
%.ps: %.dvi
	dvips -o $@ $?
%.pdf: %ps
	ps2pdf $?
This looks more natural, and widens the set of possible patterns that can be used. However, the semantics aren't completely identical between the two makes: sometimes one of them will remake a target while the other won't, using the same Makefile.

Before you know it, your dependencies are too complex for these constructs. In particular, dependencies are often dynamic: they change during the course of a project, but a program or script can be written to determine them. For example, makedepend is a tool to determine the appropriate Makefile rules for compiling *.o files out of *.c and *.h files, by looking into the *.c files and analysing the #include statements in them (gcc, which has swallowed all kinds of functionality that used to be in separate utilities, nowadays has a -M option for this purpose).

Some versions of make offer special support for dealing with this situation, but there is a more general method:

%.foo: %.foo.d
	$(MAKE) -f $? $@
%.foo.d: %.bar
	my-script-to-generate-the-makefile $? > $@
With this construction, the Makefile itself only has to list the files to which a modification may affect the dependency tree; the generated Makefile will contain the actual commands used to build the target, and will have an up to date list of dependencies for each target.

When a piece of software is insufficient for the task at hand, there are two remedies: extend it (which led to the many make versions) or wrap it. The make tool has in fact been extended with many features, but unless you standardize on a particular version of make, that doesn't help much, and even if you do, you will often meet situations that pose serious puzzles. The wrapping method consists of writing new software that extends functionality by applying the existing software in a particular way, without actually modifying it. In the case of make, this has led to software utilities that generate Makefiles from their own configuration files: an early one was imake, a very common one is GNU autoconf (which in turn has another tool that generated its configuration files, GNU automake); lately I've seen a lot of CMake which is an alternative that generates Makefiles which in turn call CMake. And lately I've patched GNU make to work around a limitation that I found too surprising to ignore. Oh well.

I use make in certain circumstances because it is extremely useful, but not often enough that I have memorized its intricacies. Here's one (for GNU make) that I always have to look up. (Although maybe now that I've noded it, it will be imprinted into my brain.) (It turns out I was also deluded about how make thinks, and surprisingly not in a way that affected the code. Thanks, OldMiner, for clarifying!)

If you use a makefile consisting of only a pattern rule, such as

%.o: %.c
	cc -c -o $@ $<

then calling make on its own won't do anything. You can manually specify the object files to make (make foo.o bar.o), but to have it work without arguments you need at least one non-pattern rule. You might think you could get away with this:

.PHONY: all
all: *.o

%.o: %.c
	cc -c -o $@ $<

The .PHONY line declares that the all rule doesn't refer to a file named all, and that it should always be run when it is invoked.

But the *.o only works if you already have the .o files. The cheap way to solve this is to compile by hand once, and then the rules above will work fine. However, your makefile will be broken if you ever clean out your .o files.

The real solution is to use this monstrosity:

.PHONY: all
all: $(patsubst %.c,%.o,$(wildcard *.c))

%.o: %.c
	cc -c -o $@ $<

wildcard returns a list of all .c files in the current directory, and patsubst performs a simple pattern-based replacement on the list, changing all ".c" to ".o". Thus the all rule depends on .o versions of all your .c files---whether you've got the .o files already or not! Problem solved, happy makeing.

See also the GNU make documention pages on phony targets and the wildcard function.

Log in or register to write something here or to contact authors.