Files
pangolin/app/kernel/build-ios.sh
T
wangjia 7a1715bd09 merge: iOS 端 PoC M3 + entitlement 申请 [tsk_F5r4Kt9m_wQU]
解决合并冲突(原任务 tsk_nsobbj_rJdy0 分支 maestro/tsk_nsobbj_rJdy0):
两分支修改文件集合无重叠,手动 apply iOS PoC commit (e129f09)。

变更内容:
- client/ios/PacketTunnel/PacketTunnelProvider.swift:完整 M3 实现
  (startTunnel/stopTunnel/handleAppMessage/LibboxPlatformInterface)
- client/ios/PacketTunnel/MemoryMonitor.swift:新增 NE 进程内存打点
- client/ios/PacketTunnel/PacketTunnel.entitlements:NE + App Group 权限
- client/ios/Runner/Runner.entitlements:主 App NE + App Group 权限
- client/ios/Runner/VpnManager.swift:红线词修复 + App Group 缓存 + NEVPNStatus 订阅
- client/ios/Runner/Info.plist:NSVPNUsageDescription 红线词修复
- client/ios/PacketTunnel/Info.plist:CFBundleDisplayName 红线词修复
- client/ios/Runner.xcodeproj/project.pbxproj:
  CODE_SIGN_ENTITLEMENTS 添加到全部 build config;MemoryMonitor 加入 Sources
- app/kernel/build-ios.sh:M3 内存裁剪决策记录
- doc/ne-entitlement-申请指引.md:新增 NE entitlement 申请完整指引

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 20:14:32 +08:00

171 lines
7.9 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-ios.sh — gomobile bind sing-box libbox → dist/ios/Libbox.xcframework
#
# 用法: ./build-ios.sh [--force]
# --force 忽略已有产物,强制重新构建
#
# 依赖:
# - macOS + Xcode(含 iOS SDK & Simulator SDK
# - Go : VERSION 中 GO_VERSION 指定的版本
# - gomobile: 脚本自动安装至 $GOPATH/bin
#
# 产物: dist/ios/Libbox.xcframework
# 含两个 slice: iphoneos (device) + iphonesimulator (x86_64 + arm64)
#
# Build tags 说明(对齐官方 SFI — sing-box for iOS:
# with_quic — QUIC/HTTP3 协议支持(Hysteria2 必须)
# with_utls — uTLS 指纹模拟(TLS 防检测)
# with_clash_api — Clash 兼容 API(面板控制必须)
# with_gvisor — gVisor TUN 网络栈
#
# ⚠ iOS Network Extension 内存上限敏感
# 11F 实测若遇 "NEProvider memory limit exceeded" 崩溃,
# 回此脚本逐步裁剪 BUILD_TAGS,推荐优先裁减顺序:
# 1. 去掉 with_grpc → 减约 4 MB(需 gRPC 入站时保留)
# 2. 去掉 with_gvisor → 减约 2 MB(失去 gVisor TUN stack 回退,改用系统栈)
# 3. 去掉 with_quic → 仅在完全不使用 Hysteria2/QUIC 时考虑
# 裁减后执行 ./build-ios.sh --force 重出包,重新接入 Flutter 工程(见 11F)。
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/ios"
OUT_XCFW="${DIST_DIR}/Libbox.xcframework"
BUILD_SRC="${SCRIPT_DIR}/.build/singbox"
# ── Build tags ────────────────────────────────────────────────────────────────
BUILD_TAGS="with_quic,with_utls,with_clash_api,with_gvisor"
# 取消注释以增加功能(会增大 XCFramework 体积 / 内存占用):
# BUILD_TAGS="${BUILD_TAGS},with_grpc"
# BUILD_TAGS="${BUILD_TAGS},with_ech"
# BUILD_TAGS="${BUILD_TAGS},with_wireguard"
# ── M3 内存裁剪决策记录(tsk_nsobbj_rJdy0)────────────────────────────────────
# NE 进程内存上限实测(MemoryMonitor 打点):
# 初始 build tagswith_quic,with_utls,with_clash_api,with_gvisor):
# 预估 RSS~20-25 MB(含 Go runtime ~8MB + gVisor ~4MB + QUIC ~5MB + uTLS ~2MB + 基础~6MB
# 旧设备(≤ A9)15MB 上限下会触顶;新设备(≥ A12)50MB 上限下可运行
#
# 若真机测试出现 "NEProvider memory limit exceeded" / jetsam 杀进程,按以下顺序裁减:
# Step 1: 去掉 with_gvisor → 改用系统 TUN 网络栈;减约 2-4 MB
# 取消注释下一行:
# BUILD_TAGS="with_quic,with_utls,with_clash_api"
# Step 2: 去掉 with_grpc → 若不需要 gRPC 入站;减约 4 MB
# 取消注释下一行:
# BUILD_TAGS="with_quic,with_utls,with_clash_api" # 已无 gvisor,此行等效
# Step 3: 去掉 with_quic → 仅在放弃 Hysteria2/QUIC 协议时考虑;减约 3 MB
# 取消注释下一行:
# BUILD_TAGS="with_utls,with_clash_api"
#
# 裁减后执行: ./build-ios.sh --force 重出 XCFramework,然后在 Xcode 中
# 重新拖入 dist/ios/Libbox.xcframework(或更新 SPM/Pods 引用)。
# 在此注释中记录最终选定的 build tags 及峰值 RSS(由 MemoryMonitor 汇报)。
#
# 当前状态:等待真机 10min 压测结果(MemoryMonitor 每 10s 打点)。
# ── 幂等检查 ─────────────────────────────────────────────────────────────────
if [[ "${FORCE}" == false && -d "${OUT_XCFW}" ]]; then
printf '✓ %s 已存在,跳过构建(传 --force 强制重建)\n' "${OUT_XCFW}"
exit 0
fi
# ── 前置检查 ─────────────────────────────────────────────────────────────────
printf '==> 检查前置依赖…\n'
if [[ "$(uname)" != "Darwin" ]]; then
printf '✗ iOS 构建仅支持 macOS\n' >&2
exit 1
fi
if ! command -v xcodebuild >/dev/null 2>&1; then
printf '✗ xcodebuild 未找到,请安装 Xcode\n' >&2
exit 1
fi
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//')"
XCODE_VER="$(xcodebuild -version 2>/dev/null | head -1)"
printf ' Go 版本: %s(期望 %s\n' "${ACTUAL_GO}" "${GO_VERSION}"
printf ' Xcode: %s\n' "${XCODE_VER}"
# ── 安装 gomobile ─────────────────────────────────────────────────────────────
printf '==> 安装 gomobile@%s…\n' "${GOMOBILE_VERSION}"
go install "golang.org/x/mobile/cmd/gomobile@${GOMOBILE_VERSION}"
go install "golang.org/x/mobile/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
# ── 构建 XCFramework ──────────────────────────────────────────────────────────
printf '==> 构建 Libbox.xcframework\n'
printf ' target : ios,iossimulator\n'
printf ' tags : %s\n' "${BUILD_TAGS}"
mkdir -p "${DIST_DIR}"
# 若已有旧产物先清理(gomobile 输出到固定路径时不会自动覆盖目录)
if [[ -d "${OUT_XCFW}" ]]; then
rm -rf "${OUT_XCFW}"
fi
(
cd "${BUILD_SRC}"
go mod download
gomobile bind \
-target ios,iossimulator \
-tags "${BUILD_TAGS}" \
-o "${OUT_XCFW}" \
./experimental/libbox
)
printf '\n✓ 构建完成: %s\n' "${OUT_XCFW}"
printf ' Slice 验证:\n'
if command -v xcodebuild >/dev/null 2>&1; then
xcodebuild -create-xcframework -output /dev/null 2>&1 || true
fi
# 列出 XCFramework 内的 framework slice 目录
for slice_dir in "${OUT_XCFW}"/*/; do
printf ' %s\n' "${slice_dir}"
done