During validation, abuild creates a
DependencyGraph object to represent the
space of build items and their dependencies. It performs a
topological sort on this graph to determine dependency order as
well as to detect errors and cycles in the dependency graph.
During the actual build, abuild needs to expand the dependency
graph to include not just build items but build item/platform
pairs. Every “instantiated” build item has to exist
on a particular platform. We refer to this platform-aware
dependency graph as the build graph. The
build graph can be inspected by running abuild with the
--dump-build-graph command-line option.
There are several steps required in order to determine exactly
which build items are to be built on which platforms and which
build item/platform pairs depend on which other pairs. Before
we do anything else, we need to perform several validations and
computations. The first of these is the determination of what
platform types a build item belongs to. For most build items,
this is simply the list of platform types declared in the build
item's Abuild.conf file. For build items
that have no build or interface files, there are no platform
types declared. In this case, the rules are different: if the
build item declares any dependencies and all of its directly
declared dependencies have identical platform type sets, then
the build item inherits its platform types from the items it
depends on. Otherwise, it has no platform types and has the
special target type all. Note that this analysis
is performed on build items in reverse build order. That way,
every build item's platform types and target type has been
determined before any items that depend on it are analyzed.
Once we have determined the list of platform types for each
build item, we can figure out which platforms a build item may
be built on. We refer to the list as the buildable
platform list. The buildable platform list for a
build item is included in the --dump-data
output (see Appendix C, --dump-data Format). Note that this is
generally a broader list than the list of platforms on which a
given build item will actually be built; the actual build
platform list is determined later in the build graph
construction process. For build items that have a specific
target type and platform types, the list of buildable platforms
is the union of all platforms supported on all platform types a
build item has. For items of target type all, we
don't explicitly compute a buildable platform list. These
platforms are allowed to “build” on any platform
since there are no actual build steps for such build items.
(Remember that for a build item to have target type
all, it must not have any declared platform types,
and this in turn means that it must have no build or interface
files.)
All of the above steps can be completed without knowing which
build items are actually included in the build set. These
computations, in fact, are determined at startup for every build
item in every known build tree regardless of whether the items
are in the build set. During this validation, we also perform
checks to ensure that each build item's dependencies have
appropriate platform types. Specifically, a build item of
target type all may depend on any build item, and
any build item may depend on an item of target type
all or platform-independent. For
other build items, if a build item depends on another build item
and declares the dependency with a
-platform=
option, the dependent build item must have the platform type
mentioned in the platform selector. Otherwise, every platform
type of the depending build item must be present in the
dependent item. For example, if selectorA has
platform types X and Y and depends on
B which has types X,
Y, and Z, this is okay because
B has all of A's
platform types. It would be an error if
B depended on A in
this case since the instances of B
building for platform type Z would not be able to
satisfy their dependences on A since it
doesn't support that platform type.
The above validations are all completed before abuild starts to build. If any errors are found in the above checks, abuild will report them and exit before it attempts to construct the build graph. This means that the build graph construction itself can operate under the assumption that all of the above constraints have been satisfied.
The next step is the construction of the actual build graph
itself. This is performed only when all previous validations
have been performed successfully, and this step is also
performed only for build items that are actually in the build
set. This process is a bit tricky. We present a prose
description of the process here. For a fully detailed
description, please read the comments and code in
addItemToBuildGraph in
Abuild.cc in the abuild sources. We
construct the build graph in reverse build order;
i.e., we start with most dependent build
item and end with the least dependent build item. For each
build item, we add to the list of build
platforms (platforms on which the item will actually
be built), based on our platform selection criteria, as
described in Section 21.1, “Platform Selection”. The list
may be initially empty, or it may have been modified by items
that depend on this item. For build items of target type
all, we would not add any items to the list at this
step. Then, for each direct dependency, we determine which
instance of it we will depend on. If the dependency in question
is declared with a platform selector, we pick the best platform
from among the dependency's buildable platform list that
satisfies the platform selector and make this the
dependent platform. If there are no
matches, it is an error. If there was no platform selector in
the dependency and the dependent item is platform-independent,
the dependent platform is indep. Otherwise, there
is no specific dependent platform. Then, for each of our build
platforms, we add a node to the build graph for the current
build item and platform. If we have a specific dependent
platform, we create a dependency from our new node to the node
consisting of the dependency item and the dependent platform.
Otherwise, we check the dependency item to make sure that it is
buildable on our current platform. If not, it is an error. If
so, we create a dependency from our node to the node consisting
of the dependency item and our platform, and we also add the
platform to the dependency's build platform list. This is the
mechanism by which abuild will build an item on a platform as
needed to satisfy a dependency. An example of this is presented
in Section 21.3, “Cross-Platform Dependency Example”.
Even if the exact steps of constructing the build graph are involved, there are some implications that are relatively easy to understand. In particular, the way the build graph works implies that, in general, a build item may only depend on another build item if the dependent item builds on all the depending item's platforms. There are two ways around this: one is to use a platform selector on a dependency, and the other is to use a build item of target type all. A build item of target type all is a pass-through build item, as described in Section 4.4, “Special Types of Build Items”. A build item gets to be this way by not having a build or interface file, not declaring any platform types, and depending on items with different groups of platform types.