Designing CMaize’s CMaizeProject Component
In Overview of CMaize’s Design we motivated the need for a CMaizeProject component in order to track the state of the project. This page details the design process of the CMaizeProject component.
What is the CMaizeProject Component?
A build system is written to direct a build tool how to build a project. The CMaizeProject component is the part of CMaize charged with representing the overall state of the project. In practice, a CMaizeProject will serve as a container for collecting the project’s configuration options, dependencies, build targets, and packaging information.
Why Do We Need a CMaizeProject Component?
While CMake has a concept of a project, CMake lacks the ability to easily see the pieces of the project. For example, at the time of writing there is not a simple CMake function which returns a list of a project’s dependencies. Without a mechanism to query the properties of a project, CMake relies on the user to resupply the same information to a number of functions.
One of the main considerations for writing CMaize is minimizing the redundancy found in typical CMake-based build systems. To this end, the CMaizeProject component is designed to collect the project’s properties into a single source of truth. With access to a project’s properties it is easier for CMaize to automatically propagate the project’s information to multiple CMake functions.
CMaizeProject Component Considerations
In designing the CMaizeProject component we considered:
- workspace
The cmake-based build system consideration means that the user API of CMaize will rely on functional-style programming. Under the hood, we will store the objects associated with the active project in a
CMaizeProject
object. Thus many of the user api functions are implemented using a pattern of:Retrieve the active
CMaizeProject
.Use the objects in the active
CMaizeProject
to complete a task.If necessary, update the objects in the active
CMaizeProject
.Save the active
CMaizeProject
in such a manner that this process can be repeated.
Subjected to such a workflow, the
CMaizeProject
object takes on a workspace-like role.
- building
Following from workspace, the CMaizeProject component must store the information necessary to build the project. This includes:
coding languages,
configure options,
compile options,
dependencies, and
build targets.
Tracking the interdependency among the dependencies and targets will be the responsibility of CMaize’s Target component (see Designing CMaize’s Target Component). The CMaizeProject component simply must maintain a list of targets.
- packaging
The building consideration means that the CMaizeProject component will already contain most of the information necessary for automating the generation of packaging information (e.g. the CMake
xxx-config.cmake
files). The present consideration is that we should ensure that the CMaizeProject component has all of the information necessary to automate packaging.After consideration building the CMaizeProject component just needs metadata about the project in order to automate packaging. Additional needed metadata includes: the package name and the version.
Somewhat of a corollary, packaging the project requires being able to distinguish among build targets which stem from dependencies and build targets which are part of the package since they may be (and often are) treated differently.
- recursive
In Overview of CMaize’s Design we noted that the overall design of CMaize needed to account for the recursive consideration. For the CMaizeProject component this means being able to:
have multiple instances of a
CMaizeProject
,associate each
CMaizeProject
instance with a specific project,store the
CMaizeProject
instances in a manner that facilitates retrieval given the active CMake project, andaccess
CMaizeProject
instances from higher/lower levels of build system recursion.
Design of the CMaizeProject Component
Fig. 5 shows the design of the CMaizeProject component.
In keeping with object-oriented practices we introduce a class CMaizeProject
to represent a project which uses CMaize as its build system. Together the
building and the packaging considerations establish the
state the CMaizeProject
object must possess. The recursive
consideration leads to the realization that the current project (in package
form) will in general be a dependency of one or more downstream projects. When
the downstream project needs to find the current
project, the downstream project will need the ability to uniquely distinguish
among package iterations (instances of a package that differ in configuration
and/or version). This requires tagging the package with some subset of a
CMaizeProject
object’s state and to this end we introduce
the PackageSpecification
class (full design discussion
Designing the Package Specification).
Keeping with DRY we want each piece of state in the CMaizeProject
component to appear in only one place and thus one of the key design decisions
regarding the CMaizeProject
and PackageSpecification
classes is the
partitioning of the state between the classes. In CMaize-based build systems
we will rely on PackageManager
objects for managing packages (see
Overview of CMaize’s Design). Briefly we envision PackageManager
objects as being maps from a PackageSpecification
object to the package. The
PackageManager
is an abstraction which hides the building and storage
details of the packages it manages, thus it needs to know which package
we want to build and what settings/options to build the package with.
Finally, because of the workspace consideration, we opt to store most
CMaizeProject
state not in the PackageSpecification
class, but as
targets. More specifically, modern CMake is target-based. We thus need a
mechanism to
retrieve the build system targets from within the implementation of CMaize’s
user API. By storing them in the CMaizeProject
we can access the
targets by retrieving the CMaizeProject
object for the project, and then
accessing the targets.
Summary
- workspace
We have designed the
CMaizeProject
class in a way which stores the state of an entire project.CMaizeProject
objects can be saved and loaded via global CMake variables in order to retrieve a project’s state.- building
The
CMaizeProject
includes package managers which can be used to build/find dependencies, information about the coding languages of the project, and the literal build targets.- packaging
Each
CMaizeProject
object includes aPackageSpecification
object. This object contains the information needed to retrieve the resulting package from a package manager. The total state of theCMaizeProject
class (including itsPackageSpecification
member) is sufficient for auto- generating packaging files.- recursive
Each
CMaizeProject
object is associated with a single CMaize project. MultipleCMaizeProject
objects may exist, thus allowing dependencies of a CMaize-based build system to also rely on CMaize.