add support for external translate-c dependency usage (#92) · silbinarywolf/zig-android-sdk@9f00193 · GitHub
Skip to content

Commit 9f00193

Browse files
add support for external translate-c dependency usage (#92)
Fixes #91
1 parent a73fc01 commit 9f00193

17 files changed

Lines changed: 390 additions & 76 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 16 additions & 16 deletions

‎build.zig.zon‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
"build.zig.zon",
88
"src",
99
},
10+
.minimum_zig_version = "0.16.0",
1011
.fingerprint = 0x92bcb62d42fb2cee,
1112
}

‎src/androidbuild/Apk.zig‎

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -512,19 +512,49 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
512512
while (iter.next()) |it| {
513513
const module = it.value_ptr.*;
514514
const root_source_file = module.root_source_file orelse continue;
515+
const c_translate_target = module.resolved_target orelse continue;
516+
if (!c_translate_target.result.abi.isAndroid()) continue;
515517
switch (root_source_file) {
516518
.generated => |gen| {
517519
const step = gen.file.step;
518-
if (step.id != .translate_c) {
519-
continue;
520-
}
521-
const c_translate_target = module.resolved_target orelse continue;
522-
if (!c_translate_target.result.abi.isAndroid()) {
523-
continue;
520+
switch (step.id) {
521+
.translate_c => {
522+
// Detect if using Translate-C vendored version
523+
//
524+
// NOTE(jae): 2026-04-29
525+
// Longterm this will deprecated from Zig
526+
527+
const translate_c: *std.Build.Step.TranslateC = @fieldParentPtr("step", step);
528+
translate_c.addIncludePath(.{ .cwd_relative = apk.ndk.include_path });
529+
translate_c.addSystemIncludePath(.{ .cwd_relative = apk.getSystemIncludePath(c_translate_target) });
530+
},
531+
.run => {
532+
// Detect if using Translate-C external dependency and make assumptions about the flags
533+
// we can pass into it such as "isystem" and "-I"
534+
//
535+
// Name: https://codeberg.org/ziglang/translate-c/src/commit/71642ad0084d433f14b091a7b2b109f0be915dbb/build/Translator.zig#L89
536+
// Imports: https://codeberg.org/ziglang/translate-c/src/commit/71642ad0084d433f14b091a7b2b109f0be915dbb/build/Translator.zig#L103-L104
537+
if (std.mem.startsWith(u8, step.name, "translate-c ") and
538+
(module.import_table.contains("c_builtins") and module.import_table.contains("helpers")))
539+
{
540+
const run: *std.Build.Step.Run = @fieldParentPtr("step", step);
541+
542+
const ndk_include_path: LazyPath = .{ .cwd_relative = apk.ndk.include_path };
543+
const system_include_path: LazyPath = .{ .cwd_relative = apk.getSystemIncludePath(c_translate_target) };
544+
545+
// Exposes the system include path `path` to both translate-c and to `t.mod`.
546+
// https://codeberg.org/ziglang/translate-c/src/commit/71642ad0084d433f14b091a7b2b109f0be915dbb/build/Translator.zig#L207-L211
547+
module.addSystemIncludePath(system_include_path);
548+
run.addPrefixedDirectoryArg("-isystem", system_include_path);
549+
550+
// Exposes the include path `path` to both translate-c and to `t.mod`.
551+
// https://codeberg.org/ziglang/translate-c/src/commit/71642ad0084d433f14b091a7b2b109f0be915dbb/build/Translator.zig#L203-L206
552+
module.addIncludePath(ndk_include_path);
553+
run.addPrefixedDirectoryArg("-I", ndk_include_path);
554+
}
555+
},
556+
else => continue,
524557
}
525-
const translate_c: *std.Build.Step.TranslateC = @fieldParentPtr("step", step);
526-
translate_c.addIncludePath(.{ .cwd_relative = apk.ndk.include_path });
527-
translate_c.addSystemIncludePath(.{ .cwd_relative = apk.getSystemIncludePath(c_translate_target) });
528558
},
529559
else => continue,
530560
}

‎src/androidbuild/androidbuild.zig‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ pub fn standardTargets(b: *std.Build, target: ResolvedTarget) []ResolvedTarget {
107107
// Seperated logic into "resolveTargets" so that consumers of this library can create this option themselves and use "b.lazyImport"
108108
// See: https://github.com/silbinarywolf/zig-android-sdk/pull/82
109109
const all_targets = b.option(bool, "android", "Build for all Android targets (x86, x86_64, aarch64, arm, etc)") orelse false;
110+
110111
return resolveTargets(b, .{
111112
.default_target = target,
112113
.all_targets = all_targets,

‎test/build.zig‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
const Build = @import("std").Build;
2+
const builtin = @import("builtin");
3+
const eql = @import("std").mem.eql;
4+
5+
/// Make sure this is a stable version of Zig
6+
const is_latest_stable_zig = builtin.zig_version.pre == null and
7+
builtin.zig_version.major == 0 and builtin.zig_version.minor >= 16;
8+
9+
pub fn build(b: *Build) void {
10+
const target = b.standardTargetOptions(.{});
11+
const optimize = b.standardOptimizeOption(.{});
12+
13+
const all_step = b.step("all", "Run all tests");
14+
b.default_step = all_step;
15+
16+
for (b.available_deps) |available_dep| {
17+
const test_name, _ = available_dep;
18+
const test_case_dep_step: *Build.Step = blk: {
19+
if (is_latest_stable_zig) {
20+
const dep = b.dependency(test_name, .{
21+
.target = target,
22+
.optimize = optimize,
23+
.android = true,
24+
});
25+
break :blk dep.builder.default_step;
26+
} else {
27+
// Skip translate_c_dep as it requires Zig 0.16.0 stable as of 2026-04-29
28+
if (eql(u8, test_name, "translate_c_dep")) {
29+
continue;
30+
}
31+
32+
// NOTE(jae): 2026-04-29
33+
// Due to b.dependency() trying to always load Zig dependencies regardless
34+
// of version requirements, we make other Zig versions invoke build manually
35+
// so that ignoring dependency logic is respected.
36+
const cmd = b.addSystemCommand(&.{ "zig", "build", "-Dandroid=true" });
37+
cmd.setCwd(b.path(test_name));
38+
cmd.expectExitCode(0);
39+
break :blk &cmd.step;
40+
}
41+
};
42+
const test_step = b.step(test_name, b.fmt("Run the '{s}' test", .{test_name}));
43+
test_step.dependOn(test_case_dep_step);
44+
all_step.dependOn(test_step);
45+
}
46+
}

‎test/build.zig.zon‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.{
2+
.name = .tests,
3+
.version = "0.0.0",
4+
.fingerprint = 0x1260fc5e4c176d8b,
5+
.paths = .{""},
6+
.dependencies = .{
7+
.build = .{ .path = "build" },
8+
.lazy_android = .{ .path = "lazy_android" },
9+
.translate_c_dep = .{ .path = "translate_c_dep" },
10+
},
11+
}

‎test/build/build.zig‎

Lines changed: 62 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,20 @@
66

77
const std = @import("std");
88
const builtin = @import("builtin");
9+
const log = std.log.scoped(.build);
910

1011
const android = @import("android");
1112

13+
/// Make sure this is a stable version of Zig
14+
const is_latest_stable_zig = builtin.zig_version.pre == null and
15+
builtin.zig_version.major == 0 and builtin.zig_version.minor >= 16;
16+
1217
pub fn build(b: *std.Build) void {
1318
const exe_name: []const u8 = "build_test";
1419
const root_target = b.standardTargetOptions(.{});
1520
const optimize = b.standardOptimizeOption(.{});
1621
const android_targets = android.standardTargets(b, root_target);
1722

18-
// NOTE(jae): 2026-04-12
19-
// Run it *after* the "standardTargets" call
20-
testLazyImportAndResolveTargets(b, root_target);
21-
2223
var root_target_single = [_]std.Build.ResolvedTarget{root_target};
2324
const targets: []std.Build.ResolvedTarget = if (android_targets.len == 0)
2425
root_target_single[0..]
@@ -46,77 +47,91 @@ pub fn build(b: *std.Build) void {
4647
};
4748

4849
for (targets) |target| {
50+
if (!target.result.abi.isAndroid()) {
51+
std.debug.panic("For testing Android builds only. Target(s) should be Android not: {t}", .{target.result.abi});
52+
}
53+
54+
const translate_c_vendored_mod = testTranslateCVendor(b, target, optimize) orelse return;
55+
4956
const app_module = b.createModule(.{
5057
.target = target,
5158
.optimize = optimize,
5259
.root_source_file = b.path("src/build_test_main.zig"),
60+
.imports = &.{
61+
.{
62+
.name = "translate_c_internal",
63+
.module = translate_c_vendored_mod,
64+
},
65+
},
5366
});
5467

55-
var exe: *std.Build.Step.Compile = if (target.result.abi.isAndroid()) b.addLibrary(.{
68+
const libmain = b.addLibrary(.{
5669
.name = "main",
5770
.root_module = app_module,
5871
.linkage = .dynamic,
59-
}) else b.addExecutable(.{
60-
.name = exe_name,
61-
.root_module = app_module,
6272
});
6373

64-
// if building as library for Android, add this target
65-
// NOTE: Android has different CPU targets so you need to build a version of your
66-
// code for x86, x86_64, arm, arm64 and more
67-
if (target.result.abi.isAndroid()) {
68-
const apk: *android.Apk = android_apk orelse @panic("Android APK should be initialized");
69-
const android_dep = b.dependency("android", .{
70-
.optimize = optimize,
71-
.target = target,
72-
});
73-
exe.root_module.addImport("android", android_dep.module("android"));
74-
75-
apk.addArtifact(exe);
76-
} else {
77-
b.installArtifact(exe);
78-
79-
// If only 1 target, add "run" step
80-
if (targets.len == 1) {
81-
const run_step = b.step("run", "Run the application");
82-
const run_cmd = b.addRunArtifact(exe);
83-
run_step.dependOn(&run_cmd.step);
84-
}
85-
}
74+
const apk: *android.Apk = android_apk orelse @panic("Android APK should be initialized");
75+
const android_dep = b.dependency("android", .{
76+
.optimize = optimize,
77+
.target = target,
78+
});
79+
libmain.root_module.addImport("android", android_dep.module("android"));
80+
81+
apk.addArtifact(libmain);
8682
}
8783
if (android_apk) |apk| {
8884
testInstallAndAddRunStep(b, apk);
8985
}
9086
}
9187

92-
/// Test calling lazyImport and then calling "resolveTargets"
93-
///
94-
/// PR: https://github.com/silbinarywolf/zig-android-sdk/pull/83
95-
fn testLazyImportAndResolveTargets(b: *std.Build, root_target: std.Build.ResolvedTarget) void {
96-
const all_android_targets = true;
97-
const android_targets: []std.Build.ResolvedTarget = blk: {
98-
if (all_android_targets or root_target.result.abi.isAndroid()) {
99-
if (b.lazyImport(@This(), "lazy_android")) |lazy_android| {
100-
break :blk lazy_android.resolveTargets(b, .{
101-
.default_target = root_target,
102-
.all_targets = true,
103-
});
104-
}
105-
}
106-
break :blk &[0]std.Build.ResolvedTarget{};
107-
};
108-
if (android_targets.len != 4) @panic("expected 'resolveTargets' it to return 4 Android targets");
88+
/// Test the Translate-C vendored copy, this will eventually be deprecated and removed from Zig but for now exists in 0.16.X
89+
fn testTranslateCVendor(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) ?*std.Build.Module {
90+
const trans_c = b.addTranslateC(.{
91+
.root_source_file = b.addWriteFiles().add("android_c.h",
92+
\\#include <android/log.h>
93+
),
94+
.target = target,
95+
.optimize = optimize,
96+
});
97+
return trans_c.createModule();
98+
}
99+
100+
/// Test the Translate-C external dependency version
101+
fn testTranslateCExternal(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) ?*std.Build.Module {
102+
const translate_c_dep_name = if (comptime builtin.zig_version.major == 0 and builtin.zig_version.minor == 15)
103+
"translate_c_previous_stable"
104+
else
105+
"translate_c_stable";
106+
const translate_c_import = b.lazyImport(@This(), translate_c_dep_name) orelse return null;
107+
const translate_c = b.lazyDependency(translate_c_dep_name, .{}) orelse return null;
108+
const Translator = translate_c_import.Translator;
109+
110+
const trans_libandroid: Translator = .init(translate_c, .{
111+
.c_source_file = b.addWriteFiles().add("android_c.h",
112+
\\#include <android/log.h>
113+
),
114+
.target = target,
115+
.optimize = optimize,
116+
});
117+
return trans_libandroid.mod;
109118
}
110119

111120
/// Test the addLibraryFile functionality
112121
///
113122
/// Requested feature here: https://github.com/silbinarywolf/zig-android-sdk/issues/77
114123
fn testAddLibraryFile(b: *std.Build, apk: *android.Apk) void {
124+
if (!is_latest_stable_zig) {
125+
return;
126+
}
127+
115128
const vulkan_validation_dep = b.lazyDependency("vulkan_validation", .{}) orelse return;
116129
apk.addLibraryFile(.arm64_v8a, vulkan_validation_dep.path("arm64-v8a/libVkLayer_khronos_validation.so"));
117130
apk.addLibraryFile(.armeabi_v7a, vulkan_validation_dep.path("armeabi-v7a/libVkLayer_khronos_validation.so"));
118131
apk.addLibraryFile(.x86, vulkan_validation_dep.path("x86/libVkLayer_khronos_validation.so"));
119132
apk.addLibraryFile(.x86_64, vulkan_validation_dep.path("x86_64/libVkLayer_khronos_validation.so"));
133+
134+
log.info("testAddLibraryFile: add vulkan validation layers to APK", .{});
120135
}
121136

122137
fn testInstallAndAddRunStep(b: *std.Build, apk: *android.Apk) void {

‎test/build/build.zig.zon‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
.android = .{
66
.path = "../..",
77
},
8-
.lazy_android = .{
9-
.path = "../..",
10-
.lazy = true,
11-
},
128
.vulkan_validation = .{
139
.url = "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.341.0/android-binaries-1.4.341.0.zip",
1410
.hash = "N-V-__8AABTXlAV0z_BGl5-lZeOEm_d2gHEhExT2qjxMqQ72",

‎test/build/src/build_test_main.zig‎

Lines changed: 5 additions & 0 deletions

0 commit comments

Comments
 (0)