Xcode 由于 Mach-O header 中缺少 LC_VERSION_MIN,Mac App Store 分发的应用存档验证失败

Xcode app archive validation fails for Mac App Store distribution due to lack of LC_VERSION_MIN in a Mach-O header

我有一个 Swift 项目,它使用 NSTask 调用命令行实用程序。

为了便携性,我将该命令行实用程序包含在应用程序的捆绑资源中。

该实用程序非常复杂 - 它是一个 Ruby 解释型应用程序,带有一堆 gem,其中一些具有本机扩展。 gem 都与 Bundler 一起安装到实用程序中的标准 vendor/ 目录中。

我已经成功存档、验证、分发,并且 运行 应用程序 Mac 没有通过 Developer ID 安装该 CLI 实用程序(或 Xcode) ('outside the Mac App Store') 多次工作流程。

但是,存档无法验证 Mac App Store 分发,出现此错误:

2016-08-28 15:26:41 +0000 [MT] 呈现:错误域=DVTFoundationNSBundleAdditionsErrorDomain 代码=1 "Couldn't find platform family in Info.plist CFBundleSupportedPlatforms or Mach-O LC_VERSION_MIN for AbstractMemory.o" UserInfo={NSLocalizedDescription=无法在 [=75= 中找到平台系列] CFBundleSupportedPlatforms 或 Mach-O LC_VERSION_MIN 对于 AbstractMemory.o}

那么...为什么应用存档验证在 MAS 工作流程中会失败,而不是 Developer ID 工作流程,并出现这样的错误?


可能感兴趣的点:

TLDR

依赖项目最初是用一个非常旧的Xcode和OS X编译的,所以它里面的一些Mach-O可执行文件没有LC_VERSION_MIN_MACOSX 在他们的 header 中加载命令。基本上我必须找到一种方法来压缩加载命令。

唯一可靠且干净的方法是使用 Xcode 7 构建工具重新编译依赖项目中的所有本机代码。如果依赖项是开源的,你可以自己做。如果不是,您将不得不向您的软件供应商求情。

如果您需要 Yosemite 部署目标兼容性,您可以冒险在 Yosemite 构建箱上使用 Xcode 7.1 GM。但我建议您在 El Capitan build box 上使用 Xcode 7.1+ 重新编译代码,以确保您的二进制依赖项与 SIP 等 El Capitan 兼容。

详情

在我的例子中,依赖项目是开源的,所以我能够:

  1. 在 GitHub 上找到它的源代码。
  2. 叉它。
  3. 在不对 OS X 版本进行任何更改的情况下构建项目,该版本尽可能匹配第 3 方开发人员最初在其构建盒上用于编译依赖项的版本。
  4. 当(如果)失败时查看构建日志。
  5. 进行所有必要的更改以使构建绿色化。您现在已经达到与使用的原始构建框相同的水平。
  6. 现在将构建盒 OS X 版本更新为 Yosemite + Xcode 7.1 GM(如果你必须具有 Yosemite 兼容性)或 El Capitan + Xcode 7.1+.
  7. 重建,修复,yada yada。
  8. 再次变绿时,抓取新编译的二进制神器。
  9. 在捆绑包中找到 Mach-O 可执行文件。
  10. 运行 otool -l [path to executable].
  11. 在 Mach-O header 加载命令的大列表中,您现在应该看到 LC_VERSION_MIN_MACOSX 10.10 或 10.11(取决于您使用的是什么)。
  12. 你的二进制文件应该可以运行了。

通过使用 Travis CI 的 Mac 构建队列,我能够设置多个 OS X 构建 VM 而不会发疯。

剩余 non-showstopper 个问题

"Include symbols for debugging" 必须取消选中以进行存档验证或导出。这是因为符号器无法处理第三方 object 代码。我想这是因为它是为发布而构建的,所以 object 代码没有附带任何调试符号。

如果无法重新编译代码

如果您无法重新编译代码,我听说有肮脏的解决方法可以将 Mach-O 加载命令填充到 pre-existing 二进制文件中。这存在以下问题:

  • 这种方法非常脆弱,因为它依赖于在 header 中为您的新加载命令留下足够的空白位。不出所料,我听说修改现有加载命令的值往往比插入新命令更可靠。
  • 二进制 不能 已经签名。 Gatekeeper 将立即拒绝在签名后以任何方式被篡改的二进制文件。
  • Mach-O header 重写不是一件小事,因此往往需要借助第三方工具来完成。如果您不习惯在二进制 + 十六进制编辑器级别工作,则很难验证这些工具做了什么,因此您将非常信任它们 (a) 正确地执行它并且 (b)不要做任何邪恶的事情。

额外讨论

我 cross-posted 在 https://forums.developer.apple.com/message/175427 的 Apple Developer 论坛上提出这个问题,看看更专业的社区是否能更快地回答这个问题。它不是,但您可能会通过 link.

找到对该问题的更多讨论