The Make utility:

• Defines which components go together to make a program.
• Defines how to "put the pieces together".
• Keeps track of dependencies among components.
• Helps avoid doing unnecessary work and forgetting to do necessary work (in particular, it is usually used to control (re)compilation of code that depends on code in other files).

To use Make you must create a file named makefile or Makefile. A Makefile contains a collection of rules, each of which includes:

• a target (usually a file name)
• a list of files on which the target depends
• a command to be executed to create the target file

Here's an example Makefile:

examples.class: examples.java IO.class Sequence.class
javac -g examples.java

Sequence.class: Sequence.java NoCurrentException.class
javac -g Sequence.java

NoCurrentException.class: NoCurrentException.java
javac -g NoCurrentException.java

IO.class: IO.java
javac -g IO.java


In this example, each rule consists of 2 lines (so there are 4 rules total). In each rule, the file name to the left of the colon is the target. To the right of the colon are listed the files needed to create that target. The line that starts with "javac -g ..." is the command to create the target.

Note that the first character of every "command" line must be a tab! If you use spaces instead, you'll get an obscure error message when you try to use the Makefile.

How do you know what to use for the "list of files on which the target depends"? To understand that, you need to know that "A depends on B" means: if B changes, then A may need to be recreated (recompiled in our example). So for Java programs, for the rule with target xxx.class (created by compiling xxx.java), the list should include all files that define classes that are used in xxx.java, (except the ones from the standard Java library). So the above example makes sense if the following hold:

 File Classes Used IO.java -- none -- NoCurrentException.java -- none -- Sequence.java NoCurrentException examples.java IO, Sequence

To create an up-to-date target file, type:

make <target-name>
(If you just type:
make
the first target file will be created. Other than this, the order in which the targets are listed is not important.)

Make will:

• Create a dependency graph (starting with the target to be made, and adding an edge from node B to node A iff A depends on B).
• Traverse the graph (in topological order) visiting a node only after all its predecessors have been visited. "Visit" a node means:
• For a leaf node (no predecessors): verify that the file exists; if not, give an error message and quit.
• For a non-leaf node: if the file does not exist or its creation date is earlier than that of some predecessor, then re-create the file by executing the associated command.

For example, the dependency graph for our running example is:

Suppose you "make examples.class", then edit Sequence.java, then "make examples.class" again. Here are the steps that make will carry out:

1. Build the above graph.
2. Verify that examples.java, IO.java, Sequence.java, and NoCurrentException.java exist.
3. See that IO.class is no older than IO.java (so there is no need to re-make IO.class); see that NoCurrentExcepiton.class is no older than NoCurrentException.java (so there is no need to re-make NoCurrentException.class); see that Sequence.class is older than Sequence.java, so re-make Sequence.class by executing the command whose target is Sequence.class (javac -g Sequence.java).
4. See that examples.class is older than (the newly created) Sequence.class) so re-make examples.class.

Things to note:

1. You are responsible for keeping the Makefile up to date! You may need to add/remove a file from a target's "list of dependencies" if you add/remove a file from your program, or add/remove a use of a class defined in one file from another file.

2. Make can be used for tasks other than compilation. For example, this is a rule that you can use to "clean up" your directory by typing "make clean" (it will remove all .class files, and all files ending with a tilde -- those are files created by emacs as backups during editing; the -f flag "forces" the removal):

clean:
rm -f *.class *~


3. You can include comments and variables in your Makefile. Any line that starts with a # is considered to be a comment. Variables are defined like this:
JC = /s/std/bin/javac
JFLAGS = -g

and used like this:
IO.class: IO.java
$(JC)$(JFLAGS) IO.java

One reason for using variables is the same as for using named constants in a program: if you decide to change the value, you only need to make a change in one place in your Makefile. Another use for variables is to allow a value to be specified at make time. For example if the Makefile includes this rule:
test:	examples.class
java examples \$(INPUT)

then typing make test INPUT=in.data causes the program to be run with one command-line argument (the string "in.data").

4. There is a lot of overhead involved in starting up the javac compiler (so it is very slow). So it may be faster to recompile all files (using a single invocation of javac) whenever any file has changed since the last time you ran your program. Assuming that it is the main method in the examples class that is of interest, the following Makefile could be used:
examples.class:	examples.java Sequence.java NoCurrentException.java IO.java
javac -g examples.java Sequence.java NoCurrentException.java IO.java

Sequence.class: Sequence.java NoCurrentException.class
javac -g Sequence.java

NoCurrentException.class: NoCurrentException.java
javac -g NoCurrentException.java

IO.class: IO.java
javac -g IO.java

This Makefile allows you to compile Sequence.java, NoCurrentException.java, and IO.java individually (e.g., if you want to make sure they have no errors), but when you are ready to run your program, you would simply type: make, and if any .class file is not up to date, all .java files would be recompiled.