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
CMaizeProjectobject. Thus many of the user api functions are implemented using a pattern of:Retrieve the active
CMaizeProject.Use the objects in the active
CMaizeProjectto complete a task.If necessary, update the objects in the active
CMaizeProject.Save the active
CMaizeProjectin such a manner that this process can be repeated.
Subjected to such a workflow, the
CMaizeProjectobject 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.cmakefiles). 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
CMaizeProjectinstance with a specific project,store the
CMaizeProjectinstances in a manner that facilitates retrieval given the active CMake project, andaccess
CMaizeProjectinstances from higher/lower levels of build system recursion.
Design of the CMaizeProject Component
Fig. 5 The design of the CMaizeProject component of CMaize.
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
CMaizeProjectclass in a way which stores the state of an entire project.CMaizeProjectobjects can be saved and loaded via global CMake variables in order to retrieve a project’s state.- building
The
CMaizeProjectincludes 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
CMaizeProjectobject includes aPackageSpecificationobject. This object contains the information needed to retrieve the resulting package from a package manager. The total state of theCMaizeProjectclass (including itsPackageSpecificationmember) is sufficient for auto- generating packaging files.- recursive
Each
CMaizeProjectobject is associated with a single CMaize project. MultipleCMaizeProjectobjects may exist, thus allowing dependencies of a CMaize-based build system to also rely on CMaize.