再谈编译WebKit

本文聊一聊如何编译WebKit,并调试 WebKit 的 WebContent 进程,以解决WKWebView的疑难问题。

作者编译过WebKit,并写文章记录。但当时是跟着网上的几篇文章按部就班地执行,对每一步都比较懵懂;能跑起来实属运气,如果中途发生问题,那就不知如何解决了。

现在再次尝试构建WebKit工程,跟着以前的步骤竟无法完成,还是因为当时知其然不知其所以然。

因此,本文尝试再对编译WebKit的过程做一次梳理,并把每一步的问题尽量分析。此外,作者抛弃所有对外部资源的参考(往往都过期了),仅跟着WebKit官网 - WebKit 的资料操作。

从官网进入 Get Started 这一主题:Getting Started | WebKit,本文将唯一地引用这里的描述。

代码Clone

WebKit的源码早已从SVN迁移到github。GitHub - WebKit/WebKit: Home of the WebKit project, the browser engine used by Safari, Mail, App Store and many other applications on macOS, iOS and Linux.

git clone 这一步除了慢也没什么问题。

编译准备

WebKit 分支选择

基于iOS系统版本的Tag

作者本身是在main分支上构建的,下图中的tag没有实际编译。但这些tag肯定是和iOS系统版本严格对应的,可以根据自己需要的iOS版本来check out。

一个问题是,当前(2023.10.27)iOS 17 已全量发布,iOS 17 对应的tag还是没有。

⚠️ 猜测这里可能是Apple为了安全,防止未知漏洞被利用,因此不放出最新系统和WebKit的对应关系。有更懂的兄弟欢迎补充。

基于WebKit版本的Tag

WebKit的每个 Release 都有tag。若需 debug 最新的 iOS 系统,可以通过WebKit的版本在找到对应代码。

历史iOS与WebKit版本的对应关系,可以在这里查询:https://en.wikipedia.org/wiki/Safari_version_history

若这个列表没有,比如当前 iOS 17,可以去Xcode里看下iOS基础库的符号文件里写的版本。

比如,Xcode 15 中 iOS WebKit 的 tbd 文件位于 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk/System/Library/Frameworks/WebKit.framework/WebKit.tbd

查看该文件中描述的版本标识,可知 iOS 17 用的是 WebKit: 616.1.27

一个问题是,616.1.27这个版本,在git中有许许多多个tag,如下图:

具体选哪个?我也不知道。随便选一个iOS 17正式发布之前的吧!毕竟这些版本号如此接近,代码应该变更不大,大多情况下对问题排查没有影响。

基于main分支

WebKit修问题很慢很慢的。如果你的问题发生在较新的 iOS,main分支往往还能复现问题。因此,随便在main分支上盲狙一个commit也不是不行。

Xcode版本选择

Xcode版本选择,本质上是选择编译工具的版本。Xcode版本选择的操作,指的是通过xcode-select切换 Xcode 的命令行工具版本,以使 WebKit 的编译脚本使用我们指定的Xcode。

WebKit是一个非常复杂的,和Apple紧密合作的工程。Xcode近几年变动很多,也很有野心。从 New Building System 到 New Linker,这些变动虽然往往对iOS开发者无太大影响,但很有可能会有WebKit这样极度复杂的工程有影响。因此,建议直接使用上一个 Xcode 大版本的最高版本。比如,现在 Xcode 15 是最新的大版本,刚发布没几个月;上一个大版本的的最高版本是 Xcode 14.3.1;考虑到 Xcode 15 不一定被 WebKit 适配,已经稳定使用一年的 Xcode 14.3.1 成功率是最高的。

⚠️ 若编译的WebKit版本实在过早,作者猜测编译也有可能失败。此时可尝试对应时期版本的Xcode再试

⚠️ 如果深入理解WebKit的构建系统,那么对于不同WebKit版本和Xcode的对应关系之间,肯定会有更深的理解。然而对于主要想解决项目问题的我们,有时候盲目地尝试一下反而更快。该不深究的地方,需要及时放过,解决问题优先。

开始编译
这一阶段,只要唯一地参考 Building WebKit | WebKit 这篇WebKit官方的编译指南即可。建议先通读。
确定目标架构
编译指南明确指出:
Note that while simulator builds will be able to be run in the matching simulated device type, device builds of WebKit cannot be installed on embedded devices.
这段明确指出,我们的 WebKit Build 不支持iOS真机安装,因此我们只能在虚拟机上调试。(⚠️ 真机调试WebKit我没有试过。即然文档明确提出,我就先不撞南墙了)
Intel Mac
如果你是intel mac,那么你的虚拟机只能是x86_64架构。
●在 intel mac 上编译 x86_64 架构的 WebKit,无需额外操作。
ARM Mac
如果你是arm mac,那么你的虚拟机可以是arm64架构,也可以是x86_64架构。
●在 arm mac 上编译 arm64 架构的 WebKit,无需额外操作。
●在 arm mac 上编译 x86_64 架构的 WebKit,需要在下方编译脚本之前加上 arch -x86_64
在arm mac选择编译什么版本的 WebKit,取决于你的 App 需要运行在 arm64 原生虚拟机上,还是需要运行在 Rosetta 虚拟机上。
执行Xcode Configure脚本
根据文档指导,在编译之前,需要先执行
sudo Tools/Scripts/configure-xcode-for-embedded-development,
否则会报 JSCLLIntOffsetsExtractor: target specifies product type ‘com.apple.product-type.tool’, but there’s no such product type for the embedded platform的错误。
⚠️ 这个脚本具体做什么,我什么要这么做,我完全不清楚!!!猜测是 iOS 平台不允许创建 Command Line Tool 类型的 target,因此需要这个脚本做 workaround。比如,使用Xcode新建一个工程,若选择为macOS创建一个CLT类型的target,则此target的类型,在Xcode文件中即为 com.apple.product-type.tool,与JSCLLIntOffsetsExtractor的类型一致。
至于为什么不把这个脚本加入到build自动化过程中,反而这么弱智地让所有人显示地多理解一步,多做一步,否则会有一个百分百的报错,我完全不理解。

image.png


执行编译脚本
接下来即可执行WebKit工程的编译总入口:
sudo Tools/Scripts/build-webkit --ios-simulator --debug。
这里不能是 --ios-device,因为调试WebKit不支持真机。按需添加 arch -x86_64。

需要注意的是,在编译之前,脚本会先打印一下编译配置,如下图

image.png


要看清楚自己的目标架构。比如,我们的iOS工程因为没有适配,现在还是只能在 x86_64 架构的虚拟机上运行;因为,在我的 arm 芯片的电脑上,我直接加入 arch -86_64 的前缀,让脚本直接跑在 x86_64 的环境里,这样它自动识别的编译架构就是 x86_64 了。
⚠️ 当然,这里 build-webkit 应该提供了某个编译选项调整目标架构。我没查到。
Run
====================================================================
WebKit is now built (54m:28s).
====================================================================
编译完成后,控制台会有以上日志。在我的 m1 芯片的小mac上,编译 WebKit 需要1小时。
使用Xcode打开 WebKit 工程
使用你编译 WebKit 使用的 Xcode 打开根目录中的 WebKit.xcworkspace。
该工程已默认选择 WebKit.framework 作为 target。
选择目标虚拟机
目标虚拟机请选择与你上一步编译时匹配的架构的虚拟机。
比如,作者在 arm mac 上使用 Xcode 14.3.1 编译了 main 分支 x86_64 架构的 WebKit,此时我选择了 iPhone 14 Pro (Rosetta, 17.0.1) 。
作者因为要解决 iOS 17 上的问题,因此额外安装了 iOS 17 的虚拟机。

image.png


指定需要调试的App
编辑当前的 Xcode Scheme,如下图:

image.png


当前 Executable 默认为 None。因为 WebKit.framework 不是一个可执行的App。点击这里,选择 Other...,在弹出的 Finder 中选择我们的 *.app(需提前编译好)。

image.png


选择完成后如上图。点击Close。
指定Build文件夹
如不额外操作,WebKit的编译产物默认放在 WebKitBuild 文件夹中。为了使 Xcode 复用这些编译产物,需要将 Xcode 的 build 产物文件夹指定到该文件夹。
File -> Workspace Settings... -> Advanced...
选择Custom,两个Input全部输入WebKitBuild。如下图。保存后退出。

image.png


Run
因为 WebKit.framework 已提前编译好,此时我们可以直接点击
Product -> Perform Action -> Run Without Building。

image.png


运行起来后,lldb会自动attach到所有的WebKit进程上。如下图:

image.png


注:每一个WKWebView对应一个 WebContent 进程;所有 WKWebView 共享同一个 Networking 进程以及 GPU 进程。
Run, Debug, Rerun
运行起来之后,你很有可能会看到第一个Crash。如下图:

image.png


毕竟本文的读者大都使用 SchemeTaskHandler 来拦截了 WKWebView 所有的HTTP网络请求。
HTTP的拦截,从对外API的角度,其实是被Apple禁止的。这一点WebKit源码通过一个仅Debug时检查的assert来拦截。解决这个问题,仅需将上图中该行assert注掉即可。

注掉后,在Xcode中直接Run (CMD + R 也可以)。因为编译产物都在 WebKit 文件夹中,可被复用,工程应该很快重新编译完成。再次启动后,就没有这个crash了。
到这里,也就完成了首个对 WebKit 的 Run, Debug, Rerun 的过程。

若有收获,就点个赞吧

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值