ffi #jni_main R.2: drop native_app_glue from Android link when #jni_main present

`target.link` now takes a `has_jni_main: bool` parameter (passed by
main.zig from `comp.getJniMainEmissions().len > 0`). When set:

  - native_app_glue.c is not compiled — no `.glue.o` produced.
  - `-u ANativeActivity_onCreate` is not added to the link argv.
  - The Java-driven Activity is the entry; the .so just provides JNI
    impls, bound at load time via the `JNI_OnLoad` slice R.3 will
    synthesize.

Legacy NativeActivity builds (no `#jni_main` decl) are unchanged: glue
is still compiled and `ANativeActivity_onCreate` still retained.

Verified end-to-end:
  - #jni_main .so: `llvm-nm -D` shows neither `ANativeActivity_onCreate`
    nor `android_main` (correct — Java side drives entry).
  - Legacy .so (99-android-egl-clear): both symbols still exported.

131 host / 4 cross / zig build test all green.
This commit is contained in:
agra
2026-05-20 14:59:49 +03:00
parent 8ae4e0c653
commit 2461218111
2 changed files with 40 additions and 24 deletions

View File

@@ -595,7 +595,7 @@ fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []cons
// Link (sx .o + C .o files) // Link (sx .o + C .o files)
timer.mark(); timer.mark();
sx.target.link(allocator, io, obj_path, c_obj_paths, final_output, libs, fws, merged_config) catch { sx.target.link(allocator, io, obj_path, c_obj_paths, final_output, libs, fws, merged_config, comp.getJniMainEmissions().len > 0) catch {
std.debug.print("error: linking failed\n", .{}); std.debug.print("error: linking failed\n", .{});
return error.CompileError; return error.CompileError;
}; };

View File

@@ -686,7 +686,7 @@ pub fn discoverAppleSdk(allocator: std.mem.Allocator, io: std.Io, sdk_name: []co
return out; return out;
} }
pub fn link(allocator: std.mem.Allocator, io: std.Io, output_obj: []const u8, extra_objects: []const []const u8, output_bin: []const u8, libraries: []const []const u8, frameworks: []const []const u8, target_config: TargetConfig) !void { pub fn link(allocator: std.mem.Allocator, io: std.Io, output_obj: []const u8, extra_objects: []const []const u8, output_bin: []const u8, libraries: []const []const u8, frameworks: []const []const u8, target_config: TargetConfig, has_jni_main: bool) !void {
var argv = std.ArrayList([]const u8).empty; var argv = std.ArrayList([]const u8).empty;
if (target_config.isIOS()) { if (target_config.isIOS()) {
@@ -733,13 +733,24 @@ pub fn link(allocator: std.mem.Allocator, io: std.Io, output_obj: []const u8, ex
while (it.next()) |part| try argv.append(allocator, part); while (it.next()) |part| try argv.append(allocator, part);
} }
} else if (target_config.isAndroid()) { } else if (target_config.isAndroid()) {
// Android: NDK clang. Produces a shared library (.so) loaded by // Android: NDK clang. Produces a shared library (.so).
// NativeActivity. native_app_glue.c (from the NDK) is compiled and //
// linked alongside the sx code so apps can use the conventional // Two entry shapes:
// `android_main(struct android_app*)` event-loop shape — the glue //
// owns `ANativeActivity_onCreate` and forwards into android_main on // - **#jni_main path (`has_jni_main = true`)** — the Java side
// a dedicated thread. `-u ANativeActivity_onCreate` keeps the glue's // drives lifecycle (the bundled classes.dex declares an
// symbol from being stripped (nothing in our .o references it). // Activity that overrides `onCreate` etc.). The .so just
// provides JNI implementations bound at load time via the
// `JNI_OnLoad` synthesized in slice R.3. No native_app_glue
// is needed: there's no `ANativeActivity_onCreate` to host,
// no `android_main` event loop to run.
//
// - **Legacy NativeActivity path (`has_jni_main = false`)** —
// native_app_glue.c is compiled and linked alongside the sx
// code; the glue owns `ANativeActivity_onCreate` and forwards
// into the user's `android_main` on a worker thread. The
// `-u ANativeActivity_onCreate` keeps the glue's symbol from
// being stripped (nothing in our .o references it).
// //
// The `libraries` parameter (collected from `#library` directives) // The `libraries` parameter (collected from `#library` directives)
// and `frameworks` parameter (Apple-only by definition) are // and `frameworks` parameter (Apple-only by definition) are
@@ -755,19 +766,22 @@ pub fn link(allocator: std.mem.Allocator, io: std.Io, output_obj: []const u8, ex
const host_tag: []const u8 = if (@import("builtin").os.tag == .macos) "darwin-x86_64" else "linux-x86_64"; const host_tag: []const u8 = if (@import("builtin").os.tag == .macos) "darwin-x86_64" else "linux-x86_64";
const clang = try std.fmt.allocPrint(allocator, "{s}/toolchains/llvm/prebuilt/{s}/bin/clang", .{ ndk_root, host_tag }); const clang = try std.fmt.allocPrint(allocator, "{s}/toolchains/llvm/prebuilt/{s}/bin/clang", .{ ndk_root, host_tag });
const glue_src = try std.fmt.allocPrint(allocator, "{s}/sources/android/native_app_glue/android_native_app_glue.c", .{ndk_root}); const glue_obj_opt: ?[]const u8 = if (has_jni_main) null else blk: {
const glue_obj = try std.fmt.allocPrint(allocator, "{s}.glue.o", .{output_obj}); const glue_src = try std.fmt.allocPrint(allocator, "{s}/sources/android/native_app_glue/android_native_app_glue.c", .{ndk_root});
var glue_argv = std.ArrayList([]const u8).empty; const glue_obj = try std.fmt.allocPrint(allocator, "{s}.glue.o", .{output_obj});
try glue_argv.appendSlice(allocator, &.{ clang, "-c", "-fPIC" }); var glue_argv = std.ArrayList([]const u8).empty;
if (target_config.triple) |t| { try glue_argv.appendSlice(allocator, &.{ clang, "-c", "-fPIC" });
try glue_argv.append(allocator, "-target"); if (target_config.triple) |t| {
try glue_argv.append(allocator, std.mem.span(t)); try glue_argv.append(allocator, "-target");
} try glue_argv.append(allocator, std.mem.span(t));
try glue_argv.appendSlice(allocator, &.{ glue_src, "-o", glue_obj }); }
const glue_slice = try glue_argv.toOwnedSlice(allocator); try glue_argv.appendSlice(allocator, &.{ glue_src, "-o", glue_obj });
var glue_child = std.process.spawn(io, .{ .argv = glue_slice }) catch return error.LinkError; const glue_slice = try glue_argv.toOwnedSlice(allocator);
const glue_term = glue_child.wait(io) catch return error.LinkError; var glue_child = std.process.spawn(io, .{ .argv = glue_slice }) catch return error.LinkError;
if (glue_term != .exited or glue_term.exited != 0) return error.LinkError; const glue_term = glue_child.wait(io) catch return error.LinkError;
if (glue_term != .exited or glue_term.exited != 0) return error.LinkError;
break :blk glue_obj;
};
try argv.append(allocator, clang); try argv.append(allocator, clang);
if (target_config.triple) |t| { if (target_config.triple) |t| {
@@ -776,9 +790,11 @@ pub fn link(allocator: std.mem.Allocator, io: std.Io, output_obj: []const u8, ex
} }
try argv.append(allocator, "-shared"); try argv.append(allocator, "-shared");
try argv.append(allocator, "-fPIC"); try argv.append(allocator, "-fPIC");
try argv.appendSlice(allocator, &.{ "-u", "ANativeActivity_onCreate" }); if (!has_jni_main) {
try argv.appendSlice(allocator, &.{ "-u", "ANativeActivity_onCreate" });
}
try argv.append(allocator, output_obj); try argv.append(allocator, output_obj);
try argv.append(allocator, glue_obj); if (glue_obj_opt) |go| try argv.append(allocator, go);
for (extra_objects) |eo| try argv.append(allocator, eo); for (extra_objects) |eo| try argv.append(allocator, eo);
try argv.append(allocator, "-o"); try argv.append(allocator, "-o");
try argv.append(allocator, output_bin); try argv.append(allocator, output_bin);