33.3. Traversal Details

This section describes how abuild traverses build trees to
resolve build item names to paths. Here we describe the process
at a level of detail that is closer to the code. The
traverse function in the abuild source
code is responsible for the behavior described here. It will
likely be necessary to read this section more than once to fully
understand what is happening as some parts of the description
won't make sense without knowing about parts you won't have read
yet. (Fortunately, the human brain is better at resolving
circular dependencies than a build system is.)

Internally, abuild maintains tree data structures to hold onto
the shape and contents of build forests:
BuildForest,
BuildTree and
BuildItem. The
BuildForest object has a map from build
tree names to BuildTree objects and also
from build item names to BuildItem
objects. The BuildForest object also
contains the list of backing areas that apply to the forest as
well as the list of items and trees that are specified as deleted
in the Abuild.backing file.

The BuildTree object contains
tree-specific information, such as the tree's list of plugins,
tree dependencies, supported traits, etc. It also contains the
absolute path of the root build item of the tree. The
BuildItem object contains the absolute
path of the build item, the name of the containing build tree,
its dependencies, and various other information from the
Abuild.conf file. Additionally, both
objects store the tree's or item's backing
depth, which is a count of the number of backing
areas that had to be followed to resolve the item or tree.
Although the backing depth is an integer value, nothing in
abuild cares about the depth other than whether it is zero or
not. A backing depth of zero indicates that the tree or item
appears locally in the current forest.

When abuild starts up, it first locates the root of the local
forest. It does this by starting with the current directory and
walking up the file system (toward the root) until it encounters
an Abuild.conf that is not referenced as a
child of the next higher Abuild.conf, if
any. When it finds such an Abuild.conf, it
verifies that it is either a tree root build item or that is has
only a child-dirs key. In either case, it
is the root of the forest. Otherwise, it is an error, and
abuild indicates that it is not able to find the forest root.

Once abuild has found the root of the local build tree, it
begins traversal. The actual traversal logic is more complicated
than what is described here because it contains code to recognize
the abuild 1.0 build tree structure (with external directories
and unnamed trees) as well as the simpler 1.1 format. We omit
those details from the description and refer you instead to
comments in the code. Continuing with our description of the 1.1
traversal algorithm, we just read each build item's
Abuild.conf doing a breadth-first traversal
of the tree formed by following child-dirs
keys. If the child directory does not exist and the forest has a
backing area, we ignore this condition. This is what allows
backed forests to be sparse. Otherwise, if the directory exists,
it must have an Abuild.conf, and no
directory between the child directory and the parent may have
Abuild.conf files (possible only if a
child-dirs value has multiple path
elements).

After traversing the local forest, abuild traverses each
backing area, creating a separate
BuildForest object for each backing
forest. Finally, once abuild has traversed all the build items
in all known forests, abuild creates a dependency graph of
backing areas. Then, working from the leaves of the dependency
graph, it copies into the forest from the backing areas all the
BuildTree and
BuildItem objects of items and trees that
do not appear locally and increments the backing depth of the
local copies. Items that are marked as deleted or that are in
trees that are marked as deleted are not copied. Also, trees
that are marked as deleted are not copied. This is where
abuild notices if you have multiple backing areas and one of
them backs to another. In this case, abuild simply ignores the
further back of the backing areas since it will already get
copies of those items and trees through the closer backing area.

Finally, after all the traversal is completed, abuild validates
each forest, again starting with the furthest back forest and
working toward the local forest. Numerous validations are
performed. For details, please refer to the
validateForest method in the abuild source
code.