Check every JNI symbol, not just the first one
🚴 Build and Test BikeControl / Workspace tests (push) Successful in 16m55s
Build & Release / Run tests (push) Successful in 6m35s
🚴 Build and Test BikeControl / Android compile check (push) Successful in 3m41s
Build & Release / Build Linux (deb + AppImage) (push) Successful in 15m18s
Build & Release / Build Arch package (push) Successful in 29m19s
Build & Release / Build Android APK (push) Failing after 29s
Build & Release / Create release (push) Skipped
🚴 Build and Test BikeControl / Workspace tests (push) Successful in 16m55s
Build & Release / Run tests (push) Successful in 6m35s
🚴 Build and Test BikeControl / Android compile check (push) Successful in 3m41s
Build & Release / Build Linux (deb + AppImage) (push) Successful in 15m18s
Build & Release / Build Arch package (push) Successful in 29m19s
Build & Release / Build Android APK (push) Failing after 29s
Build & Release / Create release (push) Skipped
The android-check job failed on a tree that is perfectly correct: android.rs exports three `Java_..._MainActivity_*` symbols, the check compared all three against a single expected name and called it a mismatch. Two bugs, both from assuming one native: - SYM collected every export while EXPECTED was built from one method, so the comparison was three lines against one. - The Kotlin side matched `external fun name()` on empty parens, which quietly skipped nativeSetActivity and nativeBluetoothStateChanged — parameters do not appear in the symbol name, so stop at the paren. Now both sides are collected into sets and diffed, so a native declared in Kotlin with no Rust half fails as loudly as the reverse. Verified against the real files: passes as-is, and fails with a readable diff for a Kotlin-only native and for a renamed Rust export. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -138,7 +138,7 @@ jobs:
|
|||||||
|
|
||||||
# Cheap invariants that a compiler cannot see. Both failures are silent:
|
# Cheap invariants that a compiler cannot see. Both failures are silent:
|
||||||
# the app builds, installs, launches, and then finds no trainer.
|
# the app builds, installs, launches, and then finds no trainer.
|
||||||
- name: Check the Android source layout and the JNI symbol
|
- name: Check the Android source layout and the JNI symbols
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
# gen/ is generated and must stay untracked, or a `tauri android init`
|
# gen/ is generated and must stay untracked, or a `tauri android init`
|
||||||
@@ -152,20 +152,35 @@ jobs:
|
|||||||
KT=src-tauri/android/src/main/java/paris/tourolle/bikecontrol/MainActivity.kt
|
KT=src-tauri/android/src/main/java/paris/tourolle/bikecontrol/MainActivity.kt
|
||||||
[ -f "$KT" ] || { echo "❌ $KT is missing"; exit 1; }
|
[ -f "$KT" ] || { echo "❌ $KT is missing"; exit 1; }
|
||||||
|
|
||||||
# The JNI symbol in android.rs is matched by the *runtime*, by name.
|
# JNI symbols are matched by the *runtime*, by name. Rename the
|
||||||
# Rename the Kotlin package or the method and nothing fails to
|
# Kotlin package, add a native without its Rust half, or drop a Rust
|
||||||
# compile — btleplug simply never gets initialised.
|
# export, and nothing fails to compile — the method simply is not
|
||||||
SYM=$(sed -n 's/.*pub extern "system" fn \(Java_[A-Za-z0-9_]*\).*/\1/p' src-tauri/src/android.rs)
|
# there when Java calls it. So compare the two sets, both ways.
|
||||||
|
#
|
||||||
|
# Sets, not a single pair: there are three natives now, and matching
|
||||||
|
# `external fun name()` on empty parens missed the two that take
|
||||||
|
# arguments. Parameters do not appear in the symbol name, so stop at
|
||||||
|
# the opening paren.
|
||||||
|
TMP="${RUNNER_TEMP:-/tmp}"
|
||||||
PKG=$(sed -n 's/^package \(.*\)$/\1/p' "$KT" | tr -d '\r')
|
PKG=$(sed -n 's/^package \(.*\)$/\1/p' "$KT" | tr -d '\r')
|
||||||
METHOD=$(sed -n 's/.*external fun \([A-Za-z0-9_]*\)().*/\1/p' "$KT")
|
PREFIX="Java_$(echo "$PKG" | tr '.' '_')_MainActivity_"
|
||||||
EXPECTED="Java_$(echo "$PKG" | tr '.' '_')_MainActivity_$METHOD"
|
|
||||||
if [ "$SYM" != "$EXPECTED" ]; then
|
sed -n 's/.*pub extern "system" fn \(Java_[A-Za-z0-9_]*\).*/\1/p' \
|
||||||
echo "❌ JNI symbol mismatch:"
|
src-tauri/src/android.rs | sort -u > "$TMP/jni-rust"
|
||||||
echo " android.rs exports: $SYM"
|
sed -n "s/.*external fun \([A-Za-z0-9_]*\)(.*/$PREFIX\1/p" \
|
||||||
echo " MainActivity needs: $EXPECTED"
|
"$KT" | sort -u > "$TMP/jni-kotlin"
|
||||||
|
|
||||||
|
[ -s "$TMP/jni-rust" ] || { echo "❌ No JNI exports found in android.rs"; exit 1; }
|
||||||
|
|
||||||
|
if ! diff -u "$TMP/jni-kotlin" "$TMP/jni-rust" > "$TMP/jni-diff"; then
|
||||||
|
echo "❌ JNI symbols differ between MainActivity.kt and android.rs:"
|
||||||
|
echo " - declared in MainActivity.kt, not exported by android.rs"
|
||||||
|
echo " + exported by android.rs, not declared in MainActivity.kt"
|
||||||
|
tail -n +4 "$TMP/jni-diff"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "✅ JNI symbol $SYM matches $PKG.MainActivity.$METHOD"
|
echo "✅ $(wc -l < "$TMP/jni-rust") JNI symbols match $PKG.MainActivity:"
|
||||||
|
sed 's/^/ /' "$TMP/jni-rust"
|
||||||
|
|
||||||
- name: Cargo check (aarch64-linux-android)
|
- name: Cargo check (aarch64-linux-android)
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
Reference in New Issue
Block a user