Makefile

A makefile is most often used as a build configuration file for a software project (or some part of a project's source code tree). It is used by the make program. The make program helps compile source code faster by not recompiling things that don't need to be recompiled.

Why use a makefile?
Let's say you are developing a program. To compile, you might run the gcc compiler on your source code. Let's say you have several files. Typing gcc for every source file you have would be very cumbersome. So you make a handy shell script, and now you can compile all your files with just one command. However, what if you had a huge amount of source code? Compiling all that source code would take a while, and it would be quite frustrating if you had to compile every single file for hours just because you made a minor change in one very small file. This is why the makefile and the make program were made.

The make program and the corresponding makefile solve the compilation problem by keeping track of what files were changed. Every file has a timestamp on it indicating when it was last changed. make examines the timestamps on the source code files and compares it with the compiled code. If a source code file was modified after the compiled file was modified, then make updates the compiled file by recompiling it - and only it. If the source code file was modified before the compiled file was, then nothing is done - which saves time.

How to write makefiles
Makefiles define a set of rules to determine which files to compare to others, and how the files should be updated.

Rules are comprised of two things: targets and dependencies. A target is the outcome - an executable file, an object file, a PDF document - whatever the outcome of running a certain program will be. A dependency is what needs to be used in order to reach the target - for example, to get an executable file, you need to have a source file - this is the dependency.

After the rule, the command below it specifies what needs to be done in order to get from the dependency to the target - say, run gcc, run tex.

The rule-command pair is written as follows: target: dependencies command (NB there is a TAB character before the command, not a space!)

So, a very simple rule-command pair will consist of say_hello_world: say_hello_world.c say_hello_world.h     gcc say_hello_world.c say_hello_world.h -o say_hello_world

If either say_hello_world.c or say_hello_world.h are changed, the command gcc say_hello_world.c say_hello_world.h will be run. But if neither were modified, the command would not be run. This is the key to makefiles - only what needs to be done is done.

Dependencies can be recursive. Consider this example. myprog</tt> depends on main.c</tt> and lib.o</tt>. But lib.o</tt> depends on lib.c</tt>. So, we would write our makefile as follows myprog: main.c lib.o   gcc main.c lib.o -o myprog lib.o: lib.c   gcc -c lib.c

Now, if main.c was not modified, but lib.c</tt> was, make</tt> will check the dependencies to myprog</tt> first, find that main.c is unchanged, then would check lib.o</tt>. Then, it finds a rule for lib.o</tt> and checks its dependencies, find lib.c</tt> change, run gcc -c lib.c</tt> first to get lib.o</tt> up to date, then rerun gcc main.c lib.o -o myprog</tt> to get myprog</tt> up to date.

When make</tt> is run on a makefile, the rules for either the first target it encounters, or a target called <tt>all</tt> are carried out first. This helps you shape the dependencies in your makefile (known as a dependency tree).

Specifying dependencies
Dependencies are specified in a file known as a makefile. You can call the makefile anything you like, but the make program will, however, will automatically select a file called <tt>Makefile</tt> or <tt>makefile</tt> (or some other platform-specific specification such as <tt>BSDmakefile</tt>).

For convenience, you should choose one of the defaults, which allows you to simply type $ make however, other files can be specified with the <tt>-f</tt> option to make. There may be special uses to using makefiles with different names.

Example of a makefile
all: prog prog: prog.o     gcc -g prog.o -o prog
 * 1) This specifies that the final target is the 'prog' program
 * 1) This line means that the prog program depends
 * 2) on the object file prog.o - in other words,
 * 3) if prog.o is newer than prog, run gcc again to make prog.

prog.o: prog.c     gcc -g -c prog.c
 * 1) But before we can try and make prog.o, we
 * 2) should check if there are any dependencies on prog.o.
 * 3) Indeed there is, and so the action is similar to the one before.

Make's actions are not C-specific, however. Make is much more abstract, which allows you to create Makefiles for Java programs, for example

JavaProg.class: JavaProg.java javac JavaProg.java

Any file that needs to be updated from another file with a command can benefit from a makefile. For example, you might create a piece of documentation using the SGML DocBook DTD, and then use a makefile to automatically build HTML, LaTeX, plain text, or whatever other kind of output from your sgml file you desire.

The rules in a makefile are applied when <tt>make</tt> finds the file in the current directory. When <tt>make</tt> is run without arguments and it finds a makefile, it follows the rules in the <tt>Makefile</tt> to make the 'all' target, <tt>prog</tt>. <tt>prog</tt> is made from <tt>prog.o</tt>. <tt>prog.o</tt> is made from <tt>prog.c</tt>.

More sophisticated targets will invoke simpler targets, thus meeting whatever dependencies a target declares. For example, the "all" target may declare that it needs the "foo" target, and "foo" requires "foo.o", which in turn leads to "foo.c" being compiled into "foo.o" by the C compiler (such as gcc).

Makefiles can become very complex for larger projects. Often the GNU build tools (autoconf,automake,libtool) are used to help with this. Alternatives to <tt>make</tt> include Apache Ant (which is Java-based) and Jam.

Wildcards
Wildcards can be used to simplify a lot of repetitive work. For instance, instead of having a large project with hundreds of object files, each with individual rules, you can specify one rule to catch all object files. Use of wildcards has some portability issues.

%.o : %.c++ g++ -c $< -o $@

That rule will automatically compile ANY required .o file if a .c++ file exists.