The AAR check failed in CI with "the AAR has no jni/arm64-v8a/libwai.so" although the libraries were built and packed: under pipefail, `unzip -l | grep -q` fails whenever grep stops at its first match before unzip has written the rest (SIGPIPE). On the AAR the Studio built, that pipeline fails 7 times in 300, and the listing read first 0 in 300. The workflow's AAR check reads the listing once and prints it on a failure; build-native.sh reads nm's and readelf's output whole too, where the same race could report an export or a NEEDED entry missing, or let a LOAD segment's misalignment through. Co-Authored-By: Claude Opus 5.5 <[email protected]>
115 lines
5.9 KiB
Shell
Executable file
115 lines
5.9 KiB
Shell
Executable file
#!/usr/bin/env bash
|
|
# Build libwai (wai-rs, feature `native_sink`, no codec library) and the JNI layer over its C
|
|
# interface (jni/wai_sink_jni.c, against include/wai.h).
|
|
#
|
|
# scripts/build-native.sh android the AAR's ABIs, with the pinned NDK:
|
|
# sink/build/jniLibs/<abi>/{libwai.so,libwai_sink_jni.so}
|
|
# scripts/build-native.sh host this machine, for the JVM tests: core/build/native/
|
|
#
|
|
# WAI_SINK_ABIS chooses the ABIs (default "arm64-v8a armeabi-v7a x86_64"). The NDK is
|
|
# $ANDROID_NDK_HOME, or the pinned version under the SDK; its revision is checked. The Rust
|
|
# targets need their standard library (`rustup target add aarch64-linux-android
|
|
# armv7-linux-androideabi x86_64-linux-android`). Every library is linked for 16 KiB pages, and
|
|
# the script checks that it was, and that the JNI layer exports every method WaiNative declares.
|
|
set -euo pipefail
|
|
. "$(dirname "${BASH_SOURCE[0]}")/env.sh"
|
|
what="${1:-}"
|
|
TARGET_DIR="${CARGO_TARGET_DIR:-$WAI_RS_DIR/target}"
|
|
FEATURES=(--no-default-features --features native_sink)
|
|
JNI_SRC="$WAI_SINK_DIR/jni/wai_sink_jni.c"
|
|
INC="$WAI_RS_DIR/include"
|
|
# The methods WaiNative declares, each a symbol the JNI layer must export.
|
|
METHODS="abiVersion nowUs open free info receiptName playlist playlistParent objectVerdict event observe energy attach takeCmcd end verifyBundle"
|
|
|
|
cargo_lib() { cargo build --release --locked "${FEATURES[@]}" --lib --manifest-path "$WAI_RS_DIR/Cargo.toml" "$@"; }
|
|
|
|
# Each tool's output is read whole before it is searched: under pipefail, `tool | grep -q` fails
|
|
# whenever grep stops at its first match before the tool has written the rest (SIGPIPE), which
|
|
# would report a symbol or an entry missing that is there.
|
|
check_exports() { # <nm> <library>
|
|
local nm="$1" lib="$2" missing="" syms
|
|
syms="$("$nm" -D --defined-only "$lib" 2>/dev/null || true)"
|
|
for m in $METHODS JNI_OnLoad; do
|
|
case $m in JNI_OnLoad) s=JNI_OnLoad ;; *) s=Java_wai_sink_WaiNative_$m ;; esac
|
|
grep -qE "[[:space:]]_?$s\$" <<<"$syms" || missing="$missing $s"
|
|
done
|
|
[ -z "$missing" ] || die "$lib does not export:$missing"
|
|
}
|
|
|
|
android() {
|
|
[ -n "${ANDROID_HOME:-}" ] || die "no Android SDK: set ANDROID_HOME"
|
|
local ndk="${ANDROID_NDK_HOME:-$ANDROID_HOME/ndk/$WAI_SINK_NDK_VERSION}"
|
|
[ -d "$ndk" ] || die "no NDK at $ndk (the pinned revision is $WAI_SINK_NDK_VERSION)"
|
|
local rev
|
|
rev="$(sed -n 's/^Pkg.Revision *= *//p' "$ndk/source.properties")"
|
|
[ "$rev" = "$WAI_SINK_NDK_VERSION" ] || die "the NDK at $ndk is $rev, not the pinned $WAI_SINK_NDK_VERSION"
|
|
local tag
|
|
case "$(uname -s)" in Darwin) tag=darwin-x86_64 ;; Linux) tag=linux-x86_64 ;; *) die "no NDK host for $(uname -s)" ;; esac
|
|
local bin="$ndk/toolchains/llvm/prebuilt/$tag/bin"
|
|
for abi in ${WAI_SINK_ABIS:-arm64-v8a armeabi-v7a x86_64}; do
|
|
local triple clang
|
|
case "$abi" in
|
|
arm64-v8a) triple=aarch64-linux-android; clang=aarch64-linux-android$WAI_SINK_MIN_API-clang ;;
|
|
armeabi-v7a) triple=armv7-linux-androideabi; clang=armv7a-linux-androideabi$WAI_SINK_MIN_API-clang ;;
|
|
x86_64) triple=x86_64-linux-android; clang=x86_64-linux-android$WAI_SINK_MIN_API-clang ;;
|
|
*) die "unknown ABI $abi" ;;
|
|
esac
|
|
local up lo
|
|
up="$(echo "$triple" | tr 'a-z-' 'A-Z_')"
|
|
lo="$(echo "$triple" | tr '-' '_')"
|
|
# The NDK's clang links libwai for the platform; a dependency that compiles C (cc-rs) uses it
|
|
# too. 16 KiB pages: what current devices require of an application's native code.
|
|
(
|
|
export "CARGO_TARGET_${up}_LINKER=$bin/$clang" \
|
|
"CARGO_TARGET_${up}_RUSTFLAGS=-C link-arg=-Wl,-z,max-page-size=16384" \
|
|
"CC_${lo}=$bin/$clang" "AR_${lo}=$bin/llvm-ar"
|
|
cargo_lib --target "$triple"
|
|
)
|
|
local out="$WAI_SINK_DIR/sink/build/jniLibs/$abi"
|
|
rm -rf "$out" && mkdir -p "$out"
|
|
cp "$TARGET_DIR/$triple/release/libwai.so" "$out/libwai.so"
|
|
"$bin/$clang" -shared -fPIC -O2 -Wall -Wextra -Werror -I"$INC" "$JNI_SRC" -L"$out" -lwai \
|
|
-Wl,--no-undefined -Wl,-z,max-page-size=16384 -o "$out/libwai_sink_jni.so"
|
|
"$bin/llvm-strip" --strip-unneeded "$out/libwai.so" "$out/libwai_sink_jni.so"
|
|
check_exports "$bin/llvm-nm" "$out/libwai_sink_jni.so"
|
|
deps="$("$bin/llvm-readelf" -d "$out/libwai_sink_jni.so")"
|
|
grep -q 'NEEDED.*\[libwai.so\]' <<<"$deps" || die "$abi: the JNI layer does not name libwai.so"
|
|
if [ "$abi" != armeabi-v7a ]; then
|
|
for so in "$out"/*.so; do
|
|
loads="$("$bin/llvm-readelf" -lW "$so" | awk '$1=="LOAD"{print $NF}')"
|
|
[ -n "$loads" ] || die "$abi: $(basename "$so") has no LOAD segment"
|
|
grep -qv '^0x4000$' <<<"$loads" && die "$abi: $(basename "$so") has a LOAD segment not aligned to 16 KiB"
|
|
done
|
|
fi
|
|
echo "built $abi: $(cd "$out" && ls -l *.so | awk '{print $NF" "$5}' | tr '\n' ' ')"
|
|
done
|
|
}
|
|
|
|
host() {
|
|
[ -n "${JAVA_HOME:-}" ] && [ -f "$JAVA_HOME/include/jni.h" ] || die "no JDK with jni.h: set JAVA_HOME"
|
|
cargo_lib
|
|
local out="$WAI_SINK_DIR/core/build/native"
|
|
rm -rf "$out" && mkdir -p "$out"
|
|
local cc="${CC:-cc}"
|
|
case "$(uname -s)" in
|
|
Darwin)
|
|
cp "$TARGET_DIR/release/libwai.dylib" "$out/libwai.dylib"
|
|
install_name_tool -id @rpath/libwai.dylib "$out/libwai.dylib"
|
|
"$cc" -dynamiclib -fPIC -O2 -Wall -Wextra -Werror -I"$INC" -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin" \
|
|
"$JNI_SRC" -L"$out" -lwai -Wl,-rpath,@loader_path -o "$out/libwai_sink_jni.dylib"
|
|
check_exports nm "$out/libwai_sink_jni.dylib" ;;
|
|
Linux)
|
|
cp "$TARGET_DIR/release/libwai.so" "$out/libwai.so"
|
|
"$cc" -shared -fPIC -O2 -Wall -Wextra -Werror -I"$INC" -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" \
|
|
"$JNI_SRC" -L"$out" -lwai -Wl,--no-undefined -Wl,-rpath,'$ORIGIN' -o "$out/libwai_sink_jni.so"
|
|
check_exports nm "$out/libwai_sink_jni.so" ;;
|
|
*) die "no host build for $(uname -s)" ;;
|
|
esac
|
|
echo "built host: $(ls "$out" | tr '\n' ' ')"
|
|
}
|
|
|
|
case "$what" in
|
|
android) android ;;
|
|
host) host ;;
|
|
*) die "usage: build-native.sh android|host" ;;
|
|
esac
|