Skip to content

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, the composition and the generators stages.

The preprocess and composition stages output 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 analyzes 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 the 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 consists 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 get allocated a unique private fqn, which is visible only to this local compilation unit.

A Symbol Tree is a hierarchical view of the file, it can be used to recreate a given file and should contain 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 .bdl.o file for caching purposes. Each .bdl should have its corresponding .bdl.o file after the build process.

Symbol Resolution

During the 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 words, all symbols used must have been previously defined. However, there are few exceptions that do 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 words, in hello = my.func(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 resolved 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 the 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 processes certain types of builtin expressions, such as bind for example. This assigns contracts to expressions before being evaluated. The second stage processes 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.