<!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 http-equiv="Content-Type" content="text/html; charset=UTF-8">
<META NAME="GENERATOR" Content="Visual Page 2.0 for Windows">
<TITLE>Getting Started with Jam/MR A Tutorial</TITLE>
<STYLE TYPE="text/css">
<!--
.goohl0 {
Color : black ;
Background-Color : #FFFF66
}
-->
</STYLE>
</HEAD>
<BODY BGCOLOR="white" LINK="blue" VLINK="blue">
<P>
<DIV ALIGN="CENTER">
<nobr>
<P>Getting Started with Jam/MR </nobr>
</DIV>
<DIV ALIGN="CENTER">
<P>Laura Wingerd<BR>
Perforce Software<BR>
Perforce User Conference 2001<BR>
<SPAN STYLE="Font-Weight : Bold">Abstract</SPAN>
</DIV>
<DIV ALIGN="CENTER">
<P><SPAN STYLE="Font-Style : Italic">Jam/MR ("Jam - make(1)Redux") is a software build tool. Its command
language and processing logic are enough unlike make </SPAN><BR>
<SPAN STYLE="Font-Style : Italic">that new users often have trouble learning how to use it to its best advantage.
</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">In this talk, I'll demonstrate some of Jam's most useful features in a series
of progressively </SPAN><BR>
<SPAN STYLE="Font-Style : Italic">interesting examples. The basic knowledge gleaned from these examples will give
you the insight you need to </SPAN><BR>
<SPAN STYLE="Font-Style : Italic">incorporate very sophisticated Jam features into a powerful build system. You'll
find it helpful to have the</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">Jam documentation at hand as you follow along.</SPAN>
</DIV>
<P>
<H2><SPAN STYLE="Font-Weight : Bold">1. Background</SPAN></H2>
<PRE><SPAN STYLE="Font-Weight : Bold"></SPAN></PRE>
<P>Jam was written by Christopher Seiwald. It has been freely available as C++ source for many years. <BR>
It is widely used to build commercial and academic software, and has evolved into a few variants.<BR>
It's even shipped with the Macintosh OS/X operating system. <BR>
Nevertheless, it hasn't supplanted make and the O'Reilly book on Jam has yet to be written.<BR>
<BR>
Jam's distinguishing strengths are its portability and speed. Its portability comes from the way the Jam<BR>
command language segregates platform-dependent specifications, like compile and link commands, from<BR>
platform-independent specifications, like dependencies. (More on this in a bit.) Its speed comes in part<BR>
from its single-invocation processing logic. The Jam executable is invoked once -- unlike make which<BR>
must invoke itself recursively -- to gather, analyze, and act on its input.
<H2><SPAN STYLE="Font-Weight : Bold">2. How Jam Works</SPAN></H2>
<P>Jam is a stand-alone executable program. When you invoke it, it does three things. <BR>
First, it reads in "Jamfiles" (akin to "Makefiles") containing your instructions. <BR>
Next, it scans directories (and sometimes files) looking for 'targets' defined by your instructions; <BR>
a Jam 'target' is a file that is used, created or updated during a build. <BR>
Finally, it executes system commands to create or update targets. <BR>
These three phases of Jam processing are called parsing, binding, and updating. <BR>
At the end of this presentation you'll understand why it's important to know about the three phases.
<H3><SPAN STYLE="Font-Weight : Bold">2.1 Running Jam</SPAN></H3>
<P><SPAN STYLE="Font-Weight : Bold"><BR>
</SPAN>If you have a 'jam' executable available, you can try any of the Jam examples shown here yourself.<BR>
Simply put the example text in a file and run:<BR>
<FONT FACE="Courier New, Courier">jam -f </FONT><SPAN STYLE="Font-Style : Italic"><FONT FACE="Courier New, Courier">yourfile</FONT></SPAN><SPAN
STYLE="Font-Style : Italic"><BR>
<BR>
</SPAN>(Normally you'd put your Jam commands in a file called 'Jamfile'. That's the file Jam looks for by<BR>
default when you don't use the '-f' flag. However, by default, Jam also invokes a setup file called the Jambase,<BR>
which would add unnecessary complexity to the examples here.)<BR>
<BR>
You can use a number of Jam command line flags to show revealing diagnostic information:<BR>
<FONT FACE="Courier New, Courier">-d2 </FONT>Diagnostic output level two: shows the OS commands used to create
or update files being built.<BR>
<FONT FACE="Courier New, Courier">-d5</FONT> Diagnostic output level five: shows you how your Jamfiles are being
interpreted (which<BR>
procedures are being run, which variables are being set, etc.). Try using this flag with the simple<BR>
examples discussed below.<BR>
<FONT FACE="Courier New, Courier">-n</FONT> Run without actually executing any update commands.<BR>
<FONT FACE="Courier New, Courier">-a </FONT>Rebuild all targets, whether they need it or not.<BR>
<BR>
You can combine these flags, e.g.:<BR>
<FONT FACE="Courier New, Courier">jam -nd5a -f </FONT><SPAN STYLE="Font-Style : Italic"><FONT FACE="Courier New, Courier">yourfile</FONT></SPAN>
<H2><SPAN STYLE="Font-Weight : Bold">3. The Jam Language</SPAN></H2>
<P><BR>
Before we get started with example Jamfiles, let's take a look at some interesting aspects of <BR>
Jam's simple yet intuitive language.
<H3><SPAN STYLE="Font-Weight : Bold">3.1 Syntax</SPAN></H3>
<P>The Jam language has a very straightforward syntax. Even so, it manages to confound new users. The most<BR>
misunderstood rules of the language are:<BR>
* Case is significant.<BR>
* Statement elements (called "tokens")<SPAN STYLE="Font-Style : Italic"> must be separated by whitespace.</SPAN><BR>
* Every statement ends with a semicolon --- in its own token!<BR>
<BR>
Here are some examples:<BR>
<FONT FACE="Courier New, Courier">X = foo.c ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">This is a single Jam statement that sets the value of "X" to "foo.c".</SPAN><BR>
<BR>
<FONT FACE="Courier New, Courier">X = foo.c ; x = bar.c ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">This pair of statements sets two Jam variables.</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">First it sets a variable named 'X" to "foo.c", then it sets a
variable named "x" to "bar.c".<BR>
<BR>
</SPAN><FONT FACE="Courier New, Courier">X = foo.c;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">This is an incomplete statement that will cause a syntax error, or worse, unexplainable</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">results. When Jam reads this it will set the value of X to "foo.c;"
plus whatever happens</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">to be on the next line. Why? Because there is no whitespace before the semicolon
that is<BR>
meant to end the statement.</SPAN><BR>
<BR>
Each statement in the Jam language either sets a variable or invokes a "rule". <BR>
(A "rule" is basically a procedure.) You can tell whether a statement is setting <BR>
a variable or invoking a rule by the presence of an equal sign. <BR>
For example, all the statements above set variables. <BR>
Here are some statements that invoke rules:<BR>
<FONT FACE="Courier New, Courier">X foo.c ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">This invokes a rule called "X" and passes it one argument, "foo.c".<BR>
<BR>
</SPAN><FONT FACE="Courier New, Courier">X=foo.c ;</FONT><BR>
<BR>
<SPAN STYLE="Font-Style : Italic">This is a perfectly acceptable Jam statement, but it doesn't do what you think
</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">it's going to do. This statement invokes a rule named "X=foo.c" with
no arguments. </SPAN><BR>
<SPAN STYLE="Font-Style : Italic">Why is this not treated as an assignment statement? </SPAN><BR>
<SPAN STYLE="Font-Style : Italic">Because there is no whitespace delimiting the "=" sign.</SPAN><BR>
<BR>
Actually, Jam recognizes a third type of statement as well. <BR>
Statements that begin with certain keywords are "flow-of-control" statements. <BR>
These keywords are fairly easy to recognize; they include "if", "for",<BR>
"switch", and "include".<BR>
<BR>
Flow-of-control statements and additional Jam language syntax will be introduced in later examples. By the<BR>
end of this presentation you'll be familiar enough with how Jam works that any syntax or functionality<BR>
not shown here will make perfect sense to you when you read about it in the Jam documentation.
<H3><SPAN STYLE="Font-Weight : Bold">3.2 Liberals</SPAN></H3>
<P><SPAN STYLE="Font-Weight : Bold"><BR>
</SPAN>Literals don't have to be quoted in the Jam language. <BR>
Anything that is not a variable name, a rule name, or an operator is assumed to be a literal. <BR>
And every literal is a string. (There is no other data type in Jam!)<BR>
For example:<BR>
<FONT FACE="Courier New, Courier">X = foo.c ;<BR>
foo.c = X ;</FONT><BR>
This assigns the literal value "foo.c" to a variable named X, <BR>
then assigns the literal value "X" to a variable named "foo.c".<BR>
<BR>
Quotes <SPAN STYLE="Font-Style : Italic">are </SPAN>necessary, however, to refer to a literal that contains spaces,
<BR>
"=", or other characters that Jam would interpret differently. For example:<BR>
<FONT FACE="Courier New, Courier">X = "this ; and ; this" ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">This assigns the string value "this ; and ; this" to variable X.</SPAN>
<H3><SPAN STYLE="Font-Weight : Bold">3.3 Variables</SPAN></H3>
<P><SPAN STYLE="Font-Weight : Bold"><BR>
</SPAN>Jam variable values are strings. A single Jam variable can hold a list of values, each value is a string:<BR>
<FONT FACE="Courier New, Courier">X = a b 1 '2 3' ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Assigns a list of string values, "a", "b", "1"
and "2 3" to variable X.<BR>
<BR>
</SPAN>Jam variable names are also strings. You can name a variable almost anything you want:<BR>
<FONT FACE="Courier New, Courier">"My dog has fleas!" = yes ;</FONT><BR>
<BR>
<SPAN STYLE="Font-Style : Italic">name of the variable set here is "My dog has fleas!" and its value
is a </SPAN><BR>
<SPAN STYLE="Font-Style : Italic">single-element list containing the string "yes".</SPAN><BR>
<BR>
You can also set more than one variable at once:<BR>
<FONT FACE="Courier New, Courier">My dog has fleas! = yes ;</FONT><BR>
<BR>
<SPAN STYLE="Font-Style : Italic">The one-element list containing the string "yes" is assigned to variables
named "My",</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">"dog", "has" and "fleas!".</SPAN>
<H3><SPAN STYLE="Font-Weight : Bold">3.3.1 Variable Expansion</SPAN></H3>
<P><BR>
Referring to a variable's value is called expansion in Jam. <BR>
In its simplest form, you use "$(name)" to expand a variable. For example:<BR>
<FONT FACE="Courier New, Courier">X = This is a message. ;<BR>
Echo $(X) ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Invokes Jam's built-in "Echo" rule to output the list of strings assigned
to X.</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">In other words, this outputs: This is a message.</SPAN><BR>
<BR>
You can use variable expansion on the left-hand side of an assignment statement as well:<BR>
<FONT FACE="Courier New, Courier">X = Hello ;<BR>
$(X) = Bye ;</FONT><BR>
<BR>
<SPAN STYLE="Font-Style : Italic">Assigns "Hello" to X, then assigns "Bye" to a variable named
Hello.<BR>
<BR>
</SPAN>When a variable list contains more than one item, its expansion results in a list:<BR>
<FONT FACE="Courier New, Courier">X = A B C ;<BR>
$(X) = Hi there ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Assigns strings "A", "B", and "C" to variable X.
Then assigns the list of strings</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">"Hi" and "there" to each of variables A, B, and C.</SPAN><BR>
<BR>
You can use a "subscript" to refer to specific list items, with the syntax "$(name[subscript])":<BR>
<FONT FACE="Courier New, Courier">X = A B C ;<BR>
Echo $(X[2]) ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Outputs "B".</SPAN><BR>
<BR>
You can add elements to a list with the "+=" operator:<BR>
<FONT FACE="Courier New, Courier">X = A B C ;<BR>
X += $(X) ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Now X contains the list: A B C A B C</SPAN>
<H3><SPAN STYLE="Font-Weight : Bold">3.3.2 Variable Expansion Products</SPAN></H3>
<P><SPAN STYLE="Font-Weight : Bold"><BR>
</SPAN>When variable expansions are combined with each other or with literals in a single Jam token they are<BR>
expanded to a <SPAN STYLE="Font-Style : Italic">product</SPAN>. That product is itself a list. Here are some examples:<BR>
<FONT FACE="Courier New, Courier">X = A B C ;<BR>
Y = E F ;<BR>
Echo $(X)$(Y) ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Outputs: AE AF BE BF CE CF</SPAN><BR>
<BR>
<FONT FACE="Courier New, Courier">X = A B C ;<BR>
Y = test_$(X).result</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Y now contains this list of strings: "test_A.result", " test_B.result",
and "test_C.result"</SPAN><BR>
<BR>
<FONT FACE="Courier New, Courier">X = A B C D E F G H ;<BR>
Selected = 3 7 8 ;<BR>
Echo $(X[$(Selected)]) ;<BR>
</FONT><SPAN STYLE="Font-Style : Italic"><FONT FACE="Courier New, Courier">Outputs: C G H</FONT></SPAN><BR>
<BR>
Remember that a Jam "token" is a statement element delimited by whitespace. <BR>
To identify a single token that contains a blank, use quotes. Compare these two examples:<BR>
<FONT FACE="Courier New, Courier">X = Bob Sue Pat ;<BR>
Echo 'Hello $(X)!' ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Outputs: Hello Bob! Hello Sue! Hello Pat!</SPAN><BR>
<BR>
<FONT FACE="Courier New, Courier">X = Bob Sue Pat ;<BR>
Echo Hello $(X)! ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Outputs Hello Bob! Sue! Pat!</SPAN><BR>
<BR>
When a token in a Jam statement contains a reference to a variable whose value list is empty, the expanded<BR>
result is an empty list. (Think of it as multiplying by zero.) A variable that has not been set is the same<BR>
as a variable whose value list is empty. You can also explicitly set a variable to an empty list. Note that an<BR>
empty list is not the same as a list of one or more null strings. Here are some examples:<BR>
<BR>
<FONT FACE="Courier New, Courier">X = Bob Sue Pat ;<BR>
Echo Hello $(X)$(Y) ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Y has not been set, so this outputs simply: Hello<BR>
<BR>
</SPAN><FONT FACE="Courier New, Courier">X = Bob Sue Pat ;<BR>
Y = "" "" ;<BR>
Echo Hello $(X)$(Y) ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Y is set to a list of two null strings, so this outputs: Hello Bob Sue Pat Bob
Sue</SPAN><BR>
<BR>
<FONT FACE="Courier New, Courier">Y Z = test ;<BR>
X = Bob Sue Pat ;<BR>
Y = ;<BR>
Z = $(X)$(Y) ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Because Y was unset, the $(X)$(Y) product is an empty list. As a result, this</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">explicitly unsets Z.</SPAN>
<H3><SPAN STYLE="Font-Weight : Bold">3.3.3 Variable Expansion Modifiers</SPAN></H3>
<P><BR>
"Modifiers" in Jam variable expansions can be used to change the resulting values. <BR>
Modifier syntax in variable expansion is "$(name:modifier)". <BR>
For example, you can use the "U" and "L" modifiers to force the case of the expanded result:<BR>
<BR>
<FONT FACE="Courier New, Courier">X = This is<BR>
Y = A TEST ;<BR>
Echo $(X:U) $(Y:L) ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Outputs: THIS IS a test</SPAN><BR>
<BR>
Most Jam modifiers are specifically designed for handling file names and directory paths. Of those, some<BR>
trim down the expanded result, and some replace parts of the expanded result. For example, the "S=suffix"<BR>
modifier can be used to replace the filename suffix:<BR>
<FONT FACE="Courier New, Courier">X = foo.c ;<BR>
Y = $(X:S=.obj) ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Assigns the value "foo.obj" to Y.<BR>
<BR>
</SPAN>You can combine modifiers with each other, with list item subscripts, and with product expansions. <BR>
Here's a hideous example:<BR>
<FONT FACE="Courier New, Courier">X = foo.c bar.c ola.c ;<BR>
Y = .c .obj .exe .dll ;<BR>
Echo $(X[2]:S=$(Y):U) ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Outputs: BAR.C BAR.OBJ BAR.EXE BAR.DLL</SPAN>
<H3><SPAN STYLE="Font-Weight : Bold">3.3.4 Variables Are Expanded During Parsing!</SPAN></H3>
<P><BR>
Remember the three phases of Jam execution? Variable expansion in your Jamfiles occurs<BR>
<SPAN STYLE="Font-Style : Italic">during the parsing phase</SPAN>, before Jam scans your filesystem, and before
it executes<BR>
<SPAN STYLE="Font-Style : Italic">any</SPAN> system commands.<BR>
This means that <B>you can't assign the output of system commands</B> (e.g. "ls", or "find")<BR>
to Jam variables!
<H3><SPAN STYLE="Font-Weight : Bold">3.4 Rules</SPAN></H3>
<P><BR>
Jam "rules" are procedures that are interpreted and executed during the parsing phase. <BR>
They can be invoked with arguments passed to them. <BR>
Each argument is a list; arguments are separated by colon tokens. <BR>
For example:<BR>
<FONT FACE="Courier New, Courier">Depends a : b c d ;</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Invokes the built-in "Depends" rule with two arguments. </SPAN><BR>
<SPAN STYLE="Font-Style : Italic">The first argument is a list of one item, "a". </SPAN><BR>
<SPAN STYLE="Font-Style : Italic">The second argument is a list of three items, "b", "c", and
"d".</SPAN><BR>
<BR>
Here's an example of how a rule is defined:<BR>
<FONT FACE="Courier New, Courier">rule MyRule {<BR>
Echo First arg is $(1) ;<BR>
Echo Second arg is $(2) ;<BR>
Echo Third arg is $(3) ;<BR>
}</FONT><BR>
And here's how you might invoke it:<BR>
<BR>
<FONT FACE="Courier New, Courier">MyRule a : b c : d e f</FONT><BR>
<SPAN STYLE="Font-Style : Italic">Outputs:</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">First arg is a</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">Second arg is b c</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">Third arg is d e f</SPAN><BR>
<BR>
For backward compatibility, $(<) and $(>) are allowed in place of $(1) and $(2) in the body of a rule.<BR>
In older Jamfiles you may see rule definitions that look like:<BR>
<FONT FACE="Courier New, Courier">rule MyRule {<BR>
Echo First arg is $(<) ;<BR>
Echo Second arg is $(>) ;<BR>
}</FONT>
<H3><SPAN STYLE="Font-Weight : Bold">3.5 Actions</SPAN></H3>
<P>An "action" in Jam is a special-purpose rule used to specify system commands that will be run<BR>
during Jam's updating phase. <BR>
The "actions" keyword identifies an action definition. <BR>
The body of an action definition contains system commands, not Jam language statements. <BR>
However, it<SPAN STYLE="Font-Style : Italic"> can </SPAN>contain Jam variables.<BR>
Here's a simple action definition:<BR>
<FONT FACE="Courier New, Courier">actions MyAction {<BR>
touch $(1)<BR>
cat $(2) >> $(1)<BR>
}</FONT><BR>
If this action were invoked thus:<BR>
<FONT FACE="Courier New, Courier">MyAction ola : foo bar ;</FONT><BR>
this command sequence would be passed to the OS command shell to update "ola":<BR>
<FONT FACE="Courier New, Courier">touch ola<BR>
cat foo bar >> ola</FONT><BR>
<BR>
Actions have these special characteristics which set them apart from rules:<BR>
<BR>
* They accept only two arguments. <BR>
In other words, you can refer to $(1) and $(2), but not $(3) in<BR>
the body of an action.<BR>
* All arguments passed to an action are assumed to be targets. (See below.)<BR>
* Whereas rules are run during Jam's parsing phase, <BR>
actions are run during its updating phase.<BR>
Jam variables in an action body are expanded before the action is passed to the OS command shell.
<H3><BR>
<SPAN STYLE="Font-Weight : Bold">3.6 Targets and Dependencies</SPAN></H3>
<P>When you run Jam, it assumes you want to build one ore more targets. <BR>
I said earlier that a Jam "target" is a filesystem object (file, directory, or library member). <BR>
Jam also recognizes "symbolic targets", which can be used to organize dependencies. <BR>
Jam provides one built-in symbolic target called "all".<BR>
If you don't specify a target on the Jam command line, Jam will try to build 'all'. <BR>
This is best explained with demonstration. <BR>
Put the following commands in a file called 'test':<BR>
<FONT FACE="Courier New, Courier">rule MyRule {<BR>
TouchFile $(1) ;<BR>
}<BR>
actions TouchFile {<BR>
touch $(1)<BR>
}<BR>
MyRule test.output1 ;<BR>
MyRule test.output2 ;<BR>
MyRule test.output3 ;</FONT><BR>
Now run:<BR>
<FONT FACE="Courier New, Courier">jam -ftest</FONT><BR>
You'll see that this outputs:<BR>
<SPAN STYLE="Font-Style : Italic">don't know how to make all</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">...found 1 target(s)...</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">...can't find 1 target(s)...</SPAN><BR>
To tell Jam what you really want to build you could specify a target on the command line:<BR>
<FONT FACE="Courier New, Courier">jam -ftest test.output2</FONT><BR>
This outputs:<BR>
<SPAN STYLE="Font-Style : Italic">...found 1 target(s)...</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">...updating 1 target(s)...</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">TouchFile test.output2</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">...updated 1 target(s)...</SPAN><BR>
But the efficient way to tell Jam what to update is to use the built-in 'Depends' <BR>
rule to make all your targets dependencies of 'all':<BR>
<FONT FACE="Courier New, Courier">rule MyRule {<BR>
TouchFile $(1) ;<BR>
Depends all : $(1) ;<BR>
}<BR>
actions TouchFile {<BR>
touch $(1)<BR>
}<BR>
MyRule test.output1 ;<BR>
MyRule test.output2 ;<BR>
MyRule test.output3 ;</FONT><BR>
Now you can build your files without having to specify any targets on the command line:<BR>
<FONT FACE="Courier New, Courier">jam -ftest</FONT><BR>
<BR>
Since one target was built in the previous test, only two remain to be built. Here's what Jam outputs:<BR>
<SPAN STYLE="Font-Style : Italic">...found 4 target(s)...</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">...updating 2 target(s)...</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">TouchFile test.output1</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">TouchFile test.output3</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">...updated 2 target(s)...</SPAN><BR>
Jam also takes it upon itself to tell you when it's building something that isn't in the chain of dependencies.<BR>
For example, modify your test file so it looks like this:<BR>
<FONT FACE="Courier New, Courier">rule MyRule {<BR>
TouchFile $(1) ;<BR>
Depends all : $(1) ;<BR>
}<BR>
actions TouchFile {<BR>
touch $(1)<BR>
}<BR>
MyRule test.output1 test.output2 test.output3 ;</FONT><BR>
<BR>
Now run this command to try to rebuild only one of the three files (the '-a' tells Jam to rebuild<BR>
it even if it already exists):<BR>
<FONT FACE="Courier New, Courier">jam -ftest -a test.output2</FONT><BR>
<BR>
The output shows you the additional targets Jam had to build even though they were not in the dependency<BR>
chain for your requested target:<BR>
<SPAN STYLE="Font-Style : Italic">...found 1 target(s)...</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">...updating 1 target(s)...</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">warning: using independent target test.output1</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">warning: using independent target test.output3</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">TouchFile test.output1 test.output2 test.output3</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">...updated 1 target(s)...</SPAN>
<H3><SPAN STYLE="Font-Weight : Bold">3.7 Implicitly Invoked Actions</SPAN></H3>
<P>When an action and a rule have the same name, Jam implicitly invokes the action with the same arguments<BR>
that were used in the rule invocation. Here's an example of an implicitly invoked action:<BR>
<FONT FACE="Courier New, Courier">rule MyRule {<BR>
Depends all : $(1) ;<BR>
}<BR>
actions MyRule {<BR>
p4 info > $(1)<BR>
} <BR>
MyRule info.output</FONT><BR>
A single statement invokes 'MyRule'. <BR>
The "MyRule" rule will be run during the parsing phase, <BR>
and the "MyRule" action will be run during the updating phase. <BR>
Both are passed the same argument, "info.output".
<H3><SPAN STYLE="Font-Weight : Bold">3.8 Target-specific Variables</SPAN></H3>
<P><BR>
Another syntax for setting Jam variables allows you to set values specific to individual targets. <BR>
Values set this way are expanded only in actions. <BR>
The syntax is "variable on target =". <BR>
For example:<BR>
<FONT FACE="Courier New, Courier">X on foo = A B C ;<BR>
X on bar = 1 2 3 ;</FONT><BR>
<BR>
The usefulness of this can be demonstrated by a working example:<BR>
<FONT FACE="Courier New, Courier">rule MyRule {<BR>
CMD on $(1) = $(2) ;<BR>
PORT on $(1) = $(3) ;<BR>
Depends all : $(1) ;<BR>
MyTest $(1) ;<BR>
}<BR>
actions MyTest {<BR>
p4 -p$(PORT) $(CMD) > $(1)<BR>
}<BR>
MyRule test1.output : info ;<BR>
MyRule test2.output : info : mars:1666 ;<BR>
MyRule test3.output : users : mars:1666 ;</FONT><BR>
Run Jam with this input and you get:<BR>
<SPAN STYLE="Font-Style : Italic">...found 4 target(s)...</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">...updating 3 target(s)...</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">MyTest test1.output</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">p4 info > test1.output</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">MyTest test2.output</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">p4 -pmars:1666 info > test2.output</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">MyTest test3.output</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">p4 -pmars:1666 users > test3.output</SPAN><BR>
<SPAN STYLE="Font-Style : Italic">...updated 3 target(s)...</SPAN><BR>
<BR>
In this demonstration, two variables, CMD and PORT, were set on each target. <BR>
The same action updates each target, but when the action body is expanded, <BR>
the resulting command is different for each.
<H3><SPAN STYLE="Font-Weight : Bold">4. Working Example: Jam as a Test Driver</SPAN></H3>
<P><BR>
To build on the Jam language and behavior presented so far, <BR>
I've put together a series of working Jamfile implementing a simple test driver. <BR>
Each example refines the previous one to demonstrate various Jam strengths. <BR>
You can run these examples yourself if you have a working "jam" and a working "p4".<BR>
<BR>
Note that this sequence of examples has nothing to do with traditional compile-and-link builds. <BR>
I've chosen these examples because they are small and self-contained. <BR>
Once you've studied these examples and have an understanding of how Jam works <BR>
you'll be ready to look at the compile-and-link rules in the Jam-provided Jambase. <BR>
These are the rules you'd use to implement large build systems. However, they are far too<BR>
intricate (and boring) to use in a learning example.
<H3><SPAN STYLE="Font-Weight : Bold">4.1 Simple Command Tester</SPAN></H3>
<P><BR>
The first example shows a very simple Jamfile that tests 'p4' commands. <BR>
It merely runs each test and captures the output in a file. <BR>
For each test, the test result file is the target to be updated, <BR>
and the action that updates it is the 'p4' command to be tested.<BR>
<BR>
This example demonstrates how Jam behaves when actions fail. <BR>
When Jam passes an action to the OS to update a file, <BR>
it checks the result of the final command. <BR>
If that result indicates that the command failed, <BR>
Jam removes the file that was just updated. <BR>
(This is how Jam normally behaves; it's not anything coded in this particular example.) <BR>
<BR>
Thus, the only result files left by this first test driver example will be those left by tests. <BR>
With respect to what a test driver should do, that's good, <BR>
because rerunning Jam will only rerun the tests that fail. <BR>
<BR>
But it's also bad, because it leaves no trace of failed tests!<BR>
<BR>
This example also introduces the Jam 'local' declaration. <BR>
It is used to restrict the scope of a variable to the<BR>
rule in which is declared and any rule invoked from it.<BR>
<FONT FACE="Courier New, Courier">rule Test {<BR>
local f = $(1:S=.out) ;<BR>
Depends all : $(f) ;<BR>
RunTest $(f) ;<BR>
CMD on $(f) = $(1) ;<BR>
}<BR>
actions RunTest {<BR>
p4 $(CMD) > $(1)<BR>
}<BR>
Test info ;<BR>
Test users ;<BR>
Test clients ;</FONT>
<H3><SPAN STYLE="Font-Weight : Bold">4.2 Capturing Failed Commands</SPAN></H3>
<P><BR>
A test driver isn't much use if it doesn't show you the output of failed tests. <BR>
In this example, the previous version has been modified to capture the error <BR>
message from the 'p4' command being tested. <BR>
This example introduces the 'ignored' action modifier. <BR>
(See the Jam documentation for other action modifiers.)<BR>
<BR>
"Ignored" changes Jam's behavior when an action fails: instead of removing the target file, <BR>
Jam leaves it, and continues to build any targets dependent on it.<BR>
<BR>
Each test now produces a result file whether or not the test succeeded. <BR>
However, there's no way to tell whether a test succeeded other than by <BR>
looking at the result file. <BR>
Furthermore, once a result file exists, Jam thinks that test is done; <BR>
rerunning Jam no longer reruns the failed tests.<BR>
<BR>
<FONT FACE="Courier New, Courier">rule Test {<BR>
local f = $(1:S=.out) ;<BR>
Depends all : $(f) ;<BR>
RunTest $(f) ;<BR>
CMD on $(f) = $(1) ;<BR>
}</FONT></P>
<P><FONT FACE="Courier New, Courier">actions ignore RunTest {<BR>
p4 $(CMD) > $(1) 2>&1<BR>
}<BR>
Test info ;<BR>
Test clients ;<BR>
Test users ;</FONT>
<H3><SPAN STYLE="Font-Weight : Bold">4.3 Comparing Canonical Results</SPAN></H3>
<P><BR>
A more useful test driver compares test results to previously stored results, called "canons". <BR>
Our example has been enhanced to run each test, diff the test output with the canon, <BR>
and output the diff to a 'match' file.<BR>
<BR>
After the test run, the presence of match files shows the tester which tests succeeded, and 'jam -nd1'<BR>
shows which tests failed.<BR>
<BR>
Notice the dependencies in this example. 'All' is dependent on the match files, and each match file is<BR>
dependent on its corresponding test result file, which is in turn dependent on its corresponding canon file.<BR>
With this dependency chain, only the tests with canons will be run, <BR>
and of those, only the ones that have not previously produced match files will be run.<BR>
<BR>
Also, take a close look at the targets passed to the actions. <BR>
The match file is a target of both 'RunTest',<BR>
which creates the result file, and 'DiffResults', <BR>
which compares the result to the canon. <BR>
If a match file is missing, Jam will invoke both actions to create it. <BR>
The first of the two actions, however, doesn't create it at all -- <BR>
it only creates the result file. This is a trick to make Jam rerun the test if the match file missing.<BR>
<BR>
The 'DiffResults' action, on the other hand, is invoked with only the match file as the target. <BR>
If the diff fails, only the match file will be removed; the result file will remain for the tester to examine.<BR>
<FONT FACE="Courier New, Courier">rule Test {<BR>
local f = $(1:S=.out) ;<BR>
CMD on $(f) = $(1) ;<BR>
local canon = $(1:S=.canon) ;<BR>
local match = $(1:S=.match) ;<BR>
Depends all : $(match) ;<BR>
Depends $(match) : $(f) ;<BR>
Depends $(f) : $(canon) ;<BR>
RunTest $(f) $(match) ;<BR>
DiffResults $(match) : $(f) $(canon) ;<BR>
}<BR>
actions ignore RunTest {<BR>
p4 $(CMD) > $(1) 2>&1<BR>
}<BR>
<BR>
actions DiffResults {<BR>
diff $(2) > $(1) 2>&1<BR>
}<BR>
Test info ;<BR>
Test clients ;<BR>
Test users ;</FONT>
<H3><SPAN STYLE="Font-Weight : Bold">4.4 Capturing Canonical Results</SPAN></H3>
<P><BR>
Another enhancement has been added to the test driver: <BR>
it can now create or update canon files. <BR>
This is optional behavior, triggered by the presence of a variable named 'CAPTURE'.<BR>
<BR>
Since nothing in the Jamfile sets CAPTURE, it must be set externally before running Jam. <BR>
You can set CAPTURE in your environment,<BR>
or you can simply set it on the Jam command line:<BR>
<FONT FACE="Courier New, Courier">jam -sCAPTURE=1 ...</FONT><BR>
<BR>
When CAPTURE is set, Jam follows a completely different logic with different dependencies. <BR>
'All' now depends on canon files, and if any of those are missing they are copied from <BR>
corresponding result files.<BR>
Missing result files are created by simply running the tests.<BR>
<BR>
(This is an admittedly complex alternative to simply writing a script that <BR>
copies result files into canon files, but it serves to illustrate two things, <BR>
setting Jam variables externally, and conditional logic.)<BR>
<FONT FACE="Courier New, Courier">rule Test {<BR>
if $(CAPTURE) {<BR>
CaptureCanon $(1) ;<BR>
} else {<BR>
DoTest $(1) ;<BR>
}<BR>
}<BR>
rule CaptureCanon{<BR>
local canon = $(1:S=.canon) ;<BR>
local result = $(1:S=.out) ;<BR>
CMD on $(result) = $(1) ;<BR>
Depends all : $(canon) ;<BR>
Depends $(canon) : $(result) ;<BR>
RunTest $(result) ;<BR>
CopyResult $(canon) : $(result) ;<BR>
}<BR>
actions CopyResult{<BR>
cp $(>) $(<)<BR>
}<BR>
<BR>
rule DoTest{<BR>
local f = $(1:S=.out) ;<BR>
CMD on $(f) = $(1) ;<BR>
local canon = $(1:S=.canon) ;<BR>
local match = $(1:S=.match) ;<BR>
Depends all : $(match) ;<BR>
Depends $(match) : $(f) ;<BR>
Depends $(f) : $(canon) ;<BR>
RunTest $(f) $(match) ;<BR>
DiffResults $(match) : $(f) $(canon) ;<BR>
}<BR>
actions ignore RunTest {<BR>
p4 $(CMD) > $(1) 2>&1<BR>
}<BR>
actions DiffResults {<BR>
diff $(2) > $(1) 2>&1<BR>
}<BR>
Test info ;<BR>
Test clients ;<BR>
Test users ;</FONT>
<H3><SPAN STYLE="Font-Weight : Bold">4.5 Removing Old Results and Canons</SPAN></H3>
<P><BR>
Now I've enhanced the example to demonstrate a 'Clean' rule. <BR>
This is used to removed built targets from the filesystem. <BR>
I've added two invocations of the Clean rule, <BR>
one to clean up result and match files, <BR>
one to clean up canons. <BR>
Note the symbolic target, 'clean'. <BR>
Nothing depends or is dependent on 'clean',<BR>
thus the only way to activate the Clean action is to use 'clean' as an explicit target on the Jam command line.<BR>
In other words, run:<BR>
<FONT FACE="Courier New, Courier">jam clean</FONT><BR>
<BR>
to remove the result and match files, or:<BR>
<FONT FACE="Courier New, Courier">jam -sCAPTURE=1 clean</FONT><BR>
<BR>
to remove the canon files.<BR>
<BR>
The Clean action has a number of behavior modifiers:<BR>
* <SPAN STYLE="Font-Style : Italic">ignore</SPAN> you've seen before. <BR>
It doesn't really have much purpose in this context other than to<BR>
suppress 'failed to build' messages if file permissions prevent their removal.<BR>
* <SPAN STYLE="Font-Style : Italic">together</SPAN> tells Jam this action need only be run once, <BR>
and that the $(1) that appears in the action definition body can be expanded <BR>
to all the targets it was invoked with. <BR>
(Without <SPAN STYLE="Font-Style : Italic">together</SPAN> an action would be run once per invocation. <BR>
The end result is the same, but for certain actions, <BR>
like running a compiler, <SPAN STYLE="Font-Style : Italic">together</SPAN> can make builds much more efficient.)<BR>
* <SPAN STYLE="Font-Style : Italic">existing </SPAN>tells Jam to not include any targets not already present in
the filesystem<BR>
when it expands $(1) in the action body.<BR>
* <SPAN STYLE="Font-Style : Italic">piecemeal</SPAN> tells Jam that if the size of the expanded action gets too
large for<BR>
the OS command shell, divide the targets up into groups and run the <BR>
action on each group separately.<BR>
<BR>
<FONT FACE="Courier New, Courier">rule Test {<BR>
if $(CAPTURE) {<BR>
CaptureCanon $(1) ;<BR>
} else {<BR>
DoTest $(1) ;<BR>
}<BR>
}<BR>
rule CaptureCanon {<BR>
local canon = $(1:S=.canon) ;<BR>
local result = $(1:S=.out) ;<BR>
CMD on $(result) = $(1) ;<BR>
<BR>
Depends all : $(canon) ;<BR>
Depends $(canon) : $(result) ;<BR>
<BR>
RunTest $(result) ;<BR>
CopyResult $(canon) : $(result) ;<BR>
Clean clean : $(canon) ;<BR>
}</FONT></P>
<P><FONT FACE="Courier New, Courier"><BR>
actions CopyResult {<BR>
cp $(>) $(<)<BR>
}<BR>
rule DoTest {<BR>
local f = $(1:S=.out) ;<BR>
CMD on $(f) = $(1) ;<BR>
local canon = $(1:S=.canon) ;<BR>
local match = $(1:S=.match) ;<BR>
Depends all : $(match) ;<BR>
Depends $(match) : $(f) ;<BR>
Depends $(f) : $(canon) ;<BR>
RunTest $(f) $(match) ;<BR>
DiffResults $(match) : $(f) $(canon) ;<BR>
Clean clean : $(f) $(match) ;<BR>
}</FONT></P>
<P><FONT FACE="Courier New, Courier"><BR>
actions piecemeal together existing Clean {<BR>
rm $(2)<BR>
}<BR>
actions ignore RunTest {<BR>
p4 $(CMD) > $(1) 2>&1<BR>
}<BR>
actions DiffResults {<BR>
diff $(2) > $(1) 2>&1<BR>
}<BR>
Test info ;<BR>
Test clients ;<BR>
Test users ;</FONT>
<H3>4.6 Writing Portable Actions</H3>
<P><BR>
The final refinement is this series of examples illustrates how the <BR>
same action invocations can be used to run commands that vary among OS platforms. <BR>
Before defining my actions, I've set some variables to OS-specific command names. <BR>
In the action definitions I use those variables instead of hard-coded commands.<BR>
<BR>
In some cases the commands needed to perform equivalent actions will differ so much between platforms<BR>
that simply substituting command names won't work. <BR>
For those cases you can actually define actions differently for each platform, as demonstrated here.<BR>
<FONT FACE="Courier New, Courier">if $(NT){<BR>
REMOVE = del/f/q ;<BR>
COPY = copy ;<BR>
actions DiffResults { echo n|comp $(2) > $(1) 2>&1 }<BR>
}<BR>
if $(UNIX){<BR>
REMOVE = rm ;<BR>
COPY = cp ;<BR>
actions DiffResults { diff $(2) > $(1) 2>&1 }<BR>
}<BR>
<BR>
rule Test{<BR>
if $(CAPTURE){<BR>
CaptureCanon $(1) ;<BR>
}else{<BR>
DoTest $(1) ;<BR>
}<BR>
}<BR>
<BR>
rule CaptureCano{<BR>
local canon = $(1:S=.canon) ;<BR>
local result = $(1:S=.out) ;<BR>
CMD on $(result) = $(1) ;<BR>
<BR>
Depends all : $(canon) ;<BR>
Depends $(canon) : $(result) ;<BR>
<BR>
RunTest $(result) ;<BR>
CopyResult $(canon) : $(result) ;<BR>
Clean clean : $(canon) ;<BR>
}<BR>
<BR>
actions CopyResult{<BR>
$(COPY) $(>) $(<)<BR>
}<BR>
rule DoTest {<BR>
local f = $(1:S=.out) ;<BR>
CMD on $(f) = $(1) ;<BR>
local canon = $(1:S=.canon) ;<BR>
local match = $(1:S=.match) ;<BR>
Depends all : $(match) ;<BR>
Depends $(match) : $(f) ;<BR>
Depends $(f) : $(canon) ;<BR>
RunTest $(f) $(match) ;<BR>
DiffResults $(match) : $(f) $(canon) ;<BR>
Clean clean : $(f) $(match) ;<BR>
}<BR>
actions piecemeal together existing Clean {<BR>
$(REMOVE) $(2)<BR>
}<BR>
actions ignore RunTest {<BR>
p4 $(CMD) > $(1) 2>&1<BR>
}<BR>
Test info ;<BR>
Test clients ;<BR>
Test users ;</FONT>
<H2><SPAN STYLE="Font-Weight : Bold">5. Conclusion</SPAN></H2>
<P><SPAN STYLE="Font-Weight : Bold"><BR>
</SPAN>Although I certainly have not showed you everything Jam can do, <BR>
by now you probably have a clear enough understanding that you <BR>
can fill in the blanks by reading the Jam documentation. <BR>
I'd recommend reading through all of the documents that come with the Jam source:<BR>
<BR>
* Jam/MR - Make(1) Redux <BR>
Describes the Jam language and executable command flags.<BR>
* Using Jamfiles and Jambase<BR>
Chatty overview of how to use the already-written rules that<BR>
come with the Jam source.<BR>
(These are the rules you'd use to implement a system that builds<BR>
objects by compiling, archiving, linking, etc.<BR>
These rules also provide methods of managing source and generated<BR>
files in a directory hierarchy.)<BR>
* Jambase Reference <BR>
Terse reference to those rules.<BR>
<BR>
I'd also recommend reading through the Jambase source file itself.<BR>
It's a little long, but it demonstrates a number of techniques you<BR>
can use in your own Jamfiles.
<PRE></PRE>
</BODY>
</HTML>