Files
Claude fde0b7f689 fix: harden the Arti build gates and drop initialize()'s sentinel
Follow-up to the toolchain update, from an audit of that diff.

Build script:
- The NDK pin rejected machines that have the pinned revision installed.
  An exported ANDROID_NDK_HOME or ANDROID_NDK_ROOT short-circuited the
  search and then failed the revision check, and GitHub runners export both
  at their own bundled NDK. Every candidate is now checked against its own
  source.properties and a mismatch moves on, so the build fails only when
  the pinned revision is genuinely absent, listing what it found instead.
- verify_jni_symbols printed missing exports and exited 0, so a library that
  would throw UnsatisfiedLinkError on every call could ship. It now fails the
  build, and checks only the ABIs this run built.
- Both post-build checks now use the pinned NDK's own llvm-readelf and
  llvm-nm. The stamp check silently skipped on macOS, which has no readelf,
  and Apple's nm cannot read ELF at all, so the symbol check would have
  reported every symbol missing there.
- The stamp check read its note through `readelf | grep -q`, the same
  SIGPIPE-plus-pipefail shape this branch removed from the symbol check.
- $HOME is expanded with a default, so `set -u` no longer aborts before the
  "NDK not found" message in an environment without HOME.

verify-reproducible.sh hashed every .so under jniLibs, so --release, which
rebuilds arm64 only, hashed the untouched x86_64 library identically in both
runs and reported the whole tree reproducible and matching the commit. It now
hashes and diffs only the ABIs the run builds, and prints which those are.

lib.rs:
- initialize() signalled "already initialized" out of the JNI closure as an
  empty string, re-tested after it. A destroy() landing in between would let
  the empty string through as the data directory, which resolves to relative
  state/ and cache/ paths against the process working directory. The check
  now reads the whole Option outside the closure and no sentinel exists.
- Corrected the comments claiming the error policy keeps a panic from
  crossing extern "C". It does not: the policy's panic arm runs through
  catch_unwind, which catches nothing under this crate's panic = "abort"
  profile. The Err arm, which is what the code relies on, is unaffected.

README: the troubleshooting section still told readers to install cargo-ndk
unpinned and to export ANDROID_NDK_HOME at an arbitrary revision, which was
the exact way to trip the old gate.

Verified: two clean builds byte-for-byte identical, both ABIs stamped r30,
JNI exports present, 16 KiB alignment kept. JVM tier-3 smoke test green, and
a scratch harness drove getVersion, setLogCallback, initialize, a second
initialize on a live client (the reuse path the sentinel used to carry) and
destroy over real JNI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011cSuXeu4bUTNRAUCZJcLLW
2026-09-13 20:46:31 +00:00
..