- <!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>