Engine and integration modules
Cheryl uses one shared Cheryl::Engine and independently selected integration
libraries. Each owner declares its sources, public includes, dependencies and tests.
The root selects the assembly. The module index
maps owners to Engine contracts and links their APIs; build options
control selection.
Engine has no reverse dependency on an integration. Logging, memory, workers, events, assets and utilities remain within the one Engine library. Platform owners implement display/window/input contracts, graphics owners implement rendering and resources, UI owners consume neutral contracts, and audio owners implement playback. The optional Linux Debug terminal owns process output/viewer behavior outside the runtime adapter graph. Its Engine support attachment accepts a module callback; an absent implementation leaves application startup unchanged.
Command-line bootstrap uses the separate Cheryl::Startup and
Cheryl::OpenGL::Startup support targets. They add CLI11 and backend argument
definitions to applications that select them; Engine and OpenGL do not depend
back on those targets. See startup composition.
Optional Engine helpers can use projects/engine/support/ without becoming runtime
platform modules. Preserve the same dependency direction: selected support targets
consume Engine; Engine does not acquire their SDKs or initialization as a reverse
dependency. Cheryl::WorkerTopology::Hwloc and Cheryl::WorkerTopology::Json
use that boundary for independently selected discovery/binding and configuration
helpers. Provider composition
is application-owned; neutral workers and scheduling remain in the one Engine library.
flowchart LR
Application --> Engine
Application --> NativeGLFW
Application --> OpenGL
OpenGL --> NativeGLFW
OpenGL --> Engine
NativeGLFW --> Engine
Application --> UI[Selected UI adapter]
UI --> Engine
Application --> Audio[Selected audio backend]
Audio --> Engine
Module boundary criteria
A separate module is justified when it gives an application a concrete choice or
isolation benefit: omitting a dependency, selecting an alternative implementation,
or testing an external integration independently. A directory, internal interface
or small caller count does not by itself justify a library boundary. Cheryl keeps
one shared Cheryl::Engine.
Keep the selected Native GLFW, whole OpenGL and independent TGUI/RmlUi owners cohesive unless a consumer demonstrates a benefit from another boundary. Native input depends on a live window; dividing display and input splits that coupled lifetime. OpenGL’s context interface permits another window integration within its owner and does not itself justify context/bridge libraries.
Before introducing another library target, record:
the concrete omit/replace/test benefit;
the engine contract the owner implements;
dependencies that become optional or isolated;
lifetime/dependency direction and how cycles are avoided;
test ownership and the smallest independent consumer proving the boundary;
compatibility consequences for current consumers.
If those points do not identify a real benefit, keep the code inside the existing owner. Logging, memory, workers, events, general utilities and similar internal facilities remain engine organization unless a consumer demonstrates otherwise. Evaluate architectural interfaces by their contract rather than deleting them because they have few callers.
Select an assembly
The root defaults to Native GLFW, OpenGL, TGUI and the demo. RmlUi and miniaudio
are optional and default to OFF. UI and audio selection is independent of
platform/graphics selection. OpenGL requires Native GLFW; the demo also requires
native input. Disable demo and every module option for Engine alone.
On Linux GNU/Clang this includes CHERYL_BUILD_DEBUG_TERMINAL_LINUX=OFF.
The root automatically links the selected terminal interface to its demo/shared
test entry point; external applications select it explicitly. Its configuration
policy and standalone probes are in the
owner guide.
Applications link public targets, for example:
target_link_libraries(game PRIVATE Cheryl::Engine Cheryl::NativeGLFW Cheryl::OpenGL)
Linking Engine alone supplies neutral contracts, not concrete native/graphics
implementations. Targets propagate public include roots, C++23, logging policy and
dependency requirements; raw archive consumers must supply their complete link
closure. Engine umbrellas remain neutral. Include concrete classes explicitly
through their owner’s headers, such as cheryl/backends/native-glfw.h.
CHERYL_NATIVE_INPUT=OFF omits Gainput and the default-input factory overload.
CHERYL_NATIVE_NULL_PLATFORM=ON configures an owned GLFW without X11/Wayland;
a supplied GLFW target keeps its host settings. Prefer these explicit options to
the deprecated sandbox shorthand. The build guide owns their
configuration and dependency details.
Selection preserves owner lifetimes: input detaches while its window is live, resources retire before context destruction, and runtime shutdown settles workers and recycles frames before game/adapter cleanup.
Standalone modules
Standalone entry points require an absolute CHERYL_REPOSITORY_ROOT for shared
helpers and Engine bootstrapping. Each owner reuses a supplied Cheryl::Engine
or adds Engine from that root. OpenGL also reuses Cheryl::NativeGLFW or requires
CHERYL_NATIVE_GLFW_SOURCE. Bootstrapping suppresses other owners, demo and Engine
tests in a local variable scope, without rewriting a host’s cache.
This builds only the OpenGL owner and its dependencies:
(
set -e
cd "$(git rev-parse --show-toplevel)"
cmake -S projects/modules/graphics/opengl -B build/opengl-module -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCHERYL_REPOSITORY_ROOT="$PWD" \
-DCHERYL_NATIVE_GLFW_SOURCE="$PWD/projects/modules/platform/native-glfw"
cmake --build build/opengl-module --target module_opengl --parallel
)
Other module directories are standalone source entry points too. Module-local
checks use CHERYL_BUILD_TESTS=ON without enabling Engine’s broader suites.
Hosts own their root enable_testing() policy. Independent consumers use the
owner’s tests/consumer/ entry point. Prebuilt selected owners are also available
through the installed SDK, which
uses the same public aliases and its producer’s compiled configuration.
Add a module
Resolve the boundary criteria above before adding a target. Then:
Create an owner under
projects/modules/<role>/<name>/withinclude/,src/andtests/. Keep public contracts underinclude/cheryl/; implementation helpers and SDK details stay private where the API permits it.Copy Module.cmake.in into the owner’s
CMakeLists.txt. ReplaceMODULE_NAME, the uppercaseMODULE_IDand publicMODULE_ALIAS.cheryl_prepare_module()reuses or bootstraps Engine; passREQUIRES_NATIVEonly when Native GLFW is a real prerequisite.Declare shared metadata using the same ID in the files below. Add SDK discovery inside the selected owner, reusing compatible supplied targets before owned sources. Publish required header dependencies with
PUBLIC; keep private implementation dependenciesPRIVATE. Do not rewrite supplied targets/cache.Declare a root selection with
cheryl_option(name description default_value)in CherylOptions.cmake, and conditionally add the owner in the root CMakeLists.txt. No other module should select this owner implicitly.Add owner-local behavior suites and an independent consumer with first-include probes. Keep cross-owner cases in
projects/tests/. Document the public alias, dependency selection, lifetime/thread requirements and capability limits in the owner’s README, and add it to the module index.
Metadata file |
Module declaration |
|---|---|
|
|
|
|
|
|
Scoped |
Declaration files reload metadata into the caller’s scope; helper definitions are
guarded. Load helpers before declaring a standalone project so versions and target
names are available. The template collects only its owner’s src/*.cpp with
CONFIGURE_DEPENDS, making source additions/removals trigger regeneration. Keep
conditional implementations, generated sources and tests in their own selections.
CherylTests.cmake creates an owner aggregate with
cheryl_add_test_aggregate(... OWNER); set CHERYL_TEST_AGGREGATE before adding
focused suites through cheryl_add_test_suite. Suites compile cases once into
object targets reused by focused, owner and assembly runners. Keep test hooks on
case targets so applications do not inherit them. Test selection
covers runner options; architecture validation
covers isolated graphs, supplied-target reuse and native acceptance.
UI-specific font, alpha, input and publication requirements are in Writing a UI adapter.