天天看点

Monorepo 解决方案 — Bazel 在头条 iOS 的实践

作者:闪念基因

一、头条的困境与出路

头条工程的困境

近年来,头条工程经历了以下几个阶段:

  • 2016 年开始使用 CocoaPods 管理二方组件,并逐步从 Monolith 迁移到 Multi-repo。
  • 2017 年实现了组件依赖打平和组件二进制化,通过共享预构建的二进制文件,缩短了构建时间。
  • 2021 年初,通过 CC 宏接入 Dolphin 的 clang wrapper,将 CI 构建时间减少了 50%。
Dolphin 是字节内部分布式缓存系统。Dolphin通过最小化的降低真正需要编译的源文件的数量,来提升整个的编译效率。当编译一个源文件的时候,Dolphin 会通过一系列的计算,判断这个源文件是否需要真正的调用编译器进行复杂的全流程编译,如果发现满足若干条件,Dolphin 会直接从自己的缓存系统的获取编译产物,这个时长比编译器本身编译要短很多。

然而,这些方案也带来了一些明显的副作用:

  • 统一构建问题:组件二进制化要求组件高度标准化,无法注入客制化逻辑,如预编译宏等手段均会失效。
  • 构建稳定问题:集成组件阶段强依赖网络环境,下载成功率无法保证 100%,首次构建需要同时下载 500+ 的组件,时间占比高达 30%。
  • 缓存复用问题:使用 CC 宏接入 clang wrapper 则天然缺失部分上下文信息,导致编译缓存容易被污染,缓存复用率低,并且作用范围有限,不能覆盖到 actool/ibtool 等工具的产物。

头条工程的出路

考虑到头条工程所面临的问题,于 2021 年中开始探讨采用 Monorepo + Bazel 的解决方案。在《iOS Monorepo 全源码解决方案》一文中,提到了使用 Bazel 构建 Monorepo 工程的优势,包括:

  • 代码复用:Monorepo 可以让不同的项目或模块共享代码和依赖项,避免重复编写和维护相同的代码。
  • 依赖管理:Monorepo 可以让不同的项目或模块共享依赖项,避免依赖冲突和版本管理问题。
  • 统一构建:使用 Bazel 可以统一管理 Monorepo 中的构建规则和依赖项,提高构建效率和可靠性。
  • 版本控制:Monorepo 可以让不同的项目或模块共享版本控制系统,简化版本管理和发布流程。

具体来说,头条工程的迁移至 Bazel 可分为两个阶段:

第一阶段:迁移业务组件。 基于 JoJo 完成了业务组件源码化(约 10,000 文件数)及 Bazel(3.x)化改造,初步解决了构建耗时问题,取得了不错的效能提升。

第二阶段:迁移二三方组件 。 在依赖管理和统一构建上做更激进的尝试,基于新版本的 Bazel(5.x)和 Rules 对二三方组件进行源码化(约 18,000 文件数)和 Bazel 化改造,并完全移除依赖管理工具 CocoaPods,统一构建,提升工程稳定性,BitSky 项目基于该阶段提出。

本文接下来的内容,将重点介绍第二阶段过程中,头条是如何基于 BitSky 结合 Bazel 进行工程改造的。

BitSky 构建服务

在 BitSky 中,Bazel 作为构建系统的核心,负责管理和构建应用程序和库文件的编译、链接、测试等过程,并支持自定义构建规则和工具链的集成。为了更好地理解 BitSky 的构建系统,我们先介绍 Bazel 的基础概念,再介绍 BitSky 的构建系统分层。

Bazel 概念简介

Bazel 是 Google 内部使用(Blaze)并开源的一个通用构建系统,并且内置支持构建客户端和服务端软件,包括 Android 和 iOS 平台的客户端应用程序;还提供了一个可扩展的框架(Rules),可以使用它来开发自己的构建规则。Hermeticity(封闭性)是 Bazel 构建系统的一个重要概念,指的是构建过程的可重现性和可靠性。在 Hermeticity 的理念下,Bazel 会尽可能地隔离构建过程中的环境和依赖项,从而确保构建结果的一致性和可重复性。

Monorepo 解决方案 — Bazel 在头条 iOS 的实践

以下是一些 Bazel 常用基础概念,了解其中一些概念有助于理解本文后续的内容:

Bazel 常用基础概念:https://bazel.build/concepts/build-ref
  • BUILD files(构建文件):用于描述一个 Bazel 项目的构建规则和依赖关系,类似于 CocoaPods 中的.podspec文件。
    • 类似于.podspec文件中的源文件和资源文件,我们可以在BUILD文件中声明需要编译的源文件、资源文件和其他依赖库等信息。
    • 和.podspec文件类似,BUILD文件也支持类似于版本控制的语法,可以指定具体的版本、分支或者提交号等信息。
  • Workspace(工作空间):每个工作空间都包含一个WORKSPACE文件,用于管理一个 Bazel 项目的依赖关系,类似于 CocoaPods 中的Podfile文件。
    • 类似于Podfile中的 pod,我们可以在WORKSPACE文件中声明依赖关系,指定需要依赖的库、二进制文件或者其他项目。
    • 还类似于Podfile.lock,WORKSPACE文件不会锁定依赖的版本,而是在每次构建时重新解析依赖关系,以确保构建的一致性和可重复性。
    • WORKSPACE文件还可以定义一些全局配置,比如编译器选项、构建工具选项等等。这些配置可以被整个项目共享,确保项目的构建和依赖关系的一致性和可重复性。
  • Packages(包):是包含一个BUILD文件的目录,用于组织和管理代码库中的源代码和构建规则。
  • Targets(目标):表示构建规则和依赖项的目标,它可以是源文件、库文件、可执行文件等等。
  • Labels(标签):标识构建规则和依赖项,它由包名和目标名组成。

BitSky 构建系统

本章节介绍为 BitSky 中的构建系统模块,完整的 BitSky 模块架构请关注之前文章:《iOS Monorepo 全源码解决方案》。BitSky 的构建系统分为五个分层,包括工程配置、软件服务、构建系统、构建规则和构建工具,如下图所示:

Monorepo 解决方案 — Bazel 在头条 iOS 的实践

工程配置

为宿主工程提供客制化的配置能力,以及语义化的调用命令。这一层的作用是让用户可以方便地配置 BitSky 构建系统。

  • Makefile 定义了核心的调用命令,并且都是语义化声明,方便用户生成工程以及完成构建。
  • Plugin(插件) 通过提供不同时机的 hook 函数,以及可定向化配置 Rules 的入参,同时支持配置自定义 xctoolchain,可满足大部分客制化诉求。

软件服务

包含 BitSky、Tulsi 和 BuildService 三个工具。这一层的作用是为用户提供一系列构建工具,使得用户可以更加方便地使用 BitSky 构建系统。

  • BitSky 负责桥接现有的宿主.xcodeproj工程文件,基于自研的轻量依赖管理能力,自动转换为 Bazel 构建所需的WORKSPACE/BUILD文件,不需要终端用户手工生成工程物料。
  • Tulsi 工具,用于生成能够使用 Bazel 构建的 Xcode 工程,也通过 BitSky 进行调用。
  • BuildService 工具,用于补齐 Xcode 体系中索引、高亮、日志、进度条等能力。

构建系统

Bazel 构建系统的设计理念是分层架构,其中构建系统层、构建规则层和构建工具层是由 Bazel 决定的。所以构建系统层也是 BitSky 的核心层,包含 Bazel、Dep Server 和 Remote Execution Services,这一层的作用是为了支持分布式计算集群上执行构建和测试任务。

  • Bazel 构建系统的核心是一个高度优化的构建引擎,用于调度所有构建指令、提供编译、运行、依赖查询等能力,并支持分布式计算集群上执行构建和测试任务。
  • Dep Server(依赖解析服务) 用于在 Bazel 生成 Action 时做依赖矫正。在《Monorepo 解决方案之 Remote Execution》中有相关的介绍。
  • Remote Execution Services(远程执行服务) 是一种分布式构建和测试系统,用于在云上或者分布式计算集群上执行构建和测试任务。BitSky 的 Bazel 构建系统就是在公司内部的集群上执行构建和测试任务,以提高开发效率和代码质量。

构建规则

Rules(构建规则)是 Bazel 构建系统的执行基石——定义构建规则和依赖项的基本单元。Rules 是基于 Starlark 语言(Python 子集语言),将构建过程分解为一系列可重复的步骤,并定义了这些步骤之间的依赖关系。这一层的作用是为了保证构建的正确性和可重复性。

  • Embedded Rules( Bazel 内置的 Rules) 包括objc_library、cc_library等常用规则,用于定义和管理编译、链接、测试等构建规则和依赖项。
  • Apple Rules 包含 Bazel 对苹果平台(iOS、macOS、watchOS、tvOS)的构建支持规则,如apple_binary、apple_library等,用于定义和管理苹果平台应用程序和库文件的编译、链接和打包规则。
  • bazel_generator,负责把 .podspec文件转换成BUILD文件。Bazel 构建系统能正常运行取决于调用编译器、链接器等工具链时编译参数以及构建依赖的正确性,通过该研发工具将构建规则迁移至 Bazel 体系。

构建工具

是 Bazel 构建系统的工具集合,包含 Rules 提供的 Wrapped Tools 和宿主工程提供的 Custom Toolchains 两个部分。这一层的作用是为了提供必要的构建工具。

  • Wrapped Tools 是通过 Bazel 的工具包装机制,将官方发布的编译器和链接器等工具包装为可移植的二进制文件,用于在 Bazel 构建系统中编译、链接和测试应用程序和库文件。
  • Custom Toolchains 是通过配置文件,将特定版本的编译器、链接器等工具集成到 Bazel 构建系统中,用于构建和测试应用程序和库文件。

头条迁移至 Bazel

如前文所说,头条工程在前几年已经完成了组件化的演进,近 500+ 个组件都由 CocoaPods 进行包管理,每个组件都有一个.podspec文件,用于描述组件的版本信息、构建所需的源文件和配置等。从这个角度来看,BUILD文件和.podspec文件的作用类似。在迁移到 Bazel 构建系统时,头条工程只需要添加WORKSPACE文件,并将.podspec文件转换为BUILD文件即可。WORKSPACE文件主要用于描述外部依赖,接入成本较低,因此不在此讨论,本文重点介绍BUILD文件的转换思路。

Monorepo 解决方案 — Bazel 在头条 iOS 的实践

在 Bazel 构建系统中,Objective-C 源文件可以使用objc_library规则作为最小编译目标,而 Swift 源文件则对应swift_library规则。以objc_library为例,bazel_generator 将.podspec文件转换为BUILD文件的过程如下:

  1. 解析.podspec文件,包括依赖库、源文件、编译选项等。
  2. 生成BUILD文件。根据.podspec文件中的信息,bazel_generator 会生成适用于 Bazel 的BUILD文件。对于objc_library规则,生成的BUILD文件通常包括以下内容:
    1. name: 定义库的名称。
    2. srcs: 定义源文件列表。
    3. deps: 定义依赖库列表。
    4. copts: 定义编译选项。
  3. 处理依赖关系。由于 Bazel 和 CocoaPods 的依赖管理方式不同,bazel_generator 需要处理依赖关系。具体来说,bazel_generator 会将 CocoaPods 中的依赖库转换为 Bazel 中的依赖库,并将其添加到BUILD文件中的 deps 列表中。
  4. 处理资源文件。如果.podspec文件中包含资源文件,bazel_generator 会将其转换为 Bazel 中的 data 属性,并将其添加到BUILD文件中。
  5. 处理其他配置项。bazel_generator 还会处理其他一些配置项,例如编译选项、头文件搜索路径等。

通过这些步骤,bazel_generator 将.podspec文件转换为适用于 Bazel 的BUILD文件,并自动处理依赖关系、资源文件等问题,从而简化了迁移过程。

在理解了.podspec文件转换为BUILD文件的过程后,下面我们介绍一下头条是如何完成组件迁移的。首先,头条对 500+ 二进制化组件进行拆分,分为业务组件和二三方组件。

第一阶段

业务组件迁移

Monorepo 解决方案 — Bazel 在头条 iOS 的实践

由于业务组件仅集成到头条工程,并不会提供给其他工程复用,因此头条优先考虑将业务组件全源码化并集成到主仓的Module目录中。这样做的好处是,可以减少二进制依赖,提高构建效率,同时也方便开发人员进行维护和升级。

  • 对于业务组件的BUILD文件,头条会首次自动生成,后续则由研发人员进行维护和升级。
  • 对于二三方组件,则继续存放在Pods目录中,并通过 CocoaPods 进行集成。

第二阶段

二三方组件迁移

Monorepo 解决方案 — Bazel 在头条 iOS 的实践

为了方便管理和更新二三方组件,头条将其全源码化并集成到主仓的External目录中,并通过monorepo_config.yml和deps.yml这两个文件提供版本和依赖管理的凭证。

  • 对于二三方组件的BUILD文件,头条使用 bazel_generator 工具,通过.podspec文件自动转换生成。这样做的好处是,可以减少手动编写BUILD文件的工作量,提高工作效率。
  • monorepo_config.yml文件记录了所有组件对应的仓库信息(Git 来源/二进制链接来源)和组件版本信息,用于组件源代码回溯。这样可以方便地查找和管理组件的源代码,同时也可以保证组件的版本一致性。
  • deps.yml文件记录了工程中各个 Target 的依赖组件及包含的 subspecs。这样可以清晰地了解工程中各个 Target 的依赖关系,便于管理和维护。

工程配置插件化

经历完第二阶段后,头条已经完成了组件的 Monorepo 全源码化,放弃了 CocoaPods 作为依赖管理工具,转而将所有组件放到宿主工程中。这带来了以下问题:

  • 组件 构建选项管理: Bazel 是基于产物的构建系统,构建选项只能在各个组件的BUILD文件中控制,而 CocoaPods 的 Hook 调用时机在集成阶段,统一对组件的构建选项做修改,属于中心化管理。
  • 组件 配置条件管理: 部分组件的构建选项修改需要特殊处理,不适用统一修改的方案,需要使用黑白名单的方式来控制,并且需要对应组件版本,管理容易出问题,隐蔽且排查困难。

因此,构建系统需要具备管理感知能力,Bazel 成为更好的选择。我们不应该在依赖管理工具中介入构建选项,应该将其隔离开来。为了满足宿主工程的定制化需求,我们需要提供具备以下能力的机制:

  • 宿主可以通过中心化管理的方式定制配置各个组件的构建目标参数,并且可透传 Bazel 选项,实现更加灵活的构建流程。
  • 开发者可以根据不同的条件选择性地应用构建规则,从而实现更加灵活和定制化的构建流程。

为了更好地介绍这个机制,我们首先需要了解 BitSky 和 Bazel 的调用时序:

  1. 用户直接调用 BitSky 的命令。
  2. BitSky 根据场景组装出相应的 Bazel 构建选项。
  3. BitSky 在 "to bazel opts" 阶段访问 Plugin 内容。
  4. Plugin 是可客制化的中间层,用于传递宿主工程的 Bazel 构建选项。
  5. BitSky 将组装好的 Bazel 命令选项传递给 Bazel 进行调用。
Monorepo 解决方案 — Bazel 在头条 iOS 的实践

从上图可看出,我们在 Plugin(插件)中可以根据不同宿主工程的需求提供定制化的构建选项,从而也降低了 BitSky 和 Bazel 之间的耦合度。为了达到该目的,我们结合 Bazel 的特性,把插件的组成划分为以下 4 个部分,接下来的内容将会一一介绍各个部分。

Monorepo 解决方案 — Bazel 在头条 iOS 的实践

1. 注册钩子函数——hook.py

钩子函数是工程配置插件化的重要组成部分,其作用是在特定的时机执行额外的配置工作或操作。BitSky Plugin 提供了四个钩子函数,如下图所示:

Monorepo 解决方案 — Bazel 在头条 iOS 的实践
  • pre_generate_material_hook(obj)和post_generate_material_hook(obj)分别在生成宿主工程WORKSPACE文件和BUILD文件之前和之后调用,并且通过 obj 提供所需的构建参数。可以在这个时机进行额外的配置工作,如根据不同的构建场景拉取对应的配置文件等。
  • pre_build_hook(obj)和post_build_hook(obj)则在构建前后调用,可以用于执行额外的操作,如清理、打包、上传等。

2. 定向配置构建目标参数——defs.bzl

defs.bzl是 Bazel 的一个规则文件,用于定义自定义的规则和函数。在 BitSky Plugin 中,defs.bzl文件定义了宿主工程所需的构建选项和自定义规则,并提供了统一管理宿主工程的构建目标入参的功能,解决组件 构建选项管理的问题。如下图所示,可定向对ios_application依赖的objc_library规则传入客制化的 copts。

Monorepo 解决方案 — Bazel 在头条 iOS 的实践

结合 Bazel 的特性,defs.bzl能够解决以下具体问题:

  • 定向配置构建目标参数:Bazel 支持多种不同的构建目标和参数,defs.bzl文件通过传值机制,可以帮助开发者针对不同的宿主工程进行定向配置构建目标参数,确保 BitSky 不需要关注各宿主工程的具体目标参数,而是由各宿主工程自行决策。
  • 自定义规则和函数:Bazel 提供了丰富的规则和函数,但有时候需要自定义规则和函数来满足特定的需求,defs.bzl文件可以帮助开发者定义自己的规则和函数,实现更加灵活和定制化的配置。
  • 统一管理宿主工程的构建目标入参:Bazel 支持分布式构建,但不同的BUILD文件可能会有不同的构建目标入参,defs.bzl文件可以帮助开发者实现统一管理宿主工程的构建目标入参,包括 BitSky 自动生成的和研发维护的BUILD文件。
  • 增强工程配置的可扩展性:Bazel 的规则和函数非常丰富,但有时候需要自定义规则和函数来满足特定的需求,defs.bzl文件可以帮助开发者定义自己的规则和函数,实现更加灵活和定制化的配置,增强工程配置的可扩展性。

3. 透传 Bazel 选项——bazelrc

在 Bazel 官网的最佳实践中提及:工程的特定选项可使用.bazelrc文件管理。通过--bazelrc=<path to rc>传入指定.bazelrc文件,用于设置 Bazel 的运行时参数和环境变量。

Bazel-最佳实践:https://bazel.build/configure/best-practices#bazelrc-file
Monorepo 解决方案 — Bazel 在头条 iOS 的实践

.bazelrc文件可以定义诸如构建选项、构建缓存、构建工具链、构建输出路径等等配置选项。配置使用.bazelrc文件有以下好处:

  • 定制化构建选项:.bazelrc文件可以定义构建选项,例如编译器的版本、编译参数、构建输出路径等等,可以根据不同的需求和场景进行灵活的配置和扩展,满足不同项目的需求。
  • 管理构建工具链:.bazelrc文件可以定义构建工具链,可以根据不同的需求和场景进行配置,方便管理和维护构建工具链。
  • 提高构建的可移植性:.bazelrc文件可以定义构建选项和构建工具链,可以根据不同的需求和场景进行配置,提高构建的可移植性,使得构建结果更加稳定和可靠。

综上,.bazelrc文件可以帮助开发者更好地管理和维护构建配置,提高配置的灵活性、可维护性、可复用性、可扩展性和可移植性。

4. 限定配置条件——conditions

conditions(条件)是 Bazel 中用于根据不同的条件选择性地应用构建规则的一种机制。通过 conditions,开发者可以根据不同的条件(如操作系统、编译器版本、CPU 架构等)选择性地应用构建规则,从而实现更加灵活和定制化的构建流程,解决组件 配置条件管理的问题。以下是头条工程中的一个应用场景:

  • Bazel 内置的配置条件是//conditions:default,为统一代码风格,宿主工程可在conditions目录下的BUILD文件中声明自定义配置条件,便于用以下方式访问自定义的条件标签://conditions:debug或//conditions:release。
  • 在构建规则中使用select()函数区分 debug / release 配置下所需的选项,通过这种机制对齐 Xcode 中的 Debug / Release 配置;对于有多个配置的工程,可以按需增加config_setting规则。
Monorepo 解决方案 — Bazel 在头条 iOS 的实践

头条的收益

头条工程在各个阶段的迁移中,均取得了不错的效能提升:

  • 第一阶段基于 JoJo 完成业务组件迁移后,整体构建耗时 PCT50 降低 38%,AVG 降低 45%。
Monorepo 解决方案 — Bazel 在头条 iOS 的实践
  • 第二阶段基于 BitSky 完成二三方组件迁移后,Build 耗时 PCT50 降低 20%,PCT 90 降低 50%;整体构建耗时降低 50%。
Monorepo 解决方案 — Bazel 在头条 iOS 的实践

总结

问题和挑战

在头条工程迁移至 Bazel 过程中,除了上文提及的问题,还遇到了三个主要的挑战,包括:

  • 二三方组件依赖治理
  • 产物一致性校验
  • 构建环境统一

为了解决这些问题,头条工程采取了一系列的措施,保证迁移的顺利进行。

二三方组件依赖治理

由于历史原因,头条工程在pod analyze阶段会忽略.podspec文件声明的组件依赖信息,完全置信于Podfile文件维护的组件版本。如果是集成二进制后的二三方组件,这个方案能极大地降低pod analyze阶段的耗时。若集成全源码化的二三方组件,因组件.podspec文件中声明的组件依赖信息和Podfile文件中声明的未必一致,有较大概率导致构建失败;同时,使得原本能在pod analyze阶段发现的问题,推迟到了构建甚至运行时才能被发现,也使得组件的增删改等维护变得复杂。在迁移的过程中,头条工程会处于构建系统双跑阶段,为保障 Bazel 构建成功率,也同步进行二三方组件依赖治理:

  • 借用自研工具快速决议组件版本冲突和组件依赖声明缺失,过滤出问题组件。
  • 联系组件维护人,确认问题组件在头条工程中的版本号。
  • 完善问题组件.podspec文件中的组件依赖信息。

产物一致性校验

二三方组件迁移的过程中,由集成二进制文件改成集成源码,而各组件二进制化的构建环境和宿主工程当前的构建环境未必一致,这就造成二三方组件迁移后产出的二进制文件和原本的不一致,最终导致程序在运行时的表现有差异。为此,在构建系统双跑阶段,会自动触发校验工具对比迁移前后的产物,并输出有差异的符号。

  • 校验工具的基本原理是解析 Mach-O 中segment_command_64获取映射到程序地址空间的位置和大小,然后基于.linkmap文件中的符号信息,计算出这些符号真正的文件偏移量地址,然后读出内存数据进行对比。具体实践会在后续的系列文章中介绍。
  • 根据符号定位到对应的组件,对比迁移前后的构建日志,定位差异的选项。
  • 前期主要用于双跑阶段的产物对比,消费了 1000+ 差异符号,对齐编译层面的选项;后期用于工具链升级的保障措施,确保升级引入的差异符合预期。

构建环境统一

头条工程的构建环境可以分为两种:CI-CD 和本地研发。CI-CD 是在云服务上部署的,构建环境是统一且可控的,集群内的设备都部署了相同版本的工具链。而本地研发的构建环境则更加多样化且不受控制。其中最明显的差异是构建所使用的Xcode版本。在本地研发环境下,头条工程会生成多个 Xcode 版本的构建缓存,影响本地研发的构建缓存复用。此外,不同的 Xcode 版本包含的工具链版本也不同,因此无法保证本地研发和 CI-CD 的构建产物一致,如下图所示:

Xcodecctoolsld64LLVMClangSwift14.01001.2819.614.0.014.0.0 (clang-1400.0.29.102)5.7 (swiftlang-5.7.0.127.4 clang-1400.0.29.50)14.0.11001.2819.614.0.014.0.0 (clang-1400.0.29.102)5.7 (swiftlang-5.7.0.127.4 clang-1400.0.29.50)14.11001.2820.114.0.014.0.0 (clang-1400.0.29.202)5.7.1 (swiftlang-5.7.1.135.3 clang-1400.0.29.51)14.21001.2820.114.0.014.0.0 (clang-1400.0.29.202)5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)14.31005.2857.115.0.014.0.3 (clang-1403.0.22.14.1)5.8 (swiftlang-5.8.0.124.1 clang-1403.0.22.11.100)14.3.11005.2857.115.0.014.0.3 (clang-1403.0.22.14.1)5.8.1 (swiftlang-5.8.0.124.5 clang-1403.0.22.11.100)

通过上文提及的限定配置条件,指定支持的 Xcode 版本,可解决部分构建环境不统一的问题:

Monorepo 解决方案 — Bazel 在头条 iOS 的实践
  • conditions/BUILD文件定义xcode_config 规则,标签是//conditions:host_xcodes,用于声明支持的 Xcode 版本。
  • .bazelrc文件设置--xcode_version_config=//conditions:host_xcodes,指定当前工程使用的 Xcode 版本。

头条的经验

本文主要介绍了头条迁移至 Bazel 的历程,包括构建系统的架构分层和接入方案,以及结合 Bazel 的特性来优化工程配置的管理。头条迁移至 Bazel 后,研发效率和构建稳定性都有显著提升。由于采用了 Monorepo 全源码的集成方案,头条也加快了依赖治理和架构演进方向的工作进展。然而,由于篇幅限制,本文只介绍了迁移 Bazel 过程中的部分细节,而 Infra 团队在这一路的探索远不止于此。在本系列的文章中,我们将继续介绍头条在本地研发IDE和开发流程迁移方面所面临的挑战。

作者:App Infra

来源:微信公众号:字节跳动终端技术

出处:https://mp.weixin.qq.com/s/ajkOP7yeSYCcE0MoUUmUZA

继续阅读