The Make utility:

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

Here's an example Makefile:

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:

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

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

Make will:

For example, the dependency graph for our running example is: examples.class examples.java IO.class Sequence.class IO.java Sequence.java NoCurrentException.class NoCurrentException.java

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.