Details of the FASTBuild configuration file syntax can be grouped into 4 categories:Formatting
The #define / #undef directives allow user defined tokens to simplify BFF control flow.Examples:
#if / #else / #endif directives allow parts of a bff configuration file to be applied conditionally, depending on previous #define declarations. Limited support for the boolean operators && and || for logical AND and OR respectively is available. Conditions are parsed in C precedence order (&& takes precedence over ||). Currently there is no support for parentheses within expressions.Examples:
An if can be inverted with the ! operator:Examples:
Boolean operators examples:
The following pre-defined (and reserved) symbols are available for use:
|__LINUX__||Defined if running on Linux|
|__OSX__||Defined if running on OS X|
|__WINDOWS__||Defined if running on Windows|
Additionally "exists" can be used to branch on the existence of an environment variable.Example:
Additionally "file_exists" can be used to detect the existence of external files.Example:
The #import directive allows a bff configuration file to import an Environment Variable. A variable with the same name as the Environment Variable will be created at the scope of the #import, as if it was locally declared by a normal variable assignment.Examples:
The #include directive allows a bff configuration file to include another. This allows the configuration to be split along logical lines, such as per-platform or per-configuration.Examples:
The #once directive specifies that a bff file should only be parsed once, regardless of how many times it is included.Examples:
Variable declarations begin with a period followed by a contiguous block of alphanumeric characters. Variable names are case sensitive. Four variable types are supported (String, Integer, Boolean and Array). Variable types are implied by their declaration.Examples:
Variables can be overridden or modified at any point after they have been declared, as follows:
Variables can be constructed from other variables:
Variables are valid for the scope they are declared in, and all sub-scopes. Modifications to variables within a scope exist only in the scope they are made. For example:
Variables can be pushed to the parent last used parent scope using the ^ prefix:
Structs allow the grouping of variables to allow reuse of settings in complex build configurations. Structs contain any number of variables of any type (including other structs). Arrays of structs are also permitted.
A struct contains a number of properties, isolating them from the current namespace. The Using function allows all the members of a struct to be pushed into the current scope:
The Using function also allows Structs to effectively inherit and override other structs.
Structs can be concatenated using the plus operator. The plus operator will be applied recursively to each member within the struct.
Looping through arrays of structures is a useful technique for minimizing configuration complexity.
Variable names can be dynamically constructed at parse time as follows:
Built In variables always exist. Their names begin and end with an underscore.
|_CURRENT_BFF_DIR_||The directory containing the current bff file being parsed|
|_FASTBUILD_VERSION_STRING_||The current FASTBuild version as a string: e.g. v0.97|
|_FASTBUILD_VERSION_||The current FASTBuild version as an integer: e.g. 97|
|_WORKING_DIR_||The working directory when fbuild was launched|
NOTE: See the Function Reference for a detailed list of all available functions and their Arguments and Properties.
Functions describe the dependency information to FASTBuild. Each library, executable, unit test or other "node" is described to FASTBuild through the use of Functions.
Functions generally take the form:
Functions are primarily controlled by their Properties, taken from active variable declarations (either internally declared or from an inherited scope). The recognized properties vary from function to function.
The result of the following two examples are equivalent:
Variables interact with Function properties in this way to allow common declarations to be moved to higher level scope to avoid duplication. Combined with the variable scoping rules, this also allows for bespoke specializations.
Arguments are often optional, and their quantity and syntax varies from function to function. Where they are not required (or optional), brackets should be omitted.
As an example, the Library function takes an optional alias argument to allow a user-friendly name when targetting the library to be built on the command line:
Build-Time Substitutions allow FASTBuild to replace certain configuration values at build time, as opposed to configuration parsing time ($ tokens). This functionality is used whenever the value of an argument can't be known at configuration time, or there is a many to one relationship between the configuration and the number of nodes it results in during the build.
One common example of where build time substitutions are required is when building a library. Since the compiler is invoked for each file, but we only define one set of 'CompilerOptions', a build-time substitution is required. For example:
Assuming there are 3 files in the 'Code' directory ('a.cpp', 'b.cpp' and 'c.cpp'), the compiler will be invoked 3 times. In each case %1 and %2 will be replaced with appropriate values for the input and output. In this case, that means 'a.cpp' & 'Tmp\a.obj' for the first invocation, 'b.cpp' & 'Tmp\b.obj' for the second, and finally 'c.cpp' & 'Tmp\c.obj'.
The behaviour of build-time substitutions is different for each Function (what values they provide and what properties they can be used on). For details, see the Function Reference.
User Functions can be defined in order to de-duplicate commonly used configuration. Functions are defined using the 'function' keyword followed by a parameter block of zero or more arguments and a function body. User Functions can take zero or more arguments. Arguments can be optionally comma delimitted.Example declarations: