Jam语言 中的 变量

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). 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, contents equals remove_path is removed from the beginning of each found file.
  • * any number of characters
  • ? any single character
  • [a-z] any single character in the range a-z
  • [^a-z] any single character not in the range a-z
  • \x match x
: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.
:S Select (last) filename suffix.
:S=suf Replace the suffix of file name with suf.
:M Select archive member name.
:M=mem Replace the archive member name with mem.
:D Select directory path.
:D=path Replace directory 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.
: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.

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
@-pattern

Adds pattern to the ignore list. Any file matching a pattern in the ignore list is discounted 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.
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 ../../../.

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.

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)😛) ; # <thegrist>c:/some

Echo $(FILENAME:G) ;              # &lt;thegrist&gt;
Echo $(FILENAME:G=anothergrist) ; # &lt;anothergrist&gt;c:/some/directory/filename.txt

NEW_ROOT = d:/root/directory ;
Echo $(FILENAME:R=<span class="stringliteral">&quot;d:/root/directory&quot;</span>) ; # &lt;thegrist&gt;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:\\) ;                    # &lt;thegrist&gt;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

posted @ 2026-03-24 09:42  dewxin  阅读(2)  评论(0)    收藏  举报