#!/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//{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() { # 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