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:

  1. Retrieve the active CMaizeProject.

  2. Use the objects in the active CMaizeProject to complete a task.

  3. If necessary, update the objects in the active CMaizeProject.

  4. 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, and

  • access CMaizeProject instances from higher/lower levels of build system recursion.

Design of the CMaizeProject Component

../../../_images/cmaize_project.png

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 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 a PackageSpecification object. This object contains the information needed to retrieve the resulting package from a package manager. The total state of the CMaizeProject class (including its PackageSpecification member) is sufficient for auto- generating packaging files.

recursive

Each CMaizeProject object is associated with a single CMaize project. Multiple CMaizeProject objects may exist, thus allowing dependencies of a CMaize-based build system to also rely on CMaize.