MacroSIP
EnglishРусский中文

构建

MacroSIP 仅用 SwiftPM 和 Xcode 命令行工具即可构建。没有 Xcode 项目,也不涉及 Homebrew:OpenSSL 和 Opus 与 PJSIP 一起从固定版本的源代码 tarball 编译。详情见 scripts/README-build.md、scripts/build-pjsip.sh、scripts/make-app.sh 和 ADR 0012。

要求

第 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 秒

另见 架构,安装已构建的 DMG 见 安装,运行时问题见 故障排除。

其他页面:首页 · Home (English)