diff --git a/build.zig b/build.zig index 80d4d281..032cd378 100644 --- a/build.zig +++ b/build.zig @@ -201,6 +201,56 @@ fn addPkgTest( return tm; } +// The NDK triple for an *-linux-android target, or null when the target isn't +// android. Drives the sysroot's arch-specific include + crt subdirs. +fn androidTriple(target: std.Build.ResolvedTarget) ?[]const u8 { + const t = target.result; + if (t.abi != .android and t.abi != .androideabi) return null; + return switch (t.cpu.arch) { + .aarch64 => "aarch64-linux-android", + .x86_64 => "x86_64-linux-android", + .x86 => "i686-linux-android", + .arm, .thumb => "arm-linux-androideabi", + else => null, + }; +} + +// Generate a Zig `--libc` config for the NDK sysroot and return it as a file the +// caller pins to the android artifact via `setLibCFile`. The `asm/` arch headers +// live in the triple subdir (sys_include_dir), which a plain `--sysroot` misses. +fn ndkSysroot(b: *std.Build, ndk: []const u8) []const u8 { + const base = b.fmt("{s}/toolchains/llvm/prebuilt", .{ndk}); + // The NDK ships one host toolchain dir. Probe the host-OS default FIRST (what + // the NDK actually ships — e.g. darwin-x86_64 even on Apple silicon) so the + // result is correct regardless of how accessAbsolute behaves; only fall + // through to alternates (a future darwin-arm64 toolchain) if it's absent. + const candidates: []const []const u8 = switch (@import("builtin").os.tag) { + .macos => &.{ "darwin-x86_64", "darwin-arm64" }, + .windows => &.{"windows-x86_64"}, + else => &.{ "linux-x86_64", "linux-aarch64" }, + }; + for (candidates) |host| { + const sysroot = b.fmt("{s}/{s}/sysroot", .{ base, host }); + std.Io.Dir.accessAbsolute(b.graph.io, b.fmt("{s}/usr/include", .{sysroot}), .{}) catch continue; + return sysroot; + } + return b.fmt("{s}/{s}/sysroot", .{ base, candidates[0] }); // default; clear path in errors +} + +fn androidLibcFile(b: *std.Build, ndk: []const u8, triple: []const u8, api: u32) std.Build.LazyPath { + const sysroot = ndkSysroot(b, ndk); + const content = b.fmt( + \\include_dir={s}/usr/include + \\sys_include_dir={s}/usr/include/{s} + \\crt_dir={s}/usr/lib/{s}/{d} + \\msvc_lib_dir= + \\kernel32_lib_dir= + \\gcc_dir= + \\ + , .{ sysroot, sysroot, triple, sysroot, triple, api }); + return b.addWriteFiles().add(b.fmt("android-libc-{s}.txt", .{triple}), content); +} + pub fn build(b: *std.Build) void { // The S-101 PortrayalCatalog source (submodule, or the lazy dependency for // a fetched package). On the first pass of a fetch this is null — return so @@ -218,6 +268,19 @@ pub fn build(b: *std.Build) void { // the -Doptimize option entirely. const optimize = b.option(std.builtin.OptimizeMode, "optimize", "Prioritize performance, safety, or binary size") orelse .ReleaseFast; + // Cross-compiling to *-linux-android: Zig ships no bionic headers, so the C + // deps (Lua, libtess2, nanosvg, stb) need the NDK sysroot. Pass the NDK root + // with `-Dandroid-ndk=` and this generates the Zig libc config (include + // + arch `asm/` include + per-API crt dir) and pins it to the lib artifact + // ONLY — a global `--libc` would break the host tools pinned to linux-musl. + // `zig build lib -Dtarget=aarch64-linux-android -Dandroid-ndk=$ANDROID_NDK`. + const android_ndk = b.option([]const u8, "android-ndk", "Android NDK root (enables the *-linux-android libc for the lib)"); + const android_api = b.option(u32, "android-api", "Android API level for the NDK sysroot (default 24)") orelse 24; + const android_libc: ?std.Build.LazyPath = if (androidTriple(target)) |triple| + (if (android_ndk) |ndk| androidLibcFile(b, ndk, triple, android_api) else null) + else + null; + // Lua: POSIX feature flags on Unix; Windows lets luaconf.h auto-pick // LUA_USE_WINDOWS. The portray module is target-agnostic (it inherits each // consumer's target), but on a given host the lib + baker share this OS, so @@ -496,6 +559,8 @@ pub fn build(b: *std.Build) void { lib_mod.addImport("buildinfo", buildinfo.createModule()); } const lib = b.addLibrary(.{ .name = "tile57", .linkage = .static, .root_module = lib_mod }); + // Android cross-compile: point the C deps at the NDK sysroot (see -Dandroid-ndk). + if (android_libc) |libc| lib.setLibCFile(libc); // The archive for zig-package consumers (lookout-core links it into its own // build): a named lazy path, NOT dep.artifact() — the default install step // installs the `tile57` CLI under the same name, and on macOS the lib