#!/bin/sh # Build rsync for each Android ABI from pinned source via the NDK. Output is a # stripped PIE executable named lib*.so, packaged inside the APK and exec'd from # nativeLibraryDir. Optional dependencies are disabled so the binary needs # nothing outside Bionic; zlib and popt come from rsync's bundled copies. set -eu root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) # shellcheck source=versions . "$root/versions" command -v bash >/dev/null 2>&1 || { echo "rsync: bash is required by the Android NDK compiler launchers" >&2 exit 1 } # Locate the NDK toolchain. ndk=${ANDROID_NDK_HOME:-${ANDROID_NDK_ROOT:-}} if [ -z "$ndk" ] && [ -n "${ANDROID_HOME:-}" ]; then ndk="$ANDROID_HOME/ndk/$ANDROID_NDK" fi if [ -z "$ndk" ] || [ ! -d "$ndk" ]; then echo "rsync: Android NDK not found; set ANDROID_NDK_HOME" >&2 exit 1 fi # NDK prebuilt toolchains are named by build host, not target; the target ABI is # selected separately below. ci/setup-toolchain.sh provisions Linux x86_64 only, # so that is the only host tag worth naming here. if [ "$(uname -s)" != Linux ] || [ "$(uname -m)" != x86_64 ]; then echo "rsync: only Linux x86_64 build hosts are supported" >&2 exit 1 fi tc="$ndk/toolchains/llvm/prebuilt/linux-x86_64/bin" if [ ! -d "$tc" ]; then echo "rsync: NDK toolchain not found at $tc" >&2 exit 1 fi work="$root/rsync/work" tarball="$root/rsync/rsync-${RSYNC_VERSION}.tar.gz" src="$work/rsync-${RSYNC_VERSION}" part="$tarball.part.$$" output= trap 'if [ -n "$part" ]; then rm -f "$part"; fi; if [ -n "$output" ]; then rm -f "$output"; fi' 0 1 2 3 15 # Fetch and verify the pinned source tarball. mkdir -p "$work" if [ -f "$tarball" ] && ! ( cd "$root/rsync" && sha256sum -c "rsync-${RSYNC_VERSION}.tar.gz.sha256" >/dev/null 2>&1 ); then echo "rsync: removing corrupt $(basename "$tarball")" >&2 rm -f "$tarball" fi if [ ! -f "$tarball" ]; then echo "rsync: fetching $RSYNC_URL" # Download aside and rename, so an interrupted fetch leaves no truncated # tarball for the next run to pick up. Verify before publishing it. curl --fail --silent --show-error --location --retry 3 \ --connect-timeout 30 --speed-limit 1024 --speed-time 60 \ "$RSYNC_URL" -o "$part" want=$(awk 'NR == 1 { print $1 }' "$root/rsync/rsync-${RSYNC_VERSION}.tar.gz.sha256") actual=$(sha256sum "$part") [ "${actual%% *}" = "$want" ] || { echo "rsync: checksum failed for $(basename "$tarball")" >&2 exit 1 } mv "$part" "$tarball" part= fi echo "rsync: verifying sha256" ( cd "$root/rsync" && sha256sum -c "rsync-${RSYNC_VERSION}.tar.gz.sha256" ) for abi in $ABIS; do case "$abi" in arm64-v8a) host=aarch64-linux-android cc="aarch64-linux-android${ANDROID_MIN_SDK}-clang" ;; x86_64) host=x86_64-linux-android cc="x86_64-linux-android${ANDROID_MIN_SDK}-clang" ;; *) echo "rsync: unknown ABI $abi" >&2; exit 1 ;; esac # Fresh build tree per ABI. rm -rf "$src" tar xzf "$tarball" -C "$work" # CXX must be pinned like CC: rsync's SIMD checksum is C++, and configure # otherwise falls back to the host g++, leaking a host-compiled object # into the target binary (or failing where the host lacks ifunc). echo "rsync: configuring $abi" ( cd "$src" && # No system libraries, generated docs, iconv, ACLs, or xattrs. set -- --disable-md2man --disable-openssl --disable-xxhash \ --disable-zstd --disable-lz4 --disable-iconv \ --disable-acl-support --disable-xattr-support \ --with-included-popt --with-included-zlib \ --with-nobody-user=nobody --with-nobody-group=nobody # NDK r27 needs both page-size flags for 16 KB Android devices. # rsync parses peer-controlled data in C. Keep the compiler defenses # explicit rather than relying on whichever defaults an NDK release # happens to select. CC="$tc/$cc" CXX="$tc/${cc}++" AR="$tc/llvm-ar" RANLIB="$tc/llvm-ranlib" \ rsync_cv_HAVE_SECURE_MKSTEMP=yes \ CFLAGS="-O2 -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -ffile-prefix-map=$src=." \ CXXFLAGS="-O2 -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -ffile-prefix-map=$src=." \ LDFLAGS="-pie -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384" \ ./configure --host="$host" "$@" >/dev/null ) # Bundled popt leaves conditional variables unused on Android. Append the # two narrow suppressions after rsync's own -Wall flags. build_cflags=$(sed -n 's/^CFLAGS=//p' "$src/Makefile") if [ -z "$build_cflags" ]; then echo "rsync: generated Makefile has no CFLAGS" >&2 exit 1 fi echo "rsync: building $abi" ( cd "$src" && make -s CFLAGS="$build_cflags -Wno-unused-variable -Wno-unused-but-set-variable" rsync ) dest="$root/app/src/main/jniLibs/$abi" mkdir -p "$dest" output="$dest/libxrsync.so.part.$$" cp "$src/rsync" "$output" "$tc/llvm-strip" "$output" mv "$output" "$dest/libxrsync.so" output= echo "rsync: $abi -> jniLibs/$abi/libxrsync.so" done