| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- #!/usr/bin/env bash
- # ============================================================
- # build_secure.sh — 用 Nuitka 把 lambdagent / agentpaas 编成原生 .so,
- # 再重打包成「编译后 wheel」放进 dist-secure/。
- #
- # 与普通 wheel 的区别:解开 .whl 只有机器码 .so,没有 .py 源码。
- # 产物是「平台 + Python 小版本」绑定的(如 cp311 + macOS arm64),
- # 换架构/换 Python 小版本需在对应环境重新构建。
- #
- # 用法: scripts/build_secure.sh [PYTHON]
- # PYTHON 默认 python3;必须是「原生架构 + 3.10+ + 带头文件」的解释器,
- # 且该 Python 的版本即决定产出 wheel 的 cpXXX 标签。
- #
- # 前置: C 编译器(macOS: xcode-select --install)
- # ============================================================
- set -e
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
- PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
- cd "$PROJECT_DIR"
- RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
- info() { echo -e "${BLUE}[INFO]${NC} $1"; }
- ok() { echo -e "${GREEN}[OK]${NC} $1"; }
- warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
- fail() { echo -e "${RED}[FAIL]${NC} $1"; exit 1; }
- PYTHON="${1:-python3}"
- command -v "$PYTHON" >/dev/null 2>&1 || fail "未找到 Python: $PYTHON"
- # ── 架构/版本/头文件检查 ──────────────────────────────────
- read -r PYVER PYARCH HAVE_H <<EOF
- $("$PYTHON" - <<'PY'
- import platform, sysconfig, os
- inc = sysconfig.get_path('include')
- print(f"{platform.python_version()} {platform.machine()} {int(os.path.exists(os.path.join(inc,'Python.h')))}")
- PY
- )
- EOF
- info "构建解释器: $PYTHON (Python $PYVER, $PYARCH)"
- HOSTARCH="$(uname -m)"
- if [ "$PYARCH" != "$HOSTARCH" ]; then
- warn "Python 架构($PYARCH) ≠ 主机架构($HOSTARCH)。"
- warn "多半是 Rosetta 下的 x86_64 Python,会编出错架构的 .so。"
- warn "请改用原生 $HOSTARCH 的 Python(如 Homebrew 的 python3.11)。"
- fail "架构不匹配,已中止。"
- fi
- [ "$HAVE_H" = "1" ] || fail "缺 Python.h 头文件,无法编译。请装带头文件的 Python(如 Homebrew)。"
- # ── 构建专用 venv(装 nuitka,不污染用户环境)──────────────
- BUILD_VENV="$PROJECT_DIR/.nuitka-venv"
- if [ ! -x "$BUILD_VENV/bin/python" ]; then
- info "创建构建环境 .nuitka-venv ..."
- "$PYTHON" -m venv "$BUILD_VENV"
- fi
- BPY="$BUILD_VENV/bin/python"
- "$BPY" -m pip install -q --upgrade pip nuitka packaging wheel
- # ── 1) 普通 wheel(保留正确的依赖元数据,repack 会复用)────
- info "构建基准 wheel(取依赖元数据)..."
- mkdir -p dist
- rm -f dist/lambdagent-*.whl dist/agentpaas-*.whl
- "$BPY" -m pip wheel --no-deps ./lambdagent -w dist/ --quiet
- "$BPY" -m pip wheel --no-deps ./agentpaas -w dist/ --quiet
- # ── 2) Nuitka 编译两个包为整包 .so ────────────────────────
- NOUT="$PROJECT_DIR/build_nuitka"
- rm -rf "$NOUT"; mkdir -p "$NOUT"
- COMMON_FLAGS=(--include-package-data=. --python-flag=no_docstrings --remove-output --assume-yes-for-downloads)
- info "Nuitka 编译 lambdagent(可能数分钟)..."
- "$BPY" -m nuitka --module lambdagent/src/lambdagent \
- --include-package=lambdagent --include-package-data=lambdagent \
- --python-flag=no_docstrings --remove-output --assume-yes-for-downloads \
- --output-dir="$NOUT"
- info "Nuitka 编译 agentpaas(可能数分钟)..."
- "$BPY" -m nuitka --module agentpaas/src/agentpaas \
- --include-package=agentpaas --include-package-data=agentpaas \
- --python-flag=no_docstrings --remove-output --assume-yes-for-downloads \
- --output-dir="$NOUT"
- LG_SO=$(ls "$NOUT"/lambdagent.*.so 2>/dev/null | head -1)
- AP_SO=$(ls "$NOUT"/agentpaas.*.so 2>/dev/null | head -1)
- [ -f "$LG_SO" ] && [ -f "$AP_SO" ] || fail "未找到编译产物 .so"
- # ── 3) 平台 tag ───────────────────────────────────────────
- TAG=$("$BPY" -c "from packaging.tags import sys_tags; t=next(iter(sys_tags())); print(f'{t.interpreter}-{t.abi}-{t.platform}')")
- info "目标 wheel 标签: $TAG"
- # ── 4) 重打包成 .so wheel ─────────────────────────────────
- mkdir -p dist-secure
- rm -f dist-secure/lambdagent-*.whl dist-secure/agentpaas-*.whl
- "$BPY" scripts/repack_wheel_so.py --wheel dist/lambdagent-*.whl --so "$LG_SO" --package lambdagent --tag "$TAG" --out dist-secure/
- "$BPY" scripts/repack_wheel_so.py --wheel dist/agentpaas-*.whl --so "$AP_SO" --package agentpaas --tag "$TAG" --out dist-secure/
- echo ""
- ok "编译版 wheel 已生成 → dist-secure/"
- for w in dist-secure/*.whl; do
- npy=$(unzip -l "$w" | grep -cE '\.py$'); nso=$(unzip -l "$w" | grep -cE '\.so$')
- echo " $(basename "$w") [.py=$npy .so=$nso]"
- done
- echo ""
- echo " 下一步: ./setup.sh --package # 会优先用 dist-secure/ 里的编译 wheel 组装发行包"
- echo ""
- warn "提醒: 编译版 wheel 绑定 $TAG —— 用户的 Python 小版本与架构需匹配。"
- warn " 逻辑已编成机器码不可反编译,但日志/prompt 等字符串常量仍可被 strings 提取(开源版 Nuitka 限制)。"
|