fix(android): bake android24→26 clang shim into the docker image itself
Build #13's PATH wrapper trick failed because tauri-cli invokes the linker with an absolute path (/opt/android-sdk/ndk/.../bin/aarch64-linux-android24- clang), which bypasses \$PATH entirely. The pthread_shim logs confirmed the broken API-24 stubs were still being linked: WZP_pthread_shim: dlsym(RTLD_DEFAULT, pthread_create) returned NULL: libdl.a is a stub --- use libdl.so instead Move the fix up a level — into the Dockerfile itself. On image build, for each of the four android ABIs × {clang, clang++}, rename `${abi}24-${suffix}` to `${abi}24-${suffix}.orig` and replace it with a shell wrapper that exec()s `${abi}26-${suffix}`. Any call to the API-24 wrapper — via PATH, absolute path, or otherwise — now transparently runs the API-26 wrapper, which uses the real libc.so/libdl.so bindings. The old bash-c /tmp/wrappers workaround in build-tauri-android.sh is removed now that the image handles it at the right layer. Also add `--shell` to build-tauri-android.sh: opens an interactive docker container on the remote with the same mounts/env as the build, so I can iterate on cargo tauri android build / manually patch files / etc. without the full git push → ssh → rebuild → install loop. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,27 @@ RUN yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/nu
|
|||||||
"platform-tools" \
|
"platform-tools" \
|
||||||
2>&1 | grep -v '^\[' > /dev/null
|
2>&1 | grep -v '^\[' > /dev/null
|
||||||
|
|
||||||
|
# Work around broken API-24 libc/libdl stubs in the NDK. Rust's pre-compiled
|
||||||
|
# libstd.rlib for aarch64-linux-android links against pthread_create / dlsym
|
||||||
|
# from the sysroot. With --target=...24, those resolve to the API-24 "stub"
|
||||||
|
# libc.a/libdl.a that return "libdl.a is a stub --- use libdl.so instead" at
|
||||||
|
# runtime. API-26 has real dynamic bindings. Tauri-cli hard-codes the
|
||||||
|
# android24-clang path so env var overrides don't stick.
|
||||||
|
#
|
||||||
|
# Permanent fix inside the image: replace every `${abi}24-clang` binary with
|
||||||
|
# a shim that exec()s the corresponding `${abi}26-clang`. Any build using
|
||||||
|
# this image now transparently gets the API-26 sysroot even when the caller
|
||||||
|
# asks for API-24.
|
||||||
|
RUN set -eux; \
|
||||||
|
BIN=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin; \
|
||||||
|
for abi in aarch64-linux-android armv7a-linux-androideabi i686-linux-android x86_64-linux-android; do \
|
||||||
|
for suffix in clang clang++; do \
|
||||||
|
mv "$BIN/${abi}24-${suffix}" "$BIN/${abi}24-${suffix}.orig"; \
|
||||||
|
printf '#!/bin/sh\nexec "%s/%s26-%s" "$@"\n' "$BIN" "$abi" "$suffix" > "$BIN/${abi}24-${suffix}"; \
|
||||||
|
chmod +x "$BIN/${abi}24-${suffix}"; \
|
||||||
|
done; \
|
||||||
|
done
|
||||||
|
|
||||||
# Make SDK world-readable so builder user can access it
|
# Make SDK world-readable so builder user can access it
|
||||||
RUN chmod -R a+rX $ANDROID_HOME
|
RUN chmod -R a+rX $ANDROID_HOME
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ REBUILD_RUST=0
|
|||||||
DO_PULL=1
|
DO_PULL=1
|
||||||
DO_INIT=0
|
DO_INIT=0
|
||||||
BUILD_RELEASE=0
|
BUILD_RELEASE=0
|
||||||
|
DROP_SHELL=0
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--rust) REBUILD_RUST=1 ;;
|
--rust) REBUILD_RUST=1 ;;
|
||||||
@@ -43,6 +44,7 @@ for arg in "$@"; do
|
|||||||
--no-pull) DO_PULL=0 ;;
|
--no-pull) DO_PULL=0 ;;
|
||||||
--init) DO_INIT=1 ;;
|
--init) DO_INIT=1 ;;
|
||||||
--release) BUILD_RELEASE=1 ;;
|
--release) BUILD_RELEASE=1 ;;
|
||||||
|
--shell) DROP_SHELL=1 ;; # interactive debug shell inside container
|
||||||
-h|--help)
|
-h|--help)
|
||||||
sed -n '3,30p' "$0"
|
sed -n '3,30p' "$0"
|
||||||
exit 0
|
exit 0
|
||||||
@@ -50,6 +52,34 @@ for arg in "$@"; do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# ── --shell: drop into an interactive container for fast manual iteration ──
|
||||||
|
# The container is NOT --rm'd so you can keep hacking between invocations,
|
||||||
|
# and has the same mounts / env as the build path above so `cargo tauri
|
||||||
|
# android build ...` just works.
|
||||||
|
if [ "$DROP_SHELL" = "1" ]; then
|
||||||
|
log "Starting interactive shell in wzp-android-builder container..."
|
||||||
|
log " cd /build/source/desktop/src-tauri && cargo tauri android build --debug --target aarch64 --apk"
|
||||||
|
log " (exit the shell with ^D; container will be removed)"
|
||||||
|
ssh -t -A $SSH_OPTS "$REMOTE_HOST" "
|
||||||
|
set -euo pipefail
|
||||||
|
BASE=$BASE_DIR
|
||||||
|
# Make sure the source/cache is writable by uid 1000
|
||||||
|
sudo chown -R 1000:1000 \$BASE/data/source \$BASE/data/cache 2>/dev/null || true
|
||||||
|
docker run --rm -it \
|
||||||
|
--user 1000:1000 \
|
||||||
|
-v \$BASE/data/source:/build/source \
|
||||||
|
-v \$BASE/data/cache/cargo-registry:/home/builder/.cargo/registry \
|
||||||
|
-v \$BASE/data/cache/cargo-git:/home/builder/.cargo/git \
|
||||||
|
-v \$BASE/data/cache/target:/build/source/target \
|
||||||
|
-v \$BASE/data/cache/gradle:/home/builder/.gradle \
|
||||||
|
-v \$BASE/data/cache/android-home:/home/builder/.android \
|
||||||
|
-w /build/source/desktop/src-tauri \
|
||||||
|
wzp-android-builder \
|
||||||
|
bash
|
||||||
|
"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
log() { echo -e "\033[1;36m>>> $*\033[0m"; }
|
log() { echo -e "\033[1;36m>>> $*\033[0m"; }
|
||||||
ssh_cmd() { ssh -A $SSH_OPTS "$REMOTE_HOST" "$@"; }
|
ssh_cmd() { ssh -A $SSH_OPTS "$REMOTE_HOST" "$@"; }
|
||||||
|
|
||||||
@@ -166,30 +196,6 @@ docker run --rm \
|
|||||||
wzp-android-builder \
|
wzp-android-builder \
|
||||||
bash -c '
|
bash -c '
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# ─── Linker wrappers ───────────────────────────────────────────────────────
|
|
||||||
# Tauri-cli hard-codes aarch64-linux-android24-clang as the Rust linker for
|
|
||||||
# the aarch64 target, which resolves pthread_create / dlsym / __init_tcb
|
|
||||||
# against the NDK API-24 *stub* libc.a/libdl.a. Those stubs are designed to
|
|
||||||
# crash if called — they only exist so the API-24 sysroot compiles, not so
|
|
||||||
# symbols actually work. API-26 has the real dynamic bindings to libc.so.
|
|
||||||
#
|
|
||||||
# Rather than fight tauri-cli to change which clang it invokes, we put a
|
|
||||||
# wrapper on $PATH that IS named android24-clang but exec()s the android26
|
|
||||||
# version. Same trick works for every ABI.
|
|
||||||
mkdir -p /tmp/wrappers
|
|
||||||
for abi in aarch64-linux-android armv7a-linux-androideabi i686-linux-android x86_64-linux-android; do
|
|
||||||
for suffix in clang clang++; do
|
|
||||||
cat > /tmp/wrappers/${abi}24-${suffix} <<WRAPPER
|
|
||||||
#!/bin/sh
|
|
||||||
exec /opt/android-sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/linux-x86_64/bin/${abi}26-${suffix} "\$@"
|
|
||||||
WRAPPER
|
|
||||||
chmod +x /tmp/wrappers/${abi}24-${suffix}
|
|
||||||
done
|
|
||||||
done
|
|
||||||
export PATH=/tmp/wrappers:$PATH
|
|
||||||
echo ">>> installed android24→android26 linker wrappers in /tmp/wrappers"
|
|
||||||
|
|
||||||
cd /build/source/desktop
|
cd /build/source/desktop
|
||||||
|
|
||||||
echo ">>> npm install"
|
echo ">>> npm install"
|
||||||
|
|||||||
Reference in New Issue
Block a user