Skip to content

Commit 17aa647

Browse files
add workaround for linkLibCpp not working in Zig 0.14.0 stable (fixes SDL2 example) (#30)
add workaround for linkLibCpp (fixes SDL2 example)
1 parent e9dc54f commit 17aa647

File tree

3 files changed

+95
-55
lines changed

3 files changed

+95
-55
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
This library allows you to setup and build an APK for your Android devices. This project was mostly based off the work of [ikskuh](https://github.com/ikskuh) and wouldn't exist without the work they did on the [ZigAndroidTemplate](https://github.com/ikskuh/ZigAndroidTemplate) project.
66

7-
87
```sh
98
# Target one Android architecture
109
zig build -Dtarget=x86_64-linux-android
@@ -63,6 +62,10 @@ Add the following to your build.zig.zon file and run `zig build`.
6362
* [minimal](examples/minimal): This is based off ZigAndroidTemplate's minimal example.
6463
* [SDL2](examples/sdl2): This is based off Andrew Kelly's SDL Zig Demo but modified to run on Android, Windows, Mac and Linux.
6564

65+
## Workarounds
66+
67+
- Patch any artifacts that called `linkLibCpp()` to disable linking C++ and instead manually link `c++abi` and `unwind`. This at least allows the SDL2 example to work for now.
68+
6669
## Credits
6770

6871
- [ikskuh](https://github.com/ikskuh) This would not exist without their [ZigAndroidTemplate](https://github.com/ikskuh/ZigAndroidTemplate) repository to use as a baseline for figuring this all out and also being able to use their logic for the custom panic / logging functions.

examples/sdl2/third-party/sdl2/build.zig

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ pub fn build(b: *std.Build) !void {
8181
.root = sdl_path,
8282
.files = &android_src_files,
8383
});
84+
// NOTE(jae): 2024-09-22
85+
// Build settings taken from: SDL2-2.32.2/src/hidapi/android/jni/Android.mk
86+
// SDLActivity.java by default expects to be able to load this library
87+
lib.root_module.addCSourceFiles(.{
88+
.root = sdl_path,
89+
.files = &[_][]const u8{
90+
"src/hidapi/android/hid.cpp",
91+
},
92+
.flags = &.{"-std=c++11"},
93+
});
94+
lib.linkLibCpp();
8495

8596
// This is needed for "src/render/opengles/SDL_render_gles.c" to compile
8697
lib.root_module.addCMacro("GL_GLEXT_PROTOTYPES", "1");
@@ -120,35 +131,6 @@ pub fn build(b: *std.Build) !void {
120131
// comptime {
121132
// if (builtin.abi.isAndroid()) @export(&android_sdl_main, .{ .name = "SDL_main", .linkage = .strong });
122133
// }
123-
124-
const hidapi_lib = b.addStaticLibrary(.{
125-
.name = "hidapi",
126-
.target = target,
127-
.optimize = optimize,
128-
.link_libc = true,
129-
});
130-
hidapi_lib.addIncludePath(sdl_include_path);
131-
132-
// Avoid linking with linkLibCpp() as that causes issues as Zig 0.14.0 attempts to mix
133-
// its own C++ includes with those auto-included by the Zig Android SDK.
134-
//
135-
// However, not linking c++ means when loading on X86_64 systems, you get
136-
// unresolved symbol "_Unwind_Resume" when SDL2 is loaded, so to workaround that
137-
// we link the "unwind" library
138-
hidapi_lib.linkSystemLibrary("unwind");
139-
140-
// NOTE(jae): 2024-09-22
141-
// Build settings taken from: SDL2-2.32.2/src/hidapi/android/jni/Android.mk
142-
// SDLActivity.java by default expects to be able to load this library
143-
hidapi_lib.root_module.linkSystemLibrary("log", .{});
144-
hidapi_lib.root_module.addCSourceFiles(.{
145-
.root = sdl_path,
146-
.files = &[_][]const u8{
147-
"src/hidapi/android/hid.cpp",
148-
},
149-
.flags = &.{"-std=c++11"},
150-
});
151-
lib.linkLibrary(hidapi_lib);
152134
} else {
153135
const config_header = b.addConfigHeader(.{
154136
.style = .{ .cmake = sdl_include_path.path(b, "SDL_config.h.cmake") },

src/androidbuild/apk.zig

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ pub fn create(b: *std.Build, tools: *const Tools) *Apk {
4444
.tools = tools,
4545
.key_store = null,
4646
.android_manifest = null,
47-
.artifacts = .{},
48-
.java_files = .{},
49-
.resources = .{},
47+
.artifacts = .empty,
48+
.java_files = .empty,
49+
.resources = .empty,
5050
};
5151
return apk;
5252
}
@@ -137,10 +137,10 @@ fn addLibraryPaths(apk: *Apk, module: *std.Build.Module) void {
137137
// This include is specifically needed for: #if defined(__ANDROID__) && defined(__arm__) && !defined(HAVE_GETAUXVAL)
138138
//
139139
// ie. $ANDROID_HOME/ndk/{ndk_version}/sources/android/cpufeatures
140-
if (module.resolved_target) |resolved_target| {
141-
if (resolved_target.result.cpu.arch == .arm) {
142-
module.addIncludePath(.{ .cwd_relative = b.fmt("{s}/ndk/{s}/sources/android/cpufeatures", .{ apk.tools.android_sdk_path, apk.tools.ndk_version }) });
143-
}
140+
if (target.result.cpu.arch == .arm) {
141+
module.addIncludePath(.{
142+
.cwd_relative = b.fmt("{s}/ndk/{s}/sources/android/cpufeatures", .{ apk.tools.android_sdk_path, apk.tools.ndk_version }),
143+
});
144144
}
145145

146146
// ie. $ANDROID_HOME/ndk/{ndk_version}/toolchains/llvm/prebuilt/{host_os_and_arch}/sysroot ++ usr/lib/aarch64-linux-android/35
@@ -386,21 +386,6 @@ fn doInstallApk(apk: *Apk) std.mem.Allocator.Error!*Step.InstallFile {
386386
};
387387
_ = apk_files.addCopyFile(artifact.getEmittedBin(), b.fmt("lib/{s}/libmain.so", .{so_dir}));
388388

389-
// update artifact to:
390-
// - Be configured to work correctly on Android
391-
// - To know where C header /lib files are via setLibCFile and linkLibC
392-
// - Provide path to additional libraries to link to
393-
{
394-
if (artifact.linkage) |linkage| {
395-
if (linkage == .dynamic) {
396-
updateSharedLibraryOptions(artifact);
397-
}
398-
}
399-
apk.tools.setLibCFile(artifact);
400-
apk.addLibraryPaths(artifact.root_module);
401-
artifact.linkLibC();
402-
}
403-
404389
// Add module
405390
artifact.root_module.addImport("android_builtin", android_builtin);
406391

@@ -439,6 +424,27 @@ fn doInstallApk(apk: *Apk) std.mem.Allocator.Error!*Step.InstallFile {
439424
// - use Android LibC file
440425
// - add Android NDK library paths. (libandroid, liblog, etc)
441426
apk.updateLinkObjects(artifact, so_dir, apk_files);
427+
428+
// update artifact to:
429+
// - Be configured to work correctly on Android
430+
// - To know where C header /lib files are via setLibCFile and linkLibC
431+
// - Provide path to additional libraries to link to
432+
{
433+
if (artifact.linkage) |linkage| {
434+
if (linkage == .dynamic) {
435+
updateSharedLibraryOptions(artifact);
436+
}
437+
}
438+
apk.tools.setLibCFile(artifact);
439+
apk.addLibraryPaths(artifact.root_module);
440+
artifact.linkLibC();
441+
442+
// Apply workaround for Zig 0.14.0 stable
443+
//
444+
// This *must* occur after "apk.updateLinkObjects" for the root package otherwise
445+
// you may get an error like: "unable to find dynamic system library 'c++abi_zig_workaround'"
446+
apk.applyLibLinkCppWorkaroundIssue19(artifact);
447+
}
442448
}
443449

444450
// Add *.jar files
@@ -678,9 +684,9 @@ fn updateLinkObjects(apk: *Apk, root_artifact: *Step.Compile, so_dir: []const u8
678684
}
679685

680686
// If library is built using C or C++ then setLibCFile
681-
const link_libc = artifact.root_module.link_libc orelse false;
682-
const link_libcpp = artifact.root_module.link_libcpp orelse false;
683-
if (link_libc or link_libcpp) {
687+
if (artifact.root_module.link_libc == true or
688+
artifact.root_module.link_libcpp == true)
689+
{
684690
apk.tools.setLibCFile(artifact);
685691
}
686692

@@ -689,6 +695,9 @@ fn updateLinkObjects(apk: *Apk, root_artifact: *Step.Compile, so_dir: []const u8
689695

690696
// Update libraries linked to this library
691697
apk.updateLinkObjects(artifact, so_dir, raw_top_level_apk_files);
698+
699+
// Apply workaround for Zig 0.14.0
700+
apk.applyLibLinkCppWorkaroundIssue19(artifact);
692701
},
693702
else => continue,
694703
}
@@ -698,6 +707,52 @@ fn updateLinkObjects(apk: *Apk, root_artifact: *Step.Compile, so_dir: []const u8
698707
}
699708
}
700709

710+
/// Hack/Workaround issue #19 where Zig has issues with "linkLibCpp()" in Zig 0.14.0 stable
711+
/// - Zig Android SDK: https://github.com/silbinarywolf/zig-android-sdk/issues/19
712+
/// - Zig: https://github.com/ziglang/zig/issues/23302
713+
///
714+
/// This applies in two cases:
715+
/// - If the artifact has "link_libcpp = true"
716+
/// - If the artifact is linking to another artifact that has "link_libcpp = true", ie. artifact.dependsOnSystemLibrary("c++abi_zig_workaround")
717+
fn applyLibLinkCppWorkaroundIssue19(apk: *Apk, artifact: *Step.Compile) void {
718+
const b = apk.b;
719+
720+
const should_apply_fix = (artifact.root_module.link_libcpp == true or
721+
artifact.dependsOnSystemLibrary("c++abi_zig_workaround"));
722+
if (!should_apply_fix) {
723+
return;
724+
}
725+
726+
const system_target = getAndroidTriple(artifact.root_module.resolved_target.?) catch |err| @panic(@errorName(err));
727+
const lib_path: LazyPath = .{
728+
.cwd_relative = b.pathJoin(&.{ apk.tools.ndk_sysroot_path, "usr", "lib", system_target, "libc++abi.a" }),
729+
};
730+
const libcpp_workaround = b.addWriteFiles();
731+
const libcppabi_dir = libcpp_workaround.addCopyFile(lib_path, "libc++abi_zig_workaround.a").dirname();
732+
733+
artifact.root_module.addLibraryPath(libcppabi_dir);
734+
735+
if (artifact.root_module.link_libcpp == true) {
736+
// NOTE(jae): 2025-04-06
737+
// Don't explicitly linkLibCpp
738+
artifact.root_module.link_libcpp = null;
739+
740+
// NOTE(jae): 2025-04-06 - https://github.com/silbinarywolf/zig-android-sdk/issues/28
741+
// Resolve issue where in Zig 0.14.0 stable that '__gxx_personality_v0' is missing when starting
742+
// an SDL2 or SDL3 application.
743+
//
744+
// Tested on x86_64 with:
745+
// - build_tools_version = "35.0.1"
746+
// - ndk_version = "29.0.13113456"
747+
artifact.root_module.linkSystemLibrary("c++abi_zig_workaround", .{});
748+
749+
// NOTE(jae): 2025-04-06
750+
// unresolved symbol "_Unwind_Resume" error occurs when SDL2 is loaded,
751+
// so we link the "unwind" library
752+
artifact.root_module.linkSystemLibrary("unwind", .{});
753+
}
754+
}
755+
701756
fn updateSharedLibraryOptions(artifact: *std.Build.Step.Compile) void {
702757
if (artifact.linkage) |linkage| {
703758
if (linkage != .dynamic) {

0 commit comments

Comments
 (0)