Files
pangolin/app/kernel/build-android.sh
T
wangjia b23377acde build(scripts): 各端 libbox 构建脚本修正 + 自动部署产物
排查 Android 打包"libbox.aar not found / Unresolved reference nekohasekai"踩坑:
- scripts/build-libbox.sh:
  · 构建后自动部署产物到工程引用位置(android→app/kernel/dist/android/libbox.aar;
    apple→client/{macos,ios}/Frameworks/Libbox.xcframework),不再留 /tmp 让人手动拷
  · android 自动挑 NDK ≥28(未设 ANDROID_NDK_HOME 时):官方 build_libbox 拉 cronet-go,
    其 libcronet.a 在 NDK 27 lld 链接报 unknown relocation(315)失败
  · 头注释补全前置(NDK≥28 / JDK17 / gomobile 入 PATH)与产物路径
- app/kernel/build-android.sh:补 -javapkg io.nekohasekai —— 缺它产出默认包名 `libbox`,
  与 Kotlin 的 io.nekohasekai.libbox 对不上、APK Kotlin 编译失败
- build.gradle:注释指明两条构建途径 + 包名必须 io.nekohasekai.libbox

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 07:53:35 +08:00

173 lines
8.4 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# build-android.sh — gomobile bind sing-box libbox → dist/android/libbox.aar
#
# 与 scripts/build-libbox.sh android 的关系(两条都能产 app/kernel/dist/android/libbox.aar:
# - 本脚本:裸 gomobile bind 本地 .build/singbox 源,tag 精简(无 tailscale/cronet)→
# 体积小、NDK 27 也能链接。**必须带 -javapkg io.nekohasekai**(见下方 bind),否则
# 产出包名是默认 `libbox`,与 Kotlin 的 io.nekohasekai.libbox 对不上、APK 编不过。
# - scripts/build-libbox.sh:走上游官方 build_libbox,带 tailscale/cronet → 体积大、需 NDK ≥ 28。
# 两者产出包名一致(io.nekohasekai.libbox),择一即可;product 是 gitignore 的,清掉就得重建。
#
# 用法: ./build-android.sh [--force]
# --force 忽略已有产物,强制重新构建
#
# 依赖:
# - Go : VERSION 中 GO_VERSION 指定的版本($GOROOT 或 PATH 中的 go
# - gomobile: 脚本自动安装至 $GOPATH/bin,依赖 GOMOBILE_VERSION
# - Android NDK: 需设置 ANDROID_NDK_HOME 或 ANDROID_HOME(含 ndk-bundle/ndk/<ver>
#
# 产物: dist/android/libbox.aar
# 包含三个 ABI: arm64-v8a / armeabi-v7a / x86_64
#
# Build tags 说明(对齐官方 SFA — sing-box for Android:
# with_quic — QUIC/HTTP3 协议支持(Hysteria2 必须)
# with_utls — uTLS 指纹模拟(TLS 防检测)
# with_clash_api — Clash 兼容 API(面板控制必须)
# with_gvisor — gVisor TUN 网络栈(高性能 TUN 回退)
#
# 裁剪开关(按需取消注释):
# with_grpc — gRPC 入站(约 +4 MB so 体积)
# with_ech — TLS ECH 扩展(约 +0.5 MB
# with_wireguard — WireGuard 出站(约 +2 MB
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ── 加载版本锚点 ─────────────────────────────────────────────────────────────
# shellcheck source=VERSION
. "${SCRIPT_DIR}/VERSION"
# ── 参数解析 ─────────────────────────────────────────────────────────────────
FORCE=false
for arg in "$@"; do
case "${arg}" in
--force) FORCE=true ;;
*) printf 'Unknown argument: %s\n' "${arg}" >&2; exit 1 ;;
esac
done
# ── 路径配置 ─────────────────────────────────────────────────────────────────
DIST_DIR="${SCRIPT_DIR}/dist/android"
OUT_AAR="${DIST_DIR}/libbox.aar"
BUILD_SRC="${SCRIPT_DIR}/.build/singbox"
# ── Build tags ────────────────────────────────────────────────────────────────
# 功能 tags(最小集:REALITY/VLESS + Hysteria2(quic) + uTLS + Clash API + gVisor)。
# badlinkname,tfogo_checklinkname0 = Go 1.23+ 的 //go:linkname 限制放行
# (对齐 sing-box cmd/internal/build_libbox 的 sharedTags,缺则 Go 1.24 编译失败)。
BUILD_TAGS="with_quic,with_utls,with_clash_api,with_gvisor,badlinkname,tfogo_checklinkname0"
# 取消注释以增加功能(会增大 .so 体积):
# BUILD_TAGS="${BUILD_TAGS},with_grpc"
# BUILD_TAGS="${BUILD_TAGS},with_ech"
# BUILD_TAGS="${BUILD_TAGS},with_wireguard"
# ── 幂等检查 ─────────────────────────────────────────────────────────────────
if [[ "${FORCE}" == false && -f "${OUT_AAR}" ]]; then
printf '✓ %s 已存在,跳过构建(传 --force 强制重建)\n' "${OUT_AAR}"
exit 0
fi
# ── 前置检查 ─────────────────────────────────────────────────────────────────
printf '==> 检查前置依赖…\n'
if ! command -v go >/dev/null 2>&1; then
printf '✗ go 未找到,请安装 Go %s\n' "${GO_VERSION}" >&2
exit 1
fi
if ! command -v git >/dev/null 2>&1; then
printf '✗ git 未找到\n' >&2
exit 1
fi
ACTUAL_GO="$(go version | awk '{print $3}' | sed 's/go//')"
printf ' Go 版本: %s(期望 %s\n' "${ACTUAL_GO}" "${GO_VERSION}"
if [[ -n "${ANDROID_NDK_HOME:-}" ]]; then
printf ' NDK: %s\n' "${ANDROID_NDK_HOME}"
elif [[ -n "${ANDROID_HOME:-}" ]]; then
printf ' Android SDK: %sgomobile 将自动定位 NDK\n' "${ANDROID_HOME}"
else
printf '⚠ 未设置 ANDROID_NDK_HOME / ANDROID_HOMEgomobile init 可能失败\n' >&2
fi
# ── JDKgomobile bind 末段要 javac/jar 编译 Java 绑定)─────────────────────────
# macOS 的 /usr/bin/javac 仅为桩(无系统 JDK 时报 "Unable to locate a Java Runtime")。
# 若未显式设 JAVA_HOME,优先用 Android Studio 自带 JBROpenJDK 21)。
if [[ -z "${JAVA_HOME:-}" ]]; then
AS_JBR="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
if [[ -x "${AS_JBR}/bin/javac" ]]; then
export JAVA_HOME="${AS_JBR}"
export PATH="${JAVA_HOME}/bin:${PATH}"
printf ' JDK: %sAndroid Studio JBR\n' "${JAVA_HOME}"
else
printf '⚠ 未设 JAVA_HOME 且未找到 Android Studio JBRgomobile bind 末段 javac 可能失败\n' >&2
fi
else
printf ' JDK: %sJAVA_HOME\n' "${JAVA_HOME}"
fi
# ── 安装 gomobileSagerNet fork,见 VERSION 注释)────────────────────────────
printf '==> 安装 %s/cmd/gomobile@%s…\n' "${GOMOBILE_MODULE}" "${GOMOBILE_VERSION}"
go install "${GOMOBILE_MODULE}/cmd/gomobile@${GOMOBILE_VERSION}"
go install "${GOMOBILE_MODULE}/cmd/gobind@${GOMOBILE_VERSION}"
printf '==> gomobile init…\n'
gomobile init
# ── 准备 sing-box 源码 ────────────────────────────────────────────────────────
printf '==> 准备 sing-box %s 源码…\n' "${SINGBOX_VERSION}"
EXISTING_TAG=""
if [[ -d "${BUILD_SRC}/.git" ]]; then
EXISTING_TAG="$(git -C "${BUILD_SRC}" describe --tags --exact-match 2>/dev/null)" || EXISTING_TAG="unknown"
fi
if [[ "${EXISTING_TAG}" == "${SINGBOX_VERSION}" ]]; then
printf ' 源码已是 %s,跳过克隆\n' "${SINGBOX_VERSION}"
else
if [[ -d "${BUILD_SRC}" ]]; then
printf ' 已有源码版本 %s ≠ %s,清理后重新克隆…\n' "${EXISTING_TAG}" "${SINGBOX_VERSION}"
rm -rf "${BUILD_SRC}"
fi
mkdir -p "${BUILD_SRC%/*}"
git clone --depth 1 --branch "${SINGBOX_VERSION}" \
https://github.com/SagerNet/sing-box.git "${BUILD_SRC}"
fi
# ── 构建 AAR ──────────────────────────────────────────────────────────────────
printf '==> 构建 libbox.aar\n'
printf ' target : android/arm64,android/arm,android/amd64\n'
printf ' api : 21\n'
printf ' tags : %s\n' "${BUILD_TAGS}"
mkdir -p "${DIST_DIR}"
# ldflags 对齐 sing-box cmd/internal/build_libbox 的 sharedFlags
# -checklinkname=0 放行 badlinkname 的 //go:linknameGo 1.23+ 默认 checklinkname=1
# 会拒 os.checkPidfdOnce 等内部符号引用,缺则链接失败)
# defaultGODEBUG=multipathtcp=0 关 MPTCP(与官方一致)
# -X ...constant.Version 版本戳
# -s -w -buildid= 裁剪符号表,产物更小
LDFLAGS="-X github.com/sagernet/sing-box/constant.Version=${SINGBOX_VERSION} -X internal/godebug.defaultGODEBUG=multipathtcp=0 -s -w -buildid= -checklinkname=0"
(
cd "${BUILD_SRC}"
go mod download
gomobile bind \
-v \
-trimpath \
-buildvcs=false \
-ldflags "${LDFLAGS}" \
-target android/arm64,android/arm,android/amd64 \
-androidapi 21 \
-tags "${BUILD_TAGS}" \
-javapkg io.nekohasekai \
-o "${OUT_AAR}" \
./experimental/libbox
)
printf '\n✓ 构建完成: %s\n' "${OUT_AAR}"
printf ' ABI 验证(.so 文件列表):\n'
unzip -l "${OUT_AAR}" | grep -E '\.so$' | awk '{print " " $NF}' || true