Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ jobs:
# run: zig build -Dandroid=true --verbose
# working-directory: examples/raylib

# TODO(jae): 2025-03-30
# NOTE(jae): 2025-03-30
# Need to figure out how to get 'adb shell monkey' to return an error code or be able to return an error code
# if the stdout of the command has 'Monkey aborted due to error.'

Expand Down
2 changes: 1 addition & 1 deletion examples/minimal/src/android-bind.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// TODO(jae): 2024-09-19
// NOTE(jae): 2024-09-19
// Consider just making this import from native C libraries.
// For now just wanted a basic example that compiles and runs on an Android emulator

Expand Down
11 changes: 9 additions & 2 deletions examples/sdl2/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,15 @@ pub fn build(b: *std.Build) void {
.target = android_targets[0],
});
const sdl_java_files = sdl_dep.namedWriteFiles("sdljava");
for (sdl_java_files.files.items) |file| {
apk.addJavaSourceFile(.{ .file = file.contents.copy });
if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16) {
// Deprecated: Zig 0.16.X and lower
for (sdl_java_files.files.items) |file| {
apk.addJavaSourceFile(.{ .file = file.contents.copy });
}
} else {
for (sdl_java_files.copies.items) |copy| {
apk.addJavaSourceFile(.{ .file = copy.src_file });
}
}
break :blk apk;
};
Expand Down
2 changes: 1 addition & 1 deletion src/android/Logger.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const Writer = std.Io.Writer;
/// logs with the package name.
///
/// To workaround this, we bake the package name into the Zig binaries.
const package_name: [:0]const u8 = @import("android_builtin").package_name;
const package_name: [:0]const u8 = std.fmt.comptimePrint("{s}", .{@import("android_builtin").package_name});

level: Level,
writer: Writer,
Expand Down
7 changes: 5 additions & 2 deletions src/android/android.zig
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@ fn androidLogFn(
.err => .err, // android.ANDROID_LOG_WARN = 6,
};

const fields_info = args_type_info.@"struct".fields;
if (fields_info.len == 0 and
const fields_len: comptime_int = if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16)
args_type_info.@"struct".fields.len
else
args_type_info.@"struct".field_names.len;
if (fields_len == 0 and
comptime std.mem.indexOfScalar(u8, format, '{') == null)
{
// If no formatting, log string directly with Android logging
Expand Down
2 changes: 1 addition & 1 deletion src/android/android_builtin.zig
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
//! Stub that is replaced by build system
pub const package_name: [:0]const u8 = "";
pub const package_name: []const u8 = "";
5 changes: 4 additions & 1 deletion src/android/zig016/zig016.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ const builtin = @import("builtin");
const ndk = @import("ndk");

const android_builtin = @import("android_builtin");
const package_name: ?[*:0]const u8 = if (android_builtin.package_name.len > 0) android_builtin.package_name else null;
const package_name: ?[*:0]const u8 = if (android_builtin.package_name.len > 0)
std.fmt.comptimePrint("{s}", .{android_builtin.package_name})
else
null;

const LogFunction = fn (comptime message_level: std.log.Level, comptime scope: @EnumLiteral(), comptime format: []const u8, args: anytype) void;

Expand Down
82 changes: 62 additions & 20 deletions src/androidbuild/Apk.zig
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,13 @@ pub const AddJavaSourceFileOption = struct {
/// of your APK.
pub fn addJavaSourceFile(apk: *Apk, options: AddJavaSourceFileOption) void {
const b = apk.b;
apk.java_files.append(b.allocator, options.file.dupe(b)) catch @panic("OOM");
const java_file = if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16)
// Deprecated: Just uses Build
options.file.dupe(b)
else
options.file.dupe(b.graph);

apk.java_files.append(b.allocator, java_file) catch @panic("OOM");
}

pub const AddJavaSourceFilesOptions = struct {
Expand Down Expand Up @@ -268,8 +274,8 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
@panic("call setAndroidManifestFile and point to your AndroidManifest.xml file");
};

// TODO(jae): 2024-10-01
// Add option where you can explicitly set an optional release mode with like:
// NOTE(jae): 2024-10-01
// Consider adding option where you can explicitly set an optional release mode with like:
// - setMode(.debug)
//
// If that value ISN'T set then we can just infer based on optimization level.
Expand Down Expand Up @@ -314,8 +320,11 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
aapt2link.addArg("-I");
aapt2link.addFileArg(root_jar);

if (b.verbose) {
aapt2link.addArg("-v");
if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16) {
// Deprecated: b.verbose no longer exists
if (b.verbose) {
aapt2link.addArg("-v");
}
}

// Inserts android:debuggable="true" in to the application node of the manifest,
Expand Down Expand Up @@ -392,7 +401,7 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
break :blk resources_apk_file;
};

const package_name_file = blk: {
const package_name_file: LazyPath = blk: {
const aapt2packagename = b.addSystemCommand(&[_][]const u8{
apk.build_tools.aapt2,
"dump",
Expand All @@ -403,7 +412,7 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
const aapt2_package_name_file = if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 15)
aapt2packagename.captureStdOut()
else
aapt2packagename.captureStdOut(.{});
aapt2packagename.captureStdOut(.{ .trim_whitespace = .trailing });
break :blk aapt2_package_name_file;
};

Expand Down Expand Up @@ -437,6 +446,8 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
.cwd_relative => |sub_path| sub_path,
.generated => @panic("invalid precompiled library, cannot be generated"),
.dependency => |dep| dep.sub_path,
// TODO(jae): 2026-06-29: Handle .relative in Zig 0.17.X
// .relative => @panic("UNHANDLED: invalid relative path"),
});
_ = apk_files.addCopyFile(precompiled_library.path, b.fmt("lib/{s}/{s}", .{ so_dir, precompiled_lib_basename }));
}
Expand Down Expand Up @@ -484,8 +495,17 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
if (!c_translate_target.result.abi.isAndroid()) continue;
switch (root_source_file) {
.generated => |gen| {
const step = gen.file.step;
switch (step.id) {
const step: *std.Build.Step = if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16)
// Deprecated: Zig 0.16.X, used to get Step directly
gen.file.step
else
b.graph.generated_files.items[@intFromEnum(gen.index)];
const tag = if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16)
// Deprecated: Zig 0.16.X, renamed to tag in later versions
step.id
else
step.tag;
switch (tag) {
.translate_c => {
// Detect if using Translate-C vendored version
//
Expand Down Expand Up @@ -609,10 +629,12 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
javac_cmd.addArg("-cp");
javac_cmd.addFileArg(root_jar);

// NOTE(jae): 2026-03-01
// If we have verbose logging on, telling us about deprecated Java files
if (b.verbose) {
javac_cmd.addArg("-Xlint:deprecation");
if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16) {
// NOTE(jae): 2026-03-01
// If we have verbose logging on, telling us about deprecated Java files
if (b.verbose) {
javac_cmd.addArg("-Xlint:deprecation");
}
}

// Output directory
Expand Down Expand Up @@ -641,7 +663,12 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
// d8.addArg(number_as_string);

// add each output *.class file
D8Glob.create(b, d8, java_classes_output_dir, root_jar);
if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16) {
D8Glob.create(b, d8, java_classes_output_dir, root_jar);
} else {
// TODO(jae): 2026-06-29: Update D8Glob to collect files as seperate Run artifact
@compileError("TODO: Rewrite D8Glob to use Step.Run");
}

// ie. android_sdk/platforms/android-{api-level}/android.jar
d8.addArg("--lib");
Expand All @@ -662,7 +689,8 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
apk.sdk.java_tools.jar,
});
jar.setName(runNameContext("jar (unzip resources.apk)"));
if (b.verbose) {

if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16 and b.verbose) {
jar.addArg("--verbose");
}

Expand Down Expand Up @@ -720,7 +748,7 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
// -M, --no-manifest = Do not create a manifest file for the entries
// -0, --no-compress = Store only; use no ZIP compression
const compress_zip_arg = "-cfM";
if (b.verbose) jar.addArg(compress_zip_arg ++ "v") else jar.addArg(compress_zip_arg);
if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16 and b.verbose) jar.addArg(compress_zip_arg ++ "v") else jar.addArg(compress_zip_arg);
const output_zip_file = jar.addOutputFileArg("compiled_code.zip");
jar.addArg(".");

Expand All @@ -747,7 +775,10 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
// -M, --no-manifest = Do not create a manifest file for the entries
// -0, --no-compress = Store only; use no ZIP compression
const update_zip_arg = "-ufM0";
if (b.verbose) jar.addArg(update_zip_arg ++ "v") else jar.addArg(update_zip_arg);
if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16 and b.verbose)
jar.addArg(update_zip_arg ++ "v")
else
jar.addArg(update_zip_arg);
jar.addFileArg(zip_file);
jar.addArg(".");
break :blk &jar.step;
Expand Down Expand Up @@ -777,7 +808,7 @@ fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
// Source: https://developer.android.com/tools/zipalign (10th Sept, 2024)
//
// Example: "zipalign -P 16 -f -v 4 infile.apk outfile.apk"
if (b.verbose) {
if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16 and b.verbose) {
zipalign.addArg("-v");
}
zipalign.addArgs(&.{
Expand Down Expand Up @@ -1072,7 +1103,7 @@ fn updateSharedLibraryOptions(artifact: *std.Build.Step.Compile) void {
artifact.root_module.strip = optimize == .ReleaseSmall;
}

// TODO(jae): 2024-09-19 - Copy-pasted from https://github.com/ikskuh/ZigAndroidTemplate/blob/master/Sdk.zig
// NOTE(jae): 2024-09-19 - Copy-pasted from https://github.com/ikskuh/ZigAndroidTemplate/blob/master/Sdk.zig
// Remove when https://github.com/ziglang/zig/issues/7935 is resolved.
if (artifact.root_module.resolved_target) |target| {
if (target.result.cpu.arch == .x86) {
Expand Down Expand Up @@ -1121,9 +1152,20 @@ fn updatePathWithJdk(apk: *Apk, run: *std.Build.Step.Run) Allocator.Error!void {
b.fmt("{s}/bin", .{apk.sdk.jdk_path}),
path,
});
// NOTE(jae): 2026-06-29
// Old version allowed mutating the env_map directly
try env_map.put("PATH", new_path);
} else {
} else if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16) {
// Deprecated: addPathDir was removed
run.addPathDir(b.pathJoin(&.{ apk.sdk.jdk_path, "bin" }));
} else {
var env_map = run.getEnvMap();
const path = env_map.get("PATH") orelse &[0]u8{};
const new_path = try std.mem.join(b.allocator, &[1]u8{std.fs.path.delimiter}, &.{
b.fmt("{s}/bin", .{apk.sdk.jdk_path}),
path,
});
run.setEnvironmentVariable("PATH", new_path);
}
}

Expand Down
4 changes: 0 additions & 4 deletions src/androidbuild/BuildTools.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ pub fn init(b: *std.Build, android_sdk_path: []const u8, build_tools_version: []
// ie. $ANDROID_HOME/build-tools/35.0.0
const build_tools_path = b.pathResolve(&[_][]const u8{ android_sdk_path, "build-tools", build_tools_version });

// TODO(jae): 2025-05-24
// We could validate build_tool_version to ensure its 3 numbers with dots seperating
// ie. "35.0.0"

// Check if build tools path is accessible
// ie. $ANDROID_HOME/build-tools/35.0.0
const access_wrapped_error = if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 15)
Expand Down
48 changes: 27 additions & 21 deletions src/androidbuild/BuiltinOptionsUpdate.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,45 @@ const fs = @import("std").fs;
const mem = @import("std").mem;
const assert = @import("std").debug.assert;

pub const base_id: Step.Id = .custom;

step: Step,

step: if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16) Step else void,
options: *Options,
package_name_stdout: LazyPath,

pub fn create(owner: *Build, package_name_stdout: LazyPath) *BuiltinOptionsUpdate {
const options = Build.addOptions(owner);

const builtin_options_update = owner.allocator.create(BuiltinOptionsUpdate) catch @panic("OOM");
builtin_options_update.* = .{
.step = Step.init(.{
.id = base_id,
.name = androidbuild.runNameContext("builtin_options_update"),
.owner = owner,
.makeFn = make,
}),
.options = options,
.package_name_stdout = package_name_stdout,
};
// Run step relies on this finishing
options.step.dependOn(&builtin_options_update.step);
// Depend on package name stdout before running this step
package_name_stdout.addStepDependencies(&builtin_options_update.step);

if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16) {
// Deprecated: Make Zig 0.15.X use this approach
builtin_options_update.* = .{
.step = Step.init(.{
.id = .custom,
.name = androidbuild.runNameContext("builtin_options_update"),
.owner = owner,
.makeFn = deprecatedZig016OrLowerMake,
}),
.options = Build.addOptions(owner),
.package_name_stdout = package_name_stdout,
};
// Run step relies on this finishing
builtin_options_update.options.step.dependOn(&builtin_options_update.step);
// Depend on package name stdout before running this step
package_name_stdout.addStepDependencies(&builtin_options_update.step);
} else {
builtin_options_update.* = .{
.step = {},
.options = Build.addOptions(owner),
.package_name_stdout = package_name_stdout,
};
builtin_options_update.options.addOptionPath("package_name", package_name_stdout);
}
return builtin_options_update;
}

pub fn createModule(self: *BuiltinOptionsUpdate) *Build.Module {
return self.options.createModule();
}

fn make(step: *Step, _: Build.Step.MakeOptions) !void {
fn deprecatedZig016OrLowerMake(step: *Step, _: Build.Step.MakeOptions) !void {
const b = step.owner;
const builtin_options_update: *BuiltinOptionsUpdate = @fieldParentPtr("step", step);
const options = builtin_options_update.options;
Expand Down
38 changes: 22 additions & 16 deletions src/androidbuild/DirectoryFileInput.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,28 @@ const FileInputRange = struct {
};

pub fn create(owner: *Build, run: *Run, dir: LazyPath) void {
const self = owner.allocator.create(DirectoryFileInput) catch @panic("OOM");
self.* = .{
.step = Step.init(.{
.id = .custom,
.name = androidbuild.runNameContext("directory-file-input"),
.owner = owner,
.makeFn = make,
}),
.run = run,
.dir = dir,
.file_input_range = null,
};
// Run step relies on DirectoryFileInput finishing
run.step.dependOn(&self.step);
// If dir is generated then this will wait for that dir to generate
dir.addStepDependencies(&self.step);
if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 16) {
// Deprecated: Zig 0.16.X and lower supported custom Make steps
const self = owner.allocator.create(DirectoryFileInput) catch @panic("OOM");
self.* = .{
.step = Step.init(.{
.id = .custom,
.name = androidbuild.runNameContext("directory-file-input"),
.owner = owner,
.makeFn = make,
}),
.run = run,
.dir = dir,
.file_input_range = null,
};
// Run step relies on DirectoryFileInput finishing
run.step.dependOn(&self.step);
// If dir is generated then this will wait for that dir to generate
dir.addStepDependencies(&self.step);
} else {
// TODO(jae): 2026-06-29: Find all files in directory input
@compileError("TODO: Update this in the same way I've updated D8Glob");
}
}

fn make(step: *Step, options: Build.Step.MakeOptions) !void {
Expand Down
4 changes: 2 additions & 2 deletions src/androidbuild/androidbuild.zig
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,8 @@ const AndroidTargetQuery = struct {
.abi = if (android_target.cpu_arch != .arm) .android else .androideabi,
.cpu_arch = android_target.cpu_arch,
.cpu_features_add = android_target.cpu_features_add,
// TODO(jae): 2025-05-11
// Setup Android API Level for Zig 0.14.0+
// NOTE(jae): 2025-05-11
// Consider setting Android API Level for Zig 0.14.0+
// .android_api_level = null,
};
}
Expand Down
Loading
Loading