mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
build: add support for Android NDK path configuration
This commit is contained in:
@@ -115,6 +115,7 @@
|
||||
|
||||
// Other
|
||||
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
||||
.android_ndk = .{ .path = "./pkg/android-ndk" },
|
||||
.iterm2_themes = .{
|
||||
.url = "https://deps.files.ghostty.org/ghostty-themes-release-20260202-151632-49169e9.tgz",
|
||||
.hash = "N-V-__8AAKlTAwClvdUfsAoiLgM6sb2NPZNnS6wzCiIs252Z",
|
||||
|
||||
189
pkg/android-ndk/build.zig
Normal file
189
pkg/android-ndk/build.zig
Normal file
@@ -0,0 +1,189 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn build(_: *std.Build) !void {}
|
||||
|
||||
// Configure the step to point to the Android NDK for libc and include
|
||||
// paths. This requires the Android NDK installed in the system and
|
||||
// setting the appropriate environment variables or installing the NDK
|
||||
// in the default location.
|
||||
//
|
||||
// The environment variables can be set as follows:
|
||||
// - `ANDROID_NDK_HOME`: Directly points to the NDK path.
|
||||
// - `ANDROID_HOME` or `ANDROID_SDK_ROOT`: Points to the Android SDK path;
|
||||
// latest NDK will be automatically selected.
|
||||
//
|
||||
// NB: This is a workaround until zig natively supports bionic
|
||||
// cross-compilation (ziglang/zig#23906).
|
||||
pub fn addPaths(b: *std.Build, step: *std.Build.Step.Compile) !void {
|
||||
const Cache = struct {
|
||||
const Key = struct {
|
||||
arch: std.Target.Cpu.Arch,
|
||||
abi: std.Target.Abi,
|
||||
api_level: u32,
|
||||
};
|
||||
|
||||
var map: std.AutoHashMapUnmanaged(Key, ?struct {
|
||||
libc: std.Build.LazyPath,
|
||||
cpp_include: []const u8,
|
||||
lib: []const u8,
|
||||
}) = .{};
|
||||
};
|
||||
|
||||
const target = step.rootModuleTarget();
|
||||
const gop = try Cache.map.getOrPut(b.allocator, .{
|
||||
.arch = target.cpu.arch,
|
||||
.abi = target.abi,
|
||||
.api_level = target.os.version_range.linux.android,
|
||||
});
|
||||
|
||||
if (!gop.found_existing) {
|
||||
const ndk_path = findNDKPath(b.allocator) orelse {
|
||||
gop.value_ptr.* = null;
|
||||
return error.AndroidNDKNotFound;
|
||||
};
|
||||
|
||||
var ndk_dir = std.fs.openDirAbsolute(ndk_path, .{}) catch {
|
||||
gop.value_ptr.* = null;
|
||||
return error.AndroidNDKNotFound;
|
||||
};
|
||||
defer ndk_dir.close();
|
||||
|
||||
const ndk_triple = ndkTriple(target) orelse {
|
||||
gop.value_ptr.* = null;
|
||||
return error.AndroidNDKUnsupportedTarget;
|
||||
};
|
||||
|
||||
const host = hostTag() orelse {
|
||||
gop.value_ptr.* = null;
|
||||
return error.AndroidNDKUnsupportedHost;
|
||||
};
|
||||
|
||||
const sysroot = try std.fs.path.join(b.allocator, &.{
|
||||
ndk_path, "toolchains", "llvm", "prebuilt", host, "sysroot",
|
||||
});
|
||||
const include_dir = try std.fs.path.join(
|
||||
b.allocator,
|
||||
&.{ sysroot, "usr", "include" },
|
||||
);
|
||||
const sys_include_dir = try std.fs.path.join(
|
||||
b.allocator,
|
||||
&.{ sysroot, "usr", "include", ndk_triple },
|
||||
);
|
||||
|
||||
var api_buf: [10]u8 = undefined;
|
||||
const api_level = target.os.version_range.linux.android;
|
||||
const api_level_str = std.fmt.bufPrint(&api_buf, "{d}", .{api_level}) catch unreachable;
|
||||
const c_runtime_dir = try std.fs.path.join(
|
||||
b.allocator,
|
||||
&.{ sysroot, "usr", "lib", ndk_triple, api_level_str },
|
||||
);
|
||||
|
||||
const libc_txt = try std.fmt.allocPrint(b.allocator,
|
||||
\\include_dir={s}
|
||||
\\sys_include_dir={s}
|
||||
\\crt_dir={s}
|
||||
\\msvc_lib_dir=
|
||||
\\kernel32_lib_dir=
|
||||
\\gcc_dir=
|
||||
, .{ include_dir, sys_include_dir, c_runtime_dir });
|
||||
|
||||
const wf = b.addWriteFiles();
|
||||
const libc_path = wf.add("libc.txt", libc_txt);
|
||||
const lib = try std.fs.path.join(b.allocator, &.{ sysroot, "usr", "lib", ndk_triple });
|
||||
const cpp_include = try std.fs.path.join(b.allocator, &.{ sysroot, "usr", "include", "c++", "v1" });
|
||||
|
||||
gop.value_ptr.* = .{
|
||||
.lib = lib,
|
||||
.libc = libc_path,
|
||||
.cpp_include = cpp_include,
|
||||
};
|
||||
}
|
||||
|
||||
const value = gop.value_ptr.* orelse return error.AndroidNDKNotFound;
|
||||
|
||||
step.setLibCFile(value.libc);
|
||||
step.root_module.addSystemIncludePath(.{ .cwd_relative = value.cpp_include });
|
||||
step.root_module.addLibraryPath(.{ .cwd_relative = value.lib });
|
||||
}
|
||||
|
||||
fn findNDKPath(allocator: std.mem.Allocator) ?[]const u8 {
|
||||
// Check if user has set the environment variable for the NDK path.
|
||||
if (std.process.getEnvVarOwned(allocator, "ANDROID_NDK_HOME") catch null) |value| {
|
||||
if (value.len > 0) return value;
|
||||
}
|
||||
|
||||
// Check the common environment variables for the Android SDK path and look for the NDK inside it.
|
||||
inline for (.{ "ANDROID_HOME", "ANDROID_SDK_ROOT" }) |env| {
|
||||
if (std.process.getEnvVarOwned(allocator, env) catch null) |sdk| {
|
||||
if (sdk.len > 0) {
|
||||
if (findLatestNDK(allocator, sdk)) |ndk| return ndk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As a fallback, we assume the most common/default SDK path based on the OS.
|
||||
const home = std.process.getEnvVarOwned(
|
||||
allocator,
|
||||
if (builtin.os.tag == .windows) "LOCALAPPDATA" else "HOME",
|
||||
) catch return null;
|
||||
|
||||
const default_sdk_path = std.fs.path.join(allocator, &.{
|
||||
home, switch (builtin.os.tag) {
|
||||
.linux => "Android/sdk",
|
||||
.macos => "Library/Android/Sdk",
|
||||
.windows => "Android/Sdk",
|
||||
else => return null,
|
||||
},
|
||||
}) catch return null;
|
||||
return findLatestNDK(allocator, default_sdk_path);
|
||||
}
|
||||
|
||||
fn findLatestNDK(allocator: std.mem.Allocator, sdk_path: []const u8) ?[]const u8 {
|
||||
const ndk_dir = std.fs.path.join(allocator, &.{ sdk_path, "ndk" }) catch return null;
|
||||
var dir = std.fs.openDirAbsolute(ndk_dir, .{ .iterate = true }) catch return null;
|
||||
defer dir.close();
|
||||
|
||||
var latest_version: ?[]const u8 = null;
|
||||
var latest_parsed: ?std.SemanticVersion = null;
|
||||
var iterator = dir.iterate();
|
||||
|
||||
while (iterator.next() catch null) |file| {
|
||||
if (file.kind != .directory) continue;
|
||||
const parsed = std.SemanticVersion.parse(file.name) catch continue;
|
||||
if (latest_version == null or parsed.order(latest_parsed.?) == .gt) {
|
||||
if (latest_version) |old| allocator.free(old);
|
||||
latest_version = allocator.dupe(u8, file.name) catch return null;
|
||||
latest_parsed = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
if (latest_version) |version| {
|
||||
return std.fs.path.join(allocator, &.{ sdk_path, "ndk", version }) catch return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn hostTag() ?[]const u8 {
|
||||
return switch (builtin.os.tag) {
|
||||
.linux => "linux-x86_64",
|
||||
// All darwin hosts use the same prebuilt binaries
|
||||
// (https://developer.android.com/ndk/guides/other_build_systems).
|
||||
.macos => "darwin-x86_64",
|
||||
.windows => "windows-x86_64",
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
// We must map the target architecture to the corresponding NDK triple following the NDK
|
||||
// documentation: https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md#architectures
|
||||
fn ndkTriple(target: std.Target) ?[]const u8 {
|
||||
return switch (target.cpu.arch) {
|
||||
.arm => "arm-linux-androideabi",
|
||||
.aarch64 => "aarch64-linux-android",
|
||||
.x86 => "i686-linux-android",
|
||||
.x86_64 => "x86_64-linux-android",
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
7
pkg/android-ndk/build.zig.zon
Normal file
7
pkg/android-ndk/build.zig.zon
Normal file
@@ -0,0 +1,7 @@
|
||||
.{
|
||||
.name = .android_ndk,
|
||||
.version = "0.0.1",
|
||||
.dependencies = .{},
|
||||
.fingerprint = 0xee68d62c5a97b68b,
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -31,6 +31,11 @@ pub fn build(b: *std.Build) !void {
|
||||
try apple_sdk.addPaths(b, lib);
|
||||
}
|
||||
|
||||
if (target.result.abi.isAndroid()) {
|
||||
const android_ndk = @import("android_ndk");
|
||||
try android_ndk.addPaths(b, lib);
|
||||
}
|
||||
|
||||
var flags: std.ArrayList([]const u8) = .empty;
|
||||
defer flags.deinit(b.allocator);
|
||||
try flags.appendSlice(b.allocator, &.{
|
||||
|
||||
@@ -12,5 +12,6 @@
|
||||
},
|
||||
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
.android_ndk = .{ .path = "../android-ndk" },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ pub fn build(b: *std.Build) !void {
|
||||
try apple_sdk.addPaths(b, lib);
|
||||
}
|
||||
|
||||
if (target.result.abi.isAndroid()) {
|
||||
const android_ndk = @import("android_ndk");
|
||||
try android_ndk.addPaths(b, lib);
|
||||
}
|
||||
|
||||
var flags: std.ArrayList([]const u8) = .empty;
|
||||
defer flags.deinit(b.allocator);
|
||||
// Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
.android_ndk = .{ .path = "../android-ndk" },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ pub fn build(b: *std.Build) !void {
|
||||
try apple_sdk.addPaths(b, lib);
|
||||
}
|
||||
|
||||
if (target.result.abi.isAndroid()) {
|
||||
const android_ndk = @import("android_ndk");
|
||||
try android_ndk.addPaths(b, lib);
|
||||
}
|
||||
|
||||
var flags: std.ArrayList([]const u8) = .empty;
|
||||
defer flags.deinit(b.allocator);
|
||||
|
||||
|
||||
@@ -12,5 +12,6 @@
|
||||
},
|
||||
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
.android_ndk = .{ .path = "../android-ndk" },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -62,6 +62,10 @@ pub fn initShared(
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
|
||||
if (lib.rootModuleTarget().abi.isAndroid()) {
|
||||
try @import("android_ndk").addPaths(b, lib);
|
||||
}
|
||||
|
||||
if (lib.rootModuleTarget().os.tag.isDarwin()) {
|
||||
// Self-hosted x86_64 doesn't work for darwin. It may not work
|
||||
// for other platforms too but definitely darwin.
|
||||
|
||||
Reference in New Issue
Block a user