<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1">
<META NAME="VPSiteProject" CONTENT="file:///D|/rld/Docs/Project.vpp">
<META NAME="GENERATOR" Content="Visual Page 2.0 for Windows">
<TITLE><Jam></TITLE>
</HEAD>
<BODY TEXT="black" BGCOLOR="white" LINK="red" ALINK="#CC00CC" VLINK="#0066FF">
<H1><B>Jam - Make(1) Redux Paper</B></H1>
<P>Christopher Seiwald<BR>
<I>INGRES Corporation</I><BR>
Seiwald@Ingres.Com Now Seiwald@perforce.com<BR>
March 11, 1994 -- Modified May 20, 2004 to match current Jam implementation</P>
<P><BR>
Abstract</P>
<BLOCKQUOTE>
<P>Despite the progress of UNIX, the basic mechanism by which developers build their programs - <I>make</I>(1)
- has remained at its core unimproved since its inception. Most notably, the <I>make</I> language has seen few
improvements. Jam is a <I>make</I> replacement that uses an extensible, expressive language for describing ways
in which files relate. This new language simplifies the description of systems, both small and large, and renders
extending Jam's functionality not only possible but easy.
</BLOCKQUOTE>
<BLOCKQUOTE>
<P>Jam exists now and runs on many UNIX platforms, VMS, and NT. It is freely available in the comp.sources.unix
archives. As proof of concept, it has been used to build a very large commercial product, generating in a single
invocation 1,000 deliverable files from 12,000 source files.
</BLOCKQUOTE>
<H2>Introduction</H2>
<P>The UNIX <I>make</I>(1) program [Feldman, 1986], which automates the building of targets from their source files,
is widely used. Together with its compatible successors (<I>dmake</I> [Vadura, 1990], GNU <I>make</I> [Stallman,
1991], NET2 <I>make</I> [BSD, 1991], SunOS <I>make</I> [Sun, 1989]) and mutations (<I>cake</I> [Somogyi, 1987],
<I>cook</I> [Miller, 1993], <I>nmake</I> [Fowler, 1985], Plan 9 <I>mk</I> [Flandrena], <I>mms</I> on VMS, etc.),
<I>make</I> enjoys world domination in its capacity.</P>
<P>As <I>make</I>'s author, Stuart Feldman, noted, <I>make</I> itself is not suited for describing huge programs.
This is arguably because the <I>make</I> language has one useful statement: the expression of a direct dependency
among files. This makes for a clumsy, bottom-up description of how to build a system - describing large systems
this way is unmanageable. <I>make</I>'s successors try to overcome this difficulty with sundry tricks: dependencies
with wild-carded names matched against directory contents; parsing command output as Makefile syntax; macro expansions
with and without the help of the C preprocessor, etc.</P>
<P>Jam is an attempt to replace <I>make</I>'s rule system, with its bottom-up language and wayward implicit rules,
with an expressive language that makes it possible to describe explicitly and cogently the compilation of programs.
The current practice of building source simply because it matched wildcards is unreliable in the face of debris
left around by a careless programmer. <BR>
A robust product should be built explicitly, and to make this palatable, Jam makes it easy to be explicit.</P>
<P>A typical sample of Jam's language as seen by end-users will serve to anchor its description:</P>
<PRE> Main prog : prog.c ;
Libs prog : libaux.a ;
Archive libaux.a : compile.c gram.y scan.c ;
</PRE>
<P>This example invokes three rules that instruct Jam to build an archive from three source files, to compile a
fourth source file, and to link it against the archive. All three rules, as well as all other rules given as examples
in this paper, are stock ones that come with Jam (see <I>Jambase</I>(5) [Seiwald, 1993]).</P>
<P>
<H2>The Jam Language</H2>
<P>As with <I>make</I>, the most important statement in the Jam language is the expression of a relationship among
files. With <I>make</I>, the relationship is a direct dependency; with Jam, the relationship is user-defined. The
expression of such a relationship is:</P>
<PRE> <Rule> <targets> [ : <sources> ] ;
</PRE>
<P>This statement is referred to as a rule invocation, with the name of the rule leading the statement. Except
for a handful of built-in rules, the definition of a rule is user-defined. The <sources> are optional. Each
of the three lines in the example above are rule invocations.</P>
<P>Because rules invoke each other, the expression of a user-defined relationship can result in other user-defined
relationships being made among the same or different files. In the case of the example given, the MAIN rule will
invoke the rules:</P>
<PRE> Cc prog.o : prog.c ;
Link prog : prog.o ;
</PRE>
<P>These rules (presumably) handle the cases of compiling an object module from a C source file and linking that
object module into an executable.</P>
<P>
<H3>Rule Definition</H3>
<P>A rule is defined in two parts: the Jam statements to execute when the rule is invoked (essentially a procedure
call); and the actions (<I>sh</I>(1) commands) to execute in order to update the targets of the rule. A rule may
have a procedure definition, actions, or both.</P>
<P>A rule's procedure definition is given with:</P>
<PRE> rule <Rule> { <statements> }
</PRE>
<P>This statement causes <statements> to be interpreted by Jam whenever <Rule> is invoked. The <targets>
and <sources> given at rule invocation are available as the special variables $(<) and $(>) in the
<statements> defining the rule. <statements> may be any of the Jam statements listed in this document.
Carrying on the Cc example, a definition for the Cc rule might be:</P>
<PRE> rule Cc { Depends $(<) : $(>) ; }
</PRE>
<P>This particular rule definition simply arranges for the targets to depend on the sources, using the built-in
rule Depends (described below).</P>
<P>A rule's updating actions are given with:</P>
<PRE> actions <Rule> { <string> }
</PRE>
<P>This causes the <I>sh</I> script <string> to be associated with the <targets> in any invocation
of <Rule>. Later, if Jam determines that the <targets> are out-of-date, it will pass <string>
to <I>sh</I> for execution. Jam expands $(<) and $(>) in <string>, but $(<) and $(>) in this
case refer to the <targets> and <sources> after they have been bound to real file path names (see Binding,
below). Finishing out the Cc example, a definition of Cc actions would be:</P>
<PRE> actions Cc { $(CC) -c $(CCFLAGS) -I$(HDRS) $(>) }
</PRE>
<P>
<H3>Rule Effects</H3>
<P>Rule invocations have no outputs or return values and, instead, do their job through three distinct types of
side-effects. The first is when a rule's procedure invokes built-in rules to modify the target dependency graph.
These built-ins will be discussed shortly. The second is when a rule's procedure sets variables. The third is the
association of the updating actions with the targets, which occurs whenever a rule with updating actions is invoked.</P>
<P>
<H3>Built-in Rules</H3>
<P>There are six built-in rules, five of which modify the target dependency graph. None of these rules have updating
actions. The built-in rules are:</P>
<PRE> Depends, Includes, Echo, Exit, Glob, Match, Temporary, NotFile, NoCare
</PRE>
<P><I>Depends</I> and <I>Includes</I> take <I><targets></I> and <I><sources></I>. <I>Echo</I> takes
only <I><targets></I>. <I>Temporary, NotFile,</I> and <I>NoCare</I> take only <I><targets> </I>and
mark them with attributes to indicate special handling when descending the dependency graph.</P>
<P><BR>
<I>Depends</I> The basic builder of the dependency graph: it makes <I><sources></I> dependencies of <I><targets></I>,
just like the simple <I>make</I> <CITE>:</CITE> dependency. If <I><sources></I> are newer than <I><targets></I>
(using file update times for comparison), or if<I> <sources></I> are being updated, then the updating actions
of <I><targets></I> will be executed.</P>
<P><I>Includes</I> A variation on <I>Depends</I>: it makes<I> <sources></I> dependencies of any targets of
which <I><targets></I> are dependencies. This example makes both foo.c and foo.h dependencies of foo.o:</P>
<PRE> Depends foo.o : foo.c ;
Includes foo.c : foo.h ;
</PRE>
<P><BR>
<I>Echo</I> Just echoes its targets to the standard output, as a means for communicating with the user. Jam knows
no fatal error, so the message emitted by <I>Echo</I> can only be advisory.</P>
<P><BR>
<I>Temporary</I> Allows for intermediate targets to be missing and not updated if the final target is up-to-date.
If a target marked <I>Temporary</I> is not present, then it simply inherits its parent's time-stamp. <I>Temporary</I>
can be used for any <I>Temporary</I> target, such as the short-lived object module that is to be part of a library
archive.</P>
<P><BR>
<I>NotFile</I> Indicates that the target is not really a file and therefore doesn't have a time-stamp. Any updating
actions are only executed if the target's dependencies were updated, rather than on the basis of time-stamp comparisons.
<I>NotFile</I> is used for pseudo targets such as <CITE>all</CITE> or <CITE>install</CITE>, which have dependencies
but don't actually get built themselves.</P>
<P><BR>
<I>NoCare</I> Indicates that the target may both be non-existent and not have any updating actions. This loophole
is used to make up for the sloppiness of the header-file scanning.</P>
<P>
<H3>Jam Variables</H3>
<P>Part of Jam's programmability lies in its treatment of variables. As with <I>make</I> and <I>sh</I>, Jam variables
are lists of strings, with zero or more elements. But unique to Jam, the result of variable expansion is the product
of the variable values and literal constants in the token being expanded. An example helps here:</P>
<PRE> $(X) -> a b c
t$(X) -> ta tb tc
$(X)$(X) -> aa ab ac ba bb bc ca cb cc
</PRE>
<P>This approach makes quick work of many normal variable manipulations: prepending a path name to a list of file
names, prepending <CITE>-l</CITE> to a list of library names, appending a <CITE>,v</CITE> to RCS file names, etc.</P>
<P>Jam has a modicum of variable editing options to replace components of a path name and to subselect members
of a list. These options are discussed in usable detail in Jam's manual page [Seiwald, 1993].</P>
<P>Unlike <I>make</I>, Jam does not defer expansion of variables. When a variable is referenced, even to assign
a new variable, the value is expanded at that time.</P>
<P>Jam variables have two scopes: global and target-specific. Global variables behave much as one might expect,
holding their value until reassigned. Target-specific variables take precedence over global variables when the
specified target is being bound (see below) or updated. The distinction between global and target-specific variables
is made when the variables are assigned. The syntax for setting the two types is, respectively:</P>
<PRE> <var> = <value> ;
<var> on <targets> = <value> ;
</PRE>
<P>Target-specific variables have several uses. A simple one is to permit different compiler flag settings for
different source files. In this way, the actions of the Cc rule may be used to compile any C source file, with
various flags (HDRS, CCFLAGS) being adjusted per-target. Other uses of target-specific variables will be discussed
shortly.</P>
<P>
<H3>Flow-of-Control</H3>
<P>In addition to statements for defining and invoking rules and setting variables, the Jam language contains statements
for flow-of-control and file inclusion. The statements are:</P>
<PRE> if <cond> { <statements> } [ else { <statements> } ]
for <var> in <list> { <statements> }
while <cond> { <statements> } ;
switch <value> { case <pattern1> : <statements> ; ... }
break ;
continue ;
return <values> ;
include <file> ;
</PRE>
<P>The <CITE>if</CITE> statement does the obvious; the <condition> is the usual mix of comparison and logical
operators applied to variables.</P>
<P>The <CITE>for</CITE> statement iterates over the elements of <value>, assigning the (global) variable
<var> to each element and executing the statement block.</P>
<P>The <CITE>switch</CITE> statement executes the statement block whose case <value> matches the switch's
<value>.</P>
<P>The <CITE>include</CITE> statement sources another file containing Jam statements.</P>
<P>Jam neither needs nor desires a macro preprocessor. Making rule definitions and file inclusions normal statements
obviates a macro preprocessor for conditional compilation, as these statements may appear within Jam conditionals.
Further, preprocessing would require the Jam language to play <CITE>dodge-em</CITE> with the preprocessor semantics.</P>
<H2>Binding Files</H2>
<P>Jam can find source and target files in distant directories, much like the functionality of VPATH in GNU <I>make</I>
and <I>dmake</I>.</P>
<P>By default, a target is located at the actual path of the target, relative to the directory of Jam's invocation.
If the special variable $(LOCATE) is set to a directory name, Jam locates the target in that directory (correctly
concatenating the value of $(LOCATE) and the target's path name). If $(LOCATE) is unset but the special variable
$(SEARCH) is set to a directory list, Jam searches along the directory list for the target file (again, correctly
concatenating the path names).</P>
<P>Jam makes available the bound target names by using them when expanding $(<) and $(>) for updating actions.
Thus, a target can be referred to by a short, unrooted name when invoking a rule to define a relationship, but
any shell commands manipulating the target see a path name usable from the current directory.</P>
<P>$(SEARCH) provides VPATH-like functionality, allowing Jam to be invoked in directories other than where the
source lives, while $(LOCATE) liberates Jam from the directory tree altogether. With it, Jam can run anywhere.</P>
<P>By setting $(SEARCH) and $(LOCATE) properly, Jam can handle a variety of build environments. For example, read-only
source trees can be handled by pointing $(SEARCH) at a read-only source code directory while pointing $(LOCATE)
to a working directory. As another example, <CITE>sparse</CITE> source trees can be handled by having $(SEARCH)
contain two directories: first the developer's own directory, which contains only the files he is editing, and
then his group's directory, which contains the master copy of all source. Most importantly, much of any build environment
can be encoded in the settings of $(SEARCH) and $(LOCATE), which leaves the file names used in rule invocations
free from environment.</P>
<P>The power of $(SEARCH) and $(LOCATE) is realized when these variables are set per-target rather than just globally.
Each individual target file can potentially be found along different search paths. In practice, related files will
have the same search path, but Jam can efficiently accommodate the degenerate case of having these variables set
per-target. In this way, Jam can build whole source trees, with source files scattered across directories.</P>
<P>
<H2>Header-File Inclusion</H2>
<P>Jam handles the incidental dependencies caused when source files include other source files. To find such dependencies,
Jam scans source files for header-file inclusions, using a regular expression pattern match [Spencer, 1986]. The
regular expression is given in the variable $(HDRSCAN). The result of the scan is not interpreted directly by Jam;
to arrange the necessary relationship, Jam calls a user-defined rule named in the variable $(HDRRULE), with the
scanned file as <targets> and the found header-files as <sources>. Usually, the definition of $(HDRRULE)
Includes a call to the built-in rule Includes, which updates the dependency graph appropriately. An example HDRSCAN
that works for C preprocessor Includes is:</P>
<PRE> HDRSCAN = "^[ \t]*#[ \t]*include[ \t]*[<\\"]([^\\">]*)[\\">].*$" ;
</PRE>
<P>The combination of $(HDRSCAN) and $(HDRRULE), when set per-target, enables Jam to handle just about any include-file
syntax or semantics. Unfortunately, this mechanism doesn't understand conditional Includes (#include within #ifdef),
and can produce bogus dependencies that must be crudely pasted over with the application of the built-in NoCare
rule.</P>
<P>
<H2>Time-Stamps</H2>
<P>Like <I>make</I> et. al., Jam uses time-stamps to determine when targets are out-of-date. Another possible design,
a more forward-looking one, would have Jam taking file update cues from an integrated source management system.
This was deferred for two reasons: first, it would require picking a source management system with which to work
(or attempting to engineer a generic interface to source management systems); second, it would preclude using Jam
as a drop-in replacement for existing uses of <I>make</I>.</P>
<P>The code in Jam that checks dependencies is isolated enough to be altered to work with a source management system.
Internally, Jam already distinguishes between updates due to newer dependents and updates due to updated dependents.</P>
<P>
<H2>The Base Rule Set</H2>
<P>A collection of rules providing <I>make</I>-like functionality is supplied with Jam. Called Jambase, the file
provides a dozen-odd rules for compiling and linking C source code. Different versions of Jambase exist for UNIX,
VMS, and NT, all providing the same rule set.</P>
<P>Figure 1 lists the rules defined in the current Jambase (described comprehensively in <I>Jambase</I>(5)).
<PRE>
<HR ALIGN="CENTER">
Main image : source ; link executable from compiled sources
Libs image : libraries ; link libraries onto a MAIN
Undefines images : symbols ; save undefs for linking
Setuid image ; mark an executable SETUID
Archive lib : source ; archive library from compiled sources
Object objname : source ; compile object from source
HdrRule source : headers ; handle #Includes
Cc obj.o : source.c ; .c -> .o
Lex source.c : source.l ; .l -> .c
Yacc source.c : source.y ; .y -> .c
Bulk dir : files ; populate directory with many files
File dest : source ; copy file
Shell exe : source ; install a shell executable
RmTemps target : sources ; remove temp sources after target made
InstallBin dir : sources ; install binaries
InstallLib dir : sources ; install files
InstallShell dir : sources ; install man pages
<HR ALIGN="CENTER">
Figure 1 - Rules supplied with Jam
</PRE>
<P>The last act of Jambase is to include a file called Jamfile from the invoking user's current directory. Using
the rules defined in Jambase, the user's Jamfile enumerates the source files and their relationship to the targets
to be built.</P>
<P>The Jambase and Jamfile files share the same language; only their purposes distinguish them. It is possible
to write a special-purpose replacement Jambase that is totally self-contained and needs no directory-specific Jamfile.
It is also possible to use any Jam syntax - including conditionals, rule definitions, etc. - in a Jamfile.
<PRE>
<HR ALIGN="CENTER">
Main prog : prog.c ;
Depends exe : prog ;
Link prog : prog.o ;
Depends prog : prog.o ;
Object prog.o : prog.c ;
Cc prog.o : prog.c ;
Depends prog.o : prog.c ;
Libs prog : libaux.a ;
Depends prog : libaux.a ;
NEEDLIBS on prog = libaux.a ;
Archive libaux.a : compile.c gram.y scan.c ;
Depends libaux.a : libaux.a(compile.o) libaux.a(gram.o) libaux.a(scan.o) ;
Depends libaux.a(compile.o) : compile.o ;
Object compile.o : compile.c ;
Cc compile.o : compile.c ;
Depends compile.o : compile.c
Depends libaux.a(gram.o) : gram.o ;
Object gram.o : gram.y ;
Cc gram.o : gram.c ;
Depends gram.o : gram.c
Yacc gram.c : gram.y ;
Depends gram.c gram.h : gram.y ;
Includes gram.c : gram.h ;
Depends libaux.a(scan.o) : scan.o ;
Object scan.o : scan.c ;
Cc scan.o : scan.c ;
Depends scan.o : scan.c
Archive libaux.a : compile.o gram.o scan.o ;
Temporary compile.o gram.o scan.o ; ???
<HR ALIGN="CENTER">
Figure 2 - Rule execution for the example rule invocations
</PRE>
<H2>The Example</H2>
<P>Returning to our earlier example:</P>
<PRE> Main prog : prog.c ;
Libs prog : libaux.a ; ??? Libs
Archive libaux.a : compile.c gram.y scan.c ;
</PRE>
<P>This example invokes three rules that instruct Jam to build an archive from three source files, to compile a
fourth source file, and to link it against the archive. All these rules are defined by the Jambase file and do
most of their work by invoking other rules defined in the Jambase.</P>
<P>Main calls Link to set up the relationship between <B>prog</B> and <B>prog.o</B>, and then calls Object to set
up the relationship between <B>prog.o</B> and <B>prog.c</B>. Object calls a rule specific to the file suffix, in
this case, Cc for <B>.c</B>. Along the way, the various rules invoke the built-in Depends rule to set up the dependency
graph.</P>
<P><I>Libs</I> is a rule that arranges for <B>libaux.a</B> to become a dependency of <B>prog</B>, and it sets the
target-specific variable NEEDLIBS to let the actions of <I>Link</I> know that <B>libaux.a</B> should be included
on the link command line. <I>Libs</I> has no actions of its own.</P>
<P><I>Archive</I> is a rule that sets up the (somewhat complicated) dependencies between the archive <B>libaux.a</B>,
its members, and the <I>Temporary</I> object modules that are to be its members. It calls <I>Object</I> to set
up the relationship between each of the <I>Temporary</I> object modules and their source files. It also calls the
<I>FArchive</I> rule to handle the archiving of the <I>Temporary</I> object modules into <B>libaux.a</B>.</P>
<P>A more complete list of rule invocations seen by Jam for this example is given in Figure 2.</P>
<P>Probably lost in this litany of rules are some important features: the <I>Object</I> rule, when presented with
the task of making a <CITE>.o</CITE> file from a <CITE>.y</CITE> file, called both the <I>Cc</I> and <I>Yacc</I>
rules. Note that this is considerably easier and more deterministic than <I>make</I>'s approach of making a <CITE>.o</CITE>
from whatever happens to be available. Also, note that the <I>Yacc</I> rule took advantage of the <I>Includes</I>
built-in, to ensure the dependencies on the generated file are accurately registered.</P>
<P>Actually, the rule definitions include a few more machinations to give special variables sensible defaults.
For source code, $(SEARCH) is set to $(SEARCH_SOURCE); for object files, $(LOCATE) is set to $(LOCATE_OBJECT);
for C source files, $(HDRSCAN) is set to the example pattern mentioned above, and $(HDRRULE) is set to <CITE>HdrRule</CITE>
, the generic header-handling rule defined in the Jambase file.</P>
<P>
<H2>Implementation</H2>
<P>The weight of Jam's implementation is evenly divided between its rule-processing subsystem (driven by a <I>yacc</I>(1)
grammar), its recursive binding and scanning subsystem, and its recursive build subsystem.</P>
<P>The rule-processing subsystem is entirely system independent, only setting in-memory variables, building the
dependency graph, and associating update actions with targets. The <I>yacc</I> grammar is less than 200 lines.</P>
<P>The recursive binding and scanning subsystem is mostly system independent, but calls system-dependent routines
to time-stamp files and to manipulate file names.</P>
<P>The recursive build subsystem is mostly system independent, but calls system-specific routines to execute shell
commands (which are system-specific as well).</P>
<P>The system dependencies are hidden through three interfaces: one to time-stamp files; one to manipulate file
names; and one to execute shell commands.</P>
<P>The file time-stamp interface has two layers: a higher one that asks about individual files; and a lower one
that scans directories and library archives whole. The latter is more efficient, and all current implementations
(UNIX, NT) are coded against it.</P>
<P>The file name manipulation interface consists of two routines: one to break a file name down into its components
and one to build a file name from its components. These are quite simple - except on VMS, where concatenating path
names is black art.</P>
<P>The shell-command interface currently approximates the UNIX <I>system</I>(3) call interface, with an addition
for catching interrupts.</P>
<P>Jam achieves its functionality while going sparingly on features. It has only four flags (mostly to do with
debugging), six built-in rules (<I>Depends, Includes, Echo, NotFile, NoCare, Temporary</I>) and six special variables
($(>), $(<), $(SEARCH), $(LOCATE), $(HDRSCAN), $(HDRRULE)). The whole of Jam for UNIX is under 5,000 lines
of code, exclusive of Henry Spencer's <I>regexp</I>(3) regular-expression code (about another 1,300 lines).</P>
<P>A design goal of Jam was portability, specifically so that the same mechanism could be used to build the same
system on different platforms. Jam scores well in this category: the OS interface is constricted, leaving the bulk
of the system dependencies in the Jambase file. Even the Jambase file is somewhat portable, with only the filename
syntax and the actual update commands having to change between UNIX and NT. Jamfile files themselves usually contain
nothing system-specific.</P>
<H2>Performance</H2>
<P>Used to build from scratch a large commercial software system (the INGRES relational DBMS), lapse time for Jam
breaks down as follows (on an HP9000/710):</P>
<PRE> parsing 5,000 lines of Jamfiles 16 seconds
stat()'ing 12,000 source files 1 minute
scanning 12,000 source files for headers 9 minutes
actual building (compiling, linking, etc) 12 hours
</PRE>
<P>The simple conclusion is that Jam's performance in inconsequential. When everything is up-to-date, only few
improvements could be made. <I>stat</I>()'ing files is essentially unavoidable without resorting to other techniques
for determining outdated targets. Scanning source files could be avoided by caching header-file dependency information
in state files. SunOS <I>make</I> and <I>nmake</I> use this approach. The only other recourse is to hammer on the
<I>regexp</I> implementation.</P>
<P>The real performance limitation is in actual building time. Jam does not yet support parallel command execution,
which on a large SMP system can reduce build time by a factor of 5 or more. This feature is anticipated.</P>
<P>
<H2>Comparisons</H2>
<P>Jam's per-target variables are a convenience approached only by SunOS <I>make</I>'s <CITE>target := macro =
value</CITE> syntax. Both Jam and SunOS <I>make</I> make use of the value when updating the target, but Jam gets
added mileage out of the facility by using the value during the binding and header-file scanning.</P>
<P>Jam's searching mechanism is superior to VPATH in two ways: first, it provides not only searching for existing
targets, but also binding for new targets; second, Jam's SEARCH and LOCATE variables can be set per-target. GNU
<I>make</I> allows VPATH to be set selectively, using patterns, and the patterns could be full file names, but
GNU <I>make</I> handles the degenerate case of separate values per file poorly. Jam's SEARCH and LOCATE mechanism
can make the invoker's directory irrelevant, which amounts to a complete solution.</P>
<P>Jam's pattern-scanning method of header-file scanning is faster than those that offload the problem to separate
programs (<I>dmake</I>, <I>cake</I>, GNU <I>make</I>). It is not strictly correct, like SunOS <I>make</I> and GNU
<I>make</I>, which use the C preprocessor. Jam's mechanism, driven by per-target variables and user-defined relationships
is, however, quite flexible. It can handle languages that don't offer a separate preprocessor, as well as languages
where the result of a file being included is more than just a simple dependency. For example, when a <I>yacc</I>
file <I>Includes</I> a C header-file, Jam can be made to understand that the generated C source file will include
the generated C header-file. Jam supports these types of arrangements entirely in its language.</P>
<P>Jam is missing a few features cherished by some <I>make</I> users: the ability to run update commands concurrently
and fancy variable editing. These may appear in future versions of Jam.</P>
<P>
<H2>Discussion</H2>
<P>The comparison of Jam's language with <I>make</I>'s is somewhat subjective and complicated. As stated in the
introduction, Jam is an attempt to replace <I>make</I>'s rule system with an expressive language that makes it
possible to describe explicitly and cogently the compilation of programs.</P>
<P>In this respect, Jam is a success. For small systems, the Jamfile file is often not larger than the three lines
that made up our example. For large systems, any added complexity can be centralized in the Jambase file, while
the Jamfile file(s) in source directories remain simple.</P>
<P>Jam's rule semantics, that of expressing named relationships among files, is Jam's single biggest advantage
over its contemporaries. Its power and economy of expression seem unmatched. There are two other approaches deserving
mention. <I>nmake</I> and <I>dmake</I> allow new operators (replacements for the simple <CITE>:</CITE> dependency
statement) to be defined as macros, and these can be used to create new relationship types. Unfortunately, the
number of available <CITE>operator</CITE> characters is limited, and the coding of the macros would curl the eyebrows
of even seasoned <I>sendmail</I> hackers. <I>cook</I>, <I>cake</I>, Plan 9 <I>mk</I>, and NET2 <I>make</I> promote
a different approach: that of defining variables and then #including <CITE>recipes</CITE> (other <I>make</I> files)
that define the relationships. The recipes are the approximate equivalent of Jam rules, using pass-by-name variables.
This scheme works, but it is an ugly ordeal to try to recover with a preprocessor the functionality that is lacking
in a language.</P>
<P>The Jam language turns out to be fairly straightforward to program. With its reliance on keywords rather than
special characters and its use of a <CITE>;</CITE> to terminate statements, it is easier reading than most <I>make</I>
syntax.</P>
<P>Abandoning <I>make</I> syntax was an easy decision: even those new <I>make</I>s that understand traditional
<I>make</I> syntax get their added functionality through incompatible syntax. If compatibility with <I>make</I>
is the priority, users can just use <I>make</I>. If users want greater functionality, they can't use vanilla <I>make</I>
anyway.</P>
<P>The proof of Jam is in the pudding (sorry...): it is worth mentioning that the timing information given above
is for a single, non-recursive invocation of Jam to compile 12,000 source files scattered throughout 300 directories,
producing 7,000 intermediate targets and 1,000 deliverable files. Each source directory contains a single Jamfile
with an average of 1.5 words per source file (including the source file name). The author knows of no other <I>make</I>
that can approach such completeness with such economy.</P>
<H2>Availability</H2>
<P>Jam is freely available from <A HREF="http://public.perforce.com/public/index.html">Perforce Public Depot</A>.
It is known to compile and work on VMS (Alpha and VAX) and the following variants of UNIX: BSD/386, OSF/1, DGUX
5.4, HPUX 9.0, AIX, IRIX 5.0, PTX V2.1.0, SunOS 4, Solaris 2, Ultrix 4.2, Linux and NT.</P>
<H2>Bibliography</H2>
<P>[Brokken, 1994] Frank B. Brokken and Karel Kubat, <CITE>ICMAKE - the Intelligent C-like MAKEr, or the ICce MAKE
utility</CITE>, Linux Sources, 1994</P>
<P>[BSD, 1991] BSD NET2 make(1) manual page, BSD NET2 documentation, July 1991.</P>
<P>[Feldman, 1986] S. I. Feldman, <CITE>Make - A Program for Maintaining Computer Programs</CITE>, BSD NET2 documentation,
April 1986 (revision).</P>
<P>[Flandrena] R. Flandrena, <CITE>Plan 9 Mkfiles</CITE>, available via anonymous FTP from plan9.att.com.</P>
<P>[Fowler, 1985] Glenn Fowler, <CITE>The Fourth Generation Make</CITE>, Proceedings of the USENIX Summer Conference,
June 1985.</P>
<P>[Miller, 1993] Peter Miller, <CITE>Cook - A File Construction Tool</CITE>, Volume 26, comp.sources.unix archives,
1993.</P>
<P>[Seiwald, 1993] Christopher Seiwald, Jam(1) and Jambase(5) manual pages, Volume 27, comp.sources.unix archives,
1993.</P>
<P>[Spencer, 1986] Henry Spencer, Regexp code and comment, comp.sources.unix archives, 1986.</P>
<P>[Stallman, 1991] Richard M. Stallman and Roland McGrath, <CITE>GNU Make - A Program for Directed Recompilation</CITE>,
Free Software Foundation, 1991</P>
<P>[Somogyi, 1987] Zoltan Somogyi, <CITE>Cake, a Fifth Generation Version of Make</CITE>, Australian Unix System
User Group Newsletter, April 1987.</P>
<P>[Sun, 1989] Sun Microssytems Corporation, SunOS make(1) manual page, SunOS 4.1.2 documentation, September 1989.</P>
<P>[Vadura, 1990] Dennis Vadura, dmake(1) manual page, Volume 27, comp.sources.misc archives, 1990.
</BODY>
</HTML>