ffi 1.14: #objc_call OS-gating cross-compiles cleanly to Android
109/109 host tests pass; tests/cross_compile.sh's first real tuple
(`android | examples/ffi-objc-call-10-os-gate.sx`) compiles
through `sx build --target android` without finding any
`@objc_msgSend` / `@sel_registerName` symbols in the output —
the `inline if OS == .ios { #objc_call(...) }` arm is stripped
at sx compile time before emit_llvm runs, so the Android
toolchain (Bionic + libGLESv3 / NDK linker) doesn't see the
Obj-C runtime references that would otherwise be undefined.
Host (macOS): the example prints "host stripped both" — the iOS
arm is stripped (we're not iOS) AND the Android arm is stripped
(we're not Android), confirming `inline if OS == { case }`
symmetric strip-and-render works around `#objc_call` sites.
The example carries a 3-line `android_main` trampoline so the
NDK linker's `-u ANativeActivity_onCreate` / entry-point
discovery is satisfied — pattern shared with chess + the other
android examples.
This commit is contained in:
43
examples/ffi-objc-call-10-os-gate.sx
Normal file
43
examples/ffi-objc-call-10-os-gate.sx
Normal file
@@ -0,0 +1,43 @@
|
||||
// Phase 1 step 1.14 (PLAN-FFI.md): `#objc_call` inside an
|
||||
// `inline if OS == .ios { ... }` arm cross-compiles cleanly to
|
||||
// Android. The comptime gate must strip the arm BEFORE the
|
||||
// `objc_msg_send` lowering runs, otherwise emit_llvm would
|
||||
// produce calls to `@objc_msgSend` / `@sel_registerName` that
|
||||
// don't exist in Bionic + libGLESv3 / linker would fail.
|
||||
//
|
||||
// On macOS the iOS arm is also stripped (we're not iOS) so the
|
||||
// runtime test just prints "host stripped both", proving the
|
||||
// `inline if OS == { case }` form works around `#objc_call`
|
||||
// sites the same way it does elsewhere.
|
||||
|
||||
#import "modules/std.sx";
|
||||
#import "modules/compiler.sx";
|
||||
|
||||
main :: () -> s32 {
|
||||
inline if OS == {
|
||||
case .ios: {
|
||||
// Stripped on macOS + Android. Compiled on iOS / ios-sim.
|
||||
#objc_call(void)(null, "init");
|
||||
print("ios path\n");
|
||||
}
|
||||
case .android: {
|
||||
// Stripped on macOS + iOS. Compiled on Android.
|
||||
// Nothing #objc_call-shaped here — just text — so we
|
||||
// exercise the gate symmetrically across targets.
|
||||
print("android path\n");
|
||||
}
|
||||
else: {
|
||||
print("host stripped both\n");
|
||||
}
|
||||
}
|
||||
0;
|
||||
}
|
||||
|
||||
// Android target requires `android_main` as the NDK entry — kept as
|
||||
// a 3-line trampoline so this example can pass through
|
||||
// `--target android` builds in `tests/cross_compile.sh`.
|
||||
android_main :: (app: *void) {
|
||||
inline if OS == .android {
|
||||
main();
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,12 @@ TMP_DIR="${TMPDIR:-/tmp}/sx-cross-compile"
|
||||
mkdir -p "$TMP_DIR"
|
||||
|
||||
# Tuple format: "<target>|<example_path>"
|
||||
# Add entries as cross-only examples land. Empty for now — Phase 0 only
|
||||
# lays down the runner; baselines live in tests/run_examples.sh territory.
|
||||
TUPLES=()
|
||||
# Add entries as cross-only examples land. Verifies the example
|
||||
# compiles cleanly for the target's NDK / SDK without needing the
|
||||
# host to actually run it.
|
||||
TUPLES=(
|
||||
"android|examples/ffi-objc-call-10-os-gate.sx"
|
||||
)
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
|
||||
1
tests/expected/ffi-objc-call-10-os-gate.exit
Normal file
1
tests/expected/ffi-objc-call-10-os-gate.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
1
tests/expected/ffi-objc-call-10-os-gate.txt
Normal file
1
tests/expected/ffi-objc-call-10-os-gate.txt
Normal file
@@ -0,0 +1 @@
|
||||
host stripped both
|
||||
Reference in New Issue
Block a user