Build Guide
Every way to build the game: Linux desktop, the Linux bundle, WebGL 2, Android and Windows, plus tests and CI.
There is one game target, GalaxyEggbertCNA, defined in the root
CMakeLists.txt and built on CNA and cna-engine. Every platform uses that same file;
only the toolchain and the renderer change. Prerequisites and the sibling checkouts are on
Getting Started; every option is on
Configuration. Status as of 2026-11-10.
| Build | Renderer | Usual build directory | Status |
|---|---|---|---|
| Linux desktop | OPENGL33 or OPENGLES3 | build-cna-opengl3/ | Verified in CI and live |
Linux runtime bundle (.tar.gz) | as the desktop | any Linux tree | Builds launch is a host check, not a CI gate |
| Web (Emscripten) | WEBGL2 | build-cna-webgl2/ | Manual verified in headless Chrome, not in CI |
| Android (Gradle) | OPENGLES3 | android/app/build/ | Emulator builds and runs, not played on a device |
| Windows (MinGW) | SDLGPU planned | build-windows/ | Not verified |
| Release packages | Linux archive + Android APK | a Release desktop tree | Published 0.4.0 on 2026-11-12 (a GitHub release) |
Before you configure
- CMake finds CNA and cna-engine at
../cnaand../cna-engine, or where-DCNA_HOME/-DCNA_ENGINE_HOMEpoint. CNA builds statically, without its own tests and examples; the game forces those settings. - The first configure of a tree builds CNA's vendored SDL3 and SDL3_mixer (several minutes). Later configures reuse it.
- With
BUILD_TESTINGon (the default) on desktop, CMake fetches GoogleTest v1.14.0 unless-DFETCHCONTENT_SOURCE_DIR_GOOGLETEST=<checkout>names a local copy. - Optional speed-up: if ccache is installed, pass
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache(CI does). CNA's ownCNA_USE_CCACHEoption defaults to OFF in this project. - Reuse a build directory once it exists; reconfigure only when it breaks or the configuration really changes. Use a separate, clean directory for each toolchain.
Linux desktop
cmake -S . -B build-cna-opengl3 -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DCNA_GRAPHICS_RENDERER=OPENGL33
cmake --build build-cna-opengl3 --target GalaxyEggbertCNA --parallel 4
GALAXY_EGGBERT_BUILD_CNA is ON by default, so it can be left out. Without
CNA_GRAPHICS_RENDERER a native build uses OPENGLES3 (OpenGL ES 3);
OPENGL33 is desktop OpenGL 3.3 core. Both belong to CNA's EasyGL family and both are
verified on Linux; the other identities are on Platform Support.
After every link a post-build step copies Content/, worlds3d/,
textures3d/ and avatars3d/ next to the executable, changed files only
(cmake/CopyDirectoryIfDifferent.cmake). That is why the game runs from its build
directory (Running the Game).
Useful targets
| Target | What it builds |
|---|---|
GalaxyEggbertCNA | The game, output name GalaxyEggbert. |
all (no --target) | The game plus every test executable (Verify*, GalaxyEggbertWorldsTests) and the tools. |
VerifyShippedWorlds, DumpBlockDefinitions, GenerateSampleWorld3D | World and registry tools, also used by the tests (Assets & Tools). |
package | The Linux runtime bundle (below). |
GalaxyEggbertDocs | Doxygen API documentation; exists only when Doxygen is installed. |
Without cheats
Typed cheats, F12 and the hidden cheat menu are compiled in by default. For a build without them:
cmake -S . -B build-cna-opengl3 -DGALAXY_EGGBERT_ENABLE_CHEATS=OFF
cmake --build build-cna-opengl3 --target GalaxyEggbertCNA --parallel 4
See Cheats & Diagnostics for what the switch removes.
Linux runtime bundle
The package target (CPack, TGZ) makes a relocatable x86-64 archive,
GalaxyEggbert-<version>-linux-x86_64.tar.gz (the version from VERSION.txt,
e.g. GalaxyEggbert-0.4.0-linux-x86_64.tar.gz), in the build directory. Configure the tree as
Release. README.md does this in a separate build-package directory; the
same target in the desktop tree, as below, should work equally but has not been run separately:
cmake -S . -B build-cna-opengl3 -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build-cna-opengl3 --target package --parallel 4
tar -xzf build-cna-opengl3/GalaxyEggbert-0.4.0-linux-x86_64.tar.gz
./GalaxyEggbert-0.4.0-linux-x86_64/run-galaxy-eggbert
The archive holds the executable, the launcher run-galaxy-eggbert (it enters its own
directory and starts the game with your arguments), Content/, worlds/,
worlds3d/, textures3d/, avatars3d/, the SDL3, SDL3_image and
SDL3_mixer shared libraries in lib/ (found through the RPATH
$ORIGIN/lib), their licenses in licenses/SDL/, LICENSE,
README-Linux.md (from packaging/README-linux-bundle.md), and since 0.1.0
THIRD_PARTY.txt, RELEASE_NOTES.md (both from packaging/) and
VERSION.txt.
It is a runtime bundle, not an AppImage: the host still supplies the graphics stack, FFmpeg and the
C++ runtime. Turn the target off with -DGALAXY_EGGBERT_ENABLE_LINUX_BUNDLE=OFF; it is
never defined for Emscripten, Android or Apple builds. The archive needs the glibc of the system it
was built on or newer: built on Debian 13 it needs glibc 2.38. An archive for older systems has to be
built on one.
Release packages
tools/release/make_release.sh makes a release's packages in one folder. It needs a
configured Release desktop tree (it stops otherwise):
tools/release/make_release.sh build-cna-opengl3 <out-dir> # Linux archive + Android APK
tools/release/make_release.sh build-cna-opengl3 <out-dir> --no-android # Linux archive only
- Version: the first line of
VERSION.txt(0.4.0as of 2026-11-12). CMake puts it in the archive's name, and Gradle uses it as the APK'sversionName. - Linux: builds
GalaxyEggbertCNAand thepackagetarget, movesGalaxyEggbert-<version>-linux-x86_64.tar.gzto the output folder and prints the highest glibc version that the program and its bundled libraries ask for. - Android:
./gradlew assembleRelease -PgalaxyAbis=arm64-v8a, signed with the release key named inandroid/key.properties(git-ignored, like the keystore), copied asGalaxyEggbert-<version>-android-arm64.apk.-PgalaxyAbischooses the ABIs. The test-onlyandroid.injected.build.abiproperty is not used for releases. - Notes and checksums:
packaging/RELEASE_NOTES.mdis copied beside the packages andSHA256SUMS.txtis written. - Windows is not made by the script: its cross-build has not been run yet.
What the packages need and how to install them: Releases.
Tests
cmake --build build-cna-opengl3 --parallel 4 # everything, tests included
ctest --test-dir build-cna-opengl3 --output-on-failure
ctest --test-dir build-cna-opengl3 -N # list the tests
ctest --test-dir build-cna-opengl3 -R VerifyShippedWorlds
ctestneeds the whole tree built: eachVerify*program andGalaxyEggbertWorldsTests(GoogleTest, one entry per case) is its own target. Once built, the suite runs in seconds. On 2026-11-10 the maintained tree passed 120 of 120.VerifyMoveObjectTypesCnaandVerifyBigDecorParsingCnaneed themobile-eggbertsibling (itsworlds/folder).VerifyDemoReplaysreplays the shipped main-menu demos in the real game under Xvfb; it is registered only whenxvfb-runis found at configure time.- Tests that write files run in scratch folders of the build tree (
verify-run/,verify-savedata/,verify-demos/), never in your player folder. - The last few entries (
easy-gl-*) come from the easy-gl dependency; a failure there is a real signal too.
Golden frames, the movement trace and the autoplay playthroughs are separate, manual checks: Testing.
Web (Emscripten, WebGL 2)
Install and activate the Emscripten SDK, then:
source <emsdk>/emsdk_env.sh
emcmake cmake -S . -B build-cna-webgl2 \
-DCNA_GRAPHICS_RENDERER=WEBGL2 -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF
cmake --build build-cna-webgl2 --target GalaxyEggbertCNA --parallel 4
WEBGL2 is also the default under Emscripten. The link uses the page template
cmake/web/shell-cna.html (a canvas and a full-screen button), WebGL 2 only, a 256 MB
initial heap that may grow, IDBFS for saves, and CNA's Asyncify policy so the blocking
Game::Run() yields to the browser between frames.
| Output | Contents |
|---|---|
GalaxyEggbert.html | The page to open. |
GalaxyEggbert.js, GalaxyEggbert.wasm | Emscripten glue and the game. |
GalaxyEggbert.data | Preloaded, read-only: Content/icons, Content/backgrounds, Content/sounds, Content/demos, worlds3d, textures3d, avatars3d. |
Content/music/ | Not packed (27 MB): staged beside the page and fetched a track at a time when a world first plays it. |
To publish the game, copy the four GalaxyEggbert.* files and the
Content/music/ folder; without the folder the game runs silent of music. Serve them
over HTTP (a file: URL is not a valid test). Saves and the player's worlds live in
/save, an IndexedDB-backed folder. The web build carries only the 1x textures.
Running it: Running the Game.
Android (Gradle)
| Tool | Version |
|---|---|
| Android SDK | API 35 (compile and target); minimum API 24, Android 7.0 |
| NDK | 29.0.14206865 (ndkVersion in android/app/build.gradle) |
| CMake (SDK Manager) | 4.1.2 (pinned in the Gradle file) |
| JDK | 17 or 21 |
cd android
./gradlew assembleDebug # arm64-v8a and x86_64
./gradlew assembleDebug -Pandroid.injected.build.abi=x86_64 # emulator ABI only, faster
- Gradle builds the root
CMakeLists.txt, targetGalaxyEggbertCNAonly, aslibmain.so; SDL'sSDLActivityloads it and callsSDL_main, which CNA'sCNA/Platform/Entrypoint.hppmakes ofmain(). The SDL Java glue comes from CNA's own SDL copy, so the two always match. - CNA and cna-engine:
cna.home/cna.engine.homeinandroid/local.properties(template:local.properties.template), else the environment'sCNA_HOME/CNA_ENGINE_HOME, else the siblings. - Default ABIs:
arm64-v8aandx86_64(abiFilters). The debug APK lands inandroid/app/build/outputs/apk/debug/app-debug.apk; a build with the injected ABI is test-only and lands inapp/build/intermediates/apk/debug/. android/app/src/main/assets/linksContent/,worlds3d/,avatars3d/andtextures3d/; a generatedassets.txtlists them with a stamp so the game copies them into internal storage only when they changed (src/GalaxyEggbertCNA/AndroidAssets.cpp). The 2x texture folders are left out.- The application id is
org.openeggbert.galaxyeggbert, so it installs beside Mobile Eggbert's app. - Release:
./gradlew assembleReleaseneedsandroid/key.propertiesand a keystore, both git-ignored; never commit them.ANDROID.mdhas the steps. Add-PgalaxyAbis=arm64-v8afor a phone-only APK, as the release script does.versionNamecomes fromVERSION.txt.
Windows (MinGW)
Native, with a MinGW toolchain such as CLion's bundled one:
cmake -S . -B build-windows -DGALAXY_EGGBERT_BUILD_CNA=ON
cmake --build build-windows --target GalaxyEggbertCNA
Cross-build from Linux with MinGW-w64 (sudo apt install mingw-w64), in a clean directory:
cmake -S . -B build-windows \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64.cmake \
-DCNA_GRAPHICS_RENDERER=SDLGPU -DBUILD_TESTING=OFF
cmake --build build-windows --target GalaxyEggbertCNA --parallel 4
README.mdandWINDOWS.mdspell the rendererSDL_GPU. CNA renamed it toSDLGPU, also in the revision CI pins, and refuses the old spelling at configure time.- The project's default renderer for every non-web build is
OPENGLES3, so the native command above selects it unless you pass another (SDLGPUandDIRECTX9are CNA's Windows options). - On MinGW the game links the GCC and C++ runtime statically and copies
libwinpthread-1.dll,SDL3.dll,SDL3_image.dllandSDL3_mixer.dllnext toGalaxyEggbert.exe(if(MINGW)inCMakeLists.txt). - The toolchain file accepts
CNA_WINDOWS_DEPENDENCIES_ROOTfor prebuilt Windows SDL packages; the earlier run also neededZLIB_INCLUDE_DIR/ZLIB_LIBRARYoverrides (recorded inREADME.md).
Continuous integration
.github/workflows/cna-ci.yml runs on pushes and pull requests to develop
and master (changes under web/ alone do not trigger it) and on demand. On
ubuntu-latest it checks out the game, cna (with its submodules, not
recursive), cna-engine, easy-gl, sharp-runtime and
meta-gl at pinned revisions plus the worlds/ folder of
mobile-eggbert; installs the packages listed on Getting
Started; caches the SDL build and ccache; configures -B build-cna with the ccache
launchers; builds everything; and runs ctest --output-on-failure. Every test runs.
Not in CI: golden frames, the movement trace, autoplay playthroughs, the web and Android builds and
the bundle. python3 tools/ci_pin_siblings.py moves the pins to your local sibling
revisions; --check only reports drift. A second workflow, pages.yml,
deploys this website from web/.
Related pages
- Tutorial: Building - the short walkthrough.
- Configuration - all options and variables.
- Engine Integration - how the game uses CNA and cna-engine.
- Project Structure - where the sources and assets are.