Build system

CNA snapshot 009d40f5  ·  Development › Build System  ·  source links pinned to 009d40f5

✓

Evidence basis: source-verified at the pinned commit. 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, target and option names were read from CMakePresets.json and the CMake files; nothing was configured, built or run, so 'builds CnaCoreTests' means the preset names that target.

The root CMake project composes physical module targets and one implementation from each backend axis. Presets capture the developer configurations the repository supports; target names, not the CNA umbrella, drive focused builds. This page is the command entry point for a maintainer: what a configure needs and refuses without, which presets and build presets exist, how to select backends by hand, what to rebuild after a given kind of change, how to read the configure log, and where generated artefacts come from. The configure-to-link trace is on CMake architecture; install steps for a first build are in Building CNA.

ℹ

Evidence for this page: the preset and target names below were read from CMakePresets.json and the CMake files at snapshot 009d40f5. Nothing was configured, built or run for the page, so a sentence such as "builds CnaCoreTests" means the preset names that target, not that the build passed. Run cmake --list-presets=all against your own checkout before scripting a preset name.

Prerequisites

The build has a small hard floor and many conditional dependencies. Backend libraries are needed only for configurations that select them, and several optional features are three-state switches (OFF, AUTO, ON) whose AUTO default silently degrades the configuration instead of failing. Inspect the configure output (reading the configure log) and never mistake an unavailable optional backend for a runtime failure.

  • Toolchain. CMake 3.20 or newer (cmake_minimum_required in CMakeLists.txt) and a C++23 compiler with extensions off (CMAKE_CXX_STANDARD 23, CMAKE_CXX_EXTENSIONS OFF). Enabling the C API adds C17. The linked-once libcna.so layout needs CMake 3.27 and a native ELF GNU or Clang toolchain; CMake does not check compiler versions. The presets that inherit base-ninja use Ninja and turn ccache on; presets such as web and devices-* declare no generator.
  • Python 3. Several configure-time gates and CTest entries run Python scripts: the platform ratchet, the non-production SDL audit, the hot-path lint, the XNA pipeline parity gates and the module link-closure tests. Without an interpreter they are skipped with a status line rather than failing, so a configure that "passed" on a machine without Python did not run them.
  • git. Needed for git submodule update --init and, at configure time, to apply the vendored-SDL patch series when the SDL prebuilt is (re)built (the SDL prebuilt); a missing git there is a configure error.
  • Submodules. third_party/SDL, SDL_image, SDL_mixer, third_party/draco and vendor/googletest. Each missing one stops the configure with a message that names the directory and tells you to run git submodule update --init (non-recursive: the recursive form only fetches nested codec submodules CNA disables). With CNA_ENABLE_SDL=OFF the SDL ones are never read; CNA_ENABLE_DRACO=OFF and CNA_BUILD_TESTS=OFF remove the others.
  • Sibling repositories. ../sharp-runtime on its next branch for every build (missing: configure error naming CNA_SHARP_RUNTIME_ROOT); ../easy-gl with ../meta-gl when any of the five GL identities is selected, including the Linux default; ../free-direct only for FREEDIRECT. They are separate checkouts, not submodules. The first-build clone commands are in Building CNA, step 2.
  • Optional three-state features. CNA_ENABLE_VIDEO (FFmpeg, found through four pkg-config modules), CNA_CNB_ZSTD (libzstd for compressed .cnb chunks), CNA_ENABLE_FONT_PIPELINE (FreeType for the .spritefont route) and CNA_ENABLE_MEDIA_PIPELINE (FFmpeg-based build-time importers); zlib for binary FBX arrays is detected without a switch. Draco defaults ON and is off by default only under Emscripten.
  • Backend packages. find_package(Vulkan REQUIRED) for VULKAN, find_package(OpenGL REQUIRED) for OPENGL4, the X or Wayland development packages for CNA_PLATFORM=X11 or WAYLAND (refused with the package names, never replaced by SDL3), and the SDL submodules unless CNA_ENABLE_SDL=OFF.

How CMake is structured

CMake architecture follows the configure order, the target graph, the generated renderer registry, the static-archive cycles, the shared-library mode, the SDL prebuilt and a worked renderer addition. In short:

  • Root policy and options in CMakeLists.txt establish language and toolchain behaviour, then the version identity, the linker, ccache and the test-display policy.
  • Selection files resolve the platform, audio and renderer identities and decide whether SDL is needed at all, before any dependency is configured.
  • Each physical module declares its own static target, include root and dependencies through cna_add_module; the modules are entered from modules/CMakeLists.txt.
  • Backend implementation targets attach only for the selected or requested identities; a generated registry lists them and a descriptor gate compiles the ones you did not select.
  • CNA is an INTERFACE umbrella over the runtime modules plus the selected renderer. It has no sources, so it is not a buildable target: build a real target by name, or build everything.
  • Test, harness and probe registration comes after module composition, so those files can name the final targets.

Useful configurations

The repository ships 17 visible configure presets (plus a hidden Ninja base) and 17 build presets. Each configure preset writes its tree to ${sourceDir}/cmake-build-<preset> (the multi-renderer tree is cmake-build-multi and the iOS simulator tree cmake-build-ios-sim). Read both halves of the JSON: a configure preset existing does not imply a build preset of the same name.

GoalConfigure presetBuild preset (target)What the configure preset sets
Fast core feedback (cmake --preset unit)unitunit-core (CnaCoreTests), unit-math (CnaMathTests), unit-content (CnaContentTests), unit-graphics (CnaGraphicsTests)STUB renderer, Debug, tests on; examples, networking, video, Draco and the C API off. The focused binaries land in cmake-build-unit.
All unit tests the configuration registersunitunit (CnaTests)Then ctest --test-dir cmake-build-unit -N to list what this tree actually registered.
Broad repository teststeststests (CnaTests only)Ninja, Debug, OPENGLES3, tests and examples on. The build preset does not build the example executables; see the warning below.
Runtime renderer set, CPU onlymulti-renderernoneDefault HEADLESS with CNA_GRAPHICS_RENDERERS=HEADLESS;SOFTWARE;STUB; tests and examples keep their default ON. Build with cmake --build cmake-build-multi.
Extension modulescnaextcnaext (CnaTests)Debug, OPENGLES3, CNA_CNAEXT=ON, tests on, examples off.
Memory, thread and undefined-behaviour investigationdevices-asan, devices-tsan, devices-ubsanthe same names (CnaTests)Debug, OPENGLES3, CNA_DEVICES=ON, tests on, with CNA_SANITIZE set to address (optimisation O0), thread (O1) or undefined (O1). Only devices-ubsan is used by a workflow.
Content tool onlydev, dev-fast-debugthe same names (cna_tool_cnb_info)STUB, Debug, tests and examples off; dev-fast-debug adds CNA_DEBUG_INFO=LINE_TABLES.
Compile-time pilotsunit-pch, unit-unityunit-content-pch, unit-core-math-unityThe unit configuration plus CNA_ENABLE_PCH=ON or CNA_ENABLE_UNITY_BUILD=ON.
Release measurementsrelease-modules, release-ipothe same names (cna_tool_cnb_info)STUB, Release, tests and examples off; release-ipo adds CNA_ENABLE_IPO=ON.
Webwebweb (cna_house3d_demo)Release, WEBGL2, examples on, tests off. No toolchain file: run emcmake cmake --preset web.
Applemacos, ios, ios-simulatornoneRelease with SDL_RENDERER. macos has tests and examples on; the iOS presets use cmake/toolchains/ios.cmake with tests, examples and networking off.
⚠

Build presets that name a single target build only that target. cmake --build --preset tests builds CnaTests, but many CTest entries in that configuration are separate example or smoke executables, so an unfiltered ctest afterwards reports missing binaries. Run a full cmake --build cmake-build-tests first, or select entries with -L or -R, as described in the testing handbook.

No preset selects VULKAN, OPENGL4, SDL_GPU, WEBGPU, X11 or WAYLAND, the C API, or a Windows target: configure those by hand, as in the next section. The multi-renderer set contains only CPU renderers, so it needs no GPU; the private display wrapper is for tests in a hand-configured GPU tree (GPU and window tests).

Manual selection

cmake -S . -B build/dev \
  -DCMAKE_BUILD_TYPE=Debug \
  -DCNA_PLATFORM=HEADLESS \
  -DCNA_GRAPHICS_RENDERER=HEADLESS \
  -DCNA_AUDIO_PLATFORM=NULL
cmake --build build/dev --parallel

These identities exist at this snapshot, and combinations are still constrained by target logic and test needs; prefer a repository preset when one expresses the task. Two things surprise people. With the default CNA_ENABLE_SDL=AUTO the vendored SDL is configured and built even for the display-free selection above, because AUTO means "exactly as before"; only -DCNA_ENABLE_SDL=OFF gives an SDL-free tree. And the HEADLESS platform, HEADLESS renderer and NULL audio choice is three independent decisions, not one "headless" switch.

# SDL-free native X11 with several renderers compiled in (the shape of the CI x11-sdl-free-gpu cell;
# OPENGL33 needs the easy-gl and meta-gl siblings, VULKAN the Vulkan headers)
cmake -S . -B build/x11 -G Ninja \
  -DCNA_ENABLE_SDL=OFF -DCNA_PLATFORM=X11 -DCNA_AUDIO_PLATFORM=ALSA \
  -DCNA_GRAPHICS_RENDERER=OPENGL33 \
  -DCNA_GRAPHICS_RENDERERS="OPENGL33;VULKAN;SOFTWARE;HEADLESS"

# One GPU renderer, everything else default
cmake -S . -B build/vk -DCNA_GRAPHICS_RENDERER=VULKAN

What configure refuses, and where

Invalid combinations fail at configure time with a named reason and never fall back silently. The table lists the refusals a hand-written selection can hit and the file that raises each; the first three are the ones people meet first.

SelectionRefused becauseRaised in
A renderer name outside the 25, through CNA_GRAPHICS_RENDERER or a member of CNA_GRAPHICS_RENDERERS; a retired name through a CNA_RENDERER_<NAME>=ON switchUnknown names are refused by name on the selector and set routes, and retired names on all three routes, before any dependency is configured; a name that used to exist gets a separate "retired" message. A CNA_RENDERER_<X>=ON switch whose name is neither public nor retired (for example D3D9) is never read, so it is not refused and the platform default renderer is configuredcmake/RendererIdentities.cmake
Two CNA_RENDERER_<NAME> switches ON; a default that is not a member of the plural listExactly one switch may be ON; the singular default names the default within the setcmake/RendererSelection.cmake, cmake/RendererDefaultSelection.cmake
PORTABLEGL with a real-GL family, GDI with SOFTWARE, identities from different operating-system partitionsDuplicate gl* symbols, the same sources compiled twice with different definitions, one toolchain targeting two platformscmake/RendererCombinations.cmake
CNA_PLATFORM=TERMINAL with anything but SOFTWARE, PORTABLEGL, HEADLESS, STUBNo graphical window; checked before renderer dependencies are probedcmake/RendererSelection.cmake
Windows-only, macOS-only or browser-only renderers off their target; native GL profiles under EmscriptenWindows SDK, Metal or browser APIs are unavailablecmake/RendererSelection.cmake
CNA_PLATFORM=WIN32 off Windows, TERMINAL on Windows, X11 or WAYLAND without their packages; SDL12 and EMSCRIPTENHost-conditional or reserved platforms are refused with what to install, never replaced by SDL3cmake/PlatformSelection.cmake
CNA_AUDIO_PLATFORM=ALSA off Linux; OPENAL and WASAPILinux-only; reservedcmake/AudioPlatformSelection.cmake
SDL2 and SDL3 in one process (platform SDL2 with default audio, platform SDL3 with SDL2 audio, SDL2 for both with an SDL3-linking renderer)Both libraries export identically named entry points, so neither backend would be the one runningcmake/Sdl2OnlyConfiguration.cmake
CNA_ENABLE_SDL=OFF with an SDL platform, an SDL audio value, or SDL_RENDERER, SDL_GPU, FNA3D, FREEDIRECTThe configuration genuinely needs SDL; the message lists which selection doescmake/SdlAvailability.cmake
CNA_BUILD_C_API=ON with CNA_ENABLE_NET=OFFThe C API adapts GamerServices, which is built only with networkingCMakeLists.txt

I changed this — what must I rebuild?

ChangeMinimum expected rebuild and validation
.cpp in one moduleThat module's static library plus every target linking it. With the default shared layout on native ELF, libcna.so is relinked but test and example executables are not; with static linking every dependent executable relinks.
A new or deleted source or test file under a moduleModule sources and test sources are collected with file(GLOB … CONFIGURE_DEPENDS) (the C API lists its sources by hand, and a renderer family globs only its src/*.cpp), so the next build re-runs CMake by itself. A new test directory needs a focused-target name registered in cmake/UnitTests.cmake, or configure stops.
Public or Internal headerEvery target whose dependency scan includes it; validate the downstream module tests.
CMake, options, presets or target sourcesReconfigure, then rebuild the affected targets. A changed cache value (a preset, a -D) reconfigures the same tree.
Renderer contract (IGraphicsRenderer, the descriptor)Every selected family must compile, not only the default; the descriptor gate compiles the others. Then graphics tests and every affected renderer's conformance slice.
Platform contractPlatform, input and runtime tests, the SDL containment audits, and native-host CI.
C API headerThe C API shared, static and WebAssembly paths, the ABI baseline and compatibility gates, and external binding compatibility.
Content schema or readerThe pipeline tool, golden assets and loaders, and runtime content tests.
Vendored SDL source, an SDL patch or the SDL configure argumentsThe next configure rebuilds that SDL install instead of reusing it (the build manifest no longer matches); the install directory is shared by every build tree of the checkout.
Toggling a CNA_*_COMPILED_EFFECTS optionFetches FNA3D with MojoShader at configure time (offline: -DFETCHCONTENT_SOURCE_DIR_FNA3D=<path>), then rebuilds that renderer and common/mojoshader.
A shader .glsl under a renderer or engine layerRegenerate the checked-in header with its generator (compile_shaders.py beside the Vulkan and SDL_gpu shaders, or tools/shader_package/generate_shader_package.py; both load libshaderc), rebuild the affected targets, and run the matching *ShaderPackageReproducibility gate.
Fixture or reference assets under tests/No rebuild: tests read them at run time from repository-root-relative paths.

Reading the configure log

The configure prints one status line per decision that matters, and most build surprises are explained by one of them. Search the log for these prefixes before debugging a "missing feature".

Line (prefix)What it tells you
CNA: Using <X> platform implementation, CNA: Using <X> audio platform implementationThe resolved platform and audio values.
CNA: renderer set -- <list> (default: <X>), CNA: renderer registry -- <n> family/familiesThe compiled-in identities, default first, and the families the generated registry names.
CNA: renderer descriptor gate -- <n> compiled here, <m> by their own family targetHow many non-selected descriptors the gate compiles.
CNA: video enabled (…) or CNA: video disabled (…)Whether the FFmpeg backend was found; disabled means video playback reports NotSupportedException.
CNA: SDL is NOT configuredThe SDL-free configuration is active; no SDL package can be found by any project in the tree.
CNA: <name> prebuilt is current (build manifest …) or CNA: (re)building <name> because …Whether the persistent SDL install was reused or rebuilt, and why.
CNA: vendored SDL3 video drivers: …The video drivers the prebuilt SDL actually has (for example whether wayland is present).
CNA: focused unit-test targets: …Which per-module test executables this configuration can build.
CNA: runtime linked as libcna.so — <n> module archives insideShared-library mode is active.
CNA: compiler cache available (…; base directory: …) or CNA: ccache not foundWhether compile output is cached and against which base directory.
CNA: <n> GPU/window tests inherit the caller's DISPLAYThe test-display policy is active (CMake 3.28 or newer); GPU tests are not pointed at a fixed display.
CNA: MojoShader pinned with FNA3D … or MojoShader is built on the C standard libraryA compiled-effects option fetched MojoShader; the second form is the SDL-free variant without its SDL_GPU adapter.

Build speed and disk use

The build-speed controls are collected in cmake/BuildPerformance.cmake and documented in docs/build-performance.md; public ABI facts stay in cna_build_config, and everything below is target-scoped private policy.

  • ccache. CNA_USE_CCACHE defaults to ON. When ccache is found, both compiler launchers become cmake -E env CCACHE_BASEDIR=<dir> ccache. The base directory is CNA_CCACHE_BASEDIR: the exported CCACHE_BASEDIR if set, otherwise the directory above the source tree, so one translation unit hashes the same from every build tree. cmake --build <dir> --target cna_ccache_stats prints the statistics.
  • One shared library. CNA_SHARED_LIBRARY (default ON on native ELF GNU or Clang with CMake 3.27) links the engine once into libcna.so. A configuration that previously carried a full static copy in every test executable (about 104 MB per Debug executable across 798 executables, by the measurement recorded in the root CMakeLists.txt) now carries it once; a renderer .cpp edit no longer relinks every test.
  • Linker and debug information. CNA_LINKER (AUTO tries mold then lld on native ELF), CNA_DEBUG_INFO (FULL, LINE_TABLES, SPLIT; Debug builds only, and not combinable with sanitizers).
  • Sanitizers. CNA_SANITIZE=address,undefined with CNA_SANITIZE_OPTIMIZATION; ASan with TSan and TSan with MSan are refused, and MSVC and Emscripten are unsupported. The instrumentation is attached to CNA-owned targets and sharp-runtime, not to vendored SDL or ENet.
  • Pilots. CNA_ENABLE_PCH (content test objects; that target bypasses ccache), CNA_ENABLE_UNITY_BUILD (the core and math modules; refused for Emscripten and cross builds) and CNA_ENABLE_IPO.
  • Vendored SDL jobs. CNA_MAX_VENDORED_BUILD_JOBS (default 2) bounds the parallelism of the configure-time SDL sub-builds.
  • Configure audits. The slow configure-time Python audits cache their success by a content fingerprint of their inputs (CNA_CONFIGURE_AUDIT_CACHE, default ON), so an unchanged tree does not re-run them.

Generated artifacts

Build directories, compiled content and generated code belong to their configured output trees unless a generation rule explicitly checks them in. Some generated files live in the build tree (the renderer registry, CNA/Version.hpp, the Wayland protocol bindings) and are never committed; others are committed and guarded by a gate that fails when the file no longer matches what its generator writes (the SPIR-V and shader-package headers, the C API baselines and coverage inventories, the XNA pipeline parity report, the glTF goldens). Before committing a generated change, locate the generating target or script and reproduce the file from clean input. The full table is in the repository map; the source-side entry points are CMakeLists.txt and CMakePresets.json, and the generated CMake option index lists the declared options.

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

Tests and validation
Testing handbook