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
508 changes: 254 additions & 254 deletions examples/minimal/src/android-bind.zig

Large diffs are not rendered by default.

40 changes: 21 additions & 19 deletions examples/minimal/src/minimal.zig
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn nativeActivityOnCreate(activity: *androidbind.ANativeActivity, savedState: []
\\ App pid: {}
\\ Build mode: {s}
\\ ABI: {s}-{s}-{s}
\\ Compiler version: {}
\\ Compiler version: {}.{}.{}
\\ Compiler backend: {s}
, .{
"Minimal App", // build_options.app_name,
Expand All @@ -48,7 +48,9 @@ fn nativeActivityOnCreate(activity: *androidbind.ANativeActivity, savedState: []
@tagName(builtin.cpu.arch),
@tagName(builtin.os.tag),
@tagName(builtin.abi),
builtin.zig_version,
builtin.zig_version.major,
builtin.zig_version.minor,
builtin.zig_version.patch,
@tagName(builtin.zig_backend),
});

Expand Down Expand Up @@ -81,7 +83,7 @@ comptime {
}

/// Android entry point
fn NativeActivity_onCreate(activity: *androidbind.ANativeActivity, rawSavedState: ?[*]u8, rawSavedStateSize: usize) callconv(.C) void {
fn NativeActivity_onCreate(activity: *androidbind.ANativeActivity, rawSavedState: ?[*]u8, rawSavedStateSize: usize) callconv(.c) void {
const savedState: []const u8 = if (rawSavedState) |s|
s[0..rawSavedStateSize]
else
Expand Down Expand Up @@ -147,7 +149,7 @@ fn makeNativeActivityGlue(comptime App: type) androidbind.ANativeActivityCallbac
}

// return value must be created with malloc(), so we pass the c_allocator to App.onSaveInstanceState
fn onSaveInstanceState(activity: *androidbind.ANativeActivity, outSize: *usize) callconv(.C) ?[*]u8 {
fn onSaveInstanceState(activity: *androidbind.ANativeActivity, outSize: *usize) callconv(.c) ?[*]u8 {
outSize.* = 0;
if (!@hasDecl(App, "onSaveInstanceState")) {
log.debug("ANativeActivity callback onSaveInstanceState not available on {s}", .{@typeName(App)});
Expand All @@ -162,52 +164,52 @@ fn makeNativeActivityGlue(comptime App: type) androidbind.ANativeActivityCallbac
return null;
}

fn onDestroy(activity: *androidbind.ANativeActivity) callconv(.C) void {
fn onDestroy(activity: *androidbind.ANativeActivity) callconv(.c) void {
const instance = activity.instance orelse return;
const app: *App = @ptrCast(@alignCast(instance));
app.deinit();
std.heap.c_allocator.destroy(app);
}
fn onStart(activity: *androidbind.ANativeActivity) callconv(.C) void {
fn onStart(activity: *androidbind.ANativeActivity) callconv(.c) void {
invoke(activity, "onStart", .{});
}
fn onResume(activity: *androidbind.ANativeActivity) callconv(.C) void {
fn onResume(activity: *androidbind.ANativeActivity) callconv(.c) void {
invoke(activity, "onResume", .{});
}
fn onPause(activity: *androidbind.ANativeActivity) callconv(.C) void {
fn onPause(activity: *androidbind.ANativeActivity) callconv(.c) void {
invoke(activity, "onPause", .{});
}
fn onStop(activity: *androidbind.ANativeActivity) callconv(.C) void {
fn onStop(activity: *androidbind.ANativeActivity) callconv(.c) void {
invoke(activity, "onStop", .{});
}
fn onConfigurationChanged(activity: *androidbind.ANativeActivity) callconv(.C) void {
fn onConfigurationChanged(activity: *androidbind.ANativeActivity) callconv(.c) void {
invoke(activity, "onConfigurationChanged", .{});
}
fn onLowMemory(activity: *androidbind.ANativeActivity) callconv(.C) void {
fn onLowMemory(activity: *androidbind.ANativeActivity) callconv(.c) void {
invoke(activity, "onLowMemory", .{});
}
fn onWindowFocusChanged(activity: *androidbind.ANativeActivity, hasFocus: c_int) callconv(.C) void {
fn onWindowFocusChanged(activity: *androidbind.ANativeActivity, hasFocus: c_int) callconv(.c) void {
invoke(activity, "onWindowFocusChanged", .{(hasFocus != 0)});
}
fn onNativeWindowCreated(activity: *androidbind.ANativeActivity, window: *androidbind.ANativeWindow) callconv(.C) void {
fn onNativeWindowCreated(activity: *androidbind.ANativeActivity, window: *androidbind.ANativeWindow) callconv(.c) void {
invoke(activity, "onNativeWindowCreated", .{window});
}
fn onNativeWindowResized(activity: *androidbind.ANativeActivity, window: *androidbind.ANativeWindow) callconv(.C) void {
fn onNativeWindowResized(activity: *androidbind.ANativeActivity, window: *androidbind.ANativeWindow) callconv(.c) void {
invoke(activity, "onNativeWindowResized", .{window});
}
fn onNativeWindowRedrawNeeded(activity: *androidbind.ANativeActivity, window: *androidbind.ANativeWindow) callconv(.C) void {
fn onNativeWindowRedrawNeeded(activity: *androidbind.ANativeActivity, window: *androidbind.ANativeWindow) callconv(.c) void {
invoke(activity, "onNativeWindowRedrawNeeded", .{window});
}
fn onNativeWindowDestroyed(activity: *androidbind.ANativeActivity, window: *androidbind.ANativeWindow) callconv(.C) void {
fn onNativeWindowDestroyed(activity: *androidbind.ANativeActivity, window: *androidbind.ANativeWindow) callconv(.c) void {
invoke(activity, "onNativeWindowDestroyed", .{window});
}
fn onInputQueueCreated(activity: *androidbind.ANativeActivity, input_queue: *androidbind.AInputQueue) callconv(.C) void {
fn onInputQueueCreated(activity: *androidbind.ANativeActivity, input_queue: *androidbind.AInputQueue) callconv(.c) void {
invoke(activity, "onInputQueueCreated", .{input_queue});
}
fn onInputQueueDestroyed(activity: *androidbind.ANativeActivity, input_queue: *androidbind.AInputQueue) callconv(.C) void {
fn onInputQueueDestroyed(activity: *androidbind.ANativeActivity, input_queue: *androidbind.AInputQueue) callconv(.c) void {
invoke(activity, "onInputQueueDestroyed", .{input_queue});
}
fn onContentRectChanged(activity: *androidbind.ANativeActivity, rect: *const androidbind.ARect) callconv(.C) void {
fn onContentRectChanged(activity: *androidbind.ANativeActivity, rect: *const androidbind.ARect) callconv(.c) void {
invoke(activity, "onContentRectChanged", .{rect});
}
};
Expand Down
2 changes: 1 addition & 1 deletion examples/sdl2/src/sdl-zig-demo.zig
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ comptime {
}

/// This needs to be exported for Android builds
fn SDL_main() callconv(.C) void {
fn SDL_main() callconv(.c) void {
if (comptime builtin.abi.isAndroid()) {
_ = std.start.callMain();
} else {
Expand Down
16 changes: 7 additions & 9 deletions examples/sdl2/third-party/sdl2/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@ pub fn build(b: *std.Build) !void {
const sdl_include_path = sdl_path.path(b, "include");

const is_shared_library = target.result.abi.isAndroid(); // NOTE(jae): 2024-09-22: Android uses shared library as SDL2 loads it as part of SDLActivity.java
const lib = if (!is_shared_library) b.addStaticLibrary(.{
const lib = b.addLibrary(.{
.name = "SDL2",
.target = target,
.optimize = optimize,
.link_libc = true,
}) else b.addSharedLibrary(.{
.name = "SDL2",
.target = target,
.optimize = optimize,
.link_libc = true,
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
}),
.linkage = if (is_shared_library) .dynamic else .static,
});

lib.addCSourceFiles(.{
Expand Down
26 changes: 12 additions & 14 deletions src/android/android.zig
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ const LogWriter = struct {
line_len: usize = 0,

const Error = error{};
const Writer = std.io.Writer(*@This(), Error, write);
const Writer = if (builtin.zig_version.major == 0 and builtin.zig_version.minor == 14)
std.io.Writer(*@This(), Error, write)
else
std.io.GenericWriter(*@This(), Error, write);

fn write(self: *@This(), buffer: []const u8) Error!usize {
for (buffer) |char| {
Expand Down Expand Up @@ -168,8 +171,11 @@ const Panic = struct {
/// This is used to catch and handle panics triggered by the panic handler.
threadlocal var panic_stage: usize = 0;

const is_zig_014_or_less = (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 14);

fn panic(message: []const u8, ret_addr: ?usize) noreturn {
@branchHint(.cold);
if (!is_zig_014_or_less) @compileError("Android Panic needs to be updated to the newer io.Writer vtable implementation to work in Zig 0.15.0+");
if (comptime !builtin.abi.isAndroid()) @compileError("do not use Android panic for non-Android builds");
const first_trace_addr = ret_addr orelse @returnAddress();
panicImpl(first_trace_addr, message);
Expand Down Expand Up @@ -223,12 +229,6 @@ const Panic = struct {
}

const io = struct {
const tty = struct {
inline fn detectConfig(_: *LogWriter) std.io.tty.Config {
return .no_color;
}
};

var writer = LogWriter{
.level = .fatal,
};
Expand Down Expand Up @@ -307,14 +307,13 @@ const Panic = struct {

fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {
nosuspend {
const stderr = io.getStdErr().writer();
if (comptime builtin.target.cpu.arch.isWasm()) {
if (native_os == .wasi) {
const stderr = io.getStdErr().writer();
stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;
}
return;
}
const stderr = io.getStdErr().writer();
if (builtin.strip_debug_info) {
stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
return;
Expand All @@ -325,13 +324,13 @@ const Panic = struct {
};
if (builtin.zig_version.major == 0 and builtin.zig_version.minor == 13) {
// Legacy 0.13.0
writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, io.tty.detectConfig(io.getStdErr())) catch |err| {
writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, .no_color) catch |err| {
stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
return;
};
} else {
// 0.14.0-dev+
writeStackTrace(stack_trace, stderr, debug_info, io.tty.detectConfig(io.getStdErr())) catch |err| {
writeStackTrace(stack_trace, stderr, debug_info, .no_color) catch |err| {
stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
return;
};
Expand All @@ -342,14 +341,13 @@ const Panic = struct {
const writeCurrentStackTrace = std.debug.writeCurrentStackTrace;
fn dumpCurrentStackTrace(start_addr: ?usize) void {
nosuspend {
const stderr = io.getStdErr().writer();
if (comptime builtin.target.cpu.arch.isWasm()) {
if (native_os == .wasi) {
const stderr = io.getStdErr().writer();
stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;
}
return;
}
const stderr = io.getStdErr().writer();
if (builtin.strip_debug_info) {
stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
return;
Expand All @@ -358,7 +356,7 @@ const Panic = struct {
stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
return;
};
writeCurrentStackTrace(stderr, debug_info, io.tty.detectConfig(io.getStdErr()), start_addr) catch |err| {
writeCurrentStackTrace(stderr, debug_info, .no_color, start_addr) catch |err| {
stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
return;
};
Expand Down
11 changes: 7 additions & 4 deletions src/androidbuild/androidbuild.zig
Original file line number Diff line number Diff line change
Expand Up @@ -100,22 +100,25 @@ pub fn runNameContext(comptime name: []const u8) []const u8 {
pub fn printErrorsAndExit(message: []const u8, errors: []const []const u8) noreturn {
nosuspend {
log.err("{s}", .{message});
const stderr = std.io.getStdErr().writer();
const stderr = if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 14)
std.io.getStdErr().writer()
else
std.fs.File.stderr();
std.debug.lockStdErr();
defer std.debug.unlockStdErr();
for (errors) |err| {
var it = std.mem.splitScalar(u8, err, '\n');
const headline = it.next() orelse continue;
stderr.writeAll("- ") catch {};
stderr.writeAll(headline) catch {};
stderr.writeByte('\n') catch {};
stderr.writeAll("\n") catch {};
while (it.next()) |line| {
stderr.writeAll(" ") catch {};
stderr.writeAll(line) catch {};
stderr.writeByte('\n') catch {};
stderr.writeAll("\n") catch {};
}
}
stderr.writeByte('\n') catch {};
stderr.writeAll("\n") catch {};
}
std.process.exit(1);
}
Expand Down
18 changes: 17 additions & 1 deletion src/androidbuild/apk.zig
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ fn applyLibLinkCppWorkaroundIssue19(apk: *Apk, artifact: *Step.Compile) void {
const b = apk.b;

const should_apply_fix = (artifact.root_module.link_libcpp == true or
artifact.dependsOnSystemLibrary("c++abi_zig_workaround"));
dependsOnSystemLibrary(artifact, "c++abi_zig_workaround"));
if (!should_apply_fix) {
return;
}
Expand Down Expand Up @@ -821,6 +821,22 @@ fn applyLibLinkCppWorkaroundIssue19(apk: *Apk, artifact: *Step.Compile) void {
}
}

/// Copy-paste of "dependsOnSystemLibrary" that only checks if that system library is included to
/// workaround a bug with in Zig 0.15.0-dev.1092+d772c0627
fn dependsOnSystemLibrary(compile: *Step.Compile, name: []const u8) bool {
for (compile.getCompileDependencies(true)) |some_compile| {
for (some_compile.root_module.getGraph().modules) |mod| {
for (mod.link_objects.items) |lo| {
switch (lo) {
.system_lib => |lib| if (std.mem.eql(u8, lib.name, name)) return true,
else => {},
}
}
}
}
return false;
}

fn updateSharedLibraryOptions(artifact: *std.Build.Step.Compile) void {
if (artifact.linkage) |linkage| {
if (linkage != .dynamic) {
Expand Down
6 changes: 4 additions & 2 deletions src/androidbuild/builtin_options_update.zig
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ fn make(step: *Step, _: Build.Step.MakeOptions) !void {
const builtin_options_update: *BuiltinOptionsUpdate = @fieldParentPtr("step", step);
const options = builtin_options_update.options;

const package_name_path = builtin_options_update.package_name_stdout.getPath2(b, step);
const package_name_path = builtin_options_update.package_name_stdout.getPath3(b, step);

const file = try fs.openFileAbsolute(package_name_path, .{});
// NOTE(jae): 2025-07-23
// As of Zig 0.15.0-dev.1092+d772c0627, package_name_path.openFile("") is not possible as it assumes you're appending *something*
const file = try package_name_path.root_dir.handle.openFile(package_name_path.sub_path, .{});

// Read package name from stdout and strip line feed / carriage return
// ie. "com.zig.sdl2\n\r"
Expand Down
9 changes: 6 additions & 3 deletions src/androidbuild/d8glob.zig
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn make(step: *Step, _: Build.Step.MakeOptions) !void {
const glob: *@This() = @fieldParentPtr("step", step);
const d8 = glob.run;

const search_dir = glob.dir.getPath2(b, step);
const search_dir = glob.dir.getPath3(b, step);

// NOTE(jae): 2024-09-22
// Change current working directory to where the Java classes are
Expand All @@ -62,8 +62,11 @@ fn make(step: *Step, _: Build.Step.MakeOptions) !void {
// - If "d8" has the ability to pass a file of command line parameters, that would work too but I haven't seen any in the docs
d8.setCwd(glob.dir);

var dir = try fs.openDirAbsolute(search_dir, .{ .iterate = true });
// NOTE(jae): 2025-07-23
// As of Zig 0.15.0-dev.1092+d772c0627, package_name_path.openDir("") is not possible as it assumes you're appending a sub-path
var dir = try search_dir.root_dir.handle.openDir(search_dir.sub_path, .{ .iterate = true });
defer dir.close();

var walker = try dir.walk(arena);
defer walker.deinit();
while (try walker.next()) |entry| {
Expand All @@ -83,7 +86,7 @@ fn make(step: *Step, _: Build.Step.MakeOptions) !void {
// This is to avoid the Java error "command line too long" that can occur with d8
d8.addArg(entry.path);
d8.addFileInput(LazyPath{
.cwd_relative = try fs.path.resolve(arena, &.{ search_dir, entry.path }),
.cwd_relative = try search_dir.root_dir.join(b.allocator, &.{ search_dir.sub_path, entry.path }),
});
}
}
Expand Down
Loading