diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 092c061..022720a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,6 @@ jobs: include: - os: "ubuntu-22.04" - os: "windows-latest" - - os: "macos-12" # x86_64 - os: "macos-14" # arm64 as per table: https://github.com/actions/runner-images/blob/8a1eeaf6ac70c66f675a04078d1a7222edd42008/README.md#available-images runs-on: ${{matrix.os}} @@ -36,7 +35,7 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v3 with: - packages: 'tools platform-tools platforms;android-35 build-tools;35.0.0 ndk;27.0.12077973' + packages: 'tools platform-tools platforms;android-35 build-tools;35.0.0 ndk;29.0.13113456' - name: Setup Zig 0.13.0 # note(jae): 2024-09-15 @@ -53,22 +52,3 @@ jobs: - name: Build SDL2 Example (Zig 0.13.0) run: zig build -Dandroid=true --verbose working-directory: examples/sdl2 - - - name: Setup Zig Nightly - uses: mlugg/setup-zig@v1 - with: - version: "master" - - - name: Build Minimal Example (Zig Nightly) - run: zig build -Dandroid=true --verbose - working-directory: examples/minimal - - - name: Build SDL2 Example (Zig Nightly) - # "zig build -Dandroid=true" fails for 0.14.0-dev.1632 - # - # android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/android/hardware_buffer.h:322:42: - # error: expression is not an integral constant expression - # - # See: https://github.com/silbinarywolf/zig-android-sdk/actions/runs/10979711793/job/30484520174?pr=5#step:10:30 - run: zig build -Dtarget=x86_64-linux-android --verbose - working-directory: examples/sdl2 diff --git a/README.md b/README.md index 5d8fd1c..2422094 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ zig build -Dandroid=true ```zig // This is an overly simplified example to give you the gist // of how this library works, see: examples/minimal/build.zig -const android = @import("zig-android-sdk"); +const android = @import("android"); pub fn build(b: *std.Build) !void { const android_tools = android.Tools.create(b, ...); @@ -52,7 +52,7 @@ Add the following to your build.zig.zon file and run `zig build`. ```zig .{ .dependencies = .{ - .@"zig-android-sdk" = .{ + .android = .{ .path = "https://github.com/silbinarywolf/zig-android-sdk/archive/REPLACE_WITH_WANTED_COMMIT.tar.gz", // .hash = REPLACE_WITH_HASH_FROM_BUILD_ERROR }, diff --git a/build.zig.zon b/build.zig.zon index 5c5ed07..06ac663 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,5 +1,5 @@ .{ - .name = "zig-android-sdk", + .name = "android", .version = "0.1.0", .dependencies = .{}, .paths = .{ @@ -7,4 +7,6 @@ "build.zig.zon", "src", }, + .minimum_zig_version = "0.13.0", + .fingerprint = 0x92bcb62d42fb2cee, } diff --git a/examples/minimal/build.zig b/examples/minimal/build.zig index 19eb9e2..fe67f51 100644 --- a/examples/minimal/build.zig +++ b/examples/minimal/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const android = @import("zig-android-sdk"); +const android = @import("android"); pub fn build(b: *std.Build) void { const exe_name: []const u8 = "minimal"; @@ -22,7 +22,7 @@ pub fn build(b: *std.Build) void { const android_tools = android.Tools.create(b, .{ .api_level = .android15, .build_tools_version = "35.0.0", - .ndk_version = "27.0.12077973", + .ndk_version = "29.0.13113456", }); const apk = android.APK.create(b, android_tools); @@ -52,9 +52,9 @@ pub fn build(b: *std.Build) void { // if building as library for Android, add this target // NOTE: Android has different CPU targets so you need to build a version of your // code for x86, x86_64, arm, arm64 and more - if (target.result.isAndroid()) { + if (target.result.abi == .android) { const apk: *android.APK = android_apk orelse @panic("Android APK should be initialized"); - const android_dep = b.dependency("zig-android-sdk", .{ + const android_dep = b.dependency("android", .{ .optimize = optimize, .target = target, }); diff --git a/examples/minimal/build.zig.zon b/examples/minimal/build.zig.zon index 919d486..d32c5a9 100644 --- a/examples/minimal/build.zig.zon +++ b/examples/minimal/build.zig.zon @@ -2,7 +2,7 @@ .name = "minimal", .version = "0.0.0", .dependencies = .{ - .@"zig-android-sdk" = .{ + .android = .{ .path = "../..", }, }, @@ -11,4 +11,5 @@ "build.zig.zon", "src", }, + .fingerprint = 0x4037f28ad1eb4832, } diff --git a/examples/sdl2/android/src/ZigSDLActivity.java b/examples/sdl2/android/src/ZigSDLActivity.java index 79e6336..998ed62 100644 --- a/examples/sdl2/android/src/ZigSDLActivity.java +++ b/examples/sdl2/android/src/ZigSDLActivity.java @@ -9,7 +9,6 @@ public class ZigSDLActivity extends SDLActivity { // @Override // protected String[] getLibraries() { // return new String[] { - // "hidapi", // "SDL2", // // "SDL2_image", // // "SDL2_mixer", diff --git a/examples/sdl2/build.zig b/examples/sdl2/build.zig index c92ffbf..0b89686 100644 --- a/examples/sdl2/build.zig +++ b/examples/sdl2/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const android = @import("zig-android-sdk"); +const android = @import("android"); pub fn build(b: *std.Build) void { const root_target = b.standardTargetOptions(.{}); @@ -21,7 +21,16 @@ pub fn build(b: *std.Build) void { const android_tools = android.Tools.create(b, .{ .api_level = .android15, .build_tools_version = "35.0.0", - .ndk_version = "27.0.12077973", + .ndk_version = "29.0.13113456", + // NOTE(jae): 2025-03-09 + // Previously this example used 'ndk' "27.0.12077973". + // + // However that has issues with the latest SDL2 version when including 'hardware_buffer.h' + // for 32-bit builds. + // + // - AHARDWAREBUFFER_USAGE_FRONT_BUFFER = 1UL << 32 + // - ndk/27.0.12077973/toolchains/llvm/prebuilt/{OS}-x86_64/sysroot/usr/include/android/hardware_buffer.h:322:42: + // - error: expression is not an integral constant expression }); const apk = android.APK.create(b, android_tools); @@ -47,7 +56,7 @@ pub fn build(b: *std.Build) void { for (targets) |target| { const exe_name: []const u8 = "sdl-zig-demo"; - var exe: *std.Build.Step.Compile = if (target.result.isAndroid()) b.addSharedLibrary(.{ + var exe: *std.Build.Step.Compile = if (target.result.abi == .android) b.addSharedLibrary(.{ .name = exe_name, .root_source_file = b.path("src/sdl-zig-demo.zig"), .target = target, @@ -65,7 +74,7 @@ pub fn build(b: *std.Build) void { .optimize = .ReleaseFast, .target = target, }); - if (target.result.os.tag == .linux and !target.result.isAndroid()) { + if (target.result.os.tag == .linux and target.result.abi != .android) { // The SDL package doesn't work for Linux yet, so we rely on system // packages for now. exe.linkSystemLibrary("SDL2"); @@ -75,12 +84,6 @@ pub fn build(b: *std.Build) void { exe.linkLibrary(sdl_lib); } - // NOTE(jae): 2024-09-22 - // Load additional dynamic libraries that SDLActivity.java loads such as hidapi - if (target.result.isAndroid()) { - exe.linkLibrary(sdl_dep.artifact("hidapi")); - } - const sdl_module = sdl_dep.module("sdl"); exe.root_module.addImport("sdl", sdl_module); } @@ -88,9 +91,9 @@ pub fn build(b: *std.Build) void { // if building as library for Android, add this target // NOTE: Android has different CPU targets so you need to build a version of your // code for x86, x86_64, arm, arm64 and more - if (target.result.isAndroid()) { + if (target.result.abi == .android) { const apk: *android.APK = android_apk orelse @panic("Android APK should be initialized"); - const android_dep = b.dependency("zig-android-sdk", .{ + const android_dep = b.dependency("android", .{ .optimize = optimize, .target = target, }); diff --git a/examples/sdl2/build.zig.zon b/examples/sdl2/build.zig.zon index 9d39231..4a3d6aa 100644 --- a/examples/sdl2/build.zig.zon +++ b/examples/sdl2/build.zig.zon @@ -1,8 +1,8 @@ .{ - .name = "minimal", + .name = "sdl2", .version = "0.0.0", .dependencies = .{ - .@"zig-android-sdk" = .{ + .android = .{ .path = "../..", }, .sdl2 = .{ @@ -14,4 +14,5 @@ "build.zig.zon", "src", }, + .fingerprint = 0x168fc6b9a7d0df48, } diff --git a/examples/sdl2/third-party/sdl2/build.zig b/examples/sdl2/third-party/sdl2/build.zig index ffc2995..21a5ec8 100644 --- a/examples/sdl2/third-party/sdl2/build.zig +++ b/examples/sdl2/third-party/sdl2/build.zig @@ -72,89 +72,88 @@ pub fn build(b: *std.Build) !void { lib.linkFramework("CoreHaptics"); }, else => { - switch (target.result.abi) { - .android => { - lib.root_module.addCSourceFiles(.{ - .root = sdl_path, - .files = &android_src_files, - }); - - // This is needed for "src/render/opengles/SDL_render_gles.c" to compile - lib.root_module.addCMacro("GL_GLEXT_PROTOTYPES", "1"); - - // Add Java files to dependency - const java_dir = sdl_dep.path("android-project/app/src/main/java/org/libsdl/app"); - const java_files: []const []const u8 = &.{ - "SDL.java", - "SDLSurface.java", - "SDLActivity.java", - "SDLAudioManager.java", - "SDLControllerManager.java", - "HIDDevice.java", - "HIDDeviceUSB.java", - "HIDDeviceManager.java", - "HIDDeviceBLESteamController.java", - }; - const java_write_files = b.addNamedWriteFiles("sdljava"); - for (java_files) |java_file_basename| { - _ = java_write_files.addCopyFile(java_dir.path(b, java_file_basename), java_file_basename); - } - - // https://github.com/libsdl-org/SDL/blob/release-2.30.6/Android.mk#L82C62-L82C69 - lib.linkSystemLibrary("dl"); - lib.linkSystemLibrary("GLESv1_CM"); - lib.linkSystemLibrary("GLESv2"); - lib.linkSystemLibrary("OpenSLES"); - lib.linkSystemLibrary("log"); - lib.linkSystemLibrary("android"); - - // SDLActivity.java's getMainFunction defines the entrypoint as "SDL_main" - // So your main / root file will need something like this for Android - // - // fn android_sdl_main() callconv(.C) void { - // _ = std.start.callMain(); - // } - // comptime { - // if (builtin.abi == .android) @export(android_sdl_main, .{ .name = "SDL_main", .linkage = .strong }); - // } - - const use_hidapi = true; - if (!use_hidapi) { - lib.root_module.addCMacro("SDL_HIDAPI_DISABLED", ""); - } else { - // NOTE(jae): 2024-09-22 - // Build settings taken from: src/hidapi/android/jni/Android.mk - // SDLActivity.java by default expects to be able to load this library. - const hidapi_lib = b.addSharedLibrary(.{ - .name = "hidapi", - .target = target, - .optimize = optimize, - .link_libc = true, - }); - hidapi_lib.addIncludePath(sdl_include_path); - hidapi_lib.root_module.addCSourceFiles(.{ - .root = sdl_path, - .files = &[_][]const u8{ - "src/hidapi/android/hid.cpp", - }, - .flags = &.{"-std=c++11"}, - }); - hidapi_lib.linkSystemLibrary("log"); - hidapi_lib.linkLibCpp(); - lib.linkLibrary(hidapi_lib); - b.installArtifact(hidapi_lib); - } - }, - else => { - const config_header = b.addConfigHeader(.{ - .style = .{ .cmake = sdl_include_path.path(b, "SDL_config.h.cmake") }, - .include_path = "SDL/SDL_config.h", - }, .{}); - sdl_config_header = config_header; - - lib.addConfigHeader(config_header); - lib.installConfigHeader(config_header); - }, + if (target.result.abi == .android) { + lib.root_module.addCSourceFiles(.{ + .root = sdl_path, + .files = &android_src_files, + }); + + // This is needed for "src/render/opengles/SDL_render_gles.c" to compile + lib.root_module.addCMacro("GL_GLEXT_PROTOTYPES", "1"); + + // Add Java files to dependency + const java_dir = sdl_dep.path("android-project/app/src/main/java/org/libsdl/app"); + const java_files: []const []const u8 = &.{ + "SDL.java", + "SDLSurface.java", + "SDLActivity.java", + "SDLAudioManager.java", + "SDLControllerManager.java", + "HIDDevice.java", + "HIDDeviceUSB.java", + "HIDDeviceManager.java", + "HIDDeviceBLESteamController.java", + }; + const java_write_files = b.addNamedWriteFiles("sdljava"); + for (java_files) |java_file_basename| { + _ = java_write_files.addCopyFile(java_dir.path(b, java_file_basename), java_file_basename); + } + + // https://github.com/libsdl-org/SDL/blob/release-2.30.6/Android.mk#L82C62-L82C69 + lib.linkSystemLibrary("dl"); + lib.linkSystemLibrary("GLESv1_CM"); + lib.linkSystemLibrary("GLESv2"); + lib.linkSystemLibrary("OpenSLES"); + lib.linkSystemLibrary("log"); + lib.linkSystemLibrary("android"); + + // SDLActivity.java's getMainFunction defines the entrypoint as "SDL_main" + // So your main / root file will need something like this for Android + // + // fn android_sdl_main() callconv(.C) void { + // _ = std.start.callMain(); + // } + // comptime { + // if (builtin.abi == .android) @export(android_sdl_main, .{ .name = "SDL_main", .linkage = .strong }); + // } + + const hidapi_lib = b.addStaticLibrary(.{ + .name = "hidapi", + .target = target, + .optimize = optimize, + .link_libc = true, + }); + hidapi_lib.addIncludePath(sdl_include_path); + + // Avoid linking with linkLibCpp() as that causes issues as Zig 0.14.0 attempts to mix + // its own C++ includes with those auto-included by the Zig Android SDK. + // + // However, not linking c++ means when loading on X86_64 systems, you get + // unresolved symbol "_Unwind_Resume" when SDL2 is loaded, so to workaround that + // we link the "unwind" library + hidapi_lib.linkSystemLibrary("unwind"); + + // NOTE(jae): 2024-09-22 + // Build settings taken from: SDL2-2.32.2/src/hidapi/android/jni/Android.mk + // SDLActivity.java by default expects to be able to load this library + hidapi_lib.root_module.linkSystemLibrary("log", .{}); + hidapi_lib.root_module.addCSourceFiles(.{ + .root = sdl_path, + .files = &[_][]const u8{ + "src/hidapi/android/hid.cpp", + }, + .flags = &.{"-std=c++11"}, + }); + lib.linkLibrary(hidapi_lib); + } else { + const config_header = b.addConfigHeader(.{ + .style = .{ .cmake = sdl_include_path.path(b, "SDL_config.h.cmake") }, + .include_path = "SDL/SDL_config.h", + }, .{}); + sdl_config_header = config_header; + + lib.addConfigHeader(config_header); + lib.installConfigHeader(config_header); } }, } @@ -167,6 +166,8 @@ pub fn build(b: *std.Build) !void { b.installArtifact(lib); var module = b.addModule("sdl", .{ + .target = b.graph.host, + .optimize = .ReleaseFast, .root_source_file = b.path("src/sdl.zig"), }); if (sdl_config_header) |config_header| { diff --git a/examples/sdl2/third-party/sdl2/build.zig.zon b/examples/sdl2/third-party/sdl2/build.zig.zon index e427b20..cfe2e9f 100644 --- a/examples/sdl2/third-party/sdl2/build.zig.zon +++ b/examples/sdl2/third-party/sdl2/build.zig.zon @@ -5,8 +5,8 @@ .sdl2 = .{ // NOTE(jae): 2024-06-30 // Using ".zip" as "tar.gz" fails on Windows for Zig 0.13.0 due to symlink issue with something in the android folders - .url = "https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.30.4.zip", - .hash = "122086865a4d5e822c068528a4170cb7da52afad38e8232afa3e74789c4d52d27791", + .url = "https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.32.2.zip", + .hash = "12204a4a9e9f41fc906decd762be78b9e80de65a7bdec428aa0dfdf03f46e7614d9e", }, }, .paths = .{ diff --git a/src/androidbuild/androidbuild.zig b/src/androidbuild/androidbuild.zig index 49b97fc..94976b6 100644 --- a/src/androidbuild/androidbuild.zig +++ b/src/androidbuild/androidbuild.zig @@ -130,7 +130,11 @@ const AndroidTargetQuery = struct { return .{ .os_tag = .linux, .cpu_model = .baseline, - .abi = .android, + .abi = comptime if (builtin.zig_version.major == 0 and builtin.zig_version.minor == 13) + // NOTE(jae): 2025-03-09 + // Zig 0.13.0 doesn't have androideabi + .android + else if (android_target.cpu_arch != .arm) .android else .androideabi, .cpu_arch = android_target.cpu_arch, .cpu_features_add = android_target.cpu_features_add, }; @@ -151,9 +155,8 @@ const supported_android_targets = [_]AndroidTargetQuery{ .cpu_arch = .aarch64, .cpu_features_add = Target.aarch64.featureSet(&.{.v8a}), }, - // TODO(jae): 2024-09-08 - // This doesn't work for compiling C code like SDL2 or OpenXR due to "__ARM_ARCH" not being "7" - // or similar. I might be messing something up here but not sure. + // NOTE(jae): 2024-09-08 + // 'arm-linux-androideabi' doesn't work with Zig 0.13.0 for compiling C code like SDL2 or OpenXR due to "__ARM_ARCH" not being "7" // .{ // // arm-linux-androideabi // .cpu_arch = .arm, diff --git a/src/androidbuild/apk.zig b/src/androidbuild/apk.zig index ddff07e..b4d1202 100644 --- a/src/androidbuild/apk.zig +++ b/src/androidbuild/apk.zig @@ -129,6 +129,19 @@ pub const APK = struct { // - "java.lang.UnsatisfiedLinkError: dlopen failed: TLS symbol "_ZZN8gwp_asan15getThreadLocalsEvE6Locals" in dlopened" const android_api_version: u32 = @intFromEnum(apk.tools.api_level); + // NOTE(jae): 2025-03-09 + // Resolve issue where building SDL2 gets the following error for 'arm-linux-androideabi' + // SDL2-2.32.2/src/cpuinfo/SDL_cpuinfo.c:93:10: error: 'cpu-features.h' file not found + // + // This include is specifically needed for: #if defined(__ANDROID__) && defined(__arm__) && !defined(HAVE_GETAUXVAL) + // + // ie. $ANDROID_HOME/ndk/{ndk_version}/sources/android/cpufeatures + if (module.resolved_target) |resolved_target| { + if (resolved_target.result.cpu.arch == .arm) { + module.addIncludePath(.{ .cwd_relative = b.fmt("{s}/ndk/{s}/sources/android/cpufeatures", .{ apk.tools.android_sdk_path, apk.tools.ndk_version }) }); + } + } + // ie. $ANDROID_HOME/ndk/{ndk_version}/toolchains/llvm/prebuilt/{host_os_and_arch}/sysroot ++ usr/lib/aarch64-linux-android/35 module.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/usr/lib/{s}/{d}", .{ android_ndk_sysroot, system_target, android_api_version }) }); // ie. $ANDROID_HOME/ndk/{ndk_version}/toolchains/llvm/prebuilt/{host_os_and_arch}/sysroot ++ /usr/lib/aarch64-linux-android @@ -180,7 +193,7 @@ pub const APK = struct { } } if (artifact.root_module.resolved_target) |target| { - if (!target.result.isAndroid()) { + if (target.result.abi != .android) { try errors.append(b.fmt("artifact[{}]: must be targetting Android abi", .{i})); continue; } @@ -423,9 +436,9 @@ pub const APK = struct { "utf8", "-cp", apk.tools.root_jar, - // NOTE(jae): 2024-09-19 - // Debug issues with the SDL.java classes - // "-Xlint:deprecation", + // NOTE(jae): 2024-09-19 + // Debug issues with the SDL.java classes + // "-Xlint:deprecation", }); javac_cmd.setName(runNameContext("javac")); javac_cmd.addArg("-d"); @@ -611,7 +624,7 @@ pub const APK = struct { // other_step.root_module.addCMacro("__ARM_ARCH", "7"); // '__ARM_ARCH' macro redefined // other_step.root_module.addCMacro("_M_ARM", ""); // } - // other_step.root_module.addCMacro("__ANDROID__", ""); + // artifact.root_module.addCMacro("__ANDROID__", "1"); apk.tools.setLibCFile(artifact); }