场景

MSP(Model Shell Protocol)仓库是 SwiftPM 包,主要面向 iOS,但核心逻辑纯 Swift、平台无关。目标:在 WSL Ubuntu 里把它编译成 Linux 原生可执行二进制(msp-shell,一个沙箱 shell REPL),用于后续接 LLM 的 agent 运行时。

为什么「iOS SDK」能直接在 Linux 编译

  • 核心模块(虚拟文件系统、shell 解析器、命令运行时)0% 依赖 Apple,纯 Swift。
  • 名字带 Apple 的 MSPApple 底层只用 Foundation.FileManager,Linux 可用。
  • 少量 Apple 独占 API(CryptoKit 哈希、部分 Foundation/URLSession 细节)用 #if os() / #if canImport(CryptoKit) 守卫 + 纯 Swift 替代实现,同一份源码 iOS 和 Linux 都能编。
  • PTY 的 C 包装在 Linux 编译为空实现(返回 ENOTSUP),不影响。
  • 编译入口:swift build --product <name> --disable-sandbox(--disable-sandbox 在 /mnt 挂载的 WSL 上必需)。

关键坑(都踩过、已解决)

1. swift-cgit2 1.1.1 上游只有 Apple xcframework,Linux 编不了

上游 Package.swift 无条件声明 OpenSSL/libgit2/libssh2 三个 binaryTarget(Apple 二进制),Linux 无法使用。
解法:用仓库内自带的最小 Linux shim 包替代——Cgit2 target 的 umbrella header 只写 #include <git2.h>,linkerSettings 链系统 git2/ssh2/ssl/crypto/iconv/z(需要 libgit2-dev libssh2-1-dev libssl-dev zlib1g-dev)。构建脚本用 sed 把主包 Package.swift 的依赖 URL 临时替换成 shim 路径,结束 trap 还原。

2. SPM 路径依赖的「包身份」= 目录名,不是 manifest 里的 name

.package(path: "...") 后,包身份由目录名决定。若目录叫 vendor-cgit2-linux,target 里 .product(name: "Cgit2", package: "swift-cgit2") 会报 unknown package 'swift-cgit2'。
解法:目录名就叫 swift-cgit2,与 .product(package:) 引用一致。

3. 路径依赖的包必须显式声明 products

只写 targets: 不够,.product(name: "Cgit2", package: ...) 找不到产品。必须加 products: [.library(name: "Cgit2", targets: ["Cgit2"])]。

4. Swift 类型检查器对长位运算表达式「超时」

w[i] = (UInt64(b[0]) << 56) | (UInt64(b[1]) << 48) | ... 8 项 以及 [a,b,c,d].flatMap { word in [...] } 会报 unable to type-check this expression in reasonable time(debug + 慢文件系统更易触发)。
解法:拆成循环逐字节拼装 word = (word << 8) | UInt64(block[j]);数组字节输出改 for 循环逐 append。

5. fputs/fflush 要 C 的 stderr(UnsafeMutablePointer<FILE>),不是 FileHandle.standardError

CLI 里打印提示符/横幅用 stderr(import Glibc 后直接可用),命令输出用 FileHandle.standardOutput/Error.write。

6. REPL 提示符要按 isatty(STDIN_FILENO) 门控

管道模式(echo 'pwd' | msp-shell)下 stdin 非 TTY,不该打印 banner/msp$ 提示符,否则污染 stdout。用 Glibc.isatty(0) 判断,交互才显示。

7. 纯 Swift 哈希接口要跟 CryptoKit 调用点对齐

CryptoKit 的 SHA256.update(data:) 是带标签的;已有纯 Swift 的 MSPSHA224.update(_:) 是不带标签的。补丁里两种调用点混用,新实现必须按各调用点签名提供 update(data:) 或 update(_:)。

构建步骤(WSL Ubuntu)

sudo apt install -y libgit2-dev libssh2-1-dev libssl-dev zlib1g-dev   # 系统库
bash Tools/msp-shell/build-linux.sh                                    # sed 换依赖 + swift build -c release + 冒烟 + 还原

产物:.build/x86_64-pc-linux-gnu/release/<product>。

验证方法

  • msp-shell 'echo hi' 单命令;echo 'pwd' | msp-shell 管道;交互 msp$ + exit()。
  • 沙箱隔离:cat /etc/hostname 应失败(命令跑在「以 workspace 为根的虚拟文件系统」)。
  • digest 正确性:与系统 md5sum/sha256sum/sha384sum 交叉比对同一文件。

相关

  • 仓库内实现:Tools/msp-shell/(构建脚本 + cgit2 shim + README)、Implementations/Swift/Sources/MSPFakeShellCLI/。
  • 概念关联:MSP (Model Shell Protocol) 沙箱 Shell

本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Programming