本文要点:
Uber 技术栈简介
Uber 于 2010 年创立,已拥有超过 150 亿次的出行记录,为此,他们实现了很多很酷的创新技术。Go 和 Java 是通用服务器端语言,Python 和 Node 应用于特定的情况(如 Node 用于前端,Python 用于数据分析/ML)。C++被用于一些底层的库。在后端代码中使用其他语言的情况很少。
我们的 Go Monorepo 比 Linux 内核还要大,有几千名工程师在开发和维护。总而言之,很大。
Uber 是如何使用 Zig 的?
Abhinav Gupta是我们的来自 Go 平台团队的同事,其实他描述得比我好:我理解我们只是在使用 Zig 的 C 工具链,而不是将其作为语言使用。Zig 支持基于 C 的代码的交叉编译,能减少对系统 C 编译器的依赖。
Uber 技术栈发展历程
2018 年之前,Uber 的 Go 服务都有单独的存储库。2018 年,我们开始将这些服务大规模地迁移到 Go Monorepo。我的团队参与了第一波迁移——我仍然记得那有多复杂。
2019:寻求一个封闭式的工具链
当时,Go Monorepo 已经使用了一个封闭式的 Go 工具链。因此,用于构建 Go Monorepo 的 Go 编译器不会受系统上安装的编译器影响(如果有的话)。因此,无论在哪个环境下构建,都会使用相同版本的 Go。Bazel文档对此做了很好的解释。
创建于 2019 年,没有太多变动。
C++工具链是一个编译 C/C++代码的程序集。不可避免地,我们的一些 Go 代码要使用,所以它需要一个 C/C++编译器。然后,CGo 将 Go 和 C 部分链接成最终的可执行文件。
从 Go Monorepo 创建伊始,C++工具链就不是封闭式的:Bazel 会使用它在系统上发现的任何东西。也就是说,在 macOS 上使用 Clang,在 Linux 上使用 GCC(无论什么版本)。在 Bazel 中创建一个封闭式的 C++工具链是一项很大的工作(对于我们的 Go Monorepo 来说,需要花费数月时间),没有迫切的需求,也没有足够的痛苦,我们还无法接受做这样一件事。
现在,我们看下非封闭式 C++工具链的局限性:
2020 年 12 月:需要 musl
我在做一个与 Uber 无关的小项目。该项目是用 Bazel 构建的,并使用了 CGo。我希望我的二进制文件是静态的,但 Bazel 并没有让这个过程变得简单。我花了几个晚上基于创建了一个 Bazel 工具链,但没走多远,因为当时我无法深入理解 Bazel 的工具链文档,而且也没有找到一个好的示例可以参照。
2021 年 1 月:发现
2021 年 1 月,我发现了 Andrew Kelley 的博文“:一个功能强大的GCC/Clang替代品”。我推荐你读一下这篇文章;它改变了我对编译器的看法(也有助于你更好地理解本文接下来的内容,因为我是讲给 Zig 追随者听的)。综观 Andrew 的文章,有以下优势:
2021 年 2 月:请求关注
我向Zig报告了Bug。一个星期都没有动静。我每月捐 50 美元,希望“Zig 的人”能优先处理我所报告的问题。接着又是一个星期的沉默。然后我在
#zig:libera.chat
中扔了一枚炸弹:
<motiejus> 捐赠后,有什么规约可以用来“申请”开发时间吗?
<andrewrk> ZSF只接受不附带任何条款的捐赠。
<andrewrk> 你是从哪里获得了不同的印象吗?
复制代码
当时,我希望无论谁注意到这段对话都立即忘掉它。好吧,一年多以后,我又把这段话写在这里,看着玩吧。
2021 年 6 月:bazel-zig-cc 和 Uber 的 Go Monorepo
2021 年 6 月,Adam Bouhenguel创建了一个可以工作的bazel-zig-cc原型。基本功能没问题,但仍然缺少一些特性。后来,Andrew 实现了,这是一个真正可用的 bazel-zig-cc 所缺少的最后一块拼图。我集成了,完善了文档,并在Zig邮件列表中宣布了我创建的 bazel-zig-cc 分叉。至此,它对我的小项目是有效的。
在公告发布几周后,我为 Uber 的 Go Monorepo 创建了一个“WIP DIFF”:只是按照我的上线说明,天真地将其提交到我们的 CI。几乎所有的测试它都没有通过。
将 bazel-zig-cc 加入 Uber 的 Go Monorepo。
大部分失败都是由系统库依赖导致的。关于这一点,很明显,要想真正搭载 bazel-zig-cc 并编译所有的 C/C++代码,需要巨大的投入来消除对系统库的依赖,并偿还大量的技术债务。
2021 年底:回顾
2021 年底:Uber 需要交叉编译
我的任务是为 Uber 评估 arm64。撇开评估细节不说,我需要为 linux-arm64 编译软件。大量的软件!由于我们大部分的底层基础设施都在 Go Monorepo 中,我首先需要一个交叉编译器。
我终于有了一个实现交叉编译器的商业理由。现在,时间和金钱都可以投入了。“WIP DIFF”搭配是一个良好的开始,但距离终点还很远:团队不相信这是正确的事情,DIFF 更多的是一个原型。而且,要让 zig-cc 和 bazel-zig-cc 在任何情况下都可以使用,还有很多工作要做。
在一个大公司里引进这样的技术时,最重要的是风险管理。由于 Zig 是一项新技术(甚至连 1.0 都没有!),建议用它来编译我们所有的 C 和 C++代码很不寻常。我们应该做好至少十年内都使用它的计划。人们提了一些问题,并针对这些问题做了认真仔细的评估。为此,我真心感谢 Go Monorepo 团队,特别是 Ken Micklas,为这个未经证实的原型做了大量的工作和研究。
评估不同的编译器
我们需要一个交叉编译器,摆在我们面前的选项有两个:
对于以 macOS 为目标封闭式工具链,选择的天平偏向了,连同它所有的缺陷、风险和不稳定性。
还有一个问题需要注意:我们知道,如果我们在重要的地方使用 Zig,会遇到问题,但又可能不具备解决这些问题的专业知识。作为一家大公司,我们该如何降低采用风险,确保严重的 Bug 及时得到处理?我们确信,ZSF 的出发点是好的:显然,如果我们发现并报告一个合理的 Bug,它就会得到修复。但是,怎么才能设定一个等待时间的上限呢?
金钱
50 美元的捐款无济于事,也许一份大的服务合同会有帮助?我四处打听,是否可以通过花些钱来降低“交叉编译器”的风险。获得管理层同意大约需要 10 分钟;起草、审批和签署合同大约需要 2 个月。
合同条款大致如下:
合同签署了,电汇完成了,在 2022 年 1 月:
合同金额是公开的,因为 ZSF 是非营利的。
2022 年及以后
2022 年 2 月,该工具链通过一个命令行标志(
--config=hermetic-cc
)做了限定。自此,你可以在 Uber 的 Go Monorepo 中调用了,不需要自定义补丁。
证明我们的提交队列登录了我的 WIP DIFF。
2022 年截至目前的时间线:
小结
我开始准备演讲,希望能给出一个大公司如何采用 Zig 的“运行手册”。然而,其实并没有什么“运行手册”;我为采用 zig-cc 所做的努力本可能会因为很多很多原因而失败。
回顾过去,我觉得要想获得成功,最重要的是在适当的时候有一个杀手锏特性。在我们的例子中,有两个:无需 sysroot 的 glibc 版本选择和交叉编译到 macOS。
原文链接: