feat(bgfx): compile bx/bgfx/bimg for Android via NDK sysroot (phase 1, #300) - #304
Conversation
Phase 1 of bgfx-on-Android bring-up. When the target ABI is Android, feed the NDK sysroot system-include paths, arch/API library path, and __ANDROID_API__ to all three zbgfx C/C++ libs (bx, bimg, bgfx) so they find Bionic's <stdlib.h> etc. — without this, Zig's bundled libc++ headers fail with 'unknown type name ldiv_t'. - Detect the sysroot the same way sokol-Android does (getAndroidNdkSysroot/ndkHostTag, reading ANDROID_NDK_HOME / ANDROID_HOME), copied into this build.zig. - zbgfx builds bx/bimg/bgfx as three separate *Compile artifacts but only exposes 'bgfx'; reach bx/bimg by walking bgfx's link_objects (other_step) and apply the sysroot to each, since include paths don't propagate across linkLibrary. - Use __ANDROID_API__=28 (toolkit default min_sdk): bx's file.cpp uses stdout/stderr, real Bionic symbols only from API 23 (below that they alias __sF[], marked __REMOVED_IN(23)). - Isolate from desktop-only zglfw (phase 2, #301): for an Android target install ONLY the bgfx artifact and skip the zglfw-dependent modules/glfw artifact. Desktop builds unchanged. Verified: 'zig build -Dtarget=aarch64-linux-android' and '-Dtarget=x86_64-linux-android' compile bx/bgfx/bimg clean (real aarch64 ELF objects); host 'zig build test' and example 'zig build' on macOS still pass.
PR SummaryMedium Risk Overview When the target is Android, the build resolves the NDK sysroot ( For Android phase 1, the build installs only Reviewed by Cursor Bugbot for commit 47f9ad7. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Code Review
This pull request implements phase-1 of Android support for the bgfx backend in build.zig by resolving and applying the Android NDK sysroot paths to the bgfx C/C++ compilation modules. Feedback on these changes highlights a compile error in the tagged union comparison of LinkObject (which should use a switch statement instead of ==) and recommends mapping the .thumb architecture to the 32-bit ARM triple to prevent unexpected compile-time panics.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| for (bgfx_artifact.root_module.link_objects.items) |lo| { | ||
| if (lo == .other_step) { | ||
| applyNdkSysroot(lo.other_step.root_module, inc_common, inc_arch, lib_path, android_api); | ||
| } | ||
| } |
There was a problem hiding this comment.
In Zig, 'std.Build.Module.LinkObject' is a tagged union. Comparing a tagged union directly with an enum literal using '==' (e.g., 'lo == .other_step') is a compile error. Additionally, accessing 'lo.other_step' directly without pattern matching is not safe.
Using a 'switch' statement is the idiomatic and correct way to inspect and unwrap tagged unions in Zig.
for (bgfx_artifact.root_module.link_objects.items) |lo| {
switch (lo) {
.other_step => |other| applyNdkSysroot(other.root_module, inc_common, inc_arch, lib_path, android_api),
else => {},
}
}
| const ndk_arch_triple: []const u8 = switch (target.result.cpu.arch) { | ||
| .aarch64 => "aarch64-linux-android", | ||
| .x86_64 => "x86_64-linux-android", | ||
| .arm => "arm-linux-androideabi", | ||
| .x86 => "i686-linux-android", | ||
| else => @panic("unsupported Android arch for bgfx"), | ||
| }; |
There was a problem hiding this comment.
In Zig, 32-bit ARM targets can also be represented by the '.thumb' CPU architecture. To prevent unexpected compile-time panics when targeting 32-bit ARM in thumb mode, map '.thumb' to the 'arm-linux-androideabi' triple alongside '.arm'.
const ndk_arch_triple: []const u8 = switch (target.result.cpu.arch) {
.aarch64 => \"aarch64-linux-android\",
.x86_64 => \"x86_64-linux-android\",
.arm, .thumb => \"arm-linux-androideabi\",
.x86 => \"i686-linux-android\",
else => @panic(\"unsupported Android arch for bgfx\"),
};
Gemini review: 32-bit ARM can be represented as the .thumb CPU arch, which would hit the else => @Panic in the NDK triple switch. Map .thumb alongside .arm. (Declined the companion 'lo == .other_step is a compile error' finding: comparing a tagged union to an enum literal is valid Zig — CI's bgfx-build job compiles this build.zig and passes.)
|
Bot triage:
Desktop unchanged ( |
Backend work since v0.39.1: - wgpu: macOS Metal surface + textured sprite rendering (#290, #291) - bgfx: macOS bring-up to on-device Android (#296 epic, #304/#305/#307/#308/#309) - #310 AndroidBackendContext adapters: sokol (#312) + bgfx (#313) register the core seam; bgfx-Android gamepad via the shared android_gamepad sub-package - bgfx desktop gamepad: GLFW (#315) + SDL HIDAPI / Switch-pad support (#318) - cached bgfx CI job (#295) Android codegen now calls core.registerAndroidBackend → requires labelle-core >= v1.17.0 + labelle-engine >= v1.50.0.
Phase 1 of the bgfx-on-Android bring-up. Closes #300. Part of #296.
What
Make bgfx's C++ (
bx,bgfx,bimg) compile clean foraarch64-linux-androidagainst the local NDK. Build-system only — no device, no runtime. The earlier cross-compile spike showed zbgfx does compile bgfx for the Android target; it only failed on missing NDK sysroot headers (unknown type name 'ldiv_t') because Zig's bundled libc++ reaches into the system C<stdlib.h>, which lives in the NDK sysroot.Change (only
backends/bgfx/build.zig, +135)getAndroidNdkSysroot(b)+ndkHostTag(), copied from the sokol-Android plumbing insrc/templates/build_zig.txt(readsANDROID_NDK_HOME, thenANDROID_HOME/ndk/<latest>; Zig-0.16b.graph.environ_map/std.Io.DirAPIs).is_android = os.tag == .linux and (abi == .android or .androideabi)(isAndroid()doesn't exist). Every Android-specific addition is behind it; desktop/cross builds fall through unchanged.applyNdkSysroot) —addSystemIncludePath(sysroot/usr/include)+.../<triple>(theldiv_tfix),addLibraryPath(.../usr/lib/<triple>/28),addCMacro("__ANDROID_API__", "28"),pic = true. zbgfx buildsbx/bimg/bgfxas three separate libs but only exposesbgfx; include paths don't crosslinkLibrary, so it walksbgfx_artifact.root_module.link_objects(the.other_stepentries = bx, bimg) and applies the sysroot to each.ldiv_twas fixed, bx'sfile.cppreferencesstdout/stderr, which Bionic only exposes as real symbols from API 23 (below that they alias__sF[],__REMOVED_IN(23)). 28 matches the toolkit's defaultmin_sdk_version.Verification (independent of the author)
ANDROID_NDK_HOME=.../27.0.12077973 zig build -Dtarget=aarch64-linux-android→ exit 0, produceszig-out/lib/libbgfx.a. bx/bimg/bgfx all compile clean (bimg went 424 → 0 errors). Objects are real aarch64 ELF (-target aarch64-linux-android; Zig can't emit host objects for that target).-Dtarget=x86_64-linux-androidalso compiles clean.zig build testexit 0;cd example && zig buildlinks the macOS demo. CI'sbgfx-build(macOS) job exercises both.Scope / next
This proves the renderer C++ compiles for Android. The full backend still won't link a
.so(zglfw → phase 2 #301; the GLESv3/EGL/android/log link step → later, mirroring sokol'sandroid_link). Out of scope here by design.