17.2. Abuild.interface Syntactic Details

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.

Note

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.

comment

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.

variable declaration

A scalar variable declaration takes the form

declare variable [ 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

declare variable [ scope ] list type 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 variable assignment

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:

override variable = value

Fallback assignments look like this:

fallback variable = value

Example scalar variable assignments:

fallback CODEGEN = gen_code.pl
HAS_CLASS = 0
override HAS_CLASS = 1

list variable assignment

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.

variable reset

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”.

flag-based variable assignment

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=interface-flag syntax in their Abuild.conf files. A flag-based assignment looks like this:

flag interface-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”.

after-build file specification

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”.

target type restriction

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.

conditional

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 conditions 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.