Recipe Syntax and Semantics

A-A-P home page | A-A-P Recipe Executive
--- | ---
Prev |  | Next

# Chapter 26. Recipe Syntax and Semantics

## Recipe syntax definition

This defines the recipe syntax (more or less). NOTE: this may still change quite a bit.

Table 26-1. Notation

| | separates alternatives
() | grouping a sequence of items or alternatives
[] | optional items (also does grouping)
"" | contains literal text; """ is one double quote
... | indicates the preceding item or group can be repeated
EOL | an end-of-line, optionally preceded by a comment
INDENT | an amount of white space, at least one space
INDENT2 | an amount of white space, at least one space more than INDENT

A comment starts with "#" and continues until the end-of-line. It may appear in many places.

White space may be inserted in between items. It is often ignored, but does have a meaning in a few places.

Line continuation is done with a backslash immediately before an end-of-line. It's not needed for an assignment and other commands that don't have a build_block: the item continues if the following line has more indent than the first line.

For items that have a build_block, the start of the build block is the line with the smallest indent that is larger than the indent of the line that started the command. The lines before this are continuation lines. Example:

    	mytarget : source1
    		      source2	# continuation line
    	    :print $source	# first build line of build block

aapfile | ::= | ( [ aap_item ] EOL ) ...
aap_item | ::= | dependency | rule | variant | otherfile | build_command
dependency | ::= | targets ":" [ dependencies ] [ EOL build_block ]
targets | ::= | item_list
dependencies | ::= | item_list
build_block | ::= | INDENT build_command [ EOL [ INDENT build_command ] ] ...
rule | ::= | ":rule" pattern ... ":" pattern ... [ EOL build_block ]
variant | ::= | ":variant" name ( EOL variant_item ) ...
variant_item | ::= | INDENT varvalue EOL INDENT2 build_block
otherfile | ::= | includefile | childfile
includefile | ::= | ":include" item_list
childfile | ::= | ":child" item_list
build_command | ::= | assignment | script_item | generic_command | message
assignment | ::= | assign_set | assign_append | assign_cond
assign_set | ::= | name "=" item_list
assign_append | ::= | name "+=" item_list
assign_cond | ::= | name "?=" item_list
script_item | ::= | script_line | script_block
script_line | ::= | "@" script_command
script_block | ::= | ":python" EOL ( script_command EOL ) ... ":end"
generic_command | ::= | ":" command_name [ command_argument ]
message | ::= | info_msg | error_msg
info_msg | ::= | ":print" expression ...
error_msg | ::= | ":error" expression ...
# A list can contain white-separated items and Python style expressions.
item_list | ::= | item [ white_space item ] ...
item | ::= | ( simple_item | "(" item_list ")" ) [ attribute ... ]
simple_item | ::= | ( expression | non_white_item ) ...
attribute | ::= | "{" name [ "=" expression ] "}"
expression | ::= | string_expr | python_expr
string_expr | ::= | """ text """ | "'" text "'"
python_expr | ::= | "`" python-expression "`"
non_white_item | ::= | non-white-text | "$" [ "(" ] name [ ")" ]

    A backslash at the end of the line is used for line continuation.
    - Can't have white space after it!
    - Also for comments.
    - Also for Python commands; A leading @ char and white space before it is
      removed.
    - Lines are joined before inspecting indents.

    	# comment \
    	continued comment

    	@ python command \
    	@   continued python command \
    	       still continued

    When lines are joined because a command continues with a line with more indent,
    the line break and leading white space of the next line are replaced with a
    sinble space.  An exception is when the line ends in "$br": The $br is removed,
    the line break is inserted in the text and leading white space of the next line
    is removed.

    VARIABLES:

    When concatenating variables and using rc-style expansion, the attributes of
    the last variable overrule the identical attributes of a previous one.

    	v1 = foo {check = 1}
    	v2 = bar {check = 2}
    	vv = $*v1$v2

    	-> vv = foobar{check = 1}{check = 2}

    When using rc-style expansion, quotes will not be kept as they are, but
    removed and re-inserted where used or necessary.  Example:

    	foo: "file 1.c" foo.c
    		:print "dir/$*source"

    Results in:  "dir/file 1.c" "dir/foo.c"

    Be careful with using "$\" and quotes, you may not always get what you wanted.

    To get one item out of a variable that is a list of items, use an index number
    in square brackets.  Parenthesis or curly braces must be used around the
    variable name and the index.  The first item is indexed with zero.  Example:

    	BAR = beer coffee cola
    	:print $(BAR[0])
    beer
    	BAR_ONE = $(BAR[2])
    	:print $BAR_ONE
    cola

    Using an index for which no item exists gives an empty result.  When $MESSAGE
    includes "warning" a message is printed about this.

    Normally using $VAR gets what you want.  A-A-P will use the kind of quoting
    expected and add attributes when needed.  However, when you want something
    else, this can be specified:
    	$var	depends on where it's used

    	$?var	when the variable is not set or defined use an empty string
    		instead of generating an error

    	$-var	without attributes (may collapse white space)
    	$+var	with attributes

    	$*var	use rc-style expansion (may collapse white space)

    	$=var	no quotes or backslashes
    	$'var	aap quoted (using ' and/or " where required, no backslashes)
    	$"var	quoted with " (doubled for a literal ")
    	$\var	special characters escaped with a backslash
      	$!var	depends on the shell, either like $'var or $"var

    In most places $var is expanded as $+'var (with attributes, using ' and " for
    quoting).  The exceptions are:
    	:sys			$-!var		no attributes, shell quoting
    	$n in $(v[$n])		$-=var		no attributes, no quoting
    	:del			$-'var		no attributes, normal quoting

    The quoted variables don't handle the backslash as a special character.  This
    is useful for MS-Windows file names.  Example:

    	prog : "dir\file 1.c"
    		:print $'source

    Results in:  "dir\file 1.c"

    ASSIGNMENT:

    overview:
       var = value	    assign
       var += value	    append (assign if not set yet)
       var ?= value	    only assign when not set yet
       var $= value	    evaluate when used
       var $+= value    append, evaluate when used
       var $?= value    only when not set, evaluate when used

    Assignment with "+=" or "$+=" appends the argument as a separate item.  This
    is actually done by inserting a space.  But when the variable wasn't set yet
    it works like a normal assignment:

    	VAR += something

    is equal to:

    	@if globals().has_key("VAR"):
    	@   VAR = VAR + " " + "something"
    	@else:
    	@   VAR = "something"

    Assignment with "?=" only does the assignment when the variable wasn't set
    yet.  A variable that was set to an empty string also counts as being set.
    Thus when using "aap VAR=" the empty value overrules the value set with "?=".

    	VAR ?= something

    is equal to:

    	@if not globals().has_key("VAR"):
    	    VAR = something

    When using "$=", "$+=" or "$?=" variables in the argument are not evaluated at
    the time of assignment, but this is done when the variable is used.

    	VAR = 1
    	TT $= $VAR
    	VAR = 2
    	:print $TT

    prints "2".

    When first setting a variable with "$=" and later appending with "+=" the
    evaluation is done before the new value is appended:

    	VAR = 1
    	TT $= $VAR
    	TT += 2
    	VAR = 3
    	:print $TT

    prints "1 2"

    Note that evaluating a python expressions in `` is not postponed.

    BLOCK ASSIGNMENT

    The normal assignment command uses a single line of text.  When broken into
    several lines they are joined together, just like with other commands.  $br can
    be used to insert a line break.  Example:

    	foo = first line$br
    		second line$br
    		third line $br

    The block assignment keeps the line breaks as they are.  The same example but
    using a block assignment:

    	foo << EOF
    	  first line
    	  second line
    	  third line
    	    EOF

    The generic format is:

    	{var} << {term}
    	line1
    	...
    	{term}

    {term} can be any string without white space.  The block ends when {term} is
    found in a line by itself, optionally preceded by white space and followed by
    white space and a comment.

    The amount of indent to be removed from all the lines is set by the first
    line.  When the first line should start with white space use $empty.

    All the variations of the assignment command can be used:

       var << term	    assign
       var +<< term	    append (assign if not set yet)
       var ?<< term	    only assign when not set yet
       var $<< term	    evaluate when used
       var $+<< term    append, evaluate when used
       var $?<< term    only when not set, evaluate when used

    ATTRIBUTES

    Items can be given attributes.  The form is:

    	{name = value}

    "value" is expanded like other items, with the addition that "}" cannot appear
    outside of quotes.

    This form is also possible and uses the default value of 1:

    	{name}

    Examples:

    	bar : thatfile {check = $MYCHECK}
    	foo {virtual} : somefile

    The "virtual" attribute is used for targets that don't exist (as file or
    directory) but are used for selecting the dependency to be build.  These
    targets have the "virtual" attribute set by default:

    	TARGET		COMMONLY USED FOR
    	all		build the default targets
    	clean		remove intermediate build files
    	distclean	remove all generated files

    	test		run tests
    	check		same as "test"
    	install		build and install for use
    	tryout		build and install for trying out

    	reference	generate or update the cross-reference database

    	fetch		obtain the latest version of each file
    	update		fetch and build the default targets

    	checkout	checkout (and lock) from version control system
    	commit		commit changes to VCS without unlocking
    	checkin		checkin and unlock to VCS
    	unlock		unlock files from a VCS
    	add		add new files to VCS
    	remove		remove deleted files from VCS
    	revise		like checkin + remove
    	tag		add a tag to the current version

    	prepare		prepare for publishing (generated docs but no exe)
    	publish		distribute all files for the current version

    	finally		always executed last (using "aap finally" is uncommon)

    These specific targets may have multiple build commands.  They are all
    executed to update the virtual target.  Normally there is up to one target in
    each (child) recipe.

    Note that virtual targets are not related to a specific directory.  Make sure
    no other item in this recipe or any child recipe has the same name as the
    virtual target to avoid confusion.  Specifically using a directory "test"
    while there also is a virtual target "test".  Name the directory "testdir"
    to avoid confusion.

    The "comment" attribute can be used for targets that are to be specified at
    the command line.  "aap comment" will show them.

    	% aap comment
    	target "all": build everything
    	target "foo": link the program

    EXECUTING COMMANDS

    A dependency and a rule can have a list of commands.  For these commands the
    following variables are available:

    	$source		The list of input files as a string.
    	$source_list	The list of input files as a Python list.
    	$source_dl	Only for use in Python commands: A list of
    			dictionaries, each input item is one entry.
    	$depend		The list of dependencies (source files plus virtual
    			dependencies) as a string.
    	$depend_list	The list of dependencies (source files plus virtual
    			dependencies) as a Python list.
    	$depend_dl	Only for use in Python commands: A list of
    			dictionaries, each dependency item is one entry.
    	$target		The list of output files as a string.
    	$target_list	The list of output files as a Python list.
    	$target_dl	Only for use in Python commands: A list of
    			dictionaries, each output item is one entry.
    	$buildtarget	The name of the target for which the commands are
    			executed.  It is one of the items in $target.
    	$match		For a rule: the string that matched with %

    Example:

    	doit {virtual}:
    		:print building $target
    	prog : "main file.c" doit
    		:print building $target from $source

    Results in:
    	  building doit{virtual=1}
    	  building prog from "main file.c"

    Note that quoting of expanded $var depends on the command used.

    The Python lists $source_list and $target_list can be used to loop over each
    item.  Example:
    	$OUT : foo.txt
    		@for item in target_list:
    			:print $source > $item

    Note the difference between $source and $depend: $source only contains real
    files, $depend also contains virtual dependencies.

    The list of dictionaires can be used to access the attributes of each item.
    Each dictionary has an entry "name", which is the (file) name of the item.
    Other entries are attributes.  Example:

    	prog : file.c {check = md5}
    		@print sourcelist[0]["name"], sourcelist[0]["check"]

    Results in:  file.c  md5

    RATIONALE

    In an assignment and other places a Python expression can be used in
    backticks.  Expanding this is done before expanding $VAR items, because this
    allows the possibility to use the Python expression to result in the name of a
    variable.  Example:

    	foovaridx = 5
    	FOO = $SRC`foovaridx`
    Equal to:
    	FOO = $SRC5

    In the result of the Python expression $ characters are doubled, to avoid it
    being interpreted as the start of a variable reference.  Otherwise Python
    expressions with arbitrary results would always have to be filtered explicitly.
    Variables can still be obtained without using the $, although none of the
    modifiers like $+ and $* are available.  Example:

    	FOO = foo/`glob("*.tmp")`

    May result in:

    	FOO = foo/one.tmp two.tmp

    Note that "foo/" is only prepended to the whole result, not each
    white-separated item.  If you do want rc-style expansion, use two commands:

    	TT = `glob("*.tmp")`
    	FOO = foo/$*TT

    Result:
    	FOO = foo/one.tmp foo/two.tmp

    Watch out for unexpected results when rc-style expansion is done for $*VAR.
    Example:

    	VAR = one two
    	FOO = $*VAR/`glob("*.tmp")`

    Would result in:

    	FOO = one/one.tmp two/one.tmp two.tmp

    because the `` part is expanded first, thus the assignment is executed like:

    	FOO = $*VAR/one.tmp two.tmp

    The backticks for a Python expression are also recognized inside quotes,
    because this makes the rule for doubling backticks consistent.  Example:

    	FOO = "this``file" that``file

    Doubling the backtick to avoid it being recognized as a Python expression
    makes it impossible to have an empty Python expression.  There appears to be
    no reason to use an empty Python expression.

Prev | Contents | Next
Aap Command Line Arguments | Up | A-A-P Commands