In this section, we provide the syntactic details for each of the
capabilities described in the previous section. There are some
aspects of how Abuild.interface
files are
interpreted that are different from other systems you have likely
encountered. If you are already familiar with the basics of how
these files work, this section can serve as a quick reference.
If you only read one thing, read about list assignment.
Assignment to list variables is probably different for
Abuild.interface
files than for any other
variable assignment system you're likely to have encountered.
It is specifically designed to support building up lists
gradually by interpreting multiple files in a specific order.
Any line beginning with a #
optionally
preceded by whitespace is treated as a comment. Comments are
completely ignored and, as such, have no effect on line
continuation. Note that the #
does not
have any special meaning when it appears in another context.
There is no syntax for including comments within a line that
contains other content.
A scalar variable declaration takes the form
declarevariable
[scope
]type
[ =value
]
where variable
is the name of the
variable and type
is one of
boolean, string, or
filename. If specified,
scope
may be one of
non-recursive or local. The
declaration may also be followed optionally be an
initialization, which takes the same form as assignment,
described below.
Example scalar variable declarations:
declare CODEGEN filename declare HAS_CLASS boolean declare _dist local filename = $(ABUILD_OUTPUT_DIR)/dist
A list variable declaration takes the form
declarevariable
[scope
] listtype
append-type
[ =value
]
where variable
is the name of the
variable, type
is one of
boolean, string, or
filename, and
append-type
is one of
append or prepend. The optional
scope
specification is the same as
for scalar variables (non-recursive or
local), and as with scalar variables, an optional
initialization may be provided. Example list variable
declarations:
declare QFLAGS list string append declare QPATHS list filename prepend = qfiles private-qfiles declare DEPWORDS non-recursive list string append
Scalar variables start off uninitialized. List variables start off containing zero items.
Scalar variables may be assigned in one of three ways: normal, override, or default. A normal assignment looks like this:
variable
=value
where variable
is the variable name
and value
is a single word (leading
and trailing space ignored). Extra whitespace is permitted
around the =
sign.
Override assignments look like this:
overridevariable
=value
Fallback assignments look like this:
fallbackvariable
=value
Example scalar variable assignments:
fallback CODEGEN = gen_code.pl HAS_CLASS = 0 override HAS_CLASS = 1
List variables are assigned using a simple
=
operator:
list-variable
=value
where value
consists of zero or
more words, and the semantics of the assignment depend on how
the list was declared. For append lists, the
assignment operator appends the words to the existing list in
the order in which they appear. For prepend
lists, the assignment operator prepends
the words to the existing value of list in the order in which
they appear. For example, if the variables
LIBS
is declared as a prepend
list of strings, these two statements would
result in LIBS
containing the value
lib3 lib4 lib1 lib2
:
LIBS = lib1 lib2 LIBS = lib3 lib4
The distinction of whether a list is declared as
append or prepend generally doesn't
matter to the user, but there are cases in a build environment
in which it is important to prepend to a list. One notable
example is the list of libraries that are linked into an
application: if one library calls functions from another
library, the dependent library must come
before the library on which it depends in
the link command. Since abuild reads the dependency's
interface file first, the depending library must
prepend itself to the list of libraries.
Note that multiple assignments to a single list variable would
ordinarily not occur in the same
Abuild.interface
file, but would instead
occur over successive files. It is perfectly valid to assign
multiple times in the same file, however. One instance in
which this would typically occur would be with private
interfaces, as illustrated in Section 23.3, “Private Interface Example”. Another common
case would be with conditional assignments.
List and scalar variables can both be reset. After a variable is reset, its value becomes uninitialized (for scalars) or empty (for lists) just as if it had just been declared. The syntax for resetting a variable is
reset variable
It is also possible to reset all variables with
reset-all
A reset of a specific variable, either by an explicit
reset
or a
reset-all
, can be blocked within the
scope of a single Abuild.interface
file
or any files it loads with after-build
.
To block a variable from being reset, use
no-reset variable
Any no-reset
commands will apply to the
next reset
or
reset-all
that appears in the current
file or files it explicitly loads. (Although there would be
no real reason to use no-reset
before a
specific reset
of a specific variable,
abuild does support this construct.)
Variable reset operations are used fairly infrequently, but there are use cases that justify all of the various reset operations. For examples of using them, please see Section 24.3, “Explicit Cross-Platform Dependencies” and Chapter 27, Opaque Wrappers.
There are some subtleties about the effect of a variable reset when interface files are loaded. For details, see Section 33.7, “Implementation of the Abuild Interface System”.
An Abuild.interface
file may prefix any
variable assignment (normal
,
override
, fallback
,
scalar, or list) with a flag
statement.
This indicates that that particular assignment will be ignored
by build items that don't request the particular flag through
the
-flag=
syntax in their interface-flag
Abuild.conf
files. A
flag-based assignment looks like this:
flaginterface-flag
assignment-statement
Abuild enforces that a build item's
Abuild.interface
and any
after-build
files that it reads may only
use the flag
statement for a flag
declared in the build item's
supported-flags key in its own
Abuild.conf
. For an example of using
flag-based assignment, see Section 23.3, “Private Interface Example”.
Abuild allows you to specify the name of an additional
interface file with the same syntax as
Abuild.interface
that is loaded
immediately after the current item has been built, before any
items that depend on this item are built. Because the file is
loaded after the build has been completed, any directives in
this file will be visible to items that depend on this item
but not by this item itself. To specify the name of such a
file, use
after-build filename
where filename
is the path to the
file to be loaded. A relative path is interpreted as relative
to the original Abuild.interface
file.
Note that files loaded by after-build
may
themselves not include after-build
directives. It is also not permitted to have
after-build
statements in interface files
belonging to plugins or build items that have no build files.
(Having them would be meaningless since such build items are
not built.)
Since interface statements in after-build
files are visible to items that depend on this build item but
not to the item itself, this mechanism is useful for changing
interface variables for the item's reverse dependencies
without changing what the build item itself sees. The Opaque
Wrapper example (Section 27.1, “Opaque Wrapper Example”)
does this. It also makes this construct useful for
automatically generated interface data. For an example of
that use, see Section 18.3, “Autoconf Example”.
To specify the target type to which subsequent variable declarations belong, use
target-type type
where type
is the name of the
target type. For information about target types, see Chapter 5, Target Types, Platform Types, and Platforms. In addition to the built-in target
types, the special type all may be used to
indicate that variables should be made available to all target
types. In practice, there is little reason to ever restrict a
variable to a particular target type, though many of the
abuild predefined variables are restricted. Restricting the
target type of a variable only determines whether that
variable is passed to the backend, so the only reason to
restrict a variable to a specific target type would be to
reduce the number of unneeded variables that were passed to
the backend. It has no impact on variable scope, visibility,
or even availability for use in other
Abuild.interface
files.
Conditionals in Abuild.interface
take the
following form:
if (condition
) ...valid code
... elseif (condition
) ...valid code
... elseif (condition
) ...valid code
... else ...valid code
... endif
An if
block may contain zero or more
elseif
clauses and an optional
else
clause. Any valid
Abuild.interface
code, including nested
conditionals, is permitted inside a conditional block. Recall
that all variables have global scope including variables
declared inside of conditional blocks. Code inside of
conditions that are not satisfied is ignored but must be
syntactically valid.
The condition
s specified above may
be of one of the following forms:
$(variable
)
where variable
is a
boolean variable, or
function
(arg
,arg
, ...)
where function
is a valid
Abuild.interface
conditional function and
each arg
consists of one or more
words. Only variables declared as boolean and
specific conditional functions, described in the next section,
are permitted in conditionals. There are no relational
operators, and variables of other types whose values happen to
be valid boolean values are not allowed in conditionals.