构建
MacroSIP 仅用 SwiftPM 和 Xcode 命令行工具即可构建。没有 Xcode 项目,也不涉及 Homebrew:OpenSSL 和 Opus 与 PJSIP 一起从固定版本的源代码 tarball 编译。详情见 scripts/README-build.md、scripts/build-pjsip.sh、scripts/make-app.sh 和 ADR 0012。
要求
- Apple 芯片 Mac 上的 macOS 14 或更新版本。构建主机必须是 arm64:通用构建的 x86_64 部分在同一台机器上交叉编译,
build-pjsip.sh在 Intel 主机上会停止。 - Xcode 命令行工具(
xcode-select --install)。不需要完整的 Xcode;如果已安装,它可以直接打开Package.swift。 - 首次运行时需要访问 github.com 和 downloads.xiph.org 的网络(之后 tarball 会被缓存)。
- 可选:Rosetta 2(
softwareupdate --install-rosetta --agree-to-license),用于在 Apple 芯片上运行 x86_64 切片。
第 1 步:scripts/build-pjsip.sh
scripts/build-pjsip.sh # 首次运行在 8 核 M 系列 Mac 上约 5 分钟,之后只需数秒
scripts/build-pjsip.sh --clean # 删除除下载外的所有输出并重新构建
JOBS=4 scripts/build-pjsip.sh # 限制并行度
KEEP_BUILD=1 scripts/build-pjsip.sh # 保留 third_party/build 下的源代码树该脚本以 -mmacosx-version-min=14.0 为 arm64(原生)和 x86_64(--host=x86_64-apple-darwin)构建:
| 组件 | 版本 | 选项 |
|---|---|---|
| OpenSSL | 3.5.9 (LTS) | no-shared no-module no-tests,仅 build_libs 和 install_dev;darwin64-arm64-cc / darwin64-x86_64-cc |
| Opus | 1.6.1 | --disable-shared --enable-static --disable-doc --disable-extra-programs |
| pjproject | 2.17 | 在 configure 之前安装 third_party/pjsip/config_site.h;--disable-shared --disable-pjsua2 --disable-video --disable-darwin-ssl --disable-speex-aec,不含 L16、AMR、SILK、bcg729 (G.729) 或 Lyra;--with-ssl 和 --with-opus 指向 third_party/deps/<arch> |
每个 tarball 都根据脚本顶部固定的 SHA-256 进行验证。--disable-darwin-ssl 很重要:否则 configure 会优先选择已弃用的 SecureTransport 而非 OpenSSL。脚本检查 configure 日志(找到 OpenSSL 和 Opus,G.722.1、GSM、iLBC 和 Speex 编解码器未被禁用),检查每个归档和 pjsua 的架构与 minos,并在 pjsua 链接了 /opt/homebrew 中的任何内容时失败。
config_site.h 开启 IPv6、SRTP(SDES,无 DTLS)、WebRTC 回声消除器、TLS、授权头缓存、PJSUA_MAX_CALLS 8,以及编解码器 Opus、G.711、G.722、G.722.1、GSM、iLBC 和 Speex(G.722.1、GSM、iLBC 和 Speex 在引擎中保持禁用,直到用户启用;添加编解码器见 参与贡献)。视频关闭。
各架构的结果用 lipo 合并。在架构间不同的头文件(pj/compat/m_auto.h、os_auto.h,configure 在其中写入机器名)合并为一个带 #if defined(__aarch64__) / #elif defined(__x86_64__) 分支的文件。库名去掉主机三元组后缀(libpj-aarch64-apple-darwin27.2.0.a 变为 libpj.a),因此两个切片的名称相同,链接标志在 macOS 更新后依然有效。
third_party/ 下的输出布局(除 pjsip/config_site.h 外全部被 git 忽略):
| 路径 | 内容 |
|---|---|
downloads/ |
已验证的 tarball,--clean 时保留 |
deps/<arch>/{include,lib} |
单一架构的 OpenSSL 和 Opus |
deps/universal/lib |
libssl.a、libcrypto.a、libopus.a,由 Package.swift 链接 |
pjsip/arch/<arch>/ |
单一架构的 PJSIP 头文件、库和 pjsua |
pjsip/include/、pjsip/lib/ |
合并后的头文件和通用归档 |
pjsip/bin/pjsua |
通用 pjsua CLI,自测中的第二部电话 |
pjsip/LINKFLAGS、pjsip/CFLAGS |
使用者需要的标志;Package.swift 中的 pjsipLibraries 与 LINKFLAGS 保持一致 |
build/、build-logs/<arch>/ |
源代码树(除非 KEEP_BUILD=1 否则删除)和完整的 configure/make 日志 |
**缓存。**每个组件写入一个包含其配方的印记(版本、校验和、标志、config_site.h 的 SHA-256、构建所依据的印记)。一次运行跳过印记匹配的所有组件,并从新解压的源代码树重新构建其余组件。编辑 config_site.h 后重新运行脚本:PJSIP 为两个架构重新构建(2 到 5 分钟),而 OpenSSL 和 Opus 保持不变。CI 以脚本和 config_site.h 为键缓存 third_party/pjsip 和 third_party/deps,因此缓存命中时该步骤为空操作。要升级某个组件,请在脚本中更改其版本和校验和并重新运行。
第 2 步:swift build
swift build # debug,原生架构
swift build -c release # release,原生架构
swift build --arch arm64 --arch x86_64 -c release # 通用(make-app.sh release 运行的命令)
.build/release/pjsip-smoke # 打印 PJSIP 版本、编解码器和音频设备;以 0 退出Package.swift 用绝对的 -L/-l 标志(Context.packageDirectory)链接归档,因 libwebrtc 包含 C++ 而添加 -lc++,并通过 swiftSettings: pjsipSwiftSettings(-Xcc -I)为每个导入 CPJSUA 的 Swift 目标提供头文件路径。应用目标为 Sparkle 获得 rpath @executable_path/../Frameworks。通用构建将产物放在 .build/out/Products/<Config> 而非 .build/<config>;用 swift build ... --show-bin-path 查询。SwiftPM 不跟踪预构建的归档,因此在 PJSIP 重新构建后,如果更改似乎未生效,请运行 rm -rf .build。
第 3 步:scripts/make-app.sh
scripts/make-app.sh # release:通用 dist/MacroSIP.app(默认)
scripts/make-app.sh debug # debug:仅原生架构,更快;唯一接受演示选项的构建
ARCHS=arm64 scripts/make-app.sh # 单架构 release该脚本运行 swift build --product MacroSIPApp(release 为通用,debug 为原生),用 lipo 检查切片,并组装 dist/MacroSIP.app:二进制文件、放入 Contents/Frameworks 的 Sparkle.framework、放入 Contents/Resources 的 en.lproj 和 ru.lproj 字符串及 InfoPlist.strings、packaging/AppIcon.icns,以及生成的 Info.plist,其中包含 bundle id com.hsmm.macrosip、sip/sips/tel URL scheme、Sparkle 键(SUFeedURL、SUPublicEDKey、每日检查)和版本。CFBundleShortVersionString 是最近的 git 标签(git describe --tags --abbrev=0;在任何标签之前为 Product.version),CFBundleVersion 是提交计数。它去除除 /usr/lib/swift 和 @executable_path/../Frameworks 之外的所有 rpath,并在二进制文件需要其他 @rpath 库时失败。
签名为临时签名(-)加强化运行时,按 Sparkle 文档从内到外进行:XPC 服务、Autoupdate、Updater.app、框架,然后是带 packaging/Entitlements.plist 的应用;codesign --verify --deep --strict 必须通过。entitlements 授予麦克风和通讯录访问权限并禁用库验证,临时签名需要后者才能加载内嵌的框架(ADR 0013)。
debug 应用接受 --mock-engine、--demo-state、--data-dir、--snapshot 和 --appearance(见 测试);release 应用忽略它们,并在真实数据上启动真实应用,包括钥匙串提示。所有构建都接受 --log-level 0-6(或 MACROSIP_LOG_LEVEL)来设置 PJSIP 的详细程度。
其他脚本
| 脚本 | 作用 |
|---|---|
scripts/make-dmg.sh [debug|release] |
运行 make-app.sh,将应用与一个“应用程序”符号链接一起暂存,写出 dist/MacroSIP-<version>.dmg(HFS+,UDZO,卷名“MacroSIP”),并打印其 SHA-256 |
swift scripts/make-icon.swift |
用 CoreGraphics 绘制图标(权威来源 packaging/AppIcon.svg:一个拨号键盘,其按下的键在绿色渐变上勾勒出一个 M)到 iconset,并运行 iconutil 生成 packaging/AppIcon.icns;更改图稿后重新运行 |
scripts/make-appcast.sh、scripts/publish-appcast.sh、scripts/update-cask.sh |
发布工具,见 发布 |
scripts/test.sh、scripts/selftest-call.sh、scripts/check-strings.sh |
见 测试 |
通用构建(ADR 0012)
一个应用和一个 DMG 同时服务 Apple 芯片和 Intel。应用的 release 构建是通用的,debug 构建是原生的。Opus 和 OpenSSL 作为静态归档从 third_party/deps/universal/lib 链接,绝不以 .linkedLibrary 或从 Homebrew 链接,因此应用在系统之外没有 dylib 依赖(Homebrew 的归档只有 arm64 且针对主机 macOS 构建)。在 Apple 芯片上用 Rosetta 2 以 SELFTEST_ARCH=x86_64 scripts/selftest-call.sh 测试 Intel 切片。Intel 支持将在 Apple 的工具链或 GitHub 的 Intel 运行器终止时结束。
常见问题
| 症状 | 原因与修复 |
|---|---|
Apple Silicon (arm64) host required |
build-pjsip.sh 交叉编译 x86_64,只能在 arm64 上运行。在 Intel Mac 上请从 CI 产物获取通用库(Intel CI 作业正是这样做的)。 |
SHA-256 mismatch for ... |
下载损坏或被重新发布;删除 third_party/downloads/ 下的文件以重新获取,如果上游更改了 tarball,请核查固定的校验和 |
configure did not enable OpenSSL / Opus |
configure 没有看到 third_party/deps/<arch>;阅读 third_party/build-logs/<arch>/pjsip-configure.log |
plugin for module SwiftUIMacros not found |
@State 需要 Xcode 的宏插件;请使用 Views/Components/Local.swift 中的 @Local 包装器 |
swift test 找不到 Testing 宏 |
运行 scripts/test.sh,它会传入插件路径 |
| 重新构建后应用仍运行旧的 PJSIP 代码 | SwiftPM 没有看到归档变化:rm -rf .build |
make-app.sh 中 Sparkle.framework not found |
先运行 swift package resolve 或 swift build |
二进制文件需要 Sparkle 之外的 @rpath 库 |
有新依赖被动态链接;ADR 0012 要求静态归档 |
PJSUA_INVALID_ID 在 Swift 中无法编译 |
它是 C 枚举常量:使用 PJSUA_INVALID_ID.rawValue |
Rosetta 下的 x86_64 pjsua 没有立即应答 |
Rosetta 启动需要几秒钟;selftest-call.sh 会轮询最多约 15 秒 |