mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
android: build improvements
* Use a GitHub action to download the Android NDK * Use helper functions available on `std.Build` to simplify the build script. * Use various Zig-isms to simplify the code. FYI, using Nix to seems to be a non-starter as getting any Android development kits from nixpkgs requires accepting the Android license agreement and allowing many packages to use unfree licenses. And since the packages are unfree they are not cached by NixOS so the build triggers massive memory-hungry builds.
This commit is contained in:
21
.github/workflows/test.yml
vendored
21
.github/workflows/test.yml
vendored
@@ -373,7 +373,6 @@ jobs:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
~/Android/ndk
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@2126ae7fc54c9df00dd18f7f18754393182c73cd # v31.9.1
|
||||
@@ -385,22 +384,20 @@ jobs:
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Setup Android NDK
|
||||
run: |
|
||||
NDK_ROOT="$HOME/Android/ndk"
|
||||
NDK_DIR="$NDK_ROOT/android-ndk-${{ env.ANDROID_NDK_VERSION }}"
|
||||
if [ ! -d "$NDK_DIR" ]; then
|
||||
curl -fsSL -o /tmp/ndk.zip \
|
||||
"https://dl.google.com/android/repository/android-ndk-${{ env.ANDROID_NDK_VERSION }}-linux.zip"
|
||||
mkdir -p $NDK_ROOT
|
||||
unzip -q /tmp/ndk.zip -d $NDK_ROOT
|
||||
rm /tmp/ndk.zip
|
||||
fi
|
||||
echo "ANDROID_NDK_HOME=$NDK_DIR" >> "$GITHUB_ENV"
|
||||
uses: nttld/setup-ndk@ed92fe6cadad69be94a966a7ee3271275e62f779 # v1.6.0
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r29
|
||||
add-to-path: false
|
||||
link-to-sdk: false
|
||||
local-cache: true
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
nix develop -c zig build lib-vt \
|
||||
-Dtarget=${{ matrix.target }}
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
|
||||
build-linux:
|
||||
strategy:
|
||||
|
||||
@@ -25,9 +25,9 @@ pub fn addPaths(b: *std.Build, step: *std.Build.Step.Compile) !void {
|
||||
|
||||
var map: std.AutoHashMapUnmanaged(Key, ?struct {
|
||||
libc: std.Build.LazyPath,
|
||||
cpp_include: []const u8,
|
||||
lib: []const u8,
|
||||
}) = .{};
|
||||
cpp_include: std.Build.LazyPath,
|
||||
lib: std.Build.LazyPath,
|
||||
}) = .empty;
|
||||
};
|
||||
|
||||
const target = step.rootModuleTarget();
|
||||
@@ -38,16 +38,7 @@ pub fn addPaths(b: *std.Build, step: *std.Build.Step.Compile) !void {
|
||||
});
|
||||
|
||||
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_path = findNDKPath(b) orelse return error.AndroidNDKNotFound;
|
||||
|
||||
const ndk_triple = ndkTriple(target) orelse {
|
||||
gop.value_ptr.* = null;
|
||||
@@ -59,27 +50,47 @@ pub fn addPaths(b: *std.Build, step: *std.Build.Step.Compile) !void {
|
||||
return error.AndroidNDKUnsupportedHost;
|
||||
};
|
||||
|
||||
const sysroot = try std.fs.path.join(b.allocator, &.{
|
||||
ndk_path, "toolchains", "llvm", "prebuilt", host, "sysroot",
|
||||
const sysroot = b.pathJoin(&.{
|
||||
ndk_path,
|
||||
"toolchains",
|
||||
"llvm",
|
||||
"prebuilt",
|
||||
host,
|
||||
"sysroot",
|
||||
});
|
||||
const include_dir = b.pathJoin(&.{
|
||||
sysroot,
|
||||
"usr",
|
||||
"include",
|
||||
});
|
||||
const sys_include_dir = b.pathJoin(&.{
|
||||
sysroot,
|
||||
"usr",
|
||||
"include",
|
||||
ndk_triple,
|
||||
});
|
||||
const c_runtime_dir = b.pathJoin(&.{
|
||||
sysroot,
|
||||
"usr",
|
||||
"lib",
|
||||
ndk_triple,
|
||||
b.fmt("{d}", .{target.os.version_range.linux.android}),
|
||||
});
|
||||
const lib = b.pathJoin(&.{
|
||||
sysroot,
|
||||
"usr",
|
||||
"lib",
|
||||
ndk_triple,
|
||||
});
|
||||
const cpp_include = b.pathJoin(&.{
|
||||
sysroot,
|
||||
"usr",
|
||||
"include",
|
||||
"c++",
|
||||
"v1",
|
||||
});
|
||||
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,
|
||||
const libc_txt = b.fmt(
|
||||
\\include_dir={s}
|
||||
\\sys_include_dir={s}
|
||||
\\crt_dir={s}
|
||||
@@ -90,79 +101,86 @@ pub fn addPaths(b: *std.Build, step: *std.Build.Step.Compile) !void {
|
||||
|
||||
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,
|
||||
.cpp_include = .{ .cwd_relative = cpp_include },
|
||||
.lib = .{ .cwd_relative = lib },
|
||||
};
|
||||
}
|
||||
|
||||
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 });
|
||||
step.root_module.addSystemIncludePath(value.cpp_include);
|
||||
step.root_module.addLibraryPath(value.lib);
|
||||
}
|
||||
|
||||
fn findNDKPath(allocator: std.mem.Allocator) ?[]const u8 {
|
||||
fn findNDKPath(b: *std.Build) ?[]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;
|
||||
if (std.process.getEnvVarOwned(b.allocator, "ANDROID_NDK_HOME") catch null) |value| {
|
||||
if (value.len == 0) return null;
|
||||
var dir = std.fs.openDirAbsolute(value, .{}) catch return null;
|
||||
defer dir.close();
|
||||
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 (std.process.getEnvVarOwned(b.allocator, env) catch null) |sdk| {
|
||||
if (sdk.len > 0) {
|
||||
if (findLatestNDK(allocator, sdk)) |ndk| return ndk;
|
||||
if (findLatestNDK(b, 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,
|
||||
b.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,
|
||||
const default_sdk_path = b.pathJoin(
|
||||
&.{
|
||||
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);
|
||||
);
|
||||
|
||||
return findLatestNDK(b, 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;
|
||||
fn findLatestNDK(b: *std.Build, sdk_path: []const u8) ?[]const u8 {
|
||||
const ndk_dir = b.pathJoin(&.{ sdk_path, "ndk" });
|
||||
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 latest_: ?struct {
|
||||
name: []const u8,
|
||||
version: 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;
|
||||
const version = std.SemanticVersion.parse(file.name) catch continue;
|
||||
if (latest_) |latest| {
|
||||
if (version.order(latest.version) != .gt) continue;
|
||||
}
|
||||
latest_ = .{
|
||||
.name = file.name,
|
||||
.version = version,
|
||||
};
|
||||
}
|
||||
|
||||
if (latest_version) |version| {
|
||||
return std.fs.path.join(allocator, &.{ sdk_path, "ndk", version }) catch return null;
|
||||
}
|
||||
const latest = latest_ orelse return null;
|
||||
|
||||
return null;
|
||||
return b.pathJoin(&.{ sdk_path, "ndk", latest.name });
|
||||
}
|
||||
|
||||
fn hostTag() ?[]const u8 {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
.{
|
||||
.name = .android_ndk,
|
||||
.version = "0.0.1",
|
||||
.dependencies = .{},
|
||||
.version = "0.0.2",
|
||||
.fingerprint = 0xee68d62c5a97b68b,
|
||||
.paths = .{""},
|
||||
.dependencies = .{},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user