Building CNA

CNA snapshot c1c316b9  ·  CMake ≥ 3.20  ·  C++23  ·  C17 for the optional C API

⚠

Build the documented branch, not the default branch. This guide documents CNA snapshot c1c316b9, the tip of the apple/m4-stabilization branch (3,687 commits after the v0.1.0-alpha.1 tag, which is still what the product version string reports). A plain git clone https://github.com/libcna/cna.git gives you the default branch, develop, which is the alpha.1 commit. The sibling sharp-runtime must be on its branch of the same name: its default branch lacks the Resources and Xml.Serialization components this snapshot requires. Steps 2 and 3 below use the right branches.

Step 1 - Install the toolchain and system packages

CNA needs a C++23 compiler and CMake 3.20 or newer. On Linux a windowed build also wants the X11, GL, Vulkan, ALSA and D-Bus development packages so the vendored SDL3 (and the native backends) get real drivers. FFmpeg is now optional.

# Debian / Ubuntu (24.04 is what CI uses, with g++-14)
sudo apt install build-essential cmake git pkg-config ninja-build g++-14 \
     libx11-dev libxext-dev libxrandr-dev libxi-dev libxcursor-dev libxfixes-dev \
     libxss-dev libxtst-dev libxkbcommon-dev \
     libgl1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev libvulkan-dev \
     libasound2-dev libpulse-dev libudev-dev libdbus-1-dev libwayland-dev wayland-protocols

# Optional: FFmpeg, for Video / VideoPlayer on Linux and macOS
sudo apt install libavcodec-dev libavformat-dev libavutil-dev libswresample-dev

ninja-build is optional (the default generator is Makefiles). That package list follows what CNA's own Linux CI installs (minus test tooling such as ccache, xvfb and doxygen, with FFmpeg kept optional); libpulse-dev is included because every Linux CI job that builds the native vendored SDL installs it. The prebuilt SDL is not rebuilt when a package appears later, so install everything before the first configure. The minimal sufficient set was not measured. How to build SDL3 with both of its Linux video drivers, and which private servers (Xvfb, headless Weston) its window suites run on, is on Windows, X11 and Wayland.

RequirementFloorWhat proves it
CMake3.20Enforced by cmake_minimum_required. CNA_SHARED_LIBRARY (below) additionally needs 3.27.
C++ standardC++23, extensions offEnforced through cxx_std_23.
Compiler versionNot checked by CMakeCI's workflows use GCC 14 on Linux, AppleClang on macos-26, MSVC on windows-latest, mingw-w64 GCC, and Emscripten 6.0.3 (whether each lane currently passes is a separate question; see the caveat on Platforms). The always-built content-pipeline module uses <format>, so a compiler older than roughly libstdc++ 13 / MSVC 2022 / a current libc++ cannot be assumed to work. Clang on Linux and clang-cl are not exercised by any build workflow.
macOS / iOS13.3 / 16.3Hard floors (lower is a FATAL_ERROR): the code needs Apple's floating-point std::to_chars.
AndroidDemo project: NDK 30.0.14904198, API 24, arm64-v8aWhat the packaged Devices demo pins; no CMake check and no CI.
💡

FFmpeg is no longer a hard requirement on Linux and macOS; it is an option. CNA_ENABLE_VIDEO is AUTO by default: FFmpeg is used only when pkg-config finds libavcodec, libavformat, libavutil and libswresample. ON demands them (configure fails if missing), OFF never probes. Without a backend, Video and VideoPlayer still exist and link, and playback throws System::NotSupportedException at run time.

💡

On Windows, Emscripten, Android and iOS FFmpeg is never built (AUTO falls back to the no-video backend; ON is a configure error). Nothing fails to link there: the video types exist in every build and throw at run time. See Video Playback.

Step 2 - Clone CNA and its sibling repositories

CNA does not vendor everything. Some dependencies must exist as sibling directories next to the CNA checkout, because CMake pulls them in with add_subdirectory(../<repo>). They are separate git checkouts, not submodules, and CMake will not fetch them for you.

mkdir cna-workspace && cd cna-workspace

# CNA and sharp-runtime: the apple/m4-stabilization branch of each (see the warning above)
git clone -b apple/m4-stabilization https://github.com/libcna/cna.git
git -C cna checkout c1c316b9c7a846ce8002809c151fcd1af14942c9
git clone -b apple/m4-stabilization https://github.com/libcna/sharp-runtime.git

# Needed by the default Linux renderer (OPENGLES3) and the other GL profiles:
# meta-gl has a branch of the same name; easy-gl uses its default branch (develop)
git clone https://github.com/libcna/easy-gl.git
git clone -b apple/m4-stabilization https://github.com/libcna/meta-gl.git

Alternatively, stay on any clone of CNA, fetch that branch and check out the exact snapshot: git checkout c1c316b9c7a846ce8002809c151fcd1af14942c9. CNA's CI clones the siblings shallowly (--depth 1) and picks the first branch that exists among the pushed branch, the target branch, next and develop — the same rule the commands above follow. The libcna/* and openeggbert/* organisation names resolve to identical repositories for these four; CNA’s sibling-cloning script uses libcna/, while some of its messages and one workflow still name openeggbert/.

Sibling repoRequired whenWhat it is
../sharp-runtime (branch apple/m4-stabilization) Every build A C++23 reimplementation of a .NET BCL subset. CNA is built on it: System::* collections, IO, text, threading and numerics all come from here. It evolves independently of CNA, so its own README is the authority for mutable size and test counts. Without it, CNA does not configure at all; override the location with -DCNA_SHARP_RUNTIME_ROOT=.
../easy-gl (needs ../meta-gl) The three GL-profile renderers: OPENGLES3, OPENGL33, WEBGL2 A toolkit-independent C++20 wrapper over OpenGL and OpenGL ES. It expects its own sibling checkout, meta-gl, next to it.

Nothing else is a sibling. Other third-party code is either a git submodule (Step 3), vendored in-tree (enet, stb, dr_libs, cgltf), or fetched at configure time when the matching renderer or option is selected: FNA3D (for FNA3D and every compiled-effects option), wgpu-native (for WEBGPU) and SDL_shadercross (for SDL_GPU when CNA_SDL_GPU_SHADERCROSS is on).

⚠

A default Linux build needs three sibling checkouts, not one. The Linux default renderer is OPENGLES3, which is an EasyGL profile — so a plain cmake -S . -B build on Linux requires sharp-runtime, easy-gl and meta-gl. If you would rather keep only sharp-runtime on disk, pick a renderer that does not use EasyGL, for example -DCNA_GRAPHICS_RENDERER=SDL_RENDERER.

Step 3 - Initialise the submodules

cd cna
git submodule update --init

This populates the five gitlinks: third_party/SDL, third_party/SDL_image and third_party/SDL_mixer (the SDL3 family, built from source by the CMake configure step, so no system SDL packages are required), third_party/draco (mesh decoding; needed unless you configure -DCNA_ENABLE_DRACO=OFF) and vendor/googletest (needed while CNA_BUILD_TESTS=ON). With -DCNA_ENABLE_SDL=OFF the three SDL submodules are never touched.

💡

Leave off --recursive. The non-recursive form is the correct one here, and it is much faster: it fetches exactly the five submodules CNA builds. Recursion would only pull the nested codec submodules of SDL_image and SDL_mixer, which CNA switches off. (CNA's own README still says --recursive; its CMake messages say the opposite.)

💡

The first configure builds SDL. SDL3, SDL3_image and SDL3_mixer are compiled at configure time into a persistent .sdl-prebuilt-<os>-<arch> directory inside the source tree (the key gains -wayland on a native Linux host that has the SDL Wayland prerequisites, -simulator for the iOS Simulator and -min<version> for an Apple deployment target), so it survives a clean build directory and is reused for as long as its recorded build manifest still matches: a hash of the vendored source, of the CNA patches applied on top of it and of its configure arguments, so a changed vendored source, patch or argument rebuilds it. SDL3 is built shared on Linux, macOS, Windows and Android and static on Emscripten and iOS. The video drivers it gets depend on which development packages existed when SDL was first built; installing packages later does not change an existing prebuilt cache (on a native Linux host, installing the Wayland prerequisites later selects the separate -wayland directory, which is then built). Expect the first configure to take a while; no timing was measured.

Step 4 - Configure and build

cmake -S . -B build
cmake --build build

That is the whole default build, and it is a big one: with tests and examples on it builds hundreds of executables. Which renderer it selects depends on the target: OPENGLES3 on Linux, WEBGL2 under Emscripten, and SDL_RENDERER everywhere else. For a first run, build just what you need:

cmake -S . -B build -DCNA_BUILD_TESTS=OFF
cmake --build build --target cna_demo_2d --parallel
cd build && ./cna_demo_2d          # run from the build directory: it finds the copied Content/
⚠

cmake --build build --target CNA no longer works. CNA is an add_library(CNA INTERFACE) umbrella with no sources of its own, so it is not a buildable target. Build everything with cmake --build build, or name a real target from the table below. CNA's own README still repeats the old command in several places — it does not work there either. CNA itself defines no hello-triangle-sdl target, in this snapshot or in alpha.1: the name belongs to easy-gl's example. A configure that adds easy-gl (any of the three GL identities, so the Linux default) leaves EASYGL_BUILD_EXAMPLES at easy-gl's default ON (only the web preset turns it off), and easy-gl adds that executable whenever find_package(SDL3) succeeds, so the README's --target hello-triangle-sdl can still resolve there (read from the CMake files, not configured); a build without easy-gl, such as SDL_RENDERER, has no such target.

What you can build

TargetWhat it is
CnaTestsThe aggregate GoogleTest binary: every module's test object set in one executable (needs CNA_BUILD_TESTS=ON).
22 focused test targetsCnaAudioTests, CnaContentTests, CnaContentPipelineTests, CnaCoreTests, CnaDesignTests, CnaDiagnosticsTests, CnaDevicesTests, CnaDevicesExtTests, CnaGamerServicesTests, CnaGraphicsTests, CnaGraphicsExtTests, CnaInputModuleTests, CnaInspectorTests, CnaIntegrationTests, CnaMathTests, CnaMediaTests, CnaNetTests, CnaPhoneTests, CnaPlatformModuleTests, CnaRendererTests, CnaRuntimeTests, CnaStorageTests. Build one module's tests without the rest.
cna_demo_2dThe 2D sprite demo; works with every renderer. Accepts --smoke N to run N frames and exit.
cna_house3d_demoThe 3D house demo. Exists only when the single default renderer is one of OPENGLES3, OPENGL33, WEBGL2, VULKAN, WEBGPU or FNA3D. It is not created for SDL_GPU, the Direct3D and Metal renderers or the CPU renderers, although several of them support 3D.
cna_demo_renderer_selectionReports the renderers a build contains and how run-time selection resolves.
cna_content_toolThe build-time content pipeline command line; the executable is named cna-content. See Content Pipeline.
cna_tool_gltf_to_cnj, cna_tool_cnj_to_cnb, cna_tool_gltf_to_cnb, cna_tool_source_to_cnb, cna_tool_cnb_info, cna_tool_xnb_interop_fixturesOffline CNB / glTF tools, built by default when their inputs exist.
cna_c_apiThe experimental C API library (-DCNA_BUILD_C_API=ON); see below.

The C++ framework has no install() rules, so you consume the source tree, not an installed package; that is why the library target is a CMake INTERFACE umbrella.

Build layout, speed and diagnostics

  • libcna.so by default on native Linux. With a native ELF GNU or Clang toolchain and CMake 3.27 or newer, CNA_SHARED_LIBRARY defaults to ON: the engine is linked once into libcna.so and every executable links that, instead of carrying its own static copy. CNA's own measurement of the old layout was about 104 MB per Debug executable across 798 test and example executables (about 83 GB). Windows, macOS, Android and Emscripten keep the static link; ON there is a configure error. Set -DCNA_SHARED_LIBRARY=OFF for static linking.
  • ccache is used automatically when installed (CNA_USE_CCACHE=ON); CNA_CCACHE_BASEDIR (default: $CCACHE_BASEDIR or the directory above the source) lets several build trees share cache hits.
  • Sanitizers: -DCNA_SANITIZE=address,undefined (comma list; ASan+TSan and TSan+MSan are rejected; not available on MSVC or Emscripten), with CNA_SANITIZE_OPTIMIZATION (DEFAULT, O0–O3). They cover CNA, sharp-runtime and the tests, not the vendored SDL or ENet. Sanitizer binaries are large; build only the variant you need.
  • Other build-quality options: CNA_DEBUG_INFO (FULL, LINE_TABLES, SPLIT), CNA_LINKER (AUTO, DEFAULT, LLD, MOLD; native ELF only), CNA_ENABLE_IPO, CNA_ENABLE_UNITY_BUILD and CNA_ENABLE_PCH (all off by default).
  • Diagnostics and inspector: -DCNA_DIAGNOSTICS=STATS or FULL compiles in the profiler/diagnostics level (OFF by default); -DCNA_BUILD_INSPECTOR=ON builds the inspector agent, bridge and local browser UI (a configure error on Emscripten, Android and iOS). See Diagnostics and Inspector.
  • Configure-time gates. The configure step also runs a platform "ratchet" (SDL must stay inside the platform module), a hot-path lint (both need Python 3 and are skipped without it), a renderer-descriptor gate and a source-partition validator. A failure names the rule.
  • CNA_MAX_VENDORED_BUILD_JOBS (default 2) limits the parallelism of the configure-time SDL sub-builds.

Step 5 - Choose a renderer

A compact build compiles one of the 14 public renderer identities (12 implementation families; the three GL identities share one implementation, EasyGL). The long and option forms below are equivalent and must not be mixed:

# Long form: name the renderer
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=VULKAN

# Option form: exactly one CNA_RENDERER_<NAME> may be ON
cmake -S . -B build -DCNA_RENDERER_VULKAN=ON

A few representative choices:

# Linux default - the OpenGL ES 3.0 profile of EasyGL (needs ../easy-gl and ../meta-gl)
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=OPENGLES3

# 2D only, nothing beyond the vendored SDL3 required
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=SDL_RENDERER

# Vulkan, driven directly
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=VULKAN


# SDL3's own GPU API
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=SDL_GPU

# CPU rasterizer - no GPU, no window; read pixels back with GetBackBufferData()
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=SOFTWARE

# No pixel output at all - game logic only
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=HEADLESS
⚠

These values are dead and stop the configure step with a FATAL_ERROR: EASYGL, D3D9, D3D11 and ASCII. Use one of the three GL profiles instead of EASYGL and the DIRECTX9 / DIRECTX11 spellings instead of D3D*. ASCII output is the CNAEXT AsciiPostProcessEffect, not a renderer. More generally, any name outside the 14 identities given to CNA_GRAPHICS_RENDERER or as a member of CNA_GRAPHICS_RENDERERS is refused at configure time, and a retired identity is refused by name through CNA_RENDERER_<NAME>=ON as well. A CNA_RENDERER_<NAME>=ON switch whose name is neither public nor retired (a typo, or an old spelling such as D3D9) is not read by anything and the platform default renderer is configured, so in old scripts name the renderer with CNA_GRAPHICS_RENDERER. Graphics Renderers lists the 14.

Opt into several renderers

CNA_GRAPHICS_RENDERERS links a compatible semicolon-separated set. The singular option remains the default and must occur in the set:

cmake -S . -B build-multi -G Ninja \
  -DCNA_GRAPHICS_RENDERER=HEADLESS \
  -DCNA_GRAPHICS_RENDERERS="HEADLESS;SOFTWARE;STUB"

An empty plural option is ordinary single-renderer mode. Multi-renderer builds increase dependency and binary size. One combination rule is enforced at configure time: identities gated to different operating systems cannot be mixed. In a multi-renderer binary the CNA_GRAPHICS_RENDERER environment variable chooses at run time. See Runtime Renderer Selection.

Choose platform and audio independently

# Defaults shown explicitly
cmake -S . -B build \
  -DCNA_PLATFORM=SDL3 \
  -DCNA_AUDIO_PLATFORM=SDL3 \
  -DCNA_GRAPHICS_RENDERER=OPENGLES3

# Deterministic display-free build
cmake -S . -B build-headless \
  -DCNA_PLATFORM=HEADLESS \
  -DCNA_AUDIO_PLATFORM=NULL \
  -DCNA_GRAPHICS_RENDERER=HEADLESS

# No SDL anywhere (windowless), ALSA audio through CNA's own mixer
cmake -S . -B build-sdl-free -G Ninja \
  -DCNA_ENABLE_SDL=OFF \
  -DCNA_PLATFORM=HEADLESS \
  -DCNA_AUDIO_PLATFORM=ALSA \
  -DCNA_GRAPHICS_RENDERER=HEADLESS

CNA_PLATFORM accepts three values: SDL3 (default, and the one windowing implementation — Windows, X11, Wayland, macOS, iOS, Android and the web through SDL’s video drivers), HEADLESS and TERMINAL (POSIX only; CPU renderers only). CNA_AUDIO_PLATFORM accepts three: SDL3 (default), NULL and ALSA (Linux only). Any other platform value, and the reserved audio names OPENAL and WASAPI, fail rather than falling back. Only the SDL3 and ALSA audio values define SOUND_ENABLED and provide a mixer (SDL3_mixer, or CNA's own for ALSA); Null selects low-level device code but is not a feature-equivalent game-audio backend.

The axes are independent; -DCNA_ENABLE_SDL=OFF refuses any selection that genuinely needs SDL (the SDL3 platform and audio values, and the SDL_RENDERER, SDL_GPU and FNA3D renderers), so an SDL-free build is windowless, and TERMINAL accepts only the CPU renderers. See Platforms.

Several renderers are hard-gated and refuse to configure off their platform: 2 are Windows-only (DIRECTX9 and DIRECTX11), METAL is Apple-only (macOS and iOS), WEBGL2 is Emscripten-only, and OPENGLES3 / OPENGL33 cannot be selected under Emscripten. On iOS only SDL_RENDERER and METAL are allowed. Graphics Renderers lists all 14 identities with their scope, platform gate and dependency.

Compiled-effects options

Compiled Effect Framework (.fxb) bytecode support is opt-in per renderer family, through eight options that all default to OFF: CNA_EASYGL_COMPILED_EFFECTS, CNA_VULKAN_COMPILED_EFFECTS, CNA_WEBGPU_COMPILED_EFFECTS, CNA_SOFTWARE_COMPILED_EFFECTS, CNA_DIRECTX9_COMPILED_EFFECTS, CNA_DIRECTX11_COMPILED_EFFECTS, CNA_METAL_COMPILED_EFFECTS and CNA_SDL_GPU_COMPILED_EFFECTS. FNA3D always supports compiled effects and has no option; CNA_EASYGL_COMPILED_EFFECTS enables all three GL identities at once. With the options on, 11 of the 14 identities (in 9 families) support them. Any of them (and FNA3D) fetches FNA3D with its MojoShader patch series at configure time. A default configure reports compiled effects as supported on FNA3D only (unsupported on 13 of the 14 identities). See Shader Effects and Effects System.

CMake presets

The repository ships 17 visible configure presets (plus a hidden base-ninja parent: Ninja, ccache, compile_commands.json) and 17 build presets; no test, workflow or package presets. Each preset chooses its own build directory, ${sourceDir}/cmake-build-<preset>, so drive the build with cmake --build --preset <name> rather than guessing the path. Several are maintainer tooling; the ones most readers want are tests, web, multi-renderer, macos, ios, ios-simulator, cnaext and unit.

PresetWhat it configures
devSTUB renderer, Debug; tests, examples, C API, net, video and Draco all off. Build preset dev builds cna_tool_cnb_info.
dev-fast-debugdev plus CNA_DEBUG_INFO=LINE_TABLES.
unitSTUB, Debug, tests on, examples/net/video/Draco off. Build presets: unit (CnaTests), unit-core, unit-math, unit-content, unit-graphics (the matching focused targets).
unit-pchunit plus precompiled headers (build preset unit-content-pch).
unit-unityunit plus a unity build (build preset unit-core-math-unity).
release-modulesSTUB, Release, tests/examples/net/video/Draco off (build preset builds cna_tool_cnb_info).
release-iporelease-modules plus IPO/LTO.
webAn Emscripten release build on the WEBGL2 renderer, examples on, tests off (build preset builds cna_house3d_demo). It has no toolchain file: run emcmake cmake --preset web.
devices-asanOPENGLES3 debug build with CNA_DEVICES=ON under AddressSanitizer.
devices-tsanThe same, under ThreadSanitizer.
devices-ubsanThe same, under UndefinedBehaviorSanitizer.
macosRelease, SDL_RENDERER, tests and examples on.
iosRelease with cmake/toolchains/ios.cmake, SDL_RENDERER, tests/examples/net off.
ios-simulatorThe same for the simulator SDK (CNA_IOS_SIMULATOR=ON).
testsA Ninja debug build on OPENGLES3, tests and examples on (build preset builds CnaTests).
multi-rendererNinja, HEADLESS default with CNA_GRAPHICS_RENDERERS=HEADLESS;SOFTWARE;STUB.
cnaextDebug, OPENGLES3, CNA_CNAEXT=ON, tests on, examples off (build preset builds CnaTests).
cmake --preset tests
cmake --build --preset tests

The dev, unit and release-* presets choose the STUB renderer and turn off video, Draco and networking, which makes them the lightest way to compile the framework itself. They still use the default SDL3 platform, so the SDL submodules and prebuilt SDL are needed.

Cross-compiling

Windows, from Linux

sudo apt install g++-mingw-w64-x86-64

cmake -S . -B build-win \
      -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64.cmake \
      -DCNA_GRAPHICS_RENDERER=DIRECTX9
cmake --build build-win

# Direct3D 11 in an SDL3 window (the default platform)
cmake -S . -B build-win11 -G Ninja \
      -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64.cmake \
      -DCNA_GRAPHICS_RENDERER=DIRECTX11

The 2 Windows-gated renderers require CMAKE_SYSTEM_NAME=Windows, which the MinGW-w64 toolchain file supplies (x86_64, falling back to i686; there is one toolchain file, cmake/toolchains/mingw-w64.cmake). The window comes from SDL3’s windows driver, which hands the renderer its HWND. The repository includes manual Wine + DXVK execution paths. In CI, Windows builds (Direct3D 11, the SDL3 platform suite, the content pipeline) are manual-dispatch MSVC workflows. Native MSVC builds use the same options.

Web (Emscripten)

# 1. Install and activate the Emscripten SDK (CI pins emsdk 6.0.3)
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
cd ../cna

# 2. Configure and build through the bundled preset (WEBGL2)
emcmake cmake --preset web
cmake --build --preset web

# Or build WEBGL2 and WEBGPU into one bundle and choose at run time
emcmake cmake -S . -B build-web-multi \
      -DCNA_GRAPHICS_RENDERER=WEBGL2 -DCNA_GRAPHICS_RENDERERS="WEBGL2;WEBGPU"
cmake --build build-web-multi

Only emsdk 6.0.3 is used by CI's workflows; older versions are unproven. Output is .html, .js and .wasm; serve it from a local web server, because browsers block direct file:// access. Draco is off by default under Emscripten, and exceptions are JS-lowered with Asyncify for application executables. Three web caveats matter before you ship anything: saves persist only through the browser’s IndexedDB mounts (a threaded build needs -DCNA_EMSCRIPTEN_USE_WASMFS=OFF for that), the video types throw at run time (no FFmpeg on the web), and an external final executable that uses blocking Game::Run() must link CNA::EmscriptenAsyncify (your Game no longer has to be heap-allocated). Platforms explains each.

Android

cmake -S . -B build-android \
      -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
      -DANDROID_ABI=arm64-v8a \
      -DANDROID_PLATFORM=android-24 \
      -DCNA_BUILD_TESTS=OFF \
      -DCNA_BUILD_EXAMPLES=OFF \
      -DCNA_GRAPHICS_RENDERER=SDL_RENDERER
cmake --build build-android

The Android code paths and NDK sensor implementations are real, but there is no CMake preset, no workflow and no CMake API-level check for Android. The only Android build project in the repository is the packaged Devices demo (Gradle, minSdk 24, NDK 30.0.14904198, arm64-v8a), not a reusable general-game template. CNA's own Android status note, last edited before alpha.1, records an NDK cross-build failing in two sibling sharp-runtime files; we did not re-run it against the current sibling. Treat this command as the framework cross-build shape and validate the build and final APK packaging yourself.

macOS and iOS

# macOS (Apple floor 13.3)
cmake --preset macos
cmake --build cmake-build-macos

# iOS device / simulator (Apple floor 16.3; SDL_RENDERER only)
cmake --preset ios
cmake --preset ios-simulator

iOS is experimental: the Apple workflow is configured to final-link a device smoke app and to install and launch a simulator smoke app for one frame (CNA_BUILD_APPLE_SMOKE_APP, on by default for iOS). Other renderers need -DCNA_APPLE_ALLOW_UNVALIDATED_RENDERER=ON, which is an experiment switch, not a support claim.

CMake options reference

OptionValuesDefaultDescription
CNA_GRAPHICS_RENDERERAny one of the 14 renderer identitiesOPENGLES3 on Linux, WEBGL2 under Emscripten, SDL_RENDERER elsewhereSelects the only renderer in a single-renderer build, or the default in a multi-renderer build. Unknown names fail by name.
CNA_GRAPHICS_RENDERERSSemicolon-separated compatible identities(empty)Opt-in multi-renderer build. The singular default must be a member.
CNA_RENDERER_<NAME>ON, OFFOFFThe equivalent per-renderer form. Exactly one may be ON; two or more is a configure error.
CNA_PLATFORMSDL3, HEADLESS, TERMINALSDL3Selects window, event, input, timing and host services. SDL3 reaches Windows, X11 and Wayland through SDL’s video drivers; TERMINAL POSIX-only. Reserved: SDL12, EMSCRIPTEN.
CNA_AUDIO_PLATFORMSDL3, NULL, ALSASDL3Selects low-level device integration independently. SDL3 and ALSA enable SOUND_ENABLED and a mixer. ALSA is Linux-only. Reserved: OPENAL, WASAPI.
CNA_ENABLE_SDLAUTO, ON, OFFAUTOOFF skips the vendored SDL3 sub-build entirely and refuses any selection that needs SDL, naming which.
CNA_ENABLE_VIDEOOFF, AUTO, ONAUTOFFmpeg video backend. Never available on Windows, MinGW, Emscripten, Android or iOS.
CNA_BUILD_C_APION, OFFOFFRequests the experimental native C API (ABI 0.46.0; public headers held to a C99 consumer floor, CNA's own sources compiled as C17). Requires CNA_ENABLE_NET=ON: the combination with networking off fails at configure with a named error. No CNA workflow builds it; the C# binding CNA.NET builds and uses it on Linux.
CNA_C_API_BUILD_STATICON, OFFONAlso build the static C API archive (Linux, needs Python 3).
CNA_BUILD_TESTSON, OFFONBuild the CnaTests binary and the 22 focused test targets, and register the CTest entries. Needs the vendor/googletest submodule.
CNA_BUILD_EXAMPLESON, OFFONBuild the demo programs and the example programs under the modules' examples/ directories.
CNA_BUILD_BENCHMARKSON, OFFOFFBuild the microbenchmarks.
CNA_CNAEXTON, OFFOFFThe CNAEXT extensions beyond XNA 4.0 (retro effects, debug drawing, shader packages). Off by default. See CNAEXT Extensions.
CNA_DEVICESON, OFFOFFThe CNAEXT device layer (battery, camera, clipboard, file dialogs and so on); the XNA-shaped sensors are always built. Off by default.
CNA_ENABLE_NETON, OFFONBuild the networking layer (GamerServices and Net, over vendored ENet). Required by the C API.
CNA_ENABLE_DRACOON, OFFON (OFF under Emscripten)KHR_draco_mesh_compression decoding; ON needs the third_party/draco submodule. CNA_USE_SYSTEM_DRACO (default OFF) uses a system package instead.
CNA_CNB_ZSTDAUTO, ON, OFFAUTOlibzstd for .cnb chunk compression (opt-in per chunk).
CNA_ENABLE_FONT_PIPELINEOFF, AUTO, ONAUTOThe .spritefont source route (FreeType, build-time tooling only). CNA_ENABLE_MEDIA_PIPELINE (AUTO) likewise gates the build-time MP3/WMA/WMV importers.
CNA_BUILD_INSPECTORON, OFFOFFInspector agent, bridge and local browser UI. A configure error on Emscripten, Android and iOS.
CNA_DIAGNOSTICSOFF, STATS, FULLOFFProfiler and diagnostics level (CNA_DIAGNOSTICS_LEVEL 0/1/2).
CNA_SHARED_LIBRARYON, OFFON on native ELF GNU/Clang with CMake ≥ 3.27, else OFFLink the runtime into one libcna.so. ON where unsupported is a configure error.
CNA_USE_CCACHEON, OFFONRoute compilation through ccache when it is available; CNA_CCACHE_BASEDIR sets the stable base directory.
CNA_USE_SYSTEM_SDLON, OFFOFFLink against system SDL3 packages instead of the vendored submodules.
CNA_SDL_PREBUILT_ROOT, CNA_MAX_VENDORED_BUILD_JOBSpath; integer<src>/.sdl-prebuilt-<key>; 2Where the configure-time SDL install lives (persists across clean builds; the key carries the target system and architecture and, where they apply, -wayland, -simulator and -min<version> suffixes; an install is reused only while its build manifest matches), and the parallel jobs used to build it.
CNA_EASYGL_COMPILED_EFFECTSON, OFFOFFAdd compiled Effect Framework bytecode support to EasyGL identities.
CNA_VULKAN_COMPILED_EFFECTSON, OFFOFFAdd compiled Effect Framework bytecode support to Vulkan.
CNA_WEBGPU_COMPILED_EFFECTS, CNA_SOFTWARE_COMPILED_EFFECTSON, OFFOFFThe same for WebGPU and the Software renderer.
CNA_DIRECTX9_COMPILED_EFFECTS, CNA_DIRECTX11_COMPILED_EFFECTSON, OFFOFFThe same for the two Direct3D renderers (Windows targets).
CNA_SDL_GPU_COMPILED_EFFECTS, CNA_METAL_COMPILED_EFFECTSON, OFFOFFThe same for SDL_GPU and Metal (Metal translates MojoShader’s SPIR-V to MSL with SPIRV-Cross).
CNA_SDL_GPU_SHADERCROSSON, OFFON on Windows and Apple, OFF elsewhereSPIR-V stock shaders for SDL_GPU through SDL_shadercross (fetched).
CNA_SANITIZEcomma-separated sanitizer list(empty)Enable compiler sanitizers, for example address,undefined. See Build layout.
CNA_SANITIZE_OPTIMIZATION, CNA_DEBUG_INFO, CNA_LINKERDEFAULT/O0–O3; FULL/LINE_TABLES/SPLIT; AUTO/DEFAULT/LLD/MOLDDEFAULT; FULL; AUTOOptimisation level for sanitizer builds, debug-information level, and the fast linker (native ELF only).
CNA_ENABLE_IPO, CNA_ENABLE_UNITY_BUILD, CNA_ENABLE_PCHON, OFFOFFLTO, a unity-build pilot (core and math), a precompiled-header pilot (content tests).
CNA_TEST_DISPLAYX display(empty)The DISPLAY used by window-creating CTest entries. Empty means tests inherit yours; :0 is honoured only with CNA_TEST_ALLOW_LIVE_DISPLAY=ON, and a guard stops tests from connecting to a live Wayland compositor.
CNA_PLATFORM_CTEST_BINARYCnaTests, CnaPlatformModuleTestsCnaTestsThe binary the CnaPlatform* CTest entries (including SDL3 on X11 and on Wayland) run.
CNA_SHARP_RUNTIME_ROOTpath../sharp-runtimeWhere to find the sharp-runtime checkout, when it is not the sibling directory (on the branch matching CNA’s).
CNA_WEBGPU_AUTO_DOWNLOADON, OFFONDownload the pinned wgpu-native release for the WEBGPU renderer.
CNA_WEBGPU_ROOTpath(unset)Root of a manually extracted wgpu-native release; set it together with -DCNA_WEBGPU_AUTO_DOWNLOAD=OFF for an offline build.
CNA_ENABLE_EMSCRIPTEN_THREADSON, OFFOFFShared-memory pthread ABI; Emscripten only.
CNA_EMSCRIPTEN_USE_WASMFSON, OFFONThreaded Emscripten builds use WasmFS; OFF selects the legacy file system, which persistent browser saves (IndexedDB) need. Keep WasmFS for games that load content on worker threads.
CNA_ENABLE_VOICEAUTO, ON, OFFAUTONetwork voice through libopus. AUTO carries voice when a native build finds libopus 1.3 or newer through pkg-config; it is not vendored, and a cross-compiled build (browser, Android, iOS, Windows built from Linux) never probes for it, so it carries no voice there and ON fails the configure.
CNA_MACOS_DEPLOYMENT_TARGETversion13.3macOS deployment floor. It may be raised but not lowered because CNA and sharp-runtime require Apple's floating-point std::to_chars.
CNA_IOS_DEPLOYMENT_TARGETversion16.3iOS deployment floor, with the same hard lower-bound rule.
CNA_IOS_SIMULATORON, OFFOFFWith cmake/toolchains/ios.cmake, selects the simulator SDK instead of a device SDK.
CNA_APPLE_BUNDLE_IDENTIFIER_PREFIXreverse-DNS stringcom.openeggbert.cnaPrefix for generated Apple bundle identifiers.
CNA_APPLE_BUNDLE_VERSIONnumeric version0.1.0Apple bundle version. The prerelease suffix is intentionally omitted because Apple requires numeric components.
CNA_APPLE_DEVELOPMENT_TEAMteam ID(empty)Codesigning team for iOS products; empty disables signing for simulator/CI use.
CNA_APPLE_BUNDLE_MACOS_EXECUTABLESON, OFFOFFEmit macOS executables as app bundles instead of plain command-line binaries.
CNA_BUILD_APPLE_SMOKE_APPON, OFFON for iOS; OFF otherwiseBuild the minimal Apple final-link/simulator smoke application.
CNA_APPLE_ALLOW_UNVALIDATED_RENDERERON, OFFOFFiOS experiment escape hatch for identities outside the sole validated SDL_RENDERER allow-list; unsupported configurations are expected to fail.
CNA_STRICT_XNA_APIcompile definition(not set)Purity mode, set on the target you want checked (a CMake -D option of that name does nothing). With it defined, CNAEXT-marked declarations become [[deprecated]] warnings, errors under -Werror=deprecated-declarations. CNA's own check exercises only the Microsoft::Devices and sensor surface and is skipped under MSVC, so it does not by itself prove a whole codebase stays inside the XNA 4.0 surface.

Running the tests

cmake -S . -B build -DCNA_BUILD_TESTS=ON
cmake --build build --target CnaTests
./build/CnaTests                         # from the repository root: run the binary directly
ctest --test-dir build -N                # inspect registrations; do not run unfiltered after building only CnaTests

This snapshot contains 813 C++ test source files (781 of which contain a counted macro) and 11,380 statically declared GoogleTest-family definitions, counted by the same method as alpha.1's 568 files and 8,263 definitions. There are also 781 standalone examples/**/*_test.cpp pixel programs that are not in those figures, and no CTest total is derivable from the sources. Those are source inventory figures, not a promise that one executable instantiates or runs that many cases: conditional compilation, parameterized tests, renderer families and build options change both GoogleTest and CTest inventories. Use ctest -N in the configured build to see that build's registrations, then run the labels relevant to its selected renderer and platform.

⚠

An unfiltered ctest after building only CnaTests is misleading. Many CTest entries are separate example or smoke executables that --target CnaTests does not build, so they report missing binaries; CNA's own unfiltered CI job does a full default cmake --build first. Window-creating tests also run against your DISPLAY (or a private one), which is why CI uses xvfb-run -a ctest --test-dir build -L input --output-on-failure and label or regex filters. Use -L/-R, or build the focused test target for the module you care about. The tests preset's own advice is stronger: run the CnaTests binary directly, because ctest discovers and runs each GoogleTest case as its own process, which also races tests that share hard-coded /tmp fixture paths.

💡

Test labels follow the current renderer names. ctest -L D3D9 and ctest -R D3D11 match zero tests; the labels are DIRECTX9 and DIRECTX11.

CI's workflows cover focused subsets across Linux, macOS and a headless browser (some of those lanes pin an older sharp-runtime; see Platforms). The general job configures OPENGLES3, runs a full default build and an unfiltered ctest under Xvfb, and tolerates a single known failure (its ctest step is continue-on-error, then a classification step fails the job on anything else); the alpha.1 defect of configuring a removed renderer identity is fixed. The GPU pixel/oracle matrix is not continuously gated. See Platforms for exactly which workflows fire and what they cover.

Running the demos

# 2D demo (run from the build directory so it finds Content/)
cmake --build build --target cna_demo_2d
(cd build && ./cna_demo_2d)

# House 3D demo (created only when the default renderer is OPENGLES3/OPENGL33/WEBGL2, VULKAN, WEBGPU or FNA3D)
cmake --build build --target cna_house3d_demo
(cd build && ./cna_house3d_demo)

# A short non-interactive run, useful over SSH or in CI
(cd build && ./cna_demo_2d --smoke 6)

Using CNA from your own project

⚠

The C++ framework has no general install(), export() or CPack rules. C++ consumers bring the source tree in with add_subdirectory(), alongside the same sibling checkouts CNA itself needs, and link the CNA target; a consumer should set CNA_BUILD_TESTS=OFF and CNA_BUILD_EXAMPLES=OFF. The experimental C layer declares separate CNACApi install/package rules (CNAConfig.cmake, CNACTargets.cmake) and CNA::CApi/CNA::CApiStatic targets, but it is source-level only in this snapshot — see the prerequisites below.

C API prerequisites

  • -DCNA_BUILD_C_API=ON requires -DCNA_ENABLE_NET=ON (the default). Configuring with networking off fails immediately with a named error, instead of failing later on a missing include as in alpha.1.
  • A C17 compiler; CNA turns on CMAKE_C_EXTENSIONS for the vendored C sources it compiles, while its own C API sources stay strict C17. The static archive (CNA_C_API_BUILD_STATIC) is Linux-only and needs Python 3. The build enables -fPIC for everything the shared library absorbs.
  • The C ABI version is 0.46.0 (experimental; alpha.1 was 0.7.0), with a release gate that currently reports “not ready” (631 planned public C++ declarations still have no C mapping). Language bindings pin an exact ABI version, so a binding built for one ABI does not work against another; CNA.NET admits exactly 0.46.0 (admitted after a compatibility review recorded by CNA’s Apple campaign).
  • No CI workflow builds the C API library (its workflows check headers, coverage and the release gate). The alpha.1 compile blocker in the renderer map is gone in the source, but we did not build the library, so treat it as unverified. See Experimental C API.

Troubleshooting

sharp-runtime not found

CNA requires sharp-runtime as a sibling directory:

cna-workspace/
├── cna/
└── sharp-runtime/

Clone it next to CNA (git clone -b apple/m4-stabilization) and re-run the configure step, or point -DCNA_SHARP_RUNTIME_ROOT= at your checkout. If configure finds the directory but fails while resolving Sharp Runtime components (Resources, Xml.Serialization), the checkout is on the wrong branch: check out its apple/m4-stabilization branch. (We did not reproduce that exact failure text.)

easy-gl or meta-gl not found

The three GL-profile renderers — including OPENGLES3, the Linux default — are built on the easy-gl sibling, which itself expects meta-gl next to it. Clone both alongside CNA, or select a renderer that does not use EasyGL, such as -DCNA_GRAPHICS_RENDERER=SDL_RENDERER.

Missing vendored SDL, googletest or Draco

The message tells you to run git submodule update --init (non-recursive). A ZIP or tarball export of the repository cannot contain submodule content. If you do not want Draco, configure -DCNA_ENABLE_DRACO=OFF; if you do not want tests, -DCNA_BUILD_TESTS=OFF; with -DCNA_ENABLE_SDL=OFF the SDL submodules are not needed.

"Unknown graphics renderer"

The message reads "CNA: unknown graphics renderer '<name>' (requested through CNA_GRAPHICS_RENDERER)" and lists the supported names. The value you passed is not one of the 14 public identities. The usual cause is an old name: EASYGL, D3D9, D3D11, DX3 or ASCII. A name that used to exist but has been removed gets a separate message saying it "has been retired and is no longer supported". See the translation callout in Step 5 above, or the full list on Graphics Renderers.

"renderer only builds when targeting Windows"

You selected one of the 2 Windows-gated renderers on a non-Windows target. Either build natively on Windows, or use the repository's cross-compile path with -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64.cmake and run the result under Wine + DXVK. The full-engine cross-build is not an automatic CI lane.

WEBGL2 refuses to configure

It needs the browser’s WebGL 2 and is Emscripten-only. Configure through emcmake cmake or emcmake cmake --preset web; it cannot be selected on a native target (use OPENGLES3 or OPENGL33 there).

Configure stops on a CNA_PLATFORM value

CNA_PLATFORM takes SDL3, HEADLESS or TERMINAL; any other value stops the configure step, and the message names SDL3 as the platform for a windowed build on Windows, X11 or Wayland. TERMINAL on Windows is refused as not implemented there. If SDL3 does not open a window on your Linux session, check that the X11 or Wayland development packages were installed before the vendored SDL3 was first configured. See Windows, X11 and Wayland.

"CNA_ENABLE_SDL=OFF, but this configuration genuinely requires SDL"

Something in your selection needs SDL: a platform or audio value of SDL3 (remember the default audio is SDL3, so pass -DCNA_AUDIO_PLATFORM=NULL or ALSA), or one of the renderers SDL_RENDERER, SDL_GPU, FNA3D. The message lists which. Choose a native platform, a non-SDL audio value and a non-SDL renderer.

Vulkan: no Vulkan device found

The VULKAN renderer needs Vulkan headers, a loader and a driver ICD. On Debian/Ubuntu install libvulkan-dev (and mesa-vulkan-drivers for a software driver) and check with vulkaninfo. Virtual machines without GPU passthrough usually have no usable Vulkan device.

OPENGLES3: OpenGL context creation failed

The GL profiles need a working GL/GLES driver and a display server. On a headless machine, force a software GL implementation (for example LIBGL_ALWAYS_SOFTWARE=1 with Mesa's llvmpipe) or run under xvfb-run -a, or switch to SOFTWARE, HEADLESS or STUB — none of which need a GPU at all (and none of which opens a window, whichever window system SDL3 would use).

The program starts but shows no window

You most likely built a CPU renderer (SOFTWARE, HEADLESS or STUB). On windowing platforms these render off-screen by design. Build with a GL-family renderer or VULKAN for a visible window, or use CNA_PLATFORM=TERMINAL with SOFTWARE to see frames in a terminal.

--target CNA fails with "No rule to make target"

Expected. CNA is an INTERFACE library with no sources. Drop the --target argument, or name CnaTests or a demo target instead.

Configure fails on libavcodec / libavformat / libavutil / libswresample

This only happens if you asked for it: -DCNA_ENABLE_VIDEO=ON requires all four FFmpeg development packages and fails configure when one is missing (and on Windows, MinGW, Emscripten, Android and iOS, where FFmpeg is never built). With the default AUTO, missing packages simply mean a build without video. Install libavcodec-dev, libavformat-dev, libavutil-dev and libswresample-dev (or your distribution's equivalents), or drop the option.

Video: NotSupportedException at run time

The build has no FFmpeg backend. On Linux and macOS install the four FFmpeg development packages and reconfigure with -DCNA_ENABLE_VIDEO=ON (or leave AUTO); on Windows, Emscripten, Android and iOS there is no FFmpeg build, so playback is unavailable. Nothing fails at link time.

sharp-runtime on the wrong branch: the message to look for

When the sibling directory exists but is sharp-runtime's main or develop branch, the configure stops inside sharp-runtime's own CMake, before CNA prints its sharp-runtime shape message. CNA hands its component closure to the sibling in SHARP_RUNTIME_COMPONENTS, and the sibling's registry rejects the first name it does not know. Reading both repositories (the failing configure was not run), the message has the form:

CMake Error at …/sharp-runtime/cmake/SharpRuntimeComponents.cmake:<line> (message):
  Unknown Sharp Runtime component 'Resources'. Available components: Core.Base, …, All

Resources is reported first because it precedes Xml.Serialization in CNA's list (cmake/SharpRuntimeConsumption.cmake); both exist only on sharp-runtime's apple/m4-stabilization branch. The fix is the one above: git -C ../sharp-runtime checkout apple/m4-stabilization (or clone with -b apple/m4-stabilization), then reconfigure. How the component selection works on both sides is on sharp-runtime components and consumption.