315 lines
9.3 KiB
Bash
315 lines
9.3 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
set -x
|
|
|
|
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
RUST_DIR="$SCRIPT_DIR/rust"
|
|
TOOLCHAIN="${RUST_TOOLCHAIN:-}"
|
|
OMVLL_VERSION="${OMVLL_VERSION:-1.4.1}"
|
|
OMVLL_LINUX_ASSET="${OMVLL_LINUX_ASSET:-omvll_v1-4-1_linux_2025-10-01T09.33.59.tar.gz}"
|
|
|
|
if [ ! -d "$RUST_DIR" ]; then
|
|
echo "Unable to locate Rust sources under $RUST_DIR" >&2
|
|
exit 1
|
|
fi
|
|
|
|
HOST_TAG=""
|
|
HOST_LIB_SUBDIR=""
|
|
NDK_TOOLCHAIN_DIR=""
|
|
NDK_LIB_DIR=""
|
|
|
|
detect_host_tag() {
|
|
local os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
local arch="$(uname -m)"
|
|
case "$os" in
|
|
linux*)
|
|
HOST_TAG="linux-x86_64"
|
|
HOST_LIB_SUBDIR="lib64"
|
|
;;
|
|
darwin*)
|
|
if [[ "$arch" == "arm64" ]]; then
|
|
HOST_TAG="darwin-arm64"
|
|
else
|
|
HOST_TAG="darwin-x86_64"
|
|
fi
|
|
HOST_LIB_SUBDIR="lib"
|
|
;;
|
|
msys*|mingw*|cygwin*)
|
|
HOST_TAG="windows-x86_64"
|
|
HOST_LIB_SUBDIR="lib"
|
|
;;
|
|
*)
|
|
echo "Unsupported host platform: $os ($arch)" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
ensure_ndk_for_host() {
|
|
local try_home="$1"
|
|
local bin_dir="$try_home/toolchains/llvm/prebuilt/$HOST_TAG/bin"
|
|
local lib_root="$try_home/toolchains/llvm/prebuilt/$HOST_TAG"
|
|
local lib_dir="$lib_root/$HOST_LIB_SUBDIR"
|
|
if [ ! -d "$lib_dir" ]; then
|
|
if [ -d "$lib_root/lib64" ]; then
|
|
lib_dir="$lib_root/lib64"
|
|
elif [ -d "$lib_root/lib" ]; then
|
|
lib_dir="$lib_root/lib"
|
|
fi
|
|
fi
|
|
if [ -d "$bin_dir" ] && [ -d "$lib_dir" ]; then
|
|
ANDROID_NDK_HOME="$try_home"
|
|
NDK_TOOLCHAIN_DIR="$bin_dir"
|
|
NDK_LIB_DIR="$lib_dir"
|
|
echo "$bin_dir"
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
provision_linux_ndk() {
|
|
local tmp_ndk="${ANDROID_NDK_TMP:-$HOME/.cache/purrfectsnap-ndk}"
|
|
local extracted_dir="$tmp_ndk/android-ndk-r28b"
|
|
if [ -d "$extracted_dir/toolchains/llvm/prebuilt" ]; then
|
|
if ensure_ndk_for_host "$extracted_dir"; then
|
|
ANDROID_NDK_HOME="$extracted_dir"
|
|
return
|
|
fi
|
|
fi
|
|
rm -rf "$tmp_ndk"
|
|
mkdir -p "$tmp_ndk"
|
|
curl -fsSL https://dl.google.com/android/repository/android-ndk-r28b-linux.zip -o "$tmp_ndk/ndk.zip"
|
|
unzip -oq "$tmp_ndk/ndk.zip" -d "$tmp_ndk"
|
|
ANDROID_NDK_HOME="$(find "$tmp_ndk" -maxdepth 1 -type d -name 'android-ndk-r*' | head -n1)"
|
|
# Permissions are preserved during extraction; no additional chmod required.
|
|
}
|
|
|
|
ensure_omvll_bundle() {
|
|
local archive_url="${OMVLL_ARCHIVE_URL:-https://github.com/open-obfuscator/o-mvll/releases/download/${OMVLL_VERSION}/${OMVLL_LINUX_ASSET}}"
|
|
OMVLL_CACHE_DIR="$SCRIPT_DIR/.omvll/$OMVLL_VERSION"
|
|
OMVLL_PLUGIN_PATH="$OMVLL_CACHE_DIR/omvll-ndk.so"
|
|
local tmp_tar=""
|
|
|
|
download_omvll_bundle() {
|
|
rm -rf "$OMVLL_CACHE_DIR"
|
|
mkdir -p "$OMVLL_CACHE_DIR"
|
|
tmp_tar=$(mktemp)
|
|
curl -fL "$archive_url" -o "$tmp_tar"
|
|
tar -xzf "$tmp_tar" -C "$OMVLL_CACHE_DIR"
|
|
rm -f "$tmp_tar"
|
|
}
|
|
|
|
if [ ! -f "$OMVLL_PLUGIN_PATH" ]; then
|
|
echo "Downloading O-MVLL bundle from $archive_url"
|
|
download_omvll_bundle
|
|
fi
|
|
|
|
if [ ! -f "$OMVLL_PLUGIN_PATH" ]; then
|
|
local found_plugin
|
|
found_plugin=$(find "$OMVLL_CACHE_DIR" -type f -name 'omvll-ndk.so' | head -n1 || true)
|
|
if [ -n "$found_plugin" ] && [ -f "$found_plugin" ]; then
|
|
OMVLL_PLUGIN_PATH="$found_plugin"
|
|
else
|
|
echo "Failed to locate O-MVLL plugin under $OMVLL_PLUGIN_PATH" >&2
|
|
echo "Contents:" >&2
|
|
ls -la "$OMVLL_CACHE_DIR" >&2 || true
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [ -z "${OMVLL_PYTHONPATH:-}" ]; then
|
|
local py_root
|
|
py_root=$(find "$OMVLL_CACHE_DIR" -maxdepth 1 -type d -name 'Python-*' | head -n1 || true)
|
|
if [ -z "$py_root" ] || [ ! -d "$py_root/Lib" ]; then
|
|
echo "OMVLL cache is incomplete. Re-downloading bundle..." >&2
|
|
download_omvll_bundle
|
|
py_root=$(find "$OMVLL_CACHE_DIR" -maxdepth 1 -type d -name 'Python-*' | head -n1 || true)
|
|
if [ -z "$py_root" ] || [ ! -d "$py_root/Lib" ]; then
|
|
echo "Unable to determine Python stdlib directory inside the O-MVLL bundle" >&2
|
|
echo "OMVLL_CACHE_DIR: $OMVLL_CACHE_DIR" >&2
|
|
echo "Contents:" >&2
|
|
ls -la "$OMVLL_CACHE_DIR" >&2 || true
|
|
exit 1
|
|
fi
|
|
fi
|
|
export OMVLL_PYTHONPATH="$py_root/Lib"
|
|
fi
|
|
|
|
local plugin_target="/tmp/purrfectsnap-omvll/$OMVLL_VERSION/omvll-ndk.so"
|
|
mkdir -p "$(dirname "$plugin_target")"
|
|
cp -f "$OMVLL_PLUGIN_PATH" "$plugin_target"
|
|
OMVLL_PLUGIN_PATH="$plugin_target"
|
|
echo "OMVLL plugin path: $OMVLL_PLUGIN_PATH"
|
|
|
|
export OMVLL_HOME="$OMVLL_CACHE_DIR"
|
|
}
|
|
|
|
append_rustflags() {
|
|
local var_name="$1"
|
|
local current_value="${!var_name:-}"
|
|
local addition="-Clink-arg=-fpass-plugin=$OMVLL_PLUGIN_PATH"
|
|
if [[ "$current_value" == *"$addition"* ]]; then
|
|
return
|
|
fi
|
|
if [ -n "$current_value" ]; then
|
|
export "$var_name"="$current_value $addition"
|
|
else
|
|
export "$var_name"="$addition"
|
|
fi
|
|
}
|
|
|
|
|
|
# Detect host platform early so we know which NDK prebuilt we need
|
|
detect_host_tag
|
|
|
|
# Default toolchain selection (favor GNU toolchain on Windows to avoid MSVC link.exe requirement)
|
|
if [[ -z "$TOOLCHAIN" ]]; then
|
|
if [[ "$HOST_TAG" == windows-* ]]; then
|
|
TOOLCHAIN="stable-x86_64-pc-windows-gnu"
|
|
else
|
|
TOOLCHAIN="stable"
|
|
fi
|
|
fi
|
|
|
|
# OMVLL is required and only supported on Linux/macOS/WSL. Fail fast on plain Windows.
|
|
USE_OMVLL=true
|
|
IS_WSL=false
|
|
if grep -qi microsoft /proc/version 2>/dev/null; then
|
|
IS_WSL=true
|
|
fi
|
|
if [[ "$HOST_TAG" == windows-* && "$IS_WSL" != true ]]; then
|
|
echo "OMVLL requires a Linux/WSL environment. Please run the build via WSL (e.g., set BASH_PATH=C:\\Windows\\System32\\bash.exe)." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Install rustup if it is not already available
|
|
if ! command -v rustup >/dev/null 2>&1; then
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain "$TOOLCHAIN"
|
|
fi
|
|
|
|
# Add cargo to PATH when rustup installed the toolchain locally
|
|
if [ -f "$HOME/.cargo/env" ]; then
|
|
# shellcheck source=/dev/null
|
|
source "$HOME/.cargo/env"
|
|
fi
|
|
|
|
# Ensure the requested toolchain (default: stable) is installed
|
|
rustup toolchain install "$TOOLCHAIN" --profile minimal
|
|
|
|
if [ -n "${ANDROID_NDK_HOME:-}" ] && [ -d "$ANDROID_NDK_HOME" ]; then
|
|
if ensure_ndk_for_host "$ANDROID_NDK_HOME"; then
|
|
:
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$NDK_TOOLCHAIN_DIR" ]; then
|
|
if [[ "$HOST_TAG" != linux-* ]]; then
|
|
echo "ANDROID_NDK_HOME must point to a valid NDK for $HOST_TAG" >&2
|
|
exit 1
|
|
fi
|
|
echo "Provisioning Linux NDK r28b inside container..." >&2
|
|
provision_linux_ndk
|
|
ensure_ndk_for_host "$ANDROID_NDK_HOME"
|
|
fi
|
|
|
|
if [ -z "$NDK_TOOLCHAIN_DIR" ]; then
|
|
echo "ERROR: Could not find a $HOST_TAG NDK toolchain bin directory." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Normalize NDK paths for msys/git-bash on Windows so PATH lookups work
|
|
if [[ "$HOST_TAG" == windows-* ]]; then
|
|
if command -v cygpath >/dev/null 2>&1; then
|
|
NDK_TOOLCHAIN_DIR=$(cygpath -u "$NDK_TOOLCHAIN_DIR")
|
|
NDK_LIB_DIR=$(cygpath -u "$NDK_LIB_DIR")
|
|
else
|
|
# best-effort slash normalization
|
|
NDK_TOOLCHAIN_DIR=${NDK_TOOLCHAIN_DIR//\\//}
|
|
NDK_LIB_DIR=${NDK_LIB_DIR//\\//}
|
|
fi
|
|
fi
|
|
|
|
echo "Using NDK toolchain at: $NDK_TOOLCHAIN_DIR"
|
|
ls -l "$NDK_TOOLCHAIN_DIR/aarch64-linux-android28-clang" || true
|
|
export PATH="$NDK_TOOLCHAIN_DIR:$PATH"
|
|
|
|
if [ -z "${LD_LIBRARY_PATH:-}" ]; then
|
|
export LD_LIBRARY_PATH="$NDK_LIB_DIR"
|
|
else
|
|
export LD_LIBRARY_PATH="$NDK_LIB_DIR:$LD_LIBRARY_PATH"
|
|
fi
|
|
|
|
if [[ "$HOST_TAG" == darwin-* ]]; then
|
|
if [ -z "${DYLD_LIBRARY_PATH:-}" ]; then
|
|
export DYLD_LIBRARY_PATH="$NDK_LIB_DIR"
|
|
else
|
|
export DYLD_LIBRARY_PATH="$NDK_LIB_DIR:$DYLD_LIBRARY_PATH"
|
|
fi
|
|
fi
|
|
|
|
ensure_omvll_bundle
|
|
append_rustflags CARGO_TARGET_AARCH64_LINUX_ANDROID_RUSTFLAGS
|
|
append_rustflags CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_RUSTFLAGS
|
|
|
|
if [ -z "${OMVLL_CONFIG:-}" ]; then
|
|
export OMVLL_CONFIG="$SCRIPT_DIR/omvll_config.py"
|
|
fi
|
|
|
|
if [ -z "${OMVLL_PYTHONPATH:-}" ] || [ ! -d "$OMVLL_PYTHONPATH" ]; then
|
|
echo "OMVLL_PYTHONPATH is not configured with a valid stdlib" >&2
|
|
exit 1
|
|
fi
|
|
|
|
rustup target add --toolchain "$TOOLCHAIN" "$1"
|
|
|
|
HOST_CC=""
|
|
if command -v gcc >/dev/null 2>&1; then
|
|
HOST_CC="gcc"
|
|
elif command -v clang >/dev/null 2>&1; then
|
|
HOST_CC="clang"
|
|
elif command -v cc >/dev/null 2>&1; then
|
|
HOST_CC="cc"
|
|
else
|
|
echo "Error: No host C compiler found. Build scripts require gcc, clang, or cc." >&2
|
|
echo "Install build-essential: sudo apt-get update && sudo apt-get install -y build-essential" >&2
|
|
exit 1
|
|
fi
|
|
|
|
export CC="$HOST_CC"
|
|
export HOST_CC="$HOST_CC"
|
|
|
|
case "$1" in
|
|
aarch64-linux-android)
|
|
CLANG_BIN="$NDK_TOOLCHAIN_DIR/aarch64-linux-android28-clang"
|
|
AR_BIN="$NDK_TOOLCHAIN_DIR/llvm-ar"
|
|
if [[ "$HOST_TAG" == windows-* ]]; then
|
|
CLANG_BIN="${CLANG_BIN}.cmd"
|
|
AR_BIN="${AR_BIN}.exe"
|
|
fi
|
|
export CC_aarch64_linux_android="$CLANG_BIN"
|
|
export AR_aarch64_linux_android="$AR_BIN"
|
|
export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$CLANG_BIN"
|
|
export CFLAGS_aarch64_linux_android="-DZSTD_LEGACY_SUPPORT=0 -DZSTD_DISABLE_ASM"
|
|
;;
|
|
armv7-linux-androideabi)
|
|
CLANG_BIN="$NDK_TOOLCHAIN_DIR/armv7a-linux-androideabi28-clang"
|
|
AR_BIN="$NDK_TOOLCHAIN_DIR/llvm-ar"
|
|
if [[ "$HOST_TAG" == windows-* ]]; then
|
|
CLANG_BIN="${CLANG_BIN}.cmd"
|
|
AR_BIN="${AR_BIN}.exe"
|
|
fi
|
|
export CC_armv7_linux_androideabi="$CLANG_BIN"
|
|
export AR_armv7_linux_androideabi="$AR_BIN"
|
|
export CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="$CLANG_BIN"
|
|
export CFLAGS_armv7_linux_androideabi="-DZSTD_LEGACY_SUPPORT=0 -DZSTD_DISABLE_ASM -mfloat-abi=softfp -mfpu=neon"
|
|
;;
|
|
*)
|
|
echo "Unknown target $1" >&2
|
|
;;
|
|
esac
|
|
|
|
cd "$RUST_DIR"
|
|
rustup run "$TOOLCHAIN" cargo build --release --target "$1"
|
|
|
|
|