从零构建 Blink Shell for iOS:BUILD.md 完整编译指南(依赖、资源与 Xcode 配置全解析)
从零构建 Blink Shell for iOS:BUILD.md 完整编译指南(依赖、资源与 Xcode 配置全解析)
Blink Shell 是一款基于 Mosh 与 SSH 的 iOS 专业级终端,其工程结构复杂,包含 libssh2、OpenSSL、Mosh、Protobuf 等多个底层 C 库依赖。本指南以仓库根目录的 BUILD.md 为主体,系统讲解在 macOS + Xcode 环境下从源码完整编译 Blink 的两条路径、前置条件、依赖安装、资源打包与工程配置,并结合仓库内的 get_frameworks.sh、xcfs/Package.swift、template_setup.xcconfig 等文件,深入剖析其底层依赖管理与构建机制。阅读完本文,你将能够独立完成 Blink Shell 的自编译、签名配置并在个人 iOS 设备上运行。
两条构建路线:完整自编译与预编译依赖包
BUILD.md 开篇即说明,Blink 提供两种编译安装方式,核心区别在于底层依赖的获取方式:
- 完整自编译(本文主体):自己编译 Blink 所需的全部库与资源。由于 Blink 依赖项众多,这是官方推荐的路线,但耗时最长,需要依次处理 libssh2、OpenSSL、Mosh、Protobuf 等组件。
- 预编译依赖快速路线:直接克隆仓库并获取一份"开箱即用"的依赖压缩包(tar.gz),省去逐个编译底层库的步骤。该方法在 README.md 的 "Build" 一节中有完整描述,适合希望快速跑通构建流程的开发者。
仓库根目录的两个辅助脚本正好对应上述机制:
- get_frameworks.sh:脚本实际执行
cd xcfs && swift package resolve,通过 Swift Package Manager 拉取所有预编译的 xcframework 二进制依赖; - get_resources.sh:下载并解压 vim 的运行时文件(runtime.zip)到
Resources/vim目录。
环境前置要求(Requirements)
在开始之前,需要确认本机满足以下条件,其中前两条涉及 Apple 的许可与合规要求,必须重视:
- Apple Developer 许可:要在个人设备上编译安装 Blink,必须遵守 Apple Developer 条款与条件,并取得开发者许可证(Developer License)才能签名部署到真机。
- Xcode 与命令行工具:需要 Xcode 及 Xcode 命令行开发者工具。BUILD.md 给出的版本门槛是 Xcode > 11.0;需要说明的是,从当前仓库 Blink.xcodeproj/project.pbxproj 的构建设置看,工程部署目标已提高至
IPHONEOS_DEPLOYMENT_TARGET = 15.0、MACOSX_DEPLOYMENT_TARGET = 12.0,且使用SWIFT_VERSION = 5.0,建议使用较新版本的 Xcode 以获得更好的兼容性。 - Autotools for OSX:用于编译底层 C 依赖库(libssh2、Mosh 等)的 autotools 工具链。
克隆源码与初始化子模块(Cloning)
克隆 Blink 仓库后,还必须同步其子模块:
git submodule init
git submodule update
从仓库根目录的 .gitmodules 文件可以看到当前声明的子模块:
Frameworks/ios_system:提供 shell 环境与基础命令(curl、scp、sftp、tar 等);Frameworks/MBProgressHUD:进度提示 UI 库。
BUILD.md 中提到的另一个依赖 UICKeyChainStore 同样通过 git 子模块机制引入。若使用 --recursive 参数克隆,子模块会随主仓库一并获取。
依赖体系剖析:需要预先编译的底层库
Blink 在编译前必须完成多个第三方依赖的构建,仓库提供了相关脚本,但底层库本体需要单独准备:
- Libssh2 for iOS:SSH 协议的 C 实现库,其构建过程中会一并产出 OpenSSL;
- Mosh for iOS:Mosh 协议的 iOS 移植,构建时会一并产出 Protobuf(Google Protocol Buffers,Mosh 通信编解码所需)。
BUILD.md 特别强调:Blink 目前仅支持 arm64(armv64)架构,因此为其他架构编译这些依赖并非必要,只需产出 arm64 slice 即可。
依赖安装:Libraries
Blink Shell 的 Xcode 工程会在 Framework 文件夹下查找库依赖:
- 将 [Libssh2 for iOS] 构建生成的
.framework文件(含 OpenSSL)拷贝到仓库的Framework文件夹; - 编译 [Mosh for iOS] 后,将
libmoshios.framework以及build-protobuf/protobuf-version/lib目录下的libprotobuf.a一并拷贝到Framework文件夹。
在实际构建中,框架的链接关系会在 Blink.xcodeproj/project.pbxproj 中以 PBXBuildFile / PBXFrameworksBuildPhase 的形式登记,拷贝的 framework 必须与工程引用的文件名一致,否则会出现链接失败。
从源码看当前依赖管理方式的演进
如果阅读当前仓库,会发现依赖管理已经比 BUILD.md 写作时期更进一步:仓库根目录的 xcfs/Package.swift 通过 SwiftPM 的 binaryTarget 直接声明了全部预编译依赖,每个条目均包含名称、checksum 校验和与下载 URL,例如:
libssh2(v1.9.0,libssh2-static.xcframework)openssl(v1.1.1k,openssl-dynamic.xcframework)mosh(v1.4.0+blink-17.3.0)Protobuf_C_(v3.21.1)ios_system(v3.0.3)及其衍生组件:awk、files、shell、ssh_cmd、tar、text、network_ios、curl_ios、bc、vim、xxd
这解释了快速路线中 get_frameworks.sh 的实际行为:它不需要手动拷贝 framework,而是 swift package resolve 按 xcfs/Package.resolved 锁定的版本拉取并校验这些二进制目标。而 BUILD.md 描述的"手动拷贝 framework"则是完整自编译路线下的传统做法,两者可以并存理解。
资源准备:终端、字体与主题(Resources)
Blink 的终端 UI 是一个运行在 WebView 中的 JavaScript 终端,在运行时动态加载,因此所有打包进 App 的资源必须位于 Resources 文件夹下,包括终端运行时、字体和主题。
从仓库实际的 Resources 目录可以看到完整资源集合:
- 终端运行时:
term.html、term.js、hterm_all.min.js、hterm_all.patches.js、term.css、base64js.min.js; - 字体:
Fonts(JetBrains Mono、Fira Code、Iosevka、Menlo、Source Code Pro、Roboto Mono 等)与AppFont; - 主题:
Themes(Default.js、Flat.js、Monokai Light.js、Solarized.js、WWDC 16.js); - 其他:
cacert.pem(证书文件)、commandDictionary.plist系列命令字典、locales.bundle、vim运行时目录。
webfontloader.js
BUILD.md 提到,字体样式上传依赖 webfontloader.js],但它并非 Blink 运行所必需。需要时将下载好的文件放入 Resources 文件夹即可;在 [Resources/term.html 的第 6 行可以看到 <script src="webfontloader.js"></script> 的引用。
终端渲染层:term.html 与替换其他终端
Blink 的终端由 JavaScript 代码在运行时驱动。文档指出:大多数开源终端只要提供 write 与 signal 两个函数,就可以接入 Blink。Resources/term.html 提供了一个完整示例,其脚本加载顺序为:
<script src="webfontloader.js"></script>
<link rel="stylesheet" type="text/css" href="term.css">
<script src="base64js.min.js"></script>
<script src="hterm_all.min.js"></script>
<script src="hterm_all.patches.js"></script>
<script src="term.js"></script>
如果替换为其他终端实现(例如 Terminal.js),需要同步编辑 term.html 中的脚本引用。当前仓库使用的渲染引擎是 Chromium 项目派生出的 hterm,并在此基础上打了补丁(hterm_all.patches.js)。
值得深入说明的是 JS 与原生层的桥接机制。阅读 Resources/term.js 可以看到,Blink 通过 WebKit 的 message handlers 与原生代码通信:
function _postMessage(op, data) {
window.webkit.messageHandlers.interOp.postMessage({op, data});
}
例如 copyStringToClipboard 将剪贴板内容以 {op: 'copy', content} 发给原生层;hterm.Terminal.IO.prototype.sendString 以 {op: 'sendString', string} 上报按键输入;终端尺寸变化时则发送 {op: 'sigwinch', cols, rows}。这些桥接点是理解"换一个终端 JS 也能跑"的关键:只要替代终端同样实现这些通信协议,Blink 原生层无需改动。
编译与工程配置(Compiling)
Blink 使用标准的 .xcodeproj 工程文件。任何缺失的文件会在 Xcode 中以红色标注,这可以当作安装是否完整的自检手段——打开工程后先检查文件树中有没有红名文件,若有则说明依赖或资源未就位。
工程配置步骤
按 BUILD.md 的指引,配置工程依次执行:
- 设置唯一 Bundle Identifier:
Targets > Blink > General > Identities; - 设置 Team(开发者团队):
Targets > Blink > General > Identities; - 若 Xcode 要求接受 profile 或配置开发者账号,遵循 Apple Developer 官方文档完成;
- (可选)HockeyApp 分发:将 Scheme 切换为
Blink Hockey,并在info.plist中加入HockeyID(填入你的 AppID 字符串)。需要注意,从当前仓库源码搜索看,HockeyApp 相关代码已不再保留(仅存在于历史变更记录中),此项可视为文档遗留的可选说明。
最后确认 Blink 为当前选中的 Scheme(对应 Blink.xcodeproj/xcshareddata/xcschemes/Blink.xcscheme 中 BlueprintName = "Blink" 的构建条目),然后像标准 Xcode 工程一样直接 Cmd-R 运行。
借助 xcconfig 完成身份配置(仓库扩展)
对于签名配置,当前仓库提供了比 BUILD.md 描述更便捷的方式:developer_setup.xcconfig。该文件由 template_setup.xcconfig 拷贝而来(cp template_setup.xcconfig developer_setup.xcconfig),在其中集中管理团队与标识信息:
TEAM_ID = A2H2CL32AG // 替换为你的 Apple Developer Team ID
BUNDLE_ID = sh.blink.blinkshell // 应用主 Bundle ID 前缀
GROUP_ID = sh.blink
CLOUD_ID = sh.blink.blinkshell // iCloud 容器(前缀 icloud. 会自动添加)
KEYCHAIN_ID1 = sh.blink // Keychain 共享组
BLINK_APP_FONT = JetBrains Mono
文件还定义了编译条件:
SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Debug] = BLINK_PUBLISHING_OPTION_DEVELOPER
SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Release] = BLINK_PUBLISHING_OPTION_TESTFLIGHT
以及 BLINK_MIGRATION_SCHEME = blinkv15(该值会被 Blink/Info.plist 中的 URL Scheme ${BLINK_MIGRATION_SCHEME} 引用)。在 Blink.xcodeproj/project.pbxproj 中,各 target 的构建配置均通过 baseConfigurationReference 指向 developer_setup.xcconfig,且签名与标识使用变量间接引用,例如:
DEVELOPMENT_TEAM = "$(TEAM_ID)"PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID).xxx"
这意味着只需修改 developer_setup.xcconfig 一个文件,即可完成 Team、Bundle Identifier、iCloud 容器与 Keychain 共享组的批量替换,比逐项修改 Targets 面板更高效。
可选:关闭系统能力
如果你的构建不涉及 iCloud、Push Notifications 与 Keychain Sharing,建议在动手前先在工程的 Capabilities 面板中关闭这三项能力,以免因缺少对应授权(entitlements)而导致签名或运行失败。这与 README 中"Build"一节的建议一致,也是真机自编译最常见的坑点之一。
快速路线:README 中的预编译依赖构建法
若不想逐个编译底层库,可走 README 的快速路线:使用带子模块的克隆方式获取仓库,然后依次执行仓库根目录的脚本获取预编译 framework 与资源:
git clone --recursive <仓库地址> && \
cd blink && ./get_frameworks.sh && ./get_resources.sh && \
rm -rf Blink.xcodeproj/project.xcworkspace/xcshareddata/
其中 ./get_frameworks.sh 通过 SwiftPM 解析并拉取 libssh2、OpenSSL、libmoshios、protobuf、ios_system 等全部二进制依赖(对应 xcfs/Package.swift 中的 binaryTarget 列表),./get_resources.sh 负责下载 vim 运行时等资源,最后的 rm 命令移除共享 scheme 数据以避免覆盖开发者配置。随后同样执行 cp template_setup.xcconfig developer_setup.xcconfig 并编辑其中标识,再在 Xcode 中打开工程、选择真机目标并运行。
常见问题与排查建议
基于 BUILD.md 的说明和仓库源码结构,整理几条常见问题的排查方向:
- Xcode 工程中出现红色文件:表示依赖或资源缺失。核对
Framework文件夹下的 framework/静态库是否齐全,以及Resources文件夹下的终端资源、字体、主题是否完整。 - 链接阶段报 framework 找不到:检查拷贝的
.framework名称与 Blink.xcodeproj/project.pbxproj 中引用的名称是否完全一致(大小写敏感)。 - 签名失败或无法安装到真机:确认
developer_setup.xcconfig中的TEAM_ID、BUNDLE_ID已替换为你自己的值,且已关闭不需要的系统能力。 - 终端白屏或字体异常:确认
Resources/term.html引用的 JS 文件(hterm_all.min.js、term.js 等)均存在于 Resources 文件夹,且webfontloader.js已按需放置;替换其他终端 JS 时务必同步编辑 term.html 的脚本引用。
总结
Blink Shell 的构建本质上是"底层 C 库编译 + 前端终端资源打包 + Xcode 签名配置"三件事的串联。BUILD.md 给出了完整自编译的标准路径:先满足 Xcode、Autotools 与开发者许可等前置条件,克隆并初始化子模块,然后逐一准备 libssh2/OpenSSL 与 Mosh/Protobuf 依赖,将 framework 拷贝到 Framework 目录、终端资源放入 Resources 目录,最后在 Xcode 中配置 Bundle Identifier 与 Team 后以 Blink Scheme 运行。而当前仓库已通过 get_frameworks.sh、get_resources.sh 与 xcfs/Package.swift 大幅简化了这一流程,配合 template_setup.xcconfig 生成的 developer_setup.xcconfig,开发者只需编辑一个配置文件即可完成全部身份信息替换,真正实现"打开即构建"。