android target + APK pipeline; LSP imports honor stdlib paths
Android (toolchain):
--target android / --target android-arm64 → aarch64-linux-android21.
target.zig discovers $ANDROID_NDK_HOME (or scans
~/Library/Android/sdk/ndk/* for the newest), invokes the NDK clang
with -shared -fPIC and links libsxhello.so against -llog -landroid
-lEGL -lGLESv3 -lm -ldl. native_app_glue.c from the NDK is compiled
and linked alongside the sx .o so apps can use the conventional
android_main(struct android_app*) shape; -u ANativeActivity_onCreate
keeps glue's symbol live.
Android (APK):
--apk <out> wraps the .so into a debug-signed installable APK.
target.zig discovers the SDK at $ANDROID_HOME (or
~/Library/Android/sdk), picks the newest build-tools + platforms,
generates a NativeActivity AndroidManifest.xml from --bundle-id,
packages via aapt2 link, appends the lib/ tree, zipalign, then
apksigner against ~/.android/debug.keystore (auto-generated via
keytool on first use). One command end-to-end:
sx build --target android --apk out.apk \\
--bundle-id co.swipelab.foo main.sx
Verified on Pixel 7 Pro: install + launch reaches android_main.
Compiler (entry-point linkage):
Top-level fn defs default to LLVM internal linkage and are lazily
lowered (only `main` was eagerly lowered before). Added
isExportedEntryName() — a small allowlist for names the OS loader
calls: `main`, `android_main`, `ANativeActivity_onCreate`,
`JNI_OnLoad`. These get eagerly lowered AND keep external linkage,
so they actually land in .dynsym.
LSP (imports):
DocumentStore now takes the install-discovered stdlib_paths and
forwards them into resolveImportPath, mirroring the compiler. Before
this, every `#import "modules/..."` resolved through the stdlib path
failed silently inside the LSP and identifiers from those modules
showed as `undefined variable`. Repro on label.sx: 1 false positive
before, 0 after.
This commit is contained in:
@@ -20,6 +20,16 @@ const Function = inst_mod.Function;
|
||||
const Module = mod_mod.Module;
|
||||
const Builder = mod_mod.Builder;
|
||||
|
||||
/// Names that must keep external LLVM linkage because the OS loader (not
|
||||
/// sx code) is the caller. Without this they'd default to internal and
|
||||
/// either DCE away or stay hidden from the dynamic symbol table.
|
||||
fn isExportedEntryName(name: []const u8) bool {
|
||||
return std.mem.eql(u8, name, "main") or
|
||||
std.mem.eql(u8, name, "android_main") or
|
||||
std.mem.eql(u8, name, "ANativeActivity_onCreate") or
|
||||
std.mem.eql(u8, name, "JNI_OnLoad");
|
||||
}
|
||||
|
||||
// ── Scope ───────────────────────────────────────────────────────────────
|
||||
|
||||
const Binding = struct {
|
||||
@@ -569,7 +579,7 @@ pub const Lowering = struct {
|
||||
switch (decl.data) {
|
||||
.const_decl => |cd| {
|
||||
if (cd.value.data == .fn_decl) {
|
||||
if (std.mem.eql(u8, cd.name, "main")) {
|
||||
if (isExportedEntryName(cd.name)) {
|
||||
self.lazyLowerFunction(cd.name);
|
||||
}
|
||||
} else if (cd.value.data == .comptime_expr) {
|
||||
@@ -577,7 +587,7 @@ pub const Lowering = struct {
|
||||
}
|
||||
},
|
||||
.fn_decl => |fd| {
|
||||
if (std.mem.eql(u8, fd.name, "main")) {
|
||||
if (isExportedEntryName(fd.name)) {
|
||||
self.lazyLowerFunction(fd.name);
|
||||
}
|
||||
},
|
||||
@@ -728,7 +738,7 @@ pub const Lowering = struct {
|
||||
return;
|
||||
}
|
||||
func.is_extern = false; // promote from extern stub to real function
|
||||
func.linkage = if (std.mem.eql(u8, name, "main")) .external else .internal;
|
||||
func.linkage = if (isExportedEntryName(name)) .external else .internal;
|
||||
if (fd.call_conv == .c) func.call_conv = .c;
|
||||
// Set inst_counter to param count (params occupy refs 0..N-1)
|
||||
std.debug.assert(func.params.len == fd.params.len); // AST and IR param counts must match
|
||||
@@ -836,8 +846,11 @@ pub const Lowering = struct {
|
||||
);
|
||||
_ = func_id;
|
||||
|
||||
// Set linkage for main
|
||||
if (std.mem.eql(u8, name, "main")) {
|
||||
// Set linkage. Default for fn defs is `internal` (LLVM DCE-friendly,
|
||||
// matches C `static`). isExportedEntryName lists the names the OS
|
||||
// loader calls — `main`, Android NativeActivity hooks — which must
|
||||
// stay externally visible.
|
||||
if (isExportedEntryName(name)) {
|
||||
self.builder.currentFunc().linkage = .external;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user