Chapter 33. Abuild Internals

Table of Contents

33.1. Avoiding Recursive Make
33.2. Starting Abuild in an Output Directory
33.3. Traversal Details
33.4. Compatibility Framework
33.5. Construction of the Build Set
33.6. Construction of the Build Graph
33.6.1. Validation
33.6.2. Construction
33.6.3. Implications
33.7. Implementation of the Abuild Interface System
33.8. Loading Abuild Interfaces
33.9. Parameter Block Implementation

This chapter provides detailed information about the inner workings of parts of abuild. Understanding this material is not essential even for using abuild in an advanced way, but reading it may provide insight into some of the reasons that abuild works the way it does. Understanding this material is essential to anyone who would want to modify any of abuild's core functionality.

33.1. Avoiding Recursive Make

There has been some thought and writing about recursive make, and there are various approaches to the problem of make recursion. On one extreme, you can write makefiles that iterate through subdirectories and invoke make recursively for each subdirectory. These are hostile to parallelism and invoke make recursively bounded by the depth of the file system. This use of recursive make is expensive in terms of time and system resources. At the other end of the spectrum, you can create makefiles that include all the other makefiles and effectively create one monolithic makefile for the entire project. These makefiles are fragile and very hard to maintain because you have to make sure that no makefile defines any targets or variables that conflict with those defined by other makefiles, and you have to jump through hoops to make sure that whatever paths are in the makefiles can be resolved properly regardless of the starting directory of the build.

Abuild takes a middle ground. The only files that may be included in multiple contexts that actually set variables and contain end-user knowledge are rules files. To make this work, we provide variables that contain the currently resolved path of each build item. This is necessary anyway in order to support backing areas. Abuild then allows users to create Abuild.mk files that don't have to coexist with other Abuild.mk files at runtime. Since abuild knows all the dependencies between build items, it can build items iteratively or even in parallel without using any recursion at all. Although a monolithic makefile system that is perfectly constructed would allow arbitrarily complex dependencies to be declared between specific targets in specific directories, maintaining this for a system of any size or for a system that was dynamic would be impractical. Abuild replaces this with precise management of inter-build item dependencies. Even so, abuild's make code actually does generate fine-grained dependencies at the file level, so most of the advantages of the monolithic non-recursive makefile approach are realized with abuild. We believe that this achieves the right balance between granularity and ease of maintenance and makes abuild's approach robust and efficient for both small and large build trees.