ffi #jni_main slice 2: AOT pipeline — .java + javac + d8 → classes.dex in APK
Compilation.lowering_jni_main_decls is populated by lowerToIR (iterating foreign_class_map for is_main && !is_foreign && runtime==jni_class, deduped by foreign_path); each entry carries the pre-rendered Java source from jni_java_emit.emitJavaSource. createApk extended: when the emission list is non-empty, write each .java under <stage>/java/<pkg>/<Class>.java, javac --release 11 to <stage>/classes/, d8 --release --lib <android_jar> --output <stage> to produce <stage>/classes.dex, then zip the .dex into the unaligned APK at root level. javac discovery: $JAVA_HOME/bin/javac first, then `which javac`. Manifest still hardcodes android.app.NativeActivity (slice 3 wires the user's class name + android:hasCode="true"), so the bundled .dex is present but unreferenced at runtime. End-to-end verified via dexdump on the smoke example's APK — Lco/swipelab/sxjnimain/SxApp; extending NativeActivity shows up in classes.dex. Non-#jni_main APK builds (99-android-egl-clear.sx) produce the same shape as before. Cross-compile tuple added for examples/ffi-jni-main-01-emit.sx (compile-only — APK exercise is manual).
This commit is contained in:
49
src/core.zig
49
src/core.zig
@@ -10,6 +10,7 @@ const target_mod = @import("target.zig");
|
||||
const Node = ast.Node;
|
||||
|
||||
pub const TargetConfig = target_mod.TargetConfig;
|
||||
pub const JniMainEmission = target_mod.JniMainEmission;
|
||||
|
||||
pub const Compilation = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
@@ -32,6 +33,10 @@ pub const Compilation = struct {
|
||||
/// E.g. the JNI env TL runtime when `#jni_env` is used. Merged with
|
||||
/// AST sources in `collectCImportSources`.
|
||||
lowering_extra_c_sources: std.ArrayList(c_import.CImportInfo) = .empty,
|
||||
/// `#jni_main #jni_class("...")` declarations whose Java sources were
|
||||
/// rendered during lowering. Read by the APK pipeline (`createApk`)
|
||||
/// to write `.java` files + run `javac` + `d8` + bundle `classes.dex`.
|
||||
lowering_jni_main_decls: std.ArrayList(JniMainEmission) = .empty,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, io: std.Io, file_path: []const u8, source: [:0]const u8, target_config: TargetConfig, stdlib_paths: []const []const u8) Compilation {
|
||||
return .{
|
||||
@@ -215,9 +220,53 @@ pub const Compilation = struct {
|
||||
}
|
||||
}
|
||||
|
||||
try self.collectJniMainEmissions(&lowering);
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
/// Walk `lowering.foreign_class_map` and render Java sources for every
|
||||
/// `#jni_main #jni_class("...")` declaration. Renders happen here so the
|
||||
/// AST + class-registry snapshot stay confined to the lowering pass; the
|
||||
/// downstream APK pipeline only needs `{foreign_path, java_source}` pairs.
|
||||
fn collectJniMainEmissions(self: *Compilation, lowering: *ir.Lowering) !void {
|
||||
// `foreign_class_map` registers each decl under bare + qualified names —
|
||||
// dedupe by foreign_path so a single decl emits one .java.
|
||||
var seen = std.StringHashMap(void).init(self.allocator);
|
||||
defer seen.deinit();
|
||||
|
||||
// Class registry passed to jni_java_emit for `*Foo` cross-class refs
|
||||
// and `#extends Alias` resolution.
|
||||
var registry = std.StringHashMap([]const u8).init(self.allocator);
|
||||
defer registry.deinit();
|
||||
var it_reg = lowering.foreign_class_map.iterator();
|
||||
while (it_reg.next()) |entry| {
|
||||
try registry.put(entry.key_ptr.*, entry.value_ptr.*.foreign_path);
|
||||
}
|
||||
|
||||
var it = lowering.foreign_class_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const fcd = entry.value_ptr.*;
|
||||
if (!fcd.is_main) continue;
|
||||
if (fcd.is_foreign) continue;
|
||||
if (fcd.runtime != .jni_class) continue;
|
||||
if (seen.contains(fcd.foreign_path)) continue;
|
||||
try seen.put(fcd.foreign_path, {});
|
||||
|
||||
const java_source = try ir.jni_java_emit.emitJavaSource(self.allocator, fcd, .{ .classes = ®istry });
|
||||
try self.lowering_jni_main_decls.append(self.allocator, .{
|
||||
.foreign_path = try self.allocator.dupe(u8, fcd.foreign_path),
|
||||
.java_source = java_source,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Java sources rendered from `#jni_main #jni_class("...")` decls during
|
||||
/// lowering. Empty unless `lowerToIR` has run.
|
||||
pub fn getJniMainEmissions(self: *const Compilation) []const JniMainEmission {
|
||||
return self.lowering_jni_main_decls.items;
|
||||
}
|
||||
|
||||
pub fn renderErrors(self: *const Compilation) void {
|
||||
self.diagnostics.renderDebug();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user