#!/usr/bin/env bash
# Builds proot and its loader for arm64 Android with NDK 27.1, every PT_LOAD aligned to 16 KB, and copies them to
# sandbox/src/main/jniLibs/arm64-v8a as libproot.so and libproot-loader.so (so Android extracts them executable into
# nativeLibraryDir). talloc is built static, the way Termux builds it, and linked into proot.
#
# Needs $ANDROID_HOME (with ndk/27.1.12297006), python3 (talloc's waf), make and curl. Run from anywhere.
# See SOURCES.md for the pinned sources and licenses.
set -euo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OUT="$HERE/../src/main/jniLibs/arm64-v8a"

PROOT_VERSION=5.1.107.102
PROOT_URL="https://github.com/termux/proot/archive/refs/tags/v$PROOT_VERSION.tar.gz"
PROOT_SHA256=36fb68838d86e491bc577064e23d365ea936e03ded4e90f62dc540907c04f385
TALLOC_VERSION=2.5.0
TALLOC_URL="https://www.samba.org/ftp/talloc/talloc-$TALLOC_VERSION.tar.gz"
TALLOC_SHA256=912afa237510ae542a7733998eb18a12bcda35ab6729c8e2ddb43e8d0ebab007

: "${ANDROID_HOME:?set ANDROID_HOME to the Android SDK}"
NDK="$ANDROID_HOME/ndk/27.1.12297006"
case "$(uname -s)" in
  Darwin) HOST=darwin-x86_64 ;;
  Linux) HOST=linux-x86_64 ;;
  *) echo "unsupported host $(uname -s)" >&2; exit 1 ;;
esac
TOOLS="$NDK/toolchains/llvm/prebuilt/$HOST/bin"
CC="$TOOLS/aarch64-linux-android26-clang"
AR="$TOOLS/llvm-ar"
STRIP="$TOOLS/llvm-strip"
OBJCOPY="$TOOLS/llvm-objcopy"
OBJDUMP="$TOOLS/llvm-objdump"
for t in "$CC" "$AR" "$STRIP" "$OBJCOPY" "$OBJDUMP" "$TOOLS/llvm-readelf"; do
  [ -x "$t" ] || { echo "missing $t (is NDK 27.1.12297006 installed?)" >&2; exit 1; }
done
command -v python3 >/dev/null || { echo "python3 is needed for talloc's waf" >&2; exit 1; }

# Flags from the caller's environment would reach talloc's configure and proot's += appends, and change the output.
unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS LIBS LOADER_CFLAGS LOADER_LDFLAGS CROSS_COMPILE MAKEFLAGS SOURCE_DATE_EPOCH

# make and waf split these paths on spaces; refuse rather than fail halfway.
for v in TMPDIR ANDROID_HOME; do
  case "${!v:-}" in *" "*) echo "$v contains a space (${!v}); use a path without spaces" >&2; exit 1 ;; esac
done

WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT

sha256() { if command -v sha256sum >/dev/null; then sha256sum "$1"; else shasum -a 256 "$1"; fi | cut -d' ' -f1; }

fetch() { # url sha256 dest
  curl -fsSL --retry 3 -o "$3" "$1"
  local got; got="$(sha256 "$3")"
  if [ "$got" != "$2" ]; then
    rm -f "$3"
    echo "SHA-256 mismatch for $1: got $got, want $2" >&2
    exit 1
  fi
}

fetch "$TALLOC_URL" "$TALLOC_SHA256" "$WORK/talloc.tar.gz"
fetch "$PROOT_URL" "$PROOT_SHA256" "$WORK/proot.tar.gz"
tar -xzf "$WORK/talloc.tar.gz" -C "$WORK"
tar -xzf "$WORK/proot.tar.gz" -C "$WORK"
TALLOC="$WORK/talloc-$TALLOC_VERSION"
PROOT="$WORK/proot-$PROOT_VERSION"

# ashmem_memfd.c calls strcmp and memset without <string.h>. Termux's prefix headers happen to pull it in; the NDK's
# clang makes an implicit declaration an error. The only change to the pinned source.
ASHMEM="$PROOT/src/extension/ashmem_memfd/ashmem_memfd.c"
grep -q '^#include <string.h>' "$ASHMEM" || {
  sed 's|^#include <stdlib.h>$|#include <stdlib.h>\n#include <string.h>|' "$ASHMEM" > "$ASHMEM.new"
  mv "$ASHMEM.new" "$ASHMEM"
}
grep -q '^#include <string.h>' "$ASHMEM" || { echo "could not add <string.h> to $ASHMEM" >&2; exit 1; }

# The aarch64 build makes loader/loader-info.c with a bare `readelf -s`, not $(CROSS_COMPILE)readelf.
mkdir "$WORK/bin"
ln -s "$TOOLS/llvm-readelf" "$WORK/bin/readelf"
export PATH="$WORK/bin:$PATH"
# proot's build.h runs `git describe`; keep it from finding any repository around $WORK. VERSION comes from CPPFLAGS.
export GIT_DIR=/nonexistent

# talloc, static (termux-packages/packages/libtalloc/build.sh). Termux archives only talloc*.o, but its proot links the
# shared libtalloc.so; statically, talloc 2.5.0 also needs its built-in libreplace (rep_memset_explicit). So the archive
# holds exactly the objects waf links into libtalloc.so (its `-shared` line), and not the testsuite's copies.
cp "$HERE/cross-answers.txt" "$TALLOC/cross-answers.txt"
(
  cd "$TALLOC"
  CC="$CC" ./configure --disable-rpath --disable-python --cross-compile --cross-answers=cross-answers.txt
  make
  cd bin/default
  "$AR" rcs "$TALLOC/libtalloc.a" talloc.c.6.o lib/replace/replace.c.2.o lib/replace/closefrom.c.2.o
)

# proot. Flags go in as environment variables: a variable given on the make command line would replace the
# makefile's own += appends (-D_GNU_SOURCE -I. in CPPFLAGS; -static -nostdlib -Ttext=... in LOADER_LDFLAGS).
# VERSION holds \" literally, as Termux's CPPFLAGS does, so the recipe's shell leaves a C string literal.
export CC AR STRIP OBJCOPY OBJDUMP
export PROOT_UNBUNDLE_LOADER=/nonexistent
export CPPFLAGS="-I$TALLOC -DARG_MAX=131072 -DVERSION=\\\"$PROOT_VERSION\\\""
export LDFLAGS="-L$TALLOC -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384"
export LOADER_LDFLAGS="-Wl,-z,max-page-size=16384"
# HAS_LOADER_32BIT= must override the makefile's $(eval ...) value: no 32-bit loader, the guest is arm64 only.
make -C "$PROOT/src" proot HAS_LOADER_32BIT=

mkdir -p "$OUT"
cp "$PROOT/src/proot" "$OUT/libproot.so"
cp "$PROOT/src/loader/loader" "$OUT/libproot-loader.so"
"$STRIP" "$OUT/libproot.so" "$OUT/libproot-loader.so"
chmod 755 "$OUT/libproot.so" "$OUT/libproot-loader.so"

{
  for f in libproot.so libproot-loader.so; do printf '%s  %s\n' "$(sha256 "$OUT/$f")" "$f"; done
} > "$HERE/SHA256SUMS"
cat "$HERE/SHA256SUMS"
