JamPlus manual
Variables

Basics

Jam variables are lists of zero or more elements, with each element being a string value. An undefined variable is indistinguishable from a variable with an empty list, however, a defined variable may have one more elements which are null strings. All variables are referenced as $(variable).

Variables are either global or target-specific. In the latter case, the variable takes on the given value only during the target's binding, header file scanning, and updating; and during the "on <i>target</i> <i>statement</I>" statement.

A variable is defined with:

variable = elements ; The = operator replaces any previous elements of variable with elements
variable += elements ; The += operator adds elements to variable's list of elements
variable -= elements ; The -= operator removes elements from variable's list of elements
variable ?= elements ; The ?= operator sets variable only if it was previously unset
variable on targets = elements ; The = operator replaces any previous elements of variable with elements directly on targets
variable on targets += elements ; The += operator directly adds elements to targets's variable's list of elements
variable on targets -= elements ; The -= operator directly removes elements from targets's variable's list of elements
variable on targets ?= elements ; The ?= operator sets variable on targets only if it was previously unset

Variables referenced in updating commands will be replaced with their values; target-specific values take precedence over global values. Variables passed as arguments ($(1) and $(2)) to actions are replaced with their bound values; the "bind" modifier can be used on actions to cause other variables to be replaced with bound values. See Action Modifiers above.

Jam variables are not re-exported to the environment of the shell that executes the updating actions, but the updating actions can reference Jam variables with $(variable).

# If myvar hasn't been assigned yet, assign the string list ab and cd.
myvar ?= ab cd ;
# Replace myvar's contents with ef and gh.
myvar = ef gh ;
# Add some additional elements to myvar.
myvar += ij kl ; # myvar = ef gh ij kl
# Remove some elements from myvar.
myvar -= gh kl ; # myvar = ef ij
# sometarget.myvar = zx cv
myvar on sometarget = zx cv ;
# This prints zx cv.
on sometarget Echo $(myvar) ;
# This prints ef ij, because myvar doesn't exist on anothertarget, and so it
# falls through to the global myvar.
on anothertarget Echo $(myvar) ;

Variable Expansion

During parsing, Jam performs variable expansion on each token that is not a keyword or rule name. Such tokens with embedded variable references are replaced with zero or more tokens. Variable references are of the form $(variable) or $(variable:modifiers), where variable is the variable name, and modifiers are optional modifiers.

A literal expansion is in the form @(literal) or @(literal:modifiers) or alternatively as $@(literal) or $@(literal:modifiers). Instead of expanding a variable's contents, as with a $(variable) expansion, the direct contents of literal are used. All other behavior is the same.

Variable expansion in a rule's actions is similar to variable expansion in statements, except that the action string is tokenized at whitespace regardless of quoting.

The result of a token after variable expansion is the product of the components of the token, where each component is a literal substring or a list substituting a variable reference. For example:


$(X) -> a b c
t$(X) -> ta tb tc
$(X)z -> az bz cz
$(X)-$(X) -> a-a a-b a-c b-a b-b b-c c-a c-b c-c

The variable name and modifiers can themselves contain a variable reference, and this partakes of the product as well:


$(X) -> a b c
$(Y) -> 1 2
$(Z) -> X Y
$($(Z)) -> a b c 1 2

Because of this product expansion, if any variable reference in a token is undefined, the result of the expansion is an empty list. If any variable element is a null string, the result propagates the non-null elements:


$(X) -> a ""
$(Y) -> "" 1
$(Z) ->
*$(X)$(Y)* -> a a1 ** 1
*$(X)$(Z)* ->

A variable element's string value can be parsed into grist and filename-related components. Modifiers to a variable are used to select elements, select components, and replace components. Modifiers are applied in the order of the table below:

[n] Select element number n (starting at 1). If the variable contains fewer than n elements, the result is a zero-element list.
[n-m] Select elements number n through m.
[n-] Select elements number n through the last.
:E=value Use value instead if the variable is unset. Note that :E= by itself doesn't mean anything.
:A Expand a variable expansion within a string. When a string has been read from an external source and contains a variable in or @(var) syntax, the :A modifier can be used to expand it as if it had been inline in the Jam script.
:W[=remove_path] Populate the list with directory contents matching the string. If specified, remove_path is removed from the beginning of each found file.
:Z[=target] Provides an alternate and usually faster form of on target variable = $(VAR). Instead, use variable = $(VAR:Z=target) for the same result.
:T Expand the bound name of the target. Just as when expanding within an action, the BINDING is applied first. If LOCATE is set, it is used for the path. If not, SEARCH is used to look up the file.
:C EsCapes the string as if it were a file path. On Windows, if the path has spaces, it is quoted. On other platforms, spaces, parentheses, and a few other special characters are escaped with a backslash.
:B Select filename base.
:B=base Replace the base part of file name with base.
:B?=base If the base part of the file name is empty, replace it with base.
:S Select (last) filename suffix.
:S=suf Replace the suffix of file name with suf.
:S?=suf If the suffix of the file name is empty, replace it with suf.
:M Select archive member name.
:M=mem Replace the archive member name with mem.
:M?=mem If the archive member name is empty, replace it with mem.
:D Select directory path.
:D=path Replace directory with path.
:D?=path If the directory is empty, replace it with path.
:P Select parent directory. On VMS, $(var:P) is the parent directory of $(var:D); on Unix and NT, $(var:P) and $(var:D) are the same.
:G Select grist.
:G=grist Replace grist with grist.
:G?=grist If the grist is empty, replace it with grist.
:R=root Prepend root to the whole file name, if not already rooted.
:U Replace lowercase characters with uppercase.
:L Replace uppercase characters with lowercase.
:/ Convert all backslashes (\) to forward slashes (/).
:\\ Convert all forward slashes (/) to backslashes (\).
:chars Select the components listed in chars.
:J=joinval Concatenate list elements into single element, separated by joinval.
:I=regex Include all list items matching regex. Remove everything else. More than one set of :I or :X modifiers may appear in a given expansion, and they are applied in order.
:X=regex Exclude all list items matching regex. Keep everything else. More than one set of :I or :X modifiers may appear in a given expansion, and they are applied in order.

Patterns and Wildcards

When using the :W modifier, the following file globbing syntax is available.

Wildcard Description
?

Matches any single character of the file name or directory name.

* Matches 0 or more characters of the file name or directory name.
/ at end of pattern Any pattern with a closing slash will start a directory search, instead of the default file search.
** Search files recursively.
**/
Search directories recursively.

Some examples follow:

Example Pattern

Description

File.txt Matches a file or directory called File.txt.

File*.txt

Matches any file or directory starting with File and ending with a .txt extension.
File?.txt Matches any file or directory starting with File and containing one more character.
F??e*.txt Matches a file or directory starting with F, followed by any two characters, followed by e, then any number of characters up to the extension .txt.
File*

Matches a file or directory starting with File and ending with or without an extension.

* Matches all files (non-recursive).
*/ 

Matches all directories (non-recursive).

A*/ 
Matches any directory starting with A (non-recursive).

**/* 

Matches all files (recursive).
** Shortened form of above. Matches all files (recursive). Internally, expands to
**/*
**/ 
Matches all directories (recursive).
**{filename chars}

Matches {filename chars} recursively. Internally, expands to

**/*{filename chars}

.

{dirname chars}** Expands to
{dirname chars}*/**
.
{dirname chars}**{filename chars}

Expands to

{dirname chars}*/**/*{filename chars}

.

**.h Matches all *.h files recursively. Expands to
**/*.h
.

**resource.h

Matches all *resource.h files recursively. Expands to
**/*resource.h
.
BK** Matches all files in any directory starting with BK, recursively. Expands to
BK*/**
.
BK**.h Matches all *.h files in any directory starting with BK, recursively. Expands to
BK*/**/*.h
.
c:/Src/**/*.h
Matches all *.h files recursively, starting at c:/Src/.
c:/Src/**/*Grid/
Recursively matches all directories under c:/Src/ that end with Grid.
c:/Src/**/*Grid*/
Recursively matches all directories under c:/Src/ that contain Grid.

c:/Src/**/*Grid*/**/ABC/**/Readme.txt

Recursively matches all directories under c:/Src/ that contain Grid. From the found directory, recursively matches directories until ABC/ is found. From there, the file Readme.txt is searched for recursively.

Finally, a couple flags are available. Flags are appended at the end of the pattern line. Each flag begins with an @ character. Spaces should not be inserted between flags unless they are intended as part of the string literal.

Flags and Other Expansions Description
@* Search files and directories recursively.
-pattern Adds pattern to the file ignore list. Any file matching a pattern in the file ignore list is removed from the search.
-pattern/ Adds pattern/ to the directory ignore list. Any directory matching a pattern in the directory ignore list is removed from the search.
=pattern Adds pattern to the exclusive file list. Any file not matching a pattern in the exclusive file list is automatically removed from the search.
=pattern/ Adds pattern/ to the exclusive directory list. Any directory not matching a pattern in the exclusive file list is automatically removed from the search.
More than two periods for going up parent directories. Similar to 4DOS, each period exceeding two periods goes up one additional parent directory. So, a 4 period path expands to ../../../.

Wildcards may appear anywhere in the pattern, including directories.

\verbatim */*/*/*.c\endverbatim

Note that *.* only matches files that have an extension. This is different than standard DOS behavior. Use * all by itself to match files, extension or not.

Recursive wildcards can be used anywhere:

\verbatim c:/Dir1/**/A*/**/FileDirs*/**.mp3\endverbatim

This matches all directories under c:/Dir1/ that start with A. Under all of the directories that start with A, directories starting with FileDirs are matched recursively. Finally, all files ending with an mp3 extension are matched.

And a few examples:

Example Pattern Description
Src/**/@-.git/@-.svn/
Recursively lists all directories under Src/, but directories called .git/ and .svn/ are filtered.
Src/**@=*.lua@=README
Recursively lists all files under Src/ which match *.lua or README. All other files are ignored.
Src/**/@-.git/@-.svn/@=*.lua@=README
Recursively lists all files under Src/ which match *.lua or README. The versions of those files that may exist in .git/ or .svn/ are ignored.
...../StartSearchHere/**
Expands to:
../../../../StartSearchHere/**

Variable Expansion Examples

var = ab cd ef gh ij kl ;
Echo $(var) ; # ab cd ef gh ij kl
Echo $(var[0]) ; # ab
Echo $(var[2]) ; # cd
Echo $(var[6]) ; # kl
Echo $(var[7]) ; #
Echo $(var[1-]) ; # ab cd ef gh ij kl
Echo $(var[2-]) ; # cd ef gh ij kl
Echo $(var[6-]) ; # kl
Echo $(var[7-]) ; #
Echo $(var[1-7]) ; # ab cd ef gh ij kl
Echo $(var[2-6]) ; # cd ef gh ij kl
Echo $(var[3-5]) ; # ef gh ij
Echo $(var[6-4]) ; # kl
#------------------------------------------------------------------------------
Echo $(VAR:E=**empty**) ; # empty
EMPTY_TEXT = some empty text ;
Echo $(VAR:E=$(EMPTY_TEXT)) ; # some empty text
#------------------------------------------------------------------------------
list = list of characters to put stuff in between ;
local var1 = $ ;
local var2 = (list) ;
local var1_var2 = $(var1)$(var2) ;
Echo $(var1_var2) ; # $(list)
Echo $(var1_var2:A) ; # list of characters to put stuff in between
Echo @($(var1)$(var2):A) ; # list of characters to put stuff in between
#------------------------------------------------------------------------------
FILE = somefile.txt ;
Echo $(FILE:T) ; # somefile.txt
LOCATE on $(FILE) = /somewhere/on/the/hard/drive ;
Echo $(FILE:T) ; # /somewhere/on/the/hard/drive/somefile.txt
BINDING on $(FILE) = newname.txt ;
Echo $(FILE:T) ; # /somewhere/on/the/hard/drive/newname.txt
BINDING on $(FILE) = ;
SEARCH on $(FILE) = data ;
LOCATE on $(FILE) = /somewhere/on/the/hard/drive ;
Echo $(FILE:T) ; # /somewhere/on/the/hard/drive/somefile.txt
LOCATE on $(FILE) = ;
Echo $(FILE:T) ; # data/somefile.txt
#------------------------------------------------------------------------------
FILENAME = <thegrist>c:/some/directory/filename.txt ;
Echo $(FILENAME:B) ; # filename
Echo $(FILENAME:B=anothername) ; # <thegrist>c:/some/directory/anothername.txt
Echo $(FILENAME:S) ; # .txt
Echo $(FILENAME:S=.dat) ; # <thegrist>c:/some/directory/filename.dat
Echo $(FILENAME:BS) ; # filename.txt
Echo $(FILENAME:B=anothername:S=.dat) ; # <thegrist>c:/some/directory/anothername.dat
Echo $(FILENAME:D) ; # c:/some/directory
Echo $(FILENAME:D=/usr/bin) ; # <thegrist>/usr/bin/filename.txt
Echo $(FILENAME:P) ; # <thegrist>c:/some/directory
Echo @($(FILENAME:P):P) ; # <thegrist>c:/some
Echo $(FILENAME:G) ; # <thegrist>
Echo $(FILENAME:G=anothergrist) ; # <anothergrist>c:/some/directory/filename.txt
NEW_ROOT = d:/root/directory ;
Echo $(FILENAME:R="d:/root/directory") ; # <thegrist>c:/some/directory/filename.txt
Echo @(filename.txt:R=$(NEW_ROOT)) ; # d:/root/directory/filename.txt
Echo @(lowercase_now_uppercase:U) ; # LOWERCASE_NOW_UPPERCASE
Echo @(UPPERCASE_NOW_LOWERCASE:L) ; # uppercase_now_lowercase ;
Echo @(backslash\\path\\:/) ; # backslash/path/
Echo $(FILENAME:\\) ; # <thegrist>c:\\some\\directory\\filename.txt
Echo $(FILENAME:DBS) ; # c:/some/directory/filename.txt
VAR = /home/stuff/file.txt ;
Echo $(VAR:DB:U) ; # /HOME/STUFF/FILE
#------------------------------------------------------------------------------
# Assuming ../sharedlib/app has the files Jamfile.jam and main.c:
Echo @(../sharedlib/app/*:WBS) ; # Jamfile.jam main.c
Echo @(@(../sharedlib/app/*:W):BS) ; # Jamfile.jam main.c
Echo @(../sharedlib/app/**:WB=more\\dir\\file:\\‍) ;
# ..\\sharedlib\\app\\more\\dir\\file.jam
# ..\\sharedlib\\app\\more\\dir\\file.c
Echo @(../sharedlib/app/**:W=../sharedlib/:B=more\\dir\\file:\\‍) ;
# app\\more\\dir\\file.jam
# app\\more\\dir\\file.c
#------------------------------------------------------------------------------
top = AppRoot ;
jamfile = Jamfile.jam ;
Echo included_$(top:J=_)_$(jamfile) ; # included_AppRoot_Jamfile.jam
#------------------------------------------------------------------------------
top = AppRoot With Directories ;
jamfile = SomeFile.jam ;
Echo included_$(top:J=_)_$(jamfile) ; # included_AppRoot_With_Directories_SomeFile.jam
#------------------------------------------------------------------------------
Echo $(list:J=!) ; # list!of!characters!to!put!stuff!in!between
#------------------------------------------------------------------------------
FILES = file1.c file2.c file3.c file1.h file2.h file3.h file1.obj file2.obj file3.obj ;
Echo $(FILES:I=\\.c$) ; # file1.c file2.c file3.c
Echo $(FILES:I=\\.c$:I=\\.obj:X="^file1") ;
# file2.c file3.c file2.obj file3.obj
Echo $(FILES:I=\\.c$:I=\\.obj:X="^file1":X=\\.c$:I=\\.c$) ;
# file1.c file2.c file3.c file2.obj file3.obj
Echo $(FILES:I=\\.c$:I=\\.obj:X="^file1":X=\\.c$) ;
# file2.obj file3.obj
Echo $(FILES:X="^file1":X=\\.obj$:I=\\.h$) ;
# file1.h file2.h file3.h