Getting started as a maintainer

CNA snapshot 009d40f5  ·  Development › Development home  ·  source links pinned to 009d40f5

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Preset names, targets and paths were read from CMakePresets.json and the CMake files at 009d40f5; no configure, build or test was run for this page.

The fastest way to become useful on CNA is to reach one working feedback loop on a known source snapshot, then read the code in dependency order instead of alphabetically. This page gives that loop (a display-free unit build), a reading order with the header paths that actually exist at the snapshot, and the rules that save the most time. It complements the user-facing Building CNA and Contribute pages, which cover toolchains, sibling repositories and the full option set. The commands below were read from CMakePresets.json and the CMake files at snapshot 009d40f5; none was executed for this page.

Confirm the source revision

These pages describe CNA commit 009d40f5dd085c4e674d3479675fac84b12b3e0a on branch next (24 September 2026). It is a post-alpha.1 development snapshot: the product version string is still 0.1.0-alpha.1. Two facts about clones matter before any recipe is trusted.

  • Clone next, not the default branch. GitHub's default branch of libcna/cna is develop, which is the v0.1.0-alpha.1 tag commit. Use git clone -b next https://github.com/libcna/cna.git, or check out the commit above.
  • sharp-runtime must be its next branch too. Its main and develop lack the Resources and Xml.Serialization components that SharpRuntimeConsumption.cmake asks for. CNA's own CI encodes the same rule: clone_siblings.sh takes the first of the pushed, source or target branch that a sibling has, then next, then develop, because pinning develop made every CI configure of next fail. easy-gl and meta-gl use their default branch.

Then compare your checkout with the snapshot. The first command asks whether the snapshot is in your history at all; the others show how far you have moved, and where:

git -C cna rev-parse HEAD
git -C cna merge-base --is-ancestor 009d40f5dd085c4e674d3479675fac84b12b3e0a HEAD && echo "snapshot is in your history"
git -C cna rev-list --count 009d40f5dd085c4e674d3479675fac84b12b3e0a..HEAD
git -C cna log --oneline 009d40f5dd085c4e674d3479675fac84b12b3e0a..HEAD -- modules/runtime

If your checkout differs, treat every path and symbol on these pages as a navigation hint and verify behaviour in your own tree: re-read the implementation before relying on an ordering, ownership or test-name statement, and read the diff of any file a page cites. The pages are pinned on purpose. The libcna.com repository records the snapshot in a one-line file, cnahead, and moves it only together with the prose and the generated references that depend on it; see Maintaining these pages for the procedure and the pin policy. CNA's checkout normally moves faster than the pin.

Establish a fast loop

The focused core unit preset avoids window and GPU requirements and is the safest first proof that compilers, CMake and the repository are usable:

# from the CNA repository root, with ../sharp-runtime (branch next) and the submodules in place
cmake --preset unit
cmake --build --preset unit-core --parallel
./cmake-build-unit/CnaCoreTests
./cmake-build-unit/CnaCoreTests --gtest_filter='LoggerTest.*'
ctest --test-dir cmake-build-unit -N

What those names mean at the snapshot, all from CMakePresets.json:

  • unit is a Ninja configure preset that selects Debug, the STUB renderer and CNA_BUILD_TESTS=ON, and turns examples, the C API, networking, FFmpeg video and Draco off. Its binary directory is cmake-build-unit under the source tree (ignored by CNA's .gitignore). Every preset that inherits the hidden base-ninja preset also turns CNA_USE_CCACHE and CNA_EXPORT_COMPILE_COMMANDS on.
  • unit-core is a build preset bound to the unit configure preset; it builds the single target CnaCoreTests. UnitTests.cmake adds that executable at the root of the build, and nothing sets an output directory, which is why the binary is cmake-build-unit/CnaCoreTests. It is an EXCLUDE_FROM_ALL iteration target, not an extra CTest registration.
  • The presets define no test presets. Run the binary directly (from the repository root, where CNA's own CTest registrations set their working directory) or use ctest --test-dir cmake-build-unit; -N lists what the configured tree actually registers.

The neighbours of unit-core, and what a green run of each does and does not establish:

Build presetTargetUse it forDoes not tell you
unit-coreCnaCoreTestsCore module tests (Logger, version, target platform, default window title).Anything about windows, renderers, audio output or input hosts.
unit-mathCnaMathTestsMath value types and their packed layouts.Public ABI layout beyond what the tests assert.
unit-contentCnaContentTestsContent readers, the CNB and XNB paths, ContentManager.GPU translation of loaded resources: the renderer is STUB.
unit-graphicsCnaGraphicsTestsNeutral graphics logic: state, resources, device validation.Native rendering; cases that need a renderer capability the STUB lacks skip.
unitCnaTestsThe aggregate GoogleTest corpus for this configuration.Other renderer, platform and audio combinations.

Two further pairs exist for iteration rather than validation: dev and dev-fast-debug configure a STUB Debug tree with tests off and build the cna_tool_cnb_info target (the second uses CNA_DEBUG_INFO=LINE_TABLES to shrink debug information), and unit-core-math-unity and unit-content-pch are opt-in build-time experiments that batch or precompile part of the unit tree. The desktop presets that do open windows (tests, cnaext, the three devices-* sanitizer presets) are covered on What to test after changing X.

What the first configure needs, beyond the sibling above: the vendor/googletest submodule (tests are on), and the SDL submodules, because the default platform is SDL3 and the vendored SDL3, SDL3_image and SDL3_mixer are built at configure time into a persistent .sdl-prebuilt-<os>-<arch> directory inside the source tree. Expect the first configure to take a while; later configures and clean build directories reuse that cache. The unit preset needs neither easy-gl nor meta-gl, because the renderer is STUB (they are required by the five GL-profile identities, including the Linux default). The details are in Building CNA, sibling repositories and CMake presets.

✎

What “green” means here. A passing unit-core run shows that the toolchain, CMake configure (including its platform-boundary, hot-path, descriptor and source-partition gates, which need Python 3 and are skipped without it), the siblings and the core tests work together. It says nothing about rendering: the STUB renderer draws nothing. Choose the next check from the change, not from the file extension; the route is on What to test after changing X and the change map.

Read in this order

Start from ownership boundaries. The order below follows the dependency direction: what a module is, then the object that orchestrates a frame, then the host and renderer contracts it drives, then the three build axes that decide which implementations exist.

  1. docs/physical-modules.md: what a physical module is, its CMake target and dependency edges, and how the partition validator keeps ownership total. The generated Module index shows the same modules from the tree.
  2. Game.hpp then Game.cpp: the orchestration boundary. The member-order comment in the header explains why the platform is declared first; in the source read the constructor, Tick, PollEvents and Dispose. The traces are startup, one frame and shutdown.
  3. IPlatform.hpp, then PlatformFactory.hpp with PlatformFactory.cpp: host services, the narrow service interfaces it hands out, and how an implementation name becomes an object. The backends are indexed on Platform backends.
  4. GraphicsDevice.hpp then GraphicsDevice.cpp, alongside the renderer contract IGraphicsRenderer.hpp: the renderer-facing resource owner, and the interface every renderer family implements. The tour is GraphicsDevice internals.
  5. PlatformSelection.cmake, RendererSelection.cmake and AudioPlatformSelection.cmake: the three independent build axes, with the generated Selection axes index as the table of values. Read RendererIdentities.cmake for the identity list and RendererCombinations.cmake for the refusals.
  6. UnitTests.cmake: how the test groups, focused executables and CTest registrations are assembled. It explains why an absent suite in ctest -N is information.
  7. The focused subsystem page and its tests, before touching the implementation: Internals lists them, and First 10, 50 and 100 hours sequences the study.
⚠

Paths that look right and are wrong. The public header of Game is under modules/runtime/include/Microsoft/Xna/Framework/, not under a CNA/ include root, and IPlatform.hpp is under modules/platform/include/CNA/Platform/, not under a CNA/Internal/Platform/ directory. Likewise GraphicsDevice.hpp is in the XNA-shaped Microsoft/Xna/Framework/Graphics/ tree, not in a CNA/Graphics/ directory of the graphics module; no header exists at any of those three wrong locations at this snapshot. The rule that explains the layout: a module's include/ root reproduces the public namespace path, so XNA-shaped types sit under Microsoft/Xna/Framework/, CNA extensions and platform contracts under CNA/, and renderer contracts under CNA/Internal/Renderers/Common/.

Rules that save time

  • CNA is an INTERFACE umbrella, not a binary build target. modules/CMakeLists.txt declares it with add_library(CNA INTERFACE); it has no sources of its own, so cmake --build <dir> --target CNA does not work. Build a preset, a focused module target (CnaMathTests, CnaGraphicsTests and the others listed in the Test target index) or one example or test by its own name.
  • Do not run window or GPU tests on your live desktop. CNA's own repository instructions make this mandatory: CNA_TEST_DISPLAY is empty by default so a test inherits the launcher's display, and naming the live display needs -DCNA_TEST_DISPLAY=:0 -DCNA_TEST_ALLOW_LIVE_DISPLAY=ON (TestDisplayPolicy.cmake). Use run_gpu_tests_private.sh, which starts a private headless Weston and a private Xwayland; the private-display and native-host caveats are on What to test after changing X.
  • Do not infer completeness from a backend directory. A directory under modules/renderers/ proves that source exists. Whether an identity is offered, selected, wired into a target, covered by a conformance test and actually implemented are five separate questions: registry identity (RendererRegistry.cmake), selection logic, target wiring, tests and the current implementation. modules/renderers/common/ is not even a renderer; it holds shared helpers.
  • Current source is authoritative when older prose and code disagree, including CNA's own Markdown. Repository notes are dated records: NEXT.md says near its top that entries dated before the 2026-09-17 renderer curation describe renderer sets CNA no longer has, and the instruction files can lag the CMake (see Conventions). Trust the selection files, the presets and the tests over any description of them.
  • Reuse stable build directories and let ccache work. CNA's instructions ask for stable in-repo cmake-build-<variant>/ directories so rebuilds are incremental, never a scratch or temporary directory, and CNA_USE_CCACHE (on by default) with -DCMAKE_CXX_COMPILER_LAUNCHER=ccache if a fresh configure reports the launcher missing. Build parallelism is not capped (--parallel); the constraint that remains is memory, because sanitizer and template-heavy translation units can each want several gigabytes, so lower the job count for the target that starts swapping rather than for the whole build. Build only the sanitizer variant a task needs: the binaries are large.

Where to go next

With the loop working, choose by what you have to do. To understand the whole system, follow the Human Takeover route. To change something now, start from the change map, respect the architectural invariants and the conventions, and pick tests with What to test after changing X. To chase a failure, use the debugging cookbook. The day-to-day loop (bug, feature, public API, renderer, platform, tests, self-review) is on Working on CNA.

The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.

Tests and validation
What to test after changing X