Skip to content
The CNA BibleCNA 0.1.0-alpha.1 Edition

Chapter 4 Configuring and Building CNA

4.1 Prerequisites

CNA targets C++23 and CMake 3.20+. On Linux, the minimum toolchain is GCC 12+ or Clang 15+; two sibling directories must be available to CMake alongside the CNA checkout itself: ../sharp-runtime (always required) and ../easy-gl (only required for the shared EasyGL implementation selected by OPENGLES2, OPENGLES3, OPENGL33, WEBGL1, or WEBGL2). SDL3, SDL3_image, and SDL3_mixer are built from vendored Git submodules by default, so no system SDL packages are required on a first build.

The root project version is 0.1.0 and CNA_VERSION_PRERELEASE is alpha.1; a configure of this edition prints CNA: version 0.1.0-alpha.1.

On Windows, any of MSVC 2022 (v17.8+), clang-cl, or MinGW-w64 (native or cross-compiled from Linux) works; only ../sharp-runtime is needed as a sibling dependency, and the same vendored-submodule SDL story applies.

4.1.1 Initializing submodules

Before the first build, CNA’s vendored SDL3/SDL3_image/SDL3_mixer submodules must be populated:

1 git submodule update --init

A GitHub “Download ZIP” archive does not include submodule contents. Building from such an archive leaves third_party/SDL empty, and CMake aborts at configure time with a clear, specific error pointing at cmake/ThirdPartySDL.cmake rather than failing confusingly deep in a missing-header compile error. The two ways around this are cloning with Git and running the command above, or configuring with -DCNA_USE_SYSTEM_SDL=ON to use system-installed SDL3 packages instead of the vendored submodules.

4.2 The default build: Linux, the OpenGL ES 3 renderer

1 git submodule update --init
2 cmake -S . -B build -DCNA_GRAPHICS_RENDERER=OPENGLES3
3 cmake --build build --target CNA CnaTests
4 ctest --test-dir build --output-on-failure

OPENGLES3 is CNA’s Linux default and is implemented by the mature shared EasyGL family (Chapter 22), so it is the natural first build. Swapping to SDL_RENDERER is a one-flag change:

1 cmake -S . -B build-sdlrenderer -DCNA_GRAPHICS_RENDERER=SDL_RENDERER
2 cmake --build build-sdlrenderer --target CNA CnaTests

This one-flag swap is the point of the renderer abstraction covered in Chapter 19 — the same CNA / CnaTests build targets exist regardless of which identity in Table 4.1 you select.

CNA_GRAPHICS_RENDERER value Platform constraint
GL families OPENGLES1, OPENGLES2, OPENGLES3, OPENGL1, OPENGL2, OPENGL33, OPENGL4, WEBGL1, WEBGL2, PORTABLEGL
Native and abstraction APIs BGFX, VULKAN, WEBGPU, MAGNUM, WICKED, SOKOL, DILIGENT, LLGL, IGL, METAL, FNA3D, SDL_GPU
DirectX and Windows families DIRECTX1, DIRECTX2, DIRECTX3, DIRECTX5, DIRECTX6, DIRECTX7, DIRECTX8, DIRECTX9, DIRECTX10, DIRECTX11, DIRECTX12, DIRECT2D, GDI, GLIDE, FREEDIRECT
2D, web, CPU, and diagnostic SDL_RENDERER, CANVAS, HTML_DOM, SVG_DOM, PIXIJS, SKIA, BLEND2D, OPENVG, NANOVG, HEADLESS, SOFTWARE, TINYGL, STUB
Table 4.1: All 50 public renderer identities, grouped into 46 implementation families. Windows, browser, macOS, dependency, and architecture gates are enforced during configuration; the exact per-identity matrix belongs to Chapter 20.

4.3 The full CMake option reference

-DCNA_GRAPHICS_RENDERER is the option most build invocations in this book set explicitly, but CNA’s top-level CMakeLists.txt declares several more, every one of them read directly from that file rather than assumed from memory:

Option Default Effect
CNA_USE_CCACHE ON Auto-detects ccache and wires it in as the C/C++ compiler launcher for both CNA and sharp-runtime, unless a launcher is already set.
CNA_BUILD_TESTS ON Builds the CnaTests GoogleTest target and registers CTest entries.
CNA_BUILD_EXAMPLES ON Builds examples admitted by the selected modules, renderer and host.
CNA_GRAPHICS_RENDERERS (empty) Opt-in semicolon-separated renderer set. Empty preserves single-renderer mode; otherwise CNA_GRAPHICS_RENDERER names its required default.
CNA_PLATFORM SDL3 Chooses window, event, input, timing and system-service implementation: SDL3, SDL2, HEADLESS, or POSIX-only TERMINAL.
CNA_AUDIO_PLATFORM SDL3 Independently chooses SDL3, SDL2, or deterministic NULL playback/capture infrastructure.
CNA_BUILD_C_API OFF Adds the experimental C17 shared-library target CNA::CApi. At alpha.1 that target is compile-blocked by a stale 49-identity C renderer map; see Appendix I.
CNA_CNAEXT OFF Enables an opt-in, project-specific extended render pipeline (see below) — unrelated to the CNAEXT marker macro from Chapter 3.
CNA_DEVICES OFF Enables CNA-specific device/sensor extensions beyond XNA 4.0 (battery, camera, clipboard, and similar).
CNA_ENABLE_NET ON Builds Net and GamerServices networking; pulls in the vendored ENet dependency when on.
CNA_SHARP_RUNTIME_ROOT ../sharp-runtime Overrides the required Sharp Runtime source tree. The release records a verified revision but does not enforce that revision in CMake.
CNA_USE_SYSTEM_SDL OFF Uses find_package for system SDL3 packages instead of the vendored submodules (see above).
CNA_TEST_DISPLAY ":0" The DISPLAY value baked into every GPU/window-creating CTest’s environment; override to point the suite at a virtual display (e.g. Xvfb) instead of a real desktop.
CNA_SANITIZE (empty) Comma-separated sanitizer list (e.g. address,undefined) added to both compile and link flags project-wide; diagnostics only, never for a shipped build.

These switches are separately declared, but some combinations are intentionally refused. For example, a terminal platform admits only CPU or diagnostic renderers; SDL2-only mode has an explicit consistency gate; and a renderer may add its own SDK or architecture constraint. One combination lacks such a gate: the C API references Net and GamerServices unconditionally, but CNA_BUILD_C_API=ON with CNA_ENABLE_NET=OFF configures successfully and then fails on missing GamerServices headers. Keep networking enabled when reproducing the separate NanoVG identity failure recorded in Appendix I.

4.4 Generated product-version API

Configuration generates CNA/Version.hpp under the build include tree. It exposes CNA_VERSION_MAJOR, _MINOR, _PATCH, _PRERELEASE, and _STRING, plus constexpr accessors. The tag spelling has a leading v; the API’s product string does not:

1 #include <CNA/Version.hpp>
2 #include <iostream>
3
4 int main()
5 {
6 std::cout << CNA::getVersionString() << ’\n’; // 0.1.0-alpha.1
7 return CNA::isPreReleaseVersion() ? 0 : 1;
8 }

This listing was compile-checked as C++23 against the header generated by an exact-tag HEADLESS/HEADLESS/NULL configure; the executable returned 0.1.0-alpha.1. It is a compile/runtime observation of the version header, not a renderer test. The C API’s independent ABI version remains 0.7.0.

Two similar spellings at different gates. CNA_CNAEXT (a CMake option(), off by default) and CNAEXT (the marker macro from §3.3) are independent — confusing them is an easy real mistake to make when skimming the source. The marker macro tags an individual declaration as “not part of stock XNA 4.0” and is present in every build regardless of any flag. CNA_CNAEXT the CMake option instead gates an entire CNA::Graphics engine layer in modules/graphics-ext: PBR material data, CRT/depth/ASCII post-process effects, and RenderPipelineSettings. The last of these is only a settings bag, not a description of the whole extension module. Building with the option’s OFF default leaves the guarded declarations and implementations out. Separately, CNA::CnaExt is a link umbrella over graphics-ext and devices-ext; Appendix E maps all three mechanisms.

4.5 Windows, natively and cross-compiled

On real Windows, SDL_RENDERER is selected automatically when no renderer is specified, and SDL is built from the vendored submodule with no pre-built binaries or CMAKE_PREFIX_PATH configuration needed:

1 git submodule update --init
2 cmake -S . -B build-win -DCNA_GRAPHICS_RENDERER=SDL_RENDERER
3 cmake --build build-win --target CNA CnaTests

The same target can be reached without a Windows machine, by cross-compiling from Linux with MinGW-w64 — the toolchain every Windows-targeting CTest binary in this book’s Part IV chapters (DIRECTX9/DIRECTX11/DIRECTX12, and the plain SDL_RENDERER Windows target) is actually verified through:

1 sudo apt install mingw-w64
2
3 git submodule update --init
4 cmake -S . -B build-windows \
5 -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64.cmake \
6 -DCNA_GRAPHICS_RENDERER=SDL_RENDERER
7 cmake --build build-windows --target CNA CnaTests

Chapter 28 and this book’s cross-platform verification chapter cover what happens after the cross-compiled .exe exists: running it under Wine, with DXVK (Direct3D 9/11) or vkd3d-proton (Direct3D 12) translating the Direct3D calls to Vulkan on a real GPU, which is how this project verifies Windows-only renderers without ever touching a Windows machine.

4.6 Building for Android and the Web

Neither of these two targets gets its own cmake/toolchains/ file the way mingw-w64.cmake does for Windows — both instead rely on toolchain infrastructure that ships with the NDK and the Emscripten SDK respectively, invoked directly.

4.6.1 Android (NDK)

Grounded in the project’s own docs/devices-build.md, a real cross-compile invocation looks like this:

1 cmake -S . -B cmake-build-android -G Ninja \
2 -DCMAKE_TOOLCHAIN_FILE="$HOME/Android/Sdk/ndk/30.0.14904198/build/cmake/android.toolchain.cmake" \
3 -DANDROID_ABI=arm64-v8a \
4 -DANDROID_PLATFORM=android-24 \
5 -DCNA_BUILD_TESTS=OFF
6
7 cmake --build cmake-build-android --target CNA -j"$(nproc)"

Three details worth knowing before running this yourself, each one a real, documented finding rather than a generic Android-CMake tip: the exact NDK version path (30.0.14904198) is whatever happens to be installed in a given environment, not a fixed requirement — check ls ~/Android/Sdk/ndk/ first rather than assuming either that it exists or that this exact version is what you have. -DCNA_BUILD_TESTS=OFF is not optional on this path today: googletest was never configured for the NDK toolchain, so only the CNA static library itself cross-compiles, not CnaTests. And this specific invocation is a compile-only verification — no APK packaging and no emulator/device run happen as part of it; Chapter 67 covers the further steps (the NDK’s own llvm-nm, not the host’s plain nm, is what actually confirms platform-conditional code compiled in) that turn a successful compile into a running, on-device verification.

4.6.2 Web (Emscripten)

The CANVAS renderer (Chapter 31) is Emscripten-only, hard-gated at configure time the same way the Direct3D renderers are hard-gated to Windows. Building it follows the standard Emscripten CMake pattern — emcmake wraps the configure step, emmake (or a plain cmake --build, once configured) wraps the build — applied to the same -DCNA_GRAPHICS_RENDERER flag every other renderer uses:

1 emcmake cmake -S . -B build-web -DCNA_GRAPHICS_RENDERER=CANVAS
2 cmake --build build-web
3 node build-web/CnaTests.js

This is a real, verified path, not a hopeful sketch: the project’s own tracking records a genuine emcmake / emcc 6.0.2 configure-and-build succeeding, with the resulting CnaTests.js linking cleanly and a renderer-agnostic GTest suite (35 of 35 Rectangle-family tests) genuinely passing under node. Getting there surfaced a real, pre-existing bug outside CNA’s own tree entirely: sharp-runtime’s FileSystemWatcher.hpp declared three inotify-specific fields unconditionally, even though they were only ever used under #ifdef __linux__ — a guard emcc does not define, so the fields existed but nothing initialized them, a genuine latent bug independent of Emscripten that this cross-compile attempt is what actually surfaced. Fixed directly in sharp-runtime, project-owner approved. What this build does not yet prove is anything about a real browser: this project’s own dev loop has no DOM and no CanvasRenderingContext2D at all, so node-based verification and real-browser verification remain two genuinely different claims, covered in full in Chapter 31.

4.7 System-installed SDL, if you prefer it

Rather than the vendored submodules, system SDL3/SDL3_image/SDL3_mixer packages can be used instead:

1 cmake -S . -B build -DCNA_USE_SYSTEM_SDL=ON -DCNA_GRAPHICS_RENDERER=SDL_RENDERER

This calls find_package(SDL3 REQUIRED), find_package(SDL3_image REQUIRED), and find_package(SDL3_mixer REQUIRED), and requires those development packages to already be installed system-wide.

4.8 Linux system dependencies beyond SDL

Two native dependency routes deserve explicit configuration:

  • FFmpeg (libavcodec-dev libavformat-dev libavutil-dev libswresample-dev) is required for VideoPlayer (video decoding). libswscale-dev is notably not required even though FFmpeg’s own video pipeline usually pairs with it — CNA implements YUVRGBA conversion internally, so only the runtime libswscale8 library needs to be present, not its headers.

  • Draco decoding is on by default through CNA’s immutable third_party/draco gitlink at Draco 1.5.7. A packager may request a system package with CNA_USE_SYSTEM_DRACO=ON; CNA_ENABLE_DRACO=OFF deliberately builds the refusal path. The default therefore requires that submodule to be initialized, and does not vary with the host’s installed Draco version.

4.9 Verifying a build

CNA intentionally does not ship a bundled playable game demo as its primary verification artifact — the project prioritizes framework/runtime development over a shipping demo executable. Two commands stand in for “does this build actually work”:

1 ctest --test-dir build --output-on-failure
2 cmake --build build --target cna_demo_2d

ctest runs the GoogleTest-based CnaTests suite (thousands of unit tests, plus, on renderers where a display is available, GPU pixel tests); cna_demo_2d is the shared 2D integration program used by the platform and renderer infrastructure.

On CI coverage. At the tag, 19 workflows have an automatic push or pull-request trigger and the Direct3D/GDI workflows are manual-only. The intended general unfiltered job and two Input legs carry a retired renderer value and stop during configuration; other automatic jobs exercise narrower Input, Devices, Web, Metal, or arithmetic populations. Chapter 73 traces each route from trigger through verdict. Treat a green badge as evidence for those engaged routes, not as a universal project-wide test count or proof that every renderer ran.

Type at least three characters. Results are ranked by how often and where the words occur.