II. Advanced Topics:

1. Importing environment variables

When Jam starts, it begins by importing all environment variables from the system into the interpreter. Each variable is splitted at blanks with each word becoming an element in the corresponding Jam variable's string list.

As a special case, each environment variable whose name ends in 'PATH' (like PATH, LD_LIBRARY_PATH, or CLASSPATH), is splitted instead at instances of a system-dependent path-separator character. This separator is available within Jamfiles as $(SPLITPATH); for example, it is ':' on Unix, and ';' on Windows.

Be aware that certain command-line shells provide so-called "automatic" environment variables that are never exported to launched programs, and are thus never imported by Jam. For example, the Bash shell provides an automatic 'OSTYPE' variable that cannot be seen by Jam on all systems.

Additionnally, Jam defines the following variables before running any script:

As an example on how portable Jam is, here are the valid values of the OS and OSPLAT varibales:

For OS: VMS, NT, AS400, MINGW, OS2, MAC, AIX, AMIGA, BEOS, BSDI, COHERENT, CYGWIN, FREEBDS, DGUX, HPUX, INTERIX, IRIX, ISC, LINUX, LYNX, MACHTEN, MPEIX, MVS, NCR, NETBSD, QNXNTO, QNX, RHAPSODY, NEXT, MACOSX, OSF, PTX, SCO, SCINIX, SOLARIS, SUNOS, ULTRIX, UNICOS, UNIXWARE, UNKNOWN

For OSPLAT: VAX, PPC, AXP, X86, SPARC, MIPS, ARM, IA64, 390

2. Variable expansion modifiers

The $(VARIABLE:modifier) syntax is used to perform further processing on expanded variables. There are several types of modifiers:

2.a. Simple modifiers:

Here's a list of simple modifiers:

U expand each variable element to uppercase
L expand each variable element to lowercase

Note that you can also use indexing (e.g. [n]) with modifiers

   FOO = Hello World ;
Echo $(FOO) ; # prints "Hello World" Echo $(FOO:U) ; # prints "HELLO WORLD" Echo $(FOO:L) ; # prints "hello world" Echo $(FOO[1]:U) ; # prints "HELLO" Echo $(FOO[2-]:L) ; # prints "world"

2.b. path component modifiers

Jam provides more sophisticated modifiers that can be used to treat any string as a platform-specific file path; for example, to select specific path components, like directory, base name or suffix. Here's the list of corresponding modifiers:

B

Select filename base. this does not include the suffix

S

Select last filename suffix, if any; this includes the last dot (.)

D

Select directory path

P

Select "parent" directory path. See below

G

Select the grist. See below

M

Select filename archive member. See below

Each modifier works as a flag, you can thus use several of them in a single expansion. For example $(VARIABLE:BS) or $(VARIABLE:SB) will both expand to a file's complete base name without directory path. If a component is not part of a variable element, it will expand to an empty string (but not to an empty value !).

For example, consider the following example for Unix

  # this is for Unix
  FILE = src/program/main.c ;
  Echo $(FILE:BS) ;    # prints "main.c"
  Echo $(FILE:D) ;     # prints "src/program"

The corresponding script for Windows or OS/2, would be:

  # this is for Windows or OS/2
  FILE = src\\program\\main.c ;
  Echo $(FILE:BS) ;    # prints "main.c"
  Echo $(FILE:D) ;     # prints "src\program"

The so-called "parent" directory selected with the P modifier is similar to the one expanded with D, except on the VMS platform where it will refer to the directory's parent. This is only useful within the Jambase to implement subtle path manipulation routines in a portable way.

The grist is an optional prefix in a file path, enclosed in angle brackets (i.e. "<>"). It is used by Jam in various ways that will later be explained in details. For the moment, consider the following example:

   X = nogrist ;
Y = <example>here ;
Z = <second><example>mylib.c ;

Echo $(X:G) ; # prints an empty line Echo $(Y:G) ; # prints <example> Echo $(Z:G) ; # prints <second>, there can be only one grist Echo $(Z:DBS) ; # prints <example>mylib.c

The member is an optional suffix in a file path, enclosed in parentheses (i.e. "()"). It is mainly used by Jam to distinguish several object files in a given library. For the moment, consider the following example:

  X = nomember ;
Y = mylib.a(foo.o) ;

Echo $(X:M) ; # prints an empty line Echo $(Y:M) ; # prints "(foo.o)"

Typical developers shouldn't care about grists and members in file path names, unless they want to write their own rules for the Jambase.

Note also that path decomposition is highly platform-specific !!. You can't expect the same results on all platforms for a given string. For a radical example, consider the following:

  X = A/Tricky:Path\\Example ;
Echo $(X:D) ;
Echo $(X:BS) ;

This will produce the following output, depending on your platform:


$(X:D) $(X:BS)

Unix

A
Tricky:Path\Example

Windows & OS/2

A/Tricky:Path
Example

Macintosh

A/Tricky
Path\Example

Very fortunately, Jam's expansion scheme also allows you to build platform-specific file paths in a platform-agnostic way. This is done with the help of the $(VARIABLE:X=value) syntax, where X is a component descriptor, and value its new value. More exactly, you can use the following modifiers:

B=basename

Replace each element's base name. Does not change the suffix, if any

S=suffix

Replace each element's last suffix by suffix.

D=path

Replace each element's directory path by path.

G=grist

Replace each element's grist by path.

M=member

Replace each element's member by member.

R=root

Prepend root to each element, if it's not already rooted/absolute.

Note that if a component is not present in a source element, its new value will be appended or prepended appropriately to the result. You can also perform several substitutions by separating them with a column, like in $(VARIABLE:B=basename:S=suffix). Each new component value can also be the result of an expansion. We can rewrite our previous example as:

  # works on all platforms
  FILE = main.c ;
  FILE = $(FILE:R=program) ;    # builds a platform-specific 'program/main.c' path
  FILE = $(FILE:R=src) ;        # builds a platform-specific 'src/program/main.c' path
  
  Echo $(FILE:BS) ;  # always prints 'main.c'
  Echo $(FILE:D) ;   # prints 'src/program', 'src\program', 'src:program' or
                     # something else, depending on the platform

If you are within a Jamfile, you can also use the Jambase-provided rule named FDirName, like in:

  FILE = [ FDirName src program main.c ] ;

Echo $(FILE:BS) ;
Echo $(FILE:D) ;

This function has a very simple definition:

rule FDirName
{
# Turn individual elements in $(1) into a usable path. local item ;
local result = $(DOT) ;

for item in $(1)
{
result = $(item:R=$(result)) ;
}

return $(result) ;
}

Here, the only subtlety is the definition of the DOT variable, that holds the platform-specific designator for the current directory (i.e. '.' or ':' depending on your platform)

2.c. Other modifiers:

There exists a few other useful modifiers:

E

Expand to the empty string if the variable is undefined, or normally otherwise

E=value

Expand to a given value if the variable is undefined, or normally otherwise

J=separator

Expands the variable into a single string, by concatenating its element with separator

The E modifier is nearly equivalent to ?=, the difference is that it can be used in action blocks, i.e. shell commands used to build targets, too.

The J modifier is nearly equivalent to calling the following function:

  rule Join
{
local result = $(1[1]);
local x ;

for x in $(1[2-]) {
result = $(result)"$(2)"$(x) ;
}

return result ;
}

However, it can be used in action blocks as well.

3. Scanning directories

It is possible to scan for specific files with the Glob builtin function. Its usage is the following:

  [ Glob  dirs : patterns ]

Where dirs is a list of (platforfm-specific) directory paths to scan, and patterns a list of file patterns similar to the ones used by the switch statement. The result is a list of files, with their directory appended. For example, consider the following scripts:

  for f in [ Glob  include src : *.h *.c ] {
    Echo Found $(f:BS) in $(f:D) ;
  }

Will print the name and locations of all C header and source files in the 'include' and 'src' sub-directories of the current directory.

4. Regular expression matching

The built-in Match rule can be used to perform egrep-style pattern matching. its usage is the following

  [ Match patterns : values ]

Where patterns is one more or egrep-style patterns, and values a list of values. This will return the list of all egrep sub-expressions corresponding to matching patterns. For those without a strong Unix heritage, here's a short summary of how the pattern should look like:

  rule Dump
{
for x in $(1) {
Echo - $(x) ;
}
Echo ;
}

Dump [ Match .* : foo bar ] ;
Dump [ Match (.+). : foo bar z ] ;
Dump [ Match (.*)\\.(.*) : 3.1 bar-lib foo.exe ] ;
Dump [ Match .*(oo).* .*(ar).* : foo bar foobar ] ;

We can describe it as follows:



5. Action blocks

The actions keyword can be used to define "actions blocks", i.e. shell commands that will later be expanded and sent to the command shell to build targets. Its general syntax is:

  actions OptionalArgs RuleName
  {
    action block content (i.e. shell commands)
  }

The meaning of RuleName and OptionalArgs will be explained in a later section of this document. For now, you should only remember that the action block's content is the text located between the braces. More importantly:

When Jam parses the actions keyword, it records the corresponding content as is, without any kind of expansions, and as a single string. This means that newlines, spaces and tabs are recorded as well, unlike the parsing of other parts of a Jam script.

It's only when commands need to be launched that their content will be expanded, according to Jam rules, before being sent to a command shell.

The commands in actions blocks are expanded according to the normal Jam rules, i.e. using the $(VARNAME) syntax and its variants. This is different from shell-specific expansion, like $VARNAME or %VARNAME%. Consider the following example:

  MYROOTPATH = ...some path ;
  
  if $(NT) 
  {
    actions MyTool
    {
      set PATH=$(MYROOTPATH);%PATH%
      mytool $(<) $(>)
    }
    
  } 
  else if $(UNIX) 
  {
    actions MyTool
    {
      PATH=$(MYROOTPATH):$PATH mytool $(<) $(>)
    }
  }

Here, we say two system-specific ways to change the path before calling a custom tool. In both examples, only $(MYROOTPATH) will be expanded by Jam, the rest being sent as is to the command shell.