4ee96864611f5b28dbcdc2351878bfb8
WWDC20 10214 - 移植 Mac app 到 Apple Silicon

一直以来 Apple 都勇于创新,Mac 共经历几次大的变革,从最初的的架构到 PowerPC 芯片,再到 OSX、Intel 芯片,到这次放弃 Intel x86 架构,自研 Apple Silicon ARM 芯片。第一款搭载该芯片的产品预计年底面世,在我看来这不管对 Mac 还是 Apple 生态开发者都是具有历史意义的一天。

概述

本文主要讨论如何如何为 Apple Silicon 重新编译 MacOS App,如何构建通用架构 App,以及让这些 App 启动更快,性能更好并支持平台未来发展;另外将演示如何使用 Xcode 12 运行、调试、测试你的 App, 以及处理进程内和进程外 plugins 的一些技巧。本文假设读者已拥有一定的 MacOS 平台开发经验。

CPU 架构

为了更好的理解此文,我们先了解下 Apple Silicon Mac 及 Intel-based Mac 的 CPU 架构如图所示:

Apple Silicon 本身是 arm64 架构( iOS 开发者对此并不陌生),支持 arm64 架构、x86_64 架构 ;Intel-based Mac 是 x86_64 架构, 只支持 x86_64 架构,特别的 Apple Silicon 是通过 Rosetta translation 运行 x86_64 App,关于 Rosetta 更多的信息可以查看 About the Rosetta Translation Environment.

通用 Mach-O

程序可执行文件或者动态库文件等,在应用程序包内都是 Mach-O 文件格式,他们可以编译为单架构或者通用架构,使用 "lipo" 命令可以查看具体信息。特别的,从今年开始新 Mac Apps 必须构建通用 CPU 架构的 App,也就是说要同时支持 arm64 和 x86_64 架构。

如果你已经有只支持 Intel 架构的 App 或者由于一些特别的原因还不能支持原生 arm64,Apple Silicon Mac 新硬件将提供 Rosetta traslation 环境来无缝的运行他们。另外 Rosetta traslation 不支持 Kernel extensions(内核扩展), AVX vector 指令集, Virtualization 虚拟机等。Xcode 12 支持 App 运行于 Rosetta 环境,我将在下一节讲解如何使用 Xcode 编译 Rosetta App。

使用 Xcode12 编译 Rosetta App

我们以 NetworkBenchmarking 工程为例演示如何适配 Rosetta 环境,在 Xcode 首先选中 My Mac Rosetta 编译运行,我们发现编译成功了没有任何错误。如图所示:

接下来第二步我们选中 My Mac 编译 Apple Silicon 原生架构,不出所料,我们遇到了编译错误如图所示:

这里补充一下不同 CPU 架构的条件编译列表,如图所示我们只需在 if 条件 再加一个 TARGET_OS_SIMULATOR 就可以解决这个编译错误。

改好后我们继续重新编译,很明显我们又遇到了编译错误,但这个错误发生在编译链接期,第三步解决链接期错误 Sparkle.framework 中存在 " Undefined symbols for architecture arm64" ,大概意思是我们 App 编译是通用架构,但依赖的三方库 Sparkle.framework 还不支持通用架构,如图所示:

所以我们需要对应的三方库支持 arm64 版本,我这里已经下载了已经升级的 Sparkle.framework,所以这里直接替换。特别的,所有 .a、.framework、.xcframework 都需要重新编译支持通用架构。

至此我们的 App 可以成功编译,这里总结一下操作步骤,首先编译 x86_64 Rosetta 版本;第二步编译 arm64 版本,fix 可能的编译错误;第三步 fix 链接期的编译错误,升级所有依赖的三方 framework 支持以通用架构。强调一下,我们可以使用任何装有 Xcode 12 的 Mac 电脑构建通用架构的 App。

Apple Silicon App 运行优化

虽然我们的 App 已经可以成功编译,但可能还有一些运行错误,所以我们需要在 Apple Silicon Mac 运行优化我们的 App。

运行 NetWork Benchmarking APP 点击 HTPP+TLS,会报如下图所示错误, dlopen(libTLSPlugin.dylib,2) no suitable image found,Did find libTLSPlugin.dylib:mach-o,but wrong architecture。

如图所示,我们的 .dylib 动态库试运行时通过 dlopen 或者 Bundle.load() 在运行时加载,所以编译时不报错,但运行时动态加载 libTLSPlugin.dylib 这个动态库,不支持 arm64 就会报错。我们升级 libTLSPlugin.dylib 即可解决该问题。

top Created with Sketch.