Designing CMaize’s Target Component

This page records the design process of CMaize’s target component.

What is CMaize’s Target Component?

CMaize’s target component contains classes which represent traditional CMake build targets. CMaize’s target component is an object- oriented take on the functional approach to managing targets provided by traditional CMake.

Why Do We Need CMaize’s Target Component?

Since CMake already provides native support for targets, arguably the more pertinent question is: “Why do we need an object-oriented target component?”. The original motivation was to manage complexity, while still providing a user-friendly interface. As suggested by the length of the CMake target documentation, there are many considerations which go into properly setting up a CMake target. CMaize targets are designed to manage those considerations via a separation of concerns. For example, users creating a target for a C++ executable, via the CXXExecutable class need only interact with target properties which are relevant for executables, whereas the native CMake API additionally exposes many other considerations including: libraries, object libraries, and alias targets. Another motivation for CMaize’s target component is to wrap the process of getting/ setting target properties in a more user-friendly interface (CMake’s native API can be a bit of a pain because it depends on exactly what the target represents).

Another motivation for CMaize’s target component is to extend CMake targets to other coding languages. Out of the box, CMake targets are primarily meant for use with C/C++ targets. Modern C/C++ projects often contain pieces written in other coding languages, notably Python. Being able to treat these additional project pieces, with a unified build system, decreases the project management burden since maintainers do not need to learn multiple build systems.

Target Component Considerations

In designing CMaize’s target component we have considered:

language specific

From the discussion on Designing CMaize’s Add Target Functions, it was decided that the language-specific aspects of target creation would be managed by a target class hierarchy residing below the user API.

language agnostic

While perhaps seemingly at odds with language specific, we note that from the perspective of CMake, and CMaize, targets serve primarily as nodes in a DAG. The language of the target is not relevant for assembling the DAG and it should thus be possible to work with already created targets in a language agnostic manner.

dependency tracking

From discussions on Designing CMaize’s CMaizeProject Component we established that each target will need to track the targets it depends on.

  • As a corollary we note that tracking dependencies is used for establishing build order.

  • For a working installed package we know that all dependencies have already been satisfied (otherwise the package would not currently be working). Downstream consumers of a working installed package do not need to track the package’s dependencies because the package already does this for them.

cmake based targets

Modern CMake is target-based. Since CMaize relies on CMake, CMaize will need to track the underlying CMake target(s).

lazy cmake target creation

Somewhat of a corollary to the cmake based targets consideration, we note that defining a CMake target requires some of its state to already be known, e.g., we need to know all of the source files for a C++ executable. The required state will in general live in language-specific derived classes and may be set over a number of manipulations. To accommodate such a workflow, we require that the CMake target can be created lazily, i.e., the CMake target is only created when needed.

  • This consideration applies to build targets, since already installed targets are required to know all of their state immediately.

Design of CMaize’s Target Component

../../../_images/targets.png

Fig. 8 The overall architecture of CMaize’s target component. Also shown are the API details of the language agnostic pieces.

Fig. 8 provides an overview of the architecture of CMaize’s target component. Stemming from the language specific and the language agnostic considerations the class hierarchy is seen as having a language-agnostic piece and a language-specific piece. The base CMaizeTarget class and its two child classes, BuildTarget and InstalledTarget, define the language agnostic piece of the hierarchy. The opaque elements of Fig. 8, i.e., those labeled CXX Targets, Python Targets, and CMake Targets, form the language-specific part of the target component. The design of the classes needed to support targets for each coding language can be found elsewhere (see Designing CMaize’s C++ Target Classes, Designing CMaize’s Python Target Classes, and Designing CMaize’s CMake Target Classes respectively).

The corollary of consideration dependency tracking means CMaize only needs to track dependencies for targets the build system will build; this is the motivation for the split between BuildTarget and InstalledTarget. Code factorization — including consideration cmake based targets, which means all CMaize targets must have corresponding CMake targets — is the motivation for the common base class CMaizeTarget. To address lazy cmake target creation the BuildTarget class has a virtual function make_target. Calling make_target triggers the creation of the CMake target.

Summary

language specific

A subset of the classes comprising CMaize’s target component are dedicated to representing language-specific targets.

language agnostic

The CMaizeTarget, BuildTarget, and InstallTarget classes contain the common functionality needed to interact with all targets, regardless of the coding language.

dependency tracking

The BuildTarget class contains a member which tracks the target’s dependencies. BuildTarget is intended for use with targets CMaize will build.

cmake based targets

All classes in CMaize’s target component ultimately inherit from the CMaizeTarget class. The CMaizeTarget class houses the CMake target associated with the object.

lazy cmake target creation

BuildTarget defines a virtual function make_target which, when called, will actually create the associated CMake target.