Build
Resolving symbols within a bdl
file is done at different build stages to allow lazy composition.
Overview
The build process consists of 3 main stages, the preprocess
and the composition
stages.
Both stages outputs artifacts, and the composition
process consumes the preprocess
artifact, this gives the possibility to cache and reuse outputs for various composition processes.
Preprocess
Parser
The parser anaylizes a bdl
file and extracts entities out of the file, decomposing the raw text into a visitable tree.
This is the only step in direct contact with the bdl
file, it must therefore extract all useful information from this file.
Part of this process, the parser also goes through a basic validation step and a build step.
The build step has for responsibility to gather dependencies and resolve symbols if the dependency flag is set.
Bdl Object
The output of the preprocess stage is a bdl
object which mainly consist of a Symbol Map
and a Symbol Tree
.
A Symbol Map
links symbols with their actual definition. It consists of a dictionary of fully qualified names to resolved elements. Elements that are unnamed gets allocated a unique private fqn, that is visible only by this local compilation unit.
A Symbol Tree
is a hierarchical view of the file, it can be uised to recreate a given file and should contains all the necessary
information. It only uses symbols defined in the Symbol Map
. In short it is a hierarchical view of a Symbol Map
.
A bdl file is serialized into a .o
file for caching purpose. Each .bdl
should have its corresponding .o
file after the build process.
Symbol Resolution
During file preprocessing
stage, all symbols are resolved and are matched with the previously discovered symbols.
These symbols are stored into the Symbol Map
.
In other word, all symbols used must have been precendently defined. However, there are few exception that does
not follow this rule, which are any symbol within a composition
scope or within a config
section.
In a composition
scope, only the name of the entity is registered, but the entity itself is not resolved, in other
word, in hello = my.fund(a = 1);
, the name hello
is registered but the function my.func
is not resolved, so it can
be declared later on.
In a config
section, nothing is resolved, this is because config names are not part of the id tree, they are virtual
entities that do not concretely translate into code. Parameters in config
are only resolve when the corresponding
entity is instantiated.
Composition
The composition stage brings all components together to compose a running system.
Components might come from the platform used, others are functional to perform tasks related to the application.
During composition
stage, all top level composition elements are being resolved. Nested composition elements are
only resolved if the corresponding component is instantiated. Unused components in the build tree are automatically removed.
The composition stage is done in 2 sub-stages, the first one that only process certain type of builtin expression, such as bind
for example. This assigns contracts to expressions before being evaluated.
The second stage process all the rest of top level elements and their dependencies.
Generators
Generators can follow a preprocess
or a composition
stage and are responsible for generating additional output.
For example, they are used to re-generate bdl
files after being preprocessed or generate C++ code after the composition stage.