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
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
, andInstallTarget
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. TheCMaizeTarget
class houses the CMake target associated with the object.- lazy cmake target creation
BuildTarget
defines a virtual functionmake_target
which, when called, will actually create the associated CMake target.