CSC207 Software Design
Lectures
Make

How Do You Rebuild A Program?

Easy enough to create a.class from a.java by running

javac a.java

But what if you have many source files?

javac *.java doesn't work with sub-directories

And will be very (very) slow for large programs

And what if some rely on others?

Suppose overlap.java uses rect.java

Change rect.java

Forget to compile it

Compile and run overlap.java

Whoops

Automate, Automate, Automate

Anything worth repeating is worth automating

Most widely used tool for this is called Make

Invented in 1975 by Stuart Feldman when he was a summer student at Bell Labs

Make's role:

Figure out what has changed

Work out what is affected by those changes

Execute commands to bring things up to date (e.g. by recompiling)

Hello, Make

Put program in hello.java

Put the following into a file called hello.mk:

hello.class : hello.java
        javac hello.java

Note: that indentation must be a tab

Yeah, it's lousy design...

...but he was only a summer student

Run make -f hello.mk

Make sees that hello.class depends on hello.java

But hello.class doesn't exist

So hello.java is compiled

Run make -f hello.mk again

Nothing happens

hello.class is already up to date

Terminology

hello.class is the target of the rule

hello.java is its prerequisite

May be many prerequisites

The compilation command is the rule's action

Rule may have many actions

How It Works

Make looks at when the target and its prerequisites were last modified

If the target exists, and is more recent than all its prerequisites, Make does nothing

If the target doesn't exist, or any prerequisite is more recent than the target, Make executes the action

Multiple Targets

Left.class : Left.java
        javac Left.java

Right.class : Right.java
        javac Right.java

Run make -f double.mk

Only Left.java is compiled

Becase the first target in the file is the default

Run make -f double.mk Right.class to build Right.class

Phony Targets

all : Left.class Right.class

Left.class : Left.java
        javac Left.java

Right.class : Right.java
        javac Right.java

all is a phony target

Doesn't correspond to a file

Never up to date

make -f all.mk compiles both Java files

Multiple Dependencies

Having targets depend on other targets forces Make to do things in a certain order

Test.class : Test.java Overlap.class
        javac Test.java

Overlap.class : Overlap.java Rect.class
        javac Overlap.java

Rect.class : Rect.java
        javac Rect.java

Visualizing Dependencies

[Visualizing Dependencies]

Avoiding Redundancy

Often want to set options when compiling

-classpath to include libraries

-d to specify output directory

-source 1.4 to specify Java language version

Anything repeated in two or more places will eventually be wrong in at least one

Define variables (usually called "macros" in Make)

Warning: syntax is a bit tricky

Macro Example

JC = javac -source 1.4

Test.class : Test.java Overlap.class
        ${JC} Test.java

Overlap.class : Overlap.java Rect.class
        ${JC} Overlap.java

Rect.class : Rect.java
        ${JC} Rect.java

Automatic Variables

Make defines variables to represent parts of rules

$@ The target
$< The first prerequisite
$? All out-of-date prerequisites
$^ All prerequisites

Can also put @ at the start of an action line to stop Make printing it out before executing it

Automatic Variable Example

JC = javac -source 1.4

Test.class : Test.java Overlap.class
        @echo "Building" $@
        ${JC} $<

Overlap.class : Overlap.java Rect.class
        @echo "Building" $@
        ${JC} $<

Rect.class : Rect.java
        @echo "Building" $@
        ${JC} $<

Pattern Rules

Most files are compiled the same way

So write a pattern rule for the general case

Use % to mark the stem of the file's name

Like using * in commands in DOS or UNIX

Accumulate extra prerequisites by giving rules without actions

Pattern Rule Example

JC = javac -source 1.4

Test.class : Overlap.class

Overlap.class : Rect.class

%.class : %.java
        ${JC} $<

Analysis

Pro

Simple things are simple to do...

...and to read

Con

The syntax is unpleasant

Complex things are difficult to read...

...and even more difficult to debug

Not really very portable

Uses native shell to execute commands

Do you use del or rm to delete files?


$Id: make.html,v 1.1.1.1 2004/01/04 05:02:31 reid Exp $