#!/usr/bin/env bash # ============================================================ # build_binary.sh — 用 Nuitka 把整个 agentpaas 打成单个原生可执行文件 # (standalone + onefile)。用户拿到后【无需安装 Python】,直接 ./agentpaas serve。 # # 与 --build-secure(.so wheel)的区别: # --build-secure : 仍是 wheel,用户要有匹配的 Python;保护的是源码 # --build-binary : 单文件二进制,内嵌 Python + 依赖 + 前端 + 内置包;保护源码 + 免装环境 # # 产物:dist-binary/agentpaas(绑定「平台 + 架构」,如 macOS arm64)。 # # 用法: scripts/build_binary.sh [PYTHON] # PYTHON 默认 python3,须为原生架构、3.10+、带头文件(macOS 推荐 Homebrew python3.11)。 # 环境: # PROVIDERS 要打进二进制的 LLM SDK,空格分隔,默认 "anthropic openai" # (dashscope 走 openai 兼容/HTTP、ollama 走 HTTP,均无需 SDK) # ONEFILE 1=单文件(默认,体验最好但打包耗资源);0=standalone 目录 # (产出一个文件夹,更省内存、更稳,适合 CI/资源受限机器) # # 前置: 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}" PROVIDERS="${PROVIDERS:-anthropic openai}" ONEFILE="${ONEFILE:-1}" command -v "$PYTHON" >/dev/null 2>&1 || fail "未找到 Python: $PYTHON" # ── 架构/版本/头文件检查(与 build_secure 一致)────────────── read -r PYARCH HAVE_H </dev/null # onefile 是 standalone:依赖必须真实安装进构建环境,Nuitka 才能跟进打包 "$BPY" -m pip install -q ./lambdagent ./agentpaas $PROVIDERS # 仅对真正装上的 provider 生成 --include-module PROVIDER_FLAGS=() for p in $PROVIDERS; do if "$BPY" -c "import $p" >/dev/null 2>&1; then PROVIDER_FLAGS+=("--include-module=$p") ok "provider 已纳入: $p" else warn "provider 未装上,跳过: $p" fi done # ── Nuitka standalone + onefile ─────────────────────────── OUT="$PROJECT_DIR/build_binary_out" rm -rf "$OUT"; mkdir -p "$OUT" ENTRY="agentpaas/src/agentpaas/__main__.py" MODE_FLAGS=(--standalone) [ "$ONEFILE" = "1" ] && MODE_FLAGS+=(--onefile) info "Nuitka 打包二进制(mode: $([ "$ONEFILE" = 1 ] && echo onefile单文件 || echo standalone目录),首次可能 10+ 分钟)..." "$BPY" -m nuitka \ "${MODE_FLAGS[@]}" \ --output-filename=agentpaas \ --output-dir="$OUT" \ --include-package=uvicorn \ --include-module=uvloop --include-module=httptools \ --include-module=websockets --include-module=h11 \ --include-package=agentpaas --include-package=lambdagent \ "${PROVIDER_FLAGS[@]}" \ --include-data-dir=webui-dist=webui-dist \ --include-data-dir=agentexample/agentpacks=agentexample/agentpacks \ --include-data-dir=agentexample/skills=agentexample/skills \ --python-flag=no_docstrings \ --assume-yes-for-downloads \ "$ENTRY" # 平台标签 + 可执行后缀(Windows 走 git-bash,uname 形如 MINGW64_NT) case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*) OSLABEL=windows; EXE=".exe" ;; Darwin) OSLABEL=darwin; EXE="" ;; *) OSLABEL=$(uname -s | tr 'A-Z' 'a-z'); EXE="" ;; esac PLATSUFFIX="$OSLABEL-$HOSTARCH" mkdir -p dist-binary if [ "$ONEFILE" = "1" ]; then BIN="$OUT/agentpaas$EXE" [ -x "$BIN" ] || fail "未生成二进制" cp "$BIN" "dist-binary/agentpaas-$PLATSUFFIX$EXE" cp "$BIN" "dist-binary/agentpaas$EXE" echo "" ok "单文件二进制已生成 → dist-binary/agentpaas$EXE ($(du -h "$BIN" | cut -f1))" echo " 用户用法(无需 Python): ./agentpaas$EXE serve --port 8000" else # standalone:产出一个目录,整目录即发行件 DISTDIR="$OUT/__main__.dist" [ -x "$DISTDIR/agentpaas$EXE" ] || fail "未生成 standalone 目录二进制" rm -rf "dist-binary/agentpaas-$PLATSUFFIX.app" cp -R "$DISTDIR" "dist-binary/agentpaas-$PLATSUFFIX.app" echo "" ok "standalone 目录已生成 → dist-binary/agentpaas-$PLATSUFFIX.app/ ($(du -sh "$DISTDIR" | cut -f1))" echo " 用户用法(无需 Python): ./agentpaas-$PLATSUFFIX.app/agentpaas$EXE serve --port 8000" fi echo "" warn "提醒: 二进制绑定 $PLATSUFFIX —— 换平台/架构需在对应机器重新构建。" warn " 未打包: chromadb 向量库、PDF 外部工具(pandoc/xelatex)——用到这些功能仍需系统侧依赖。"