Files
MrbWebLibre/native/go_mobile_runtime/scripts/build-android.sh
T

330 lines
9.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
RUNTIME_DIR="$(cd "$SCRIPT_DIR/.." && pwd -P)"
REPO_ROOT="$(cd "$RUNTIME_DIR/../.." && pwd -P)"
# shellcheck source=../pins.env
source "$RUNTIME_DIR/pins.env"
GRADLE_PROPERTIES="$REPO_ROOT/apps/weblibre/android/gradle.properties"
DEFAULT_ANDROID_NDK_VERSION="$(
sed -n 's/^weblibre\.ndkVersion[[:space:]]*=[[:space:]]*//p' "$GRADLE_PROPERTIES"
)"
DEFAULT_SING_BOX_SOURCE="$(cd "$REPO_ROOT/.." && pwd -P)/sing-box"
DEFAULT_IPTPROXY_SOURCE="$(cd "$REPO_ROOT/.." && pwd -P)/IPtProxy"
SING_BOX_SOURCE="${SING_BOX_SOURCE:-$DEFAULT_SING_BOX_SOURCE}"
IPTPROXY_SOURCE="${IPTPROXY_SOURCE:-$DEFAULT_IPTPROXY_SOURCE}"
DNSTT_SOURCE="${DNSTT_SOURCE:-$IPTPROXY_SOURCE/dnstt}"
OUTPUT_AAR="${OUTPUT_AAR:-$RUNTIME_DIR/build/weblibre-go.aar}"
TARGET="${TARGET:-android}"
ANDROID_API="${ANDROID_API:-24}"
ANDROID_NDK_VERSION="${ANDROID_NDK_VERSION:-$DEFAULT_ANDROID_NDK_VERSION}"
INSTALL_GOMOBILE=1
ALLOW_DIRTY=0
DEBUG=0
DEFAULT_TAGS="with_conntrack,with_gvisor,with_quic,with_wireguard,with_utls,with_naive_outbound,with_clash_api,badlinkname,tfogo_checklinkname0,netcgo"
GO_MOBILE_TAGS="${GO_MOBILE_TAGS:-$DEFAULT_TAGS}"
usage() {
cat <<'EOF'
Usage: build-android.sh [options]
Build the combined WebLibre gomobile Android AAR containing sing-box libbox and IPtProxy.
Options:
--sing-box-source PATH sing-box checkout (default: ../sing-box)
--iptproxy-source PATH IPtProxy checkout (default: ../IPtProxy)
--dnstt-source PATH dnstt checkout (default: <IPtProxy>/dnstt)
--output PATH destination AAR (default: native/go_mobile_runtime/build/weblibre-go.aar)
--target TARGET gomobile target, e.g. android/arm64 (default: android)
--android-api API Android API level for gomobile bind (default: 24)
--tags TAGS comma-separated Go build tags
--debug keep debug symbols
--allow-dirty allow dirty sing-box/IPtProxy source trees
--skip-install-gomobile require gomobile/gobind in GOPATH/bin
-h, --help show this help
Environment:
SING_BOX_SOURCE, IPTPROXY_SOURCE, DNSTT_SOURCE, OUTPUT_AAR, TARGET,
ANDROID_API, ANDROID_NDK_VERSION, GO_MOBILE_TAGS, JAVA_HOME, ANDROID_HOME,
ANDROID_NDK_HOME
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--sing-box-source)
SING_BOX_SOURCE="$2"
shift 2
;;
--iptproxy-source)
IPTPROXY_SOURCE="$2"
shift 2
;;
--dnstt-source)
DNSTT_SOURCE="$2"
shift 2
;;
--output)
OUTPUT_AAR="$2"
shift 2
;;
--target)
TARGET="$2"
shift 2
;;
--android-api)
ANDROID_API="$2"
shift 2
;;
--tags)
GO_MOBILE_TAGS="$2"
shift 2
;;
--debug)
DEBUG=1
shift
;;
--allow-dirty)
ALLOW_DIRTY=1
shift
;;
--skip-install-gomobile)
INSTALL_GOMOBILE=0
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
usage >&2
exit 2
;;
esac
done
require_dir() {
local path="$1"
local description="$2"
if [[ ! -d "$path" ]]; then
echo "$description not found at: $path" >&2
exit 1
fi
}
require_file() {
local path="$1"
local description="$2"
if [[ ! -f "$path" ]]; then
echo "$description not found at: $path" >&2
exit 1
fi
}
verify_git_pin() {
local repo="$1"
local name="$2"
local tag="$3"
local expected_commit="$4"
local tag_commit
tag_commit="$(git -C "$repo" rev-parse --verify "refs/tags/$tag^{commit}")"
if [[ "$tag_commit" != "$expected_commit" ]]; then
echo "$name tag $tag resolves to $tag_commit, expected $expected_commit" >&2
exit 1
fi
local head_commit
head_commit="$(git -C "$repo" rev-parse HEAD)"
if [[ "$head_commit" != "$expected_commit" ]]; then
echo "$name checkout is at $head_commit, expected $tag ($expected_commit)." >&2
echo "Check out the pinned tag first: git -C '$repo' checkout '$tag'" >&2
exit 1
fi
if [[ $ALLOW_DIRTY -eq 0 && -n "$(git -C "$repo" status --porcelain)" ]]; then
echo "$name checkout has uncommitted changes. Re-run with --allow-dirty to build anyway." >&2
exit 1
fi
}
require_dir "$SING_BOX_SOURCE" "sing-box source"
require_file "$SING_BOX_SOURCE/go.mod" "sing-box go.mod"
require_dir "$SING_BOX_SOURCE/experimental/libbox" "sing-box libbox package"
require_dir "$IPTPROXY_SOURCE" "IPtProxy source"
require_file "$IPTPROXY_SOURCE/IPtProxy.go/go.mod" "IPtProxy go.mod"
require_dir "$DNSTT_SOURCE" "dnstt source"
require_file "$DNSTT_SOURCE/go.mod" "dnstt go.mod"
verify_git_pin "$SING_BOX_SOURCE" "sing-box" "$SING_BOX_TAG" "$SING_BOX_COMMIT"
verify_git_pin "$IPTPROXY_SOURCE" "IPtProxy" "$IPTPROXY_TAG" "$IPTPROXY_COMMIT"
read -r _ _ iptproxy_dnstt_commit _ <<< "$(git -C "$IPTPROXY_SOURCE" ls-tree "$IPTPROXY_TAG" dnstt)"
if [[ "$iptproxy_dnstt_commit" != "$DNSTT_COMMIT" ]]; then
echo "IPtProxy tag $IPTPROXY_TAG points dnstt to $iptproxy_dnstt_commit, expected $DNSTT_COMMIT" >&2
exit 1
fi
if [[ -d "$DNSTT_SOURCE/.git" ]]; then
dnstt_head="$(git -C "$DNSTT_SOURCE" rev-parse HEAD)"
if [[ "$dnstt_head" != "$DNSTT_COMMIT" ]]; then
echo "dnstt checkout is at $dnstt_head, expected $DNSTT_COMMIT." >&2
exit 1
fi
fi
if [[ -z "${JAVA_HOME:-}" && -x /usr/lib/jvm/java-17-openjdk/bin/java ]]; then
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
fi
JAVA_BIN="${JAVA_HOME:+$JAVA_HOME/bin/}java"
if ! "$JAVA_BIN" --version 2>/dev/null | grep -q 'openjdk 17'; then
echo "gomobile Android runtime build requires OpenJDK 17." >&2
"$JAVA_BIN" --version >&2 || true
exit 1
fi
if ! command -v go >/dev/null 2>&1; then
echo "go is required to build the gomobile runtime." >&2
exit 1
fi
if [[ -z "$ANDROID_NDK_VERSION" ]]; then
echo "Missing weblibre.ndkVersion in $GRADLE_PROPERTIES." >&2
exit 1
fi
if [[ -z "${ANDROID_HOME:-}" && -d "$HOME/Android/Sdk" ]]; then
export ANDROID_HOME="$HOME/Android/Sdk"
fi
if [[ -n "${ANDROID_HOME:-}" ]]; then
export ANDROID_NDK_HOME="$ANDROID_HOME/ndk/$ANDROID_NDK_VERSION"
export ANDROID_NDK_ROOT="$ANDROID_NDK_HOME"
export NDK_HOME="$ANDROID_NDK_HOME"
fi
if [[ -z "${ANDROID_NDK_HOME:-}" ]]; then
echo "ANDROID_NDK_HOME is required to build the gomobile runtime." >&2
echo "Install Android NDK $ANDROID_NDK_VERSION and set ANDROID_HOME or ANDROID_NDK_HOME." >&2
exit 1
fi
require_file "$ANDROID_NDK_HOME/source.properties" "Android NDK source.properties"
actual_ndk_version="$(
sed -n 's/^Pkg.Revision[[:space:]]*=[[:space:]]*//p' "$ANDROID_NDK_HOME/source.properties"
)"
if [[ "$actual_ndk_version" != "$ANDROID_NDK_VERSION" ]]; then
echo "Android NDK at $ANDROID_NDK_HOME is $actual_ndk_version, expected $ANDROID_NDK_VERSION." >&2
exit 1
fi
ndk_host_tag="linux-x86_64"
case "$(uname -s)" in
Darwin) ndk_host_tag="darwin-x86_64" ;;
Linux) ndk_host_tag="linux-x86_64" ;;
*)
echo "Unsupported host OS for Android NDK: $(uname -s)" >&2
exit 1
;;
esac
ndk_clang="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/$ndk_host_tag/bin/aarch64-linux-android${ANDROID_API}-clang"
if [[ ! -x "$ndk_clang" ]]; then
echo "Expected Android NDK clang not found: $ndk_clang" >&2
exit 1
fi
echo "Using Android NDK $actual_ndk_version at $ANDROID_NDK_HOME"
echo "Using Android arm64 clang: $ndk_clang"
GOPATH="${GOPATH:-$(go env GOPATH)}"
if [[ $INSTALL_GOMOBILE -eq 1 ]]; then
needs_gomobile_install=0
if [[ ! -x "$GOPATH/bin/gomobile" || ! -x "$GOPATH/bin/gobind" ]]; then
needs_gomobile_install=1
elif ! "$GOPATH/bin/gomobile" bind -h 2>&1 | grep -q -- '-libname'; then
needs_gomobile_install=1
fi
if [[ $needs_gomobile_install -eq 1 ]]; then
go install -v "$GOMOBILE_MODULE/cmd/gomobile@$GOMOBILE_VERSION"
go install -v "$GOMOBILE_MODULE/cmd/gobind@$GOMOBILE_VERSION"
fi
fi
"$GOPATH/bin/gomobile" init
WORK_DIR="$RUNTIME_DIR/.work/android"
rm -rf "$WORK_DIR"
mkdir -p "$WORK_DIR" "$(dirname "$OUTPUT_AAR")"
cat > "$WORK_DIR/go.mod" <<EOF
module eu.weblibre.go_mobile_runtime
go 1.25.0
require (
github.com/sagernet/gomobile $GOMOBILE_VERSION
github.com/sagernet/sing-box v1.13.12
github.com/tladesignz/IPtProxy.git v0.0.0
)
replace github.com/sagernet/sing-box => $SING_BOX_SOURCE
replace github.com/tladesignz/IPtProxy.git => $IPTPROXY_SOURCE/IPtProxy.go
replace www.bamsoftware.com/git/dnstt.git => $DNSTT_SOURCE
EOF
cat > "$WORK_DIR/tools.go" <<'EOF'
package tools
import (
_ "github.com/sagernet/gomobile"
_ "github.com/sagernet/gomobile/bind"
_ "github.com/sagernet/sing-box/experimental/libbox"
_ "github.com/tladesignz/IPtProxy.git"
)
EOF
pushd "$WORK_DIR" >/dev/null
go get "$GOMOBILE_MODULE/bind@$GOMOBILE_VERSION"
go mod tidy
args=(
bind
-v
-target "$TARGET"
-androidapi "$ANDROID_API"
-javapkg=io.nekohasekai
-libname=box
-o "$OUTPUT_AAR"
-trimpath
-tags "$GO_MOBILE_TAGS"
)
if [[ $DEBUG -eq 0 ]]; then
args+=(-ldflags "-s -w -checklinkname=0")
else
args+=(-ldflags "-checklinkname=0")
fi
args+=(
github.com/sagernet/sing-box/experimental/libbox
github.com/tladesignz/IPtProxy.git
)
"$GOPATH/bin/gomobile" "${args[@]}"
popd >/dev/null
echo "Installed $OUTPUT_AAR"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$OUTPUT_AAR"
fi