29.6. Construction of the Build Graph

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.

29.6.1. Validation

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=selector 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 A 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.

29.6.2. Construction

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”.

29.6.3. Implications

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.