Tutorial 02: Setting Up Your Dev Environment

CNA Tutorial Series  ·  Beginner

ℹ

What you’ll learn

  • Installing the toolchain and system dependencies CNA needs.
  • Cloning CNA together with its sibling repositories and configuring the build.
  • Running the test suite to confirm the build is healthy.
  • Getting your IDE to resolve CNA headers.

Before you start — Tutorial 01: Introduction to CNA for context, though nothing here depends on it. You need a working C++ compiler and CMake before you begin.

System Requirements

These are the requirements this snapshot can actually back with evidence. CMake enforces a minimum CMake version and the C++23 language standard, but no compiler-version check exists anywhere in CNA’s build files, so the compiler row below records what CNA’s continuous integration builds with rather than a guaranteed minimum.

ComponentRequirement
CompilerA C++23 compiler whose standard library ships <format> (CNA’s always-built content-pipeline module uses it), so a GCC older than 13 will not do. CI builds Linux with GCC 14 on ubuntu-24.04, macOS with AppleClang on macos-26, Windows with MSVC (windows-latest, manual workflows) and mingw-w64 GCC, and WebAssembly with emsdk 6.0.3. Clang on Linux is not exercised by any build workflow.
Build systemCMake 3.20 or newer. CMake 3.27 or newer is needed for the default one-libcna.so link layout (CNA_SHARED_LIBRARY) on native Linux/BSD builds with GCC or Clang; older CMake falls back to static archives.
GitAny recent version; CNA needs five submodules (see below).
GPU driverOpenGL ES 3.0 or OpenGL 3.0+ for the OPENGLES3 renderer. CI runs the GL and Vulkan renderers on Mesa software drivers under Xvfb, not on physical GPUs.
RAMNo minimum is published. Template-heavy translation units are memory-hungry; if the compiler is killed for lack of memory, lower the parallelism (-j2) for that build.
DiskPlan for many gigabytes. The default configuration builds every test and example program; CNA’s own build notes measured about 104 MB per statically linked Debug executable across roughly 800 executables before the shared libcna.so layout existed. Build a specific target (below) rather than everything, and the vendored SDL is cached in the source tree (.sdl-prebuilt-*).
OSLinux (CI: Ubuntu 24.04), Windows (MSVC or MinGW-w64; native Windows lanes are manual), macOS 13.3 or newer (a hard floor: CMake stops below it). iOS and Android are experimental or code-only; see Tutorial 80.

SDL3, SDL3_image, and SDL3_mixer are compiled from vendored submodules. You do not need to install them from your OS package manager — but the development packages for X11, Wayland, OpenGL and audio must already be installed when SDL is first built, or the vendored SDL is built without those drivers (the configure output prints the video drivers it ended up with). Installing packages later does not change an existing .sdl-prebuilt-* cache; delete that directory and reconfigure.

ⓘ

FFmpeg is optional. CNA_ENABLE_VIDEO is AUTO by default: if the libavcodec, libavformat, libavutil and libswresample development packages are found, Video/VideoPlayer get a real decoder; if not, configure still succeeds and those types throw NotSupportedException when used. Pass -DCNA_ENABLE_VIDEO=OFF to skip FFmpeg entirely, or ON to make its absence an error. FFmpeg is never built for Windows, Emscripten, Android or iOS.

Installing Dependencies

Ubuntu / Debian

This is the package set CNA’s own Linux CI installs on Ubuntu 24.04 (the minimal sufficient subset has not been measured). It covers the compiler, the GL/EGL/Vulkan headers the renderers need, the X11 and Wayland development packages the vendored SDL3 needs for its x11 and wayland video drivers, and ALSA/PulseAudio for audio:

sudo apt update
sudo apt install -y --no-install-recommends \
    g++-14 cmake ninja-build ccache git pkg-config \
    libgl1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev \
    libvulkan-dev mesa-vulkan-drivers \
    libx11-dev libxext-dev libxrandr-dev libxi-dev libxcursor-dev \
    libxfixes-dev libxss-dev libxtst-dev \
    libwayland-dev wayland-protocols libxkbcommon-dev \
    libasound2-dev libpulse-dev libudev-dev libdbus-1-dev \
    xvfb x11-utils

Optional: libavcodec-dev libavformat-dev libavutil-dev libswresample-dev for FFmpeg video (see the callout above), and libdecor-0-dev if you want the vendored SDL3 to include client-side Wayland decorations. xvfb lets the window-creating tests run on a machine without a display.

Tell CMake which compiler to use on the first configure (the compiler is fixed when a build directory is first configured):

export CC=gcc-14 CXX=g++-14
g++-14 --version    # Ubuntu 24.04's default g++ is 13; use the g++-14 package as CI does

Fedora / RHEL

CI runs Ubuntu only, so treat these as the equivalents of the list above, not as a tested recipe:

sudo dnf install -y \
    gcc gcc-c++ cmake git ninja-build ccache pkgconf-pkg-config \
    mesa-libGL-devel mesa-libEGL-devel mesa-libGLES-devel vulkan-loader-devel \
    libX11-devel libXext-devel libXrandr-devel libXi-devel libXcursor-devel \
    libXfixes-devel libXScrnSaver-devel libXtst-devel \
    wayland-devel wayland-protocols-devel libxkbcommon-devel \
    alsa-lib-devel pulseaudio-libs-devel systemd-devel dbus-devel

For optional FFmpeg video, install the headers and link libraries for libavcodec, libavformat, libavutil and libswresample (ffmpeg-free-devel on Fedora; ffmpeg-devel from RPM Fusion on RHEL and derivatives).

Arch Linux

sudo pacman -Syu base-devel cmake git ninja ccache mesa vulkan-headers vulkan-icd-loader \
    libx11 libxext libxrandr libxi libxcursor libxfixes libxss libxtst \
    wayland wayland-protocols libxkbcommon alsa-lib libpulse dbus
# optional FFmpeg video: sudo pacman -S ffmpeg

macOS

Install Xcode or the Command Line Tools (AppleClang; CI uses macos-26), then brew install cmake ninja ccache (add ffmpeg if you want video — CI installs it, but it is optional). The deployment target defaults to macOS 13.3 and cannot be lowered. The default renderer on macOS is SDL_RENDERER (2D only); METAL is the native Apple renderer (macOS and iOS). The bundled preset is cmake --preset macos.

Windows (MSVC)

  1. Install Visual Studio 2022 with the "Desktop development with C++" workload.
  2. Install CMake 3.20+ and add it to PATH.
  3. Install Git for Windows.

Open a "Developer Command Prompt for VS 2022" for all subsequent commands. CNA’s native MSVC workflows (Direct3D and the content pipeline) are manual-dispatch only, so treat a Windows build as something you verify yourself. MinGW-w64 cross-builds from Linux use the cmake/toolchains/mingw-w64.cmake toolchain file (see Tutorial 80). Video is not built on Windows.

Cloning CNA and its sibling repositories

CNA's companion libraries are separate checkouts placed next to cna/, not submodules. A default Linux build needs three of them, because the Linux default renderer is OPENGLES3, which is implemented by EasyGL. Two branch rules matter for this snapshot: CNA, sharp-runtime and meta-gl must be on their apple/m4-stabilization branch (the GitHub default branches are older — CNA’s develop is the alpha.1 tag, and sharp-runtime’s main lacks the Resources and Xml.Serialization components CNA now requires), while easy-gl uses its default develop.

SiblingNeeded for
sharp-runtimeEvery build, without exception
easy-glThe three GL-profile renderers: OPENGLES3, OPENGL33, WEBGL2
meta-glRequired by easy-gl
mkdir my-cna-workspace
cd my-cna-workspace

# sharp-runtime first (required by every build) -- its apple/m4-stabilization branch
git clone -b apple/m4-stabilization https://github.com/libcna/sharp-runtime.git

# EasyGL and its own dependency -- needed for the default Linux renderer
git clone https://github.com/libcna/easy-gl.git
git clone -b apple/m4-stabilization https://github.com/libcna/meta-gl.git

# CNA -- its apple/m4-stabilization branch (this snapshot is commit c1c316b9c7a846ce8002809c151fcd1af14942c9)
git clone -b apple/m4-stabilization https://github.com/libcna/cna.git

# Initialize CNA's five submodules: third_party/SDL, third_party/SDL_image,
# third_party/SDL_mixer, third_party/draco and vendor/googletest.
# Non-recursive is correct here, and much faster: --recursive only pulls
# codec submodules that CNA's build disables.
cd cna
git submodule update --init

To reproduce this exact snapshot rather than the moving tip of apple/m4-stabilization, run git checkout c1c316b9c7a846ce8002809c151fcd1af14942c9 in cna/ before the submodule step. The sibling branches move too; CNA’s CI pins no sibling revision but clones, for each sibling, the branch of the same name as the CNA branch being built (falling back to next, then develop), so a sibling that later breaks configure has to be rolled back by hand.

After this, your workspace should look like:

my-cna-workspace/
├── sharp-runtime/   ← C# primitive types and BCL subset for C++ (apple/m4-stabilization branch)
├── easy-gl/         ← the GL-profile renderer implementation
├── meta-gl/         ← required by easy-gl (apple/m4-stabilization branch)
└── cna/             ← the main CNA repository (apple/m4-stabilization branch)
    ├── CMakeLists.txt
    ├── CMakePresets.json
    ├── cmake/            ← CMake modules and toolchain files
    ├── modules/          ← core, math, platform, graphics, audio, content, renderers/, ...
    ├── tools/            ← cna-content and the converters
    ├── third_party/      ← SDL, SDL_image, SDL_mixer, draco (submodules); enet, stb, dr_libs, cgltf
    └── vendor/
        └── googletest/   ← submodule, needed while CNA_BUILD_TESTS=ON
⚠

Important: the sibling directories must sit next to cna/, not inside it. CMake looks for them at ../sharp-runtime, ../easy-gl and so on, relative to the CNA root (override sharp-runtime’s location with -DCNA_SHARP_RUNTIME_ROOT=<path>). The libcna and openeggbert GitHub organisations host identical copies of cna, sharp-runtime, easy-gl and meta-gl; CNA’s CI clones from libcna.

Building CNA

OPENGLES3 (recommended, full 2D and 3D)

From inside the cna/ directory. This is also what you get on Linux if you pass no renderer flag at all. The first configure builds SDL3, SDL3_image and SDL3_mixer from source into .sdl-prebuilt-<os>-<arch>/ (the key can carry -wayland, -simulator and -min<version> suffixes) inside the CNA checkout; later configures and clean build directories reuse it while its recorded build manifest still matches:

cmake -S . -B build \
    -DCNA_GRAPHICS_RENDERER=OPENGLES3 \
    -DCMAKE_BUILD_TYPE=Debug

# A small first target -- the math-type tests -- to confirm the toolchain works:
cmake --build build --target CnaMathTests -j$(nproc)

# The whole aggregate test binary is large; build it when you need it:
cmake --build build --target CnaTests -j$(nproc)

On Linux with CMake 3.27 or newer and GCC or Clang, the engine is linked once into libcna.so and every executable links that library instead of carrying its own copy (-DCNA_SHARED_LIBRARY=OFF restores static linking).

SDL_RENDERER (2D only, fewest dependencies)

This one needs no easy-gl/meta-gl checkout, which makes it the quickest way to get a first build going — at the cost of 3D, which throws:

cmake -S . -B build-sdl \
    -DCNA_GRAPHICS_RENDERER=SDL_RENDERER \
    -DCMAKE_BUILD_TYPE=Debug

cmake --build build-sdl --target CnaMathTests -j$(nproc)
⚠

--target CNA does not work. CNA is an INTERFACE umbrella library with no sources of its own, so it is not a buildable target. Build a real target — CnaTests (or one of the 22 focused test targets such as CnaMathTests), a demo such as cna_demo_2d, or cna_content_tool — or just run cmake --build build with no --target at all (which builds every test and example). You will still find the old command in some upstream documentation.

Release build

cmake -S . -B build-release \
    -DCNA_GRAPHICS_RENDERER=OPENGLES3 \
    -DCMAKE_BUILD_TYPE=Release

cmake --build build-release -j$(nproc)

Windows (MSVC Developer Prompt)

cmake -S . -B build -DCNA_GRAPHICS_RENDERER=SDL_RENDERER
cmake --build build --config Debug --target CnaMathTests

SDL_RENDERER is the Windows default (2D only). For 3D on Windows choose a renderer explicitly, for example DIRECTX11; the two Windows-only renderers (DIRECTX9, DIRECTX11) configure only on a Windows target.

CMake options reference

OptionValuesDefault
CNA_GRAPHICS_RENDERERAny one of the 14 renderer identities, e.g. SDL_RENDERER, OPENGLES3, VULKAN, SDL_GPU, OPENGL33OPENGLES3 on Linux, WEBGL2 under Emscripten, SDL_RENDERER otherwise
CNA_PLATFORMSDL3, HEADLESS, TERMINAL (TERMINAL is POSIX-only)SDL3 — windows, events, input and host services; it reaches Windows, X11, Wayland and macOS through SDL’s own video drivers. HEADLESS and TERMINAL have no window.
CNA_AUDIO_PLATFORMSDL3, NULL, ALSA (Linux only)SDL3 — only SDL3 and ALSA provide a mixer (SOUND_ENABLED)
CNA_ENABLE_SDLAUTO, ON, OFFAUTO — OFF builds without SDL and refuses any selection that needs it
CNA_ENABLE_VIDEOOFF, AUTO, ONAUTO — FFmpeg video only when its four development packages are found
CMAKE_BUILD_TYPEDebug, Release, RelWithDebInfo(empty) — CNA sets no default, so pass it explicitly (the presets do)
CNA_BUILD_TESTSON, OFFON
CNA_CNAEXTON, OFFOFF — gates the CNAEXT extensions in CNA::Graphics (AsciiPostProcessEffect, CRTEffect, DepthEffect, DebugDraw, and the shader-package types)
CNA_DEVICESON, OFFOFF — gates the CNAEXT device layer
CNA_STRICT_XNA_APIcompile definition, not a CMake optionnot defined — define it on the target you want checked and CNAEXT-marked declarations become [[deprecated]] warnings (errors under -Werror=deprecated-declarations); passing -DCNA_STRICT_XNA_API=ON to the CMake configure step does nothing
CNA_ENABLE_NETON, OFFON — networking support (vendored ENet)
CNA_SHARED_LIBRARYON, OFFON on native ELF Linux/BSD with GCC/Clang and CMake 3.27+, otherwise OFF — link the engine once as libcna.so

Instead of naming the renderer with CNA_GRAPHICS_RENDERER you may turn on exactly one -DCNA_RENDERER_<NAME>=ON switch. The two forms are equivalent and must not be mixed. CNA also ships 17 CMake configure presets (build directories are cmake-build-<preset> inside the CNA checkout) — among them tests (Debug, OPENGLES3, tests and examples on), unit (the STUB renderer, tests only), multi-renderer, macos, ios, web (run it as emcmake cmake --preset web) and the sanitizer presets devices-asan, devices-tsan, devices-ubsan — usable with, for example, cmake --preset tests followed by cmake --build --preset tests.

The C++ framework has no general install/export package. C++ consumers bring it into their own build with add_subdirectory; see Tutorial 03. The experimental C layer (CNA_BUILD_C_API=ON, which also requires CNA_ENABLE_NET=ON) declares a separate CNACApi package; no CI job builds it, so it is not a getting-started path.

Running the Test Suite

In this snapshot CNA ships 813 C++ test source files containing 11,380 statically discoverable GoogleTest-family definitions, covering math types, geometry, curves, packed vectors, game loop semantics, renderer cases, and more (alpha.1: 568 and 8,263). The tests compiled into a build depend on the selected renderer, platform, audio implementation, options, and host. Two ways to run them:

# 1. A focused suite: one of the 22 per-module test executables
cmake --build build --target CnaMathTests -j$(nproc)
./build/CnaMathTests

# 2. The aggregate binary, run directly with a GoogleTest filter.
#    Window-creating cases need a display; on a headless machine use xvfb-run.
cmake --build build --target CnaTests -j$(nproc)
SDL_AUDIODRIVER=dummy xvfb-run -a ./build/CnaTests --gtest_filter='Vector*'

The focused targets are CnaAudioTests, CnaContentTests, CnaContentPipelineTests, CnaCoreTests, CnaDesignTests, CnaDiagnosticsTests, CnaDevicesTests, CnaDevicesExtTests, CnaGamerServicesTests, CnaGraphicsTests, CnaGraphicsExtTests, CnaInputModuleTests, CnaInspectorTests, CnaIntegrationTests, CnaMathTests, CnaMediaTests, CnaNetTests, CnaPhoneTests, CnaPlatformModuleTests, CnaRendererTests, CnaRuntimeTests and CnaStorageTests; a few exist only when their module is built (for example CnaInspectorTests needs -DCNA_BUILD_INSPECTOR=ON). CNA’s own tests preset describes running the CnaTests binary directly as the authoritative way to run the full suite. CTest also works for individual cases, because every GoogleTest case is registered by name:

# Run one case or family through CTest (window-creating cases need a DISPLAY)
ctest --test-dir build -R Vector2 --output-on-failure

# Run with verbose output, or show only failures
ctest --test-dir build -R Vector2 -V
ctest --test-dir build -R Vector2 --output-on-failure -Q
⚠

Do not run a bare ctest after building only CnaTests. CTest starts one process per case, and the registered inventory also includes many separate example and smoke executables that the CnaTests target does not build, so an unfiltered run reports those as failures. Some cases also share fixture paths and can race when run in parallel. Tests inherit the DISPLAY of the shell that launches them (CNA_TEST_DISPLAY is empty by default, and the live desktop display :0 is honoured only with -DCNA_TEST_ALLOW_LIVE_DISPLAY=ON), which is why the commands above use xvfb-run on a headless machine.

A passing run ends with GoogleTest’s summary ([ PASSED ] N tests.) or, under CTest, 100% tests passed, 0 tests failed out of N. The exact count depends on which renderer you configured, because renderer tests are registered only for the family you compiled. If something fails on a clean build, please open an issue with your renderer, platform and audio selections.

ⓘ

CTest labels use the renderer names, so ctest -L DIRECTX9 and ctest -R DIRECTX11 work on a configuration that includes those renderers, while the older -L D3D9 / -R D3D11 forms match nothing at all.

IDE Setup

VS Code

  1. Install the C/C++ extension and the CMake Tools extension.
  2. Open the cna/ folder in VS Code.
  3. CMake Tools will detect CMakeLists.txt automatically. Select your kit (GCC 14, or another C++23 compiler whose standard library ships <format>).
  4. In the status bar, choose the renderer: click "No Configure Preset" and add a configure preset, or use the CMake Tools settings to pass -DCNA_GRAPHICS_RENDERER=OPENGLES3.
  5. Press F7 to build, or click the build button in the status bar.

For IntelliSense to find CNA headers, CMake Tools uses the compile_commands.json that CNA exports by default (CNA_EXPORT_COMPILE_COMMANDS is ON with the Ninja and Makefile generators). If IntelliSense is confused, run "CMake: Configure" from the command palette.

CLion

  1. Open the cna/ directory as a CLion project.
  2. CLion auto-detects CMakeLists.txt. Go to Settings → Build, Execution, Deployment → CMake.
  3. Add a CMake profile and set CMake options: -DCNA_GRAPHICS_RENDERER=OPENGLES3.
  4. Choose the toolchain (GCC or Clang). Make sure it points to a C++23-capable version.
  5. Build with Ctrl+F9.

CLion's indexer will pick up CNA's headers (each module keeps its own modules/<name>/include/ directory) automatically once the CMake project is configured.

Neovim / Emacs (compile_commands.json)

CNA's CMake build exports build/compile_commands.json by default with the Ninja and Makefile generators (CNA_EXPORT_COMPILE_COMMANDS, on by default). Point your LSP (clangd) at it:

# Generate compile_commands.json (Ninja; the Makefile generator works too)
cmake -S . -B build -G Ninja -DCNA_GRAPHICS_RENDERER=OPENGLES3

# Symlink to project root for clangd
ln -s build/compile_commands.json compile_commands.json

clangd will then provide completion and diagnostics across all CNA headers.

ⓘ

The first configure takes several minutes because CMake builds SDL3 from source (no timing has been measured for this snapshot). Subsequent incremental builds are much faster, and if ccache is installed CNA uses it automatically (CNA_USE_CCACHE). Use -j$(nproc) (Linux) or --parallel (Windows/macOS) to parallelize, and lower it if the compiler runs out of memory.

With your environment set up and the tests passing, you are ready to write your first CNA game. Continue to Tutorial 03: Your First CNA Window.