Skip to content

Commit 7f74ebb

Browse files
Add support for Zig 0.14.0 in CI build (0.14.0-dev.1632) and add code changes to support both 0.13.0 and 0.14.0-dev (#2)
1 parent 2ca003d commit 7f74ebb

4 files changed

Lines changed: 201 additions & 66 deletions

File tree

.github/workflows/ci.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ jobs:
1818
matrix:
1919
include:
2020
- os: "ubuntu-22.04"
21-
suffix: "linux"
21+
zig: "0.13.0" # aka "latest" at time of writing (2024-09-20)
22+
23+
# Use the nightly build
24+
- os: "ubuntu-22.04"
25+
zig: "master"
2226

2327
runs-on: ${{matrix.os}}
2428

@@ -31,7 +35,7 @@ jobs:
3135
# see: https://ziglang.org/news/migrate-to-self-hosting/
3236
uses: mlugg/setup-zig@v1
3337
with:
34-
version: "0.13.0"
38+
version: ${{matrix.zig}}
3539

3640
- name: Set up JDK 17
3741
uses: actions/setup-java@v3

examples/minimal/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ zig build -Dandroid=true
1616
adb install ./zig-out/bin/minimal.apk
1717
```
1818

19+
### Uninstall your application
20+
21+
If installing your application fails with something like:
22+
```
23+
adb: failed to install ./zig-out/bin/minimal.apk: Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.zig.minimal signatures do not match newer version; ignoring!]
24+
```
25+
26+
```sh
27+
adb uninstall "com.zig.minimal"
28+
```
29+
1930
### View logs of application
2031

2132
Powershell

src/android/android.zig

Lines changed: 184 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const builtin = @import("builtin");
33

44
extern "log" fn __android_log_write(prio: c_int, tag: [*c]const u8, text: [*c]const u8) c_int;
55

6+
/// Alternate panic implementation that calls __android_log_write so that you can see the logging via "adb logcat"
67
pub const panic = Panic.panic;
78

89
/// Levels for Android
@@ -23,20 +24,26 @@ pub const Level = enum(u8) {
2324
// verbose = 2, // ANDROID_LOG_VERBOSE
2425
// default = 1, // ANDROID_LOG_DEFAULT
2526

26-
/// Returns a string literal of the given level in full text form.
27-
pub fn asText(comptime self: Level) []const u8 {
28-
return switch (self) {
29-
.err => "error",
30-
.warn => "warning",
31-
.info => "info",
32-
.debug => "debug",
33-
};
34-
}
27+
// Returns a string literal of the given level in full text form.
28+
// pub fn asText(comptime self: Level) []const u8 {
29+
// return switch (self) {
30+
// .err => "error",
31+
// .warn => "warning",
32+
// .info => "info",
33+
// .debug => "debug",
34+
// };
35+
// }
3536
};
3637

38+
/// Alternate log function implementation that calls __android_log_write so that you can see the logging via "adb logcat"
3739
pub fn logFn(
3840
comptime message_level: std.log.Level,
39-
comptime scope: @Type(.EnumLiteral),
41+
comptime scope: if (builtin.zig_version.minor != 13)
42+
// Support Zig 0.14.0-dev
43+
@Type(.enum_literal)
44+
else
45+
// Support Zig 0.13.0
46+
@Type(.EnumLiteral),
4047
comptime format: []const u8,
4148
args: anytype,
4249
) void {
@@ -61,9 +68,11 @@ pub fn logFn(
6168
}
6269
}
6370

71+
/// LogWriter was was taken basically as is from: https://github.com/ikskuh/ZigAndroidTemplate
6472
const LogWriter = struct {
6573
/// name of the application / log scope
66-
const tag: [*c]const u8 = null; // = "zig-app";
74+
/// if not set, it'll default to the "package" attribute defined in AndroidManifest.xml
75+
const tag: [*c]const u8 = null;
6776

6877
level: Level,
6978

@@ -109,6 +118,13 @@ const LogWriter = struct {
109118
}
110119
};
111120

121+
/// Panic is a copy-paste of the panic logic from Zig but replaces usages of getStdErr with our own writer
122+
///
123+
/// Example output:
124+
/// 09-22 13:08:49.578 3390 3390 F com.zig.minimal: thread 3390 panic: your panic message here
125+
/// 09-22 13:08:49.637 3390 3390 F com.zig.minimal: zig-android-sdk/examples\minimal/src/minimal.zig:33:15: 0x7ccb77b282dc in nativeActivityOnCreate (minimal)
126+
/// 09-22 13:08:49.637 3390 3390 F com.zig.minimal: zig-android-sdk/examples/minimal/src/minimal.zig:84:27: 0x7ccb77b28650 in ANativeActivity_onCreate (minimal)
127+
/// 09-22 13:08:49.637 3390 3390 F com.zig.minimal: ???:?:?: 0x7ccea4021d9c in ??? (libandroid_runtime.so)
112128
pub const Panic = struct {
113129
/// Non-zero whenever the program triggered a panic.
114130
/// The counter is incremented/decremented atomically.
@@ -126,17 +142,87 @@ pub const Panic = struct {
126142
panicImpl(stack_trace, first_trace_addr, message);
127143
}
128144

145+
/// Must be called only after adding 1 to `panicking`. There are three callsites.
146+
fn waitForOtherThreadToFinishPanicking() void {
147+
if (panicking.fetchSub(1, .seq_cst) != 1) {
148+
// Another thread is panicking, wait for the last one to finish
149+
// and call abort()
150+
if (builtin.single_threaded) unreachable;
151+
152+
// Sleep forever without hammering the CPU
153+
var futex = std.atomic.Value(u32).init(0);
154+
while (true) std.Thread.Futex.wait(&futex, 0);
155+
unreachable;
156+
}
157+
}
158+
159+
const native_os = builtin.os.tag;
160+
const updateSegfaultHandler = std.debug.updateSegfaultHandler;
161+
162+
fn resetSegfaultHandler() void {
163+
// NOTE(jae): 2024-09-22
164+
// Not applicable for Android as it runs on the OS tag Linux
165+
// if (native_os == .windows) {
166+
// if (windows_segfault_handle) |handle| {
167+
// assert(windows.kernel32.RemoveVectoredExceptionHandler(handle) != 0);
168+
// windows_segfault_handle = null;
169+
// }
170+
// return;
171+
// }
172+
var act = posix.Sigaction{
173+
.handler = .{ .handler = posix.SIG.DFL },
174+
.mask = posix.empty_sigset,
175+
.flags = 0,
176+
};
177+
// To avoid a double-panic, do nothing if an error happens here.
178+
if (builtin.zig_version.major == 0 and builtin.zig_version.minor == 13) {
179+
// Legacy 0.13.0
180+
updateSegfaultHandler(&act) catch {};
181+
} else {
182+
// 0.14.0-dev+
183+
updateSegfaultHandler(&act);
184+
}
185+
}
186+
187+
const io = struct {
188+
const tty = struct {
189+
inline fn detectConfig(_: *LogWriter) std.io.tty.Config {
190+
return .no_color;
191+
}
192+
};
193+
194+
var writer = LogWriter{
195+
.level = .fatal,
196+
};
197+
198+
inline fn getStdErr() *LogWriter {
199+
return &writer;
200+
}
201+
};
202+
203+
const posix = std.posix;
204+
const enable_segfault_handler = std.options.enable_segfault_handler;
205+
206+
/// Panic is a copy-paste of the panic logic from Zig but replaces usages of getStdErr with our own writer
207+
///
208+
/// - Provide custom "io" namespace so we can easily customize getStdErr() to be our own writer
209+
/// - Provide other functions from std.debug.*
129210
fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize, msg: []const u8) noreturn {
130-
@setCold(true);
131-
132-
// NOTE(jae): 2024-09-15
133-
// resetSegfaultHandler is not a public function
134-
// if (comptime std.options.enable_segfault_handler) {
135-
// // If a segfault happens while panicking, we want it to actually segfault, not trigger
136-
// // the handler.
137-
// std.debug.resetSegfaultHandler();
211+
// NOTE(jae): 2024-09-22
212+
// Cannot mark this as cold(true) OR setCold() depending on Zig version as we get an invalid builtin function
213+
// comptime {
214+
// if (builtin.zig_version.minor == 13)
215+
// @setCold(true)
216+
// else
217+
// @cold(true);
138218
// }
139219

220+
if (enable_segfault_handler) {
221+
// If a segfault happens while panicking, we want it to actually segfault, not trigger
222+
// the handler.
223+
resetSegfaultHandler();
224+
}
225+
140226
// Note there is similar logic in handleSegfaultPosix and handleSegfaultWindowsExtra.
141227
nosuspend switch (panic_stage) {
142228
0 => {
@@ -149,69 +235,104 @@ pub const Panic = struct {
149235
panic_mutex.lock();
150236
defer panic_mutex.unlock();
151237

152-
var logger = LogWriter{
153-
.level = .fatal,
154-
};
155-
const stderr = logger.writer();
238+
const stderr = io.getStdErr().writer();
156239
if (builtin.single_threaded) {
157-
stderr.print("panic: ", .{}) catch @trap();
240+
stderr.print("panic: ", .{}) catch posix.abort();
158241
} else {
159242
const current_thread_id = std.Thread.getCurrentId();
160-
stderr.print("thread {} panic: ", .{current_thread_id}) catch @trap();
243+
stderr.print("thread {} panic: ", .{current_thread_id}) catch posix.abort();
161244
}
162-
stderr.print("{s}\n", .{msg}) catch @trap();
245+
stderr.print("{s}\n", .{msg}) catch posix.abort();
163246
if (trace) |t| {
164-
// std.debug.dumpStackTrace(t.*);
165-
if (builtin.strip_debug_info) {
166-
stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
167-
} else {
168-
const debug_info = std.debug.getSelfDebugInfo() catch |err| {
169-
stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
170-
@trap();
171-
};
172-
var debug_info_arena_allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator);
173-
const allocator = debug_info_arena_allocator.allocator();
174-
std.debug.writeStackTrace(t.*, stderr, allocator, debug_info, .no_color) catch |err| {
175-
stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
176-
@trap();
177-
};
178-
}
179-
}
180-
// std.debug.dumpCurrentStackTrace(first_trace_addr);
181-
{
182-
if (builtin.strip_debug_info) {
183-
stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
184-
} else {
185-
const debug_info = std.debug.getSelfDebugInfo() catch |err| {
186-
stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
187-
@trap();
188-
};
189-
std.debug.writeCurrentStackTrace(stderr, debug_info, .no_color, first_trace_addr) catch |err| {
190-
stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
191-
@trap();
192-
};
193-
}
247+
dumpStackTrace(t.*);
194248
}
249+
dumpCurrentStackTrace(first_trace_addr);
195250
}
196-
// std.debug.waitForOtherThreadToFinishPanicking();
251+
252+
waitForOtherThreadToFinishPanicking();
197253
},
198254
1 => {
199255
panic_stage = 2;
200256

201257
// A panic happened while trying to print a previous panic message,
202258
// we're still holding the mutex but that's fine as we're going to
203259
// call abort()
204-
var logger = LogWriter{
205-
.level = .fatal,
206-
};
207-
const stderr = logger.writer();
208-
stderr.print("Panicked during a panic. Aborting.\n", .{}) catch @trap();
260+
const stderr = io.getStdErr().writer();
261+
stderr.print("Panicked during a panic. Aborting.\n", .{}) catch posix.abort();
209262
},
210263
else => {
211264
// Panicked while printing "Panicked during a panic."
212265
},
213266
};
214267

215-
@trap();
268+
posix.abort();
269+
}
270+
271+
const getSelfDebugInfo = std.debug.getSelfDebugInfo;
272+
const writeStackTrace = std.debug.writeStackTrace;
273+
274+
// Used for 0.13.0 compatibility, technically this allocator is completely unused by "writeStackTrace"
275+
fn getDebugInfoAllocator() std.mem.Allocator {
276+
return std.heap.page_allocator;
277+
}
278+
279+
fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {
280+
nosuspend {
281+
if (comptime builtin.target.isWasm()) {
282+
if (native_os == .wasi) {
283+
const stderr = io.getStdErr().writer();
284+
stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;
285+
}
286+
return;
287+
}
288+
const stderr = io.getStdErr().writer();
289+
if (builtin.strip_debug_info) {
290+
stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
291+
return;
292+
}
293+
const debug_info = getSelfDebugInfo() catch |err| {
294+
stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
295+
return;
296+
};
297+
if (builtin.zig_version.major == 0 and builtin.zig_version.minor == 13) {
298+
// Legacy 0.13.0
299+
writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, io.tty.detectConfig(io.getStdErr())) catch |err| {
300+
stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
301+
return;
302+
};
303+
} else {
304+
// 0.14.0-dev+
305+
writeStackTrace(stack_trace, stderr, debug_info, io.tty.detectConfig(io.getStdErr())) catch |err| {
306+
stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
307+
return;
308+
};
309+
}
310+
}
311+
}
312+
313+
const writeCurrentStackTrace = std.debug.writeCurrentStackTrace;
314+
fn dumpCurrentStackTrace(start_addr: ?usize) void {
315+
nosuspend {
316+
if (comptime builtin.target.isWasm()) {
317+
if (native_os == .wasi) {
318+
const stderr = io.getStdErr().writer();
319+
stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return;
320+
}
321+
return;
322+
}
323+
const stderr = io.getStdErr().writer();
324+
if (builtin.strip_debug_info) {
325+
stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
326+
return;
327+
}
328+
const debug_info = getSelfDebugInfo() catch |err| {
329+
stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
330+
return;
331+
};
332+
writeCurrentStackTrace(stderr, debug_info, io.tty.detectConfig(io.getStdErr()), start_addr) catch |err| {
333+
stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
334+
return;
335+
};
336+
}
216337
}
217338
};

src/androidbuild/androidbuild.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ pub fn runNameContext(comptime name: []const u8) []const u8 {
100100
const log = std.log.scoped(.@"zig-android-sdk");
101101

102102
pub fn printErrorsAndExit(message: []const u8, errors: []const []const u8) noreturn {
103-
@setCold(true);
104103
nosuspend {
105104
log.err("{s}", .{message});
106105
const stderr = std.io.getStdErr().writer();

0 commit comments

Comments
 (0)