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.