Bugs and gaps in repositories this template consumes. Those repositories are read-only from here: nothing below has been patched upstream.
Re-audited: 2026-08-11, against:
| Repo | Branch | SHA |
|---|---|---|
| cna | develop | 7a64362efef4119bf880459ef1704fb2c52199e2 |
| sharp-runtime | develop | f827a6c5349234d5ac938886788ed8eca8fe1c10 |
| easy-gl | develop | 0b46d35c394a9fb6aea6a85c6587894b5013da33 |
| meta-gl | develop | 571d3a62fe166b9781ac6193d137b12ff3757620 |
Every entry carried over from the previous audit was re-verified rather than assumed. Entries that turned out to be fixed, obsolete or simply wrong are kept below, marked as such, because knowing a thing was checked is worth as much as the finding itself.
Severity: blocks every downstream consumer.
CNA is designed to be consumed with add_subdirectory(), which makes
CMAKE_SOURCE_DIR the consumer's top-level directory, not CNA's. Three
places in CNA's production build use it as if it meant CNA's own root:
| Location | Effect on a consumer |
|---|---|
modules/CMakeLists.txt:188 |
Aborts the configure if the consumer has a top-level src/ or include/ directory, with the message "legacy global 'src/' tree reappeared at the repository root". |
modules/content/CMakeLists.txt:21 |
cna_content compiles with the wrong cgltf include path → fatal error: cgltf.h: No such file or directory. |
modules/content/CMakeLists.txt:22 |
Same for stb. |
The first was introduced by cna commit 33088cba6 ("build(modules): localize
module CMake ownership"). There is no option to disable any of them.
This is not theoretical, and it is not specific to this template. The sibling
cna-examples repository has a top-level src/ and does
add_subdirectory(../cna CNA_BUILD), so it is broken the same way;
mobile-eggbert has both src/ and include/.
Fix upstream: resolve these against CNA's own root rather than the build's
top level — ${CMAKE_CURRENT_SOURCE_DIR}/.. from modules/, or a variable set
once at CNA's top level (CNA_SOURCE_DIR), or PROJECT_SOURCE_DIR guarded for
the nested case.
What this template does meanwhile (both visible, neither hides a failure):
- Keeps application sources in
game/srcandgame/include, with a preflight check inCMakeLists.txtthat explains the real cause if a top-levelsrc/orinclude/reappears. - Adds CNA's real
third_party/cgltfandthird_party/stbpaths withinclude_directories()beforeadd_subdirectory(), since subdirectories inherit that directory property. This repairs the search path without touching CNA.
Also present, but only reachable with CNA_BUILD_TESTS=ON (which the template
forces OFF), so not currently blocking: cmake/Harnesses.cmake:205-211 and
cmake/UnitTests.cmake:323+ build helper-script paths from
${CMAKE_SOURCE_DIR}/scripts/..., plus many modules/*/examples/CMakeLists.txt.
CNA propagates CNA_RENDERER_<X> to consumers through the cna_build_flags
INTERFACE target (modules/CMakeLists.txt:62-70), but applies
CNA_GL_PROFILE_<X> with a directory-scoped add_compile_definitions()
(cmake/RendererSelection.cmake:560) that never reaches a parent-scope consumer.
All three GL renderers (OPENGLES3, OPENGL33, WEBGL2) share the define
CNA_RENDERER_EASYGL, and the profile is what tells them apart. Without it,
CNA::getCurrentGraphicsRendererName() compiled in a consumer's translation
unit falls through to its OPENGLES3 default
(modules/core/include/CNA/GraphicsRendererType.hpp:176). So on a WEBGL2 or
OPENGL33 build, the application reports "OPENGLES3" while CNA's own
translation units report the truth — a wrong answer and an ODR hazard across
the same binary.
Fix upstream: add CNA_GL_PROFILE_${CNA_GRAPHICS_RENDERER} to
cna_build_flags alongside ${CNA_RENDERER_DEFINE}.
What this template does meanwhile: re-applies the profile define to its own
target when the selected renderer is in the GL family (CMakeLists.txt).
cna/README.md §10 (the example at :588-600) still tells readers to call
device.Present() inside Draw(). It should not: Game::EndDraw() already
presents exactly once per frame, matching real XNA/FNA. Presenting twice makes
SDL show an invalid backbuffer, and the window flickers every frame. This
template's own HelloGame originally inherited the bug from that example.
Re-checking the snippet this time turned up two further defects in the same ~12 lines:
- it includes
"Microsoft/Xna/Framework/Graphics/GraphicsDeviceManager.hpp", but the real header has noGraphics/component; - it uses an unqualified
CornflowerBlue; onlyColor::CornflowerBlueexists (modules/graphics/include/Microsoft/Xna/Framework/Color.hpp:108).
As written, §10 does not compile.
cna/scripts/check_renderer_identities.pysays "exactly 42" in its docstring (:4,:19) while its own table holds 46. The table is right.- Several
plan_*.mdfiles still describeCNA_GRAPHICS_BACKENDand referencecmake/BackendSelection.cmake/cmake/BackendLibraries.cmake, neither of which exists any more.
Cosmetic, but they are exactly the sort of stale prose that misled this template before.
The modular sharp-runtime creates the compatibility target SHARP_RUNTIME
only when component All is selected
(sharp-runtime/cmake/SharpRuntimeComponents.cmake:337-365). CNA's framework
modules correctly use cna_link_sharp_runtime() and component targets, but
cmake/ToolGltfToCnj.cmake:11-15 still links the old name directly. The tool is
built unconditionally. Harnesses.cmake and UnitTests.cmake contain more of
the same pattern behind disabled template options.
Once this template selected CNA's narrow component closure, a full Emscripten build therefore reached the final tool link and failed with:
wasm-ld: error: unable to find library -lSHARP_RUNTIME
Fix upstream: migrate these targets to cna_link_sharp_runtime() or the
specific SharpRuntime::<Component> targets they consume.
What this template does meanwhile: after CNA creates the selected component
targets, it supplies a compatibility SHARP_RUNTIME INTERFACE target over
CNA's own default component list. This preserves every legacy CNA tool without
re-enabling All.
cmake/ThirdPartySDL.cmake:20-31 chooses
${CMAKE_CURRENT_SOURCE_DIR}/.sdl-prebuilt-<target> and then configures, builds
and installs SDL there during the parent CMake configure. This fails when CNA is
consumed from a read-only checkout even though both the consumer source and
binary directories are writable.
CNA_SDL_PREBUILT_ROOT is a working escape hatch. The local web verification
used a persistent writable directory under cna-template/build/. The template
now makes that its default for every target, while preserving an explicit
caller value. Upstream should default to a user
cache or binary-tree location, or at least detect a non-writable CNA source and
choose one, while retaining the explicit cache override.
Previously filed against sharp-runtime/CMakeLists.txt:5; that line no longer
exists. After modularization the same call is at
sharp-runtime/modules/io-compression/CMakeLists.txt:5. It is correctly scoped
to IO.Compression, but sharp-runtime defaults to component All
(sharp-runtime/CMakeLists.txt:49-53) and CNA sets its actual component needs
only after add_subdirectory(sharp-runtime). Every embedded CNA consumer
therefore reaches find_package(ZLIB REQUIRED) unless it anticipates CNA's
needs before adding CNA.
Status: reproduced, then resolved at the consumer integration seam. The raw failing probe was:
cmake -S . -B build-probe --toolchain cmake/toolchains/mingw-w64.cmake \
-DCNA_GRAPHICS_RENDERER=SDL_RENDERERfails with Could NOT find ZLIB (missing: ZLIB_LIBRARY) (found version "1.3.1")
from sharp-runtime/modules/io-compression/CMakeLists.txt:5, because
find_package(ZLIB) resolves the host's zlib.h (version string) but not a
MinGW-targeted .a, and CMAKE_FIND_ROOT_PATH in
cna-template/cmake/toolchains/mingw-w64.cmake does not invent a dependency
prefix. Debian's installed libz-mingw-w64 package contains runtime DLLs only,
not a development import/static archive. That lets the host header half-satisfy
the version probe and produces the confusing "found version 1.3.1" in an
otherwise-failing message.
Supplying a previously built Windows-target zlib proved that no later compiler
blocker was hidden behind the configure failure, but it was not the final
solution. The template now includes CNA's own
cmake/SharpRuntimeConsumption.cmake before add_subdirectory(CNA) and places
its CNA_SHARP_RUNTIME_DEFAULT_COMPONENTS into
SHARP_RUNTIME_COMPONENTS—only when the application has not made an explicit
selection. The final clean-cache-equivalent command needed no prefix:
cmake -S . -B build-probe --toolchain cmake/toolchains/mingw-w64.cmake \
-DCNA_GRAPHICS_RENDERER=SDL_RENDERER
cmake --build build-probe --parallel 3That configured without any ZLIB cache entry, compiled all 435 steps and linked
HelloGame.exe. The same selection reduced and unblocked both Emscripten
builds. This is still an upstream ordering issue: CNA already owns the right
list, but must apply it before adding sharp-runtime. Once it does, the template
preload can be removed.
modules/io/src/System/IO/RandomAccess.cpp:91-93 defines NativeDetail() and
ThrowNative() outside the platform branches. Every use of ThrowNative() is
inside _WIN32 or POSIX branches; Emscripten's branches throw
PlatformNotSupportedException directly. Clang therefore reports
ThrowNative as unused, and the component's own -Werror promotes it to a
build failure.
Fix upstream: guard the native helpers with #if !defined(__EMSCRIPTEN__)
or mark the intentionally unavailable helper [[maybe_unused]].
What this template does meanwhile: append
-Wno-error=unused-function to this one source in target sharp_runtime_io on
Emscripten. The warning remains visible; only its promotion to an error is
disabled. No warning policy is weakened for the application, CNA, or any other
Sharp Runtime source.
mobile-eggbert/CMakeLists.txt:88 still has it. Separately, mobile-eggbert is
now stale in its own right (last commit 2026-07-16, still using
CNA_GRAPHICS_BACKEND), so it is no longer a safe structural model for this
template. It was used as one historically; it should not be again without
checking every claim against CNA first.
Fixed, and improved since: modules/graphics/src/Xna/GraphicsDevice.cpp:415-425
now masks depth and stencil independently via SupportsDepthBuffer() /
SupportsStencilBuffer() (:492-504). Clear(const Color&) is safe on every
renderer. The template no longer carries a workaround.
Fixed in modules/runtime/src/GraphicsDeviceManager.cpp:60-79. The fix survived
the modularization, and its comment still cites this file by name.
Obsolete: the variable itself is gone (renamed CNA_GRAPHICS_RENDERER), and
WEBGPU is one of CNA's public renderer identities
(CNA_RENDERER_PUBLIC_IDENTITIES in cmake/RendererIdentities.cmake).
Wrong, and the previous audit had already half-corrected itself in place.
CMake functions are global once defined, so everything CNA include()s is
callable from the parent scope after add_subdirectory().
cna_copy_sdl_runtime() (cmake/ThirdPartySDL.cmake:323),
cna_copy_mingw_runtime() (:241) and cna_copy_mingw_cxx_runtime() (:290)
are callable; cna-samples relies on exactly that. There is one important
scope detail: the helper can copy only imported targets visible in the caller's
directory. CNA creates SDL3, SDL3_image and SDL3_mixer inside its own
subdirectory, so the template must re-run all three find_package() calls in
the parent scope before invoking the helper. Re-importing only SDL3 was proven
insufficient: HelloGame.exe imported SDL3_image.dll and SDL3_mixer.dll,
but neither was packaged. The template now re-imports and copies all three.
The old entry's stated reason ("functions defined via include() while CNA is
processed as our subdirectory are not visible back in this top-level scope") was
simply false, and it had been copied into CMakeLists.txt as a comment. Both are
now corrected.
Listed so the next session does not mistake silence for a passing result:
- The Android build was not executed: no Android SDK/NDK on this machine. CNA's
own docs disagree with each other about its current state —
cna/docs/android-graphics-limitations.md:24-50reports the NDK cross-compile failing inside sharp-runtime, whilecna/docs/devices-build.md:270-386records an APK that built and ran on an emulator on 2026-07-05. - Emscripten 4.0.7 compiled and linked a complete
WEBGL2bundle, but no browser was available to this Codex session, so page startup and frame rendering were not observed. CI's pinned Emscripten 6.0.3 was not available locally either. - The MinGW
HelloGame.exeand its PE dependency package were inspected but not executed on Windows or under Wine.