高德引擎构建及持续集成技术演进之路
01 背景
由于导航应用中的地图渲染、导航等核心功能对性能要求很高,所以高德地图客户端中大量功能采用 C++ 实现。随着业务的飞速发展,仅地图引擎库就有40多个模块,工程配置极其复杂,原有的构建及持续集成技术已无法满足日益增长的需求变化。
除了以百万计的代码行数带来的复杂度外,高德地图客户端中的 C++ 引擎库工程(以下简称引擎库)的构建和持续集成还面临以下几个挑战:
- 支持多团队协作:多团队意味着多操作系统多 IDE ,降低不同操作系统和不同 IDE 下的工程配置的难度是重点要解决的难题之一;
- 支持多业务线定制:引擎库为手机、车机、开放平台等业务线提供支持,而各个业务线的诉求不同,所以需要具备按功能构建的能力;
- 支持车机环境:在诸多业务线中,高德地图有一个非常特殊的业务线,即车机(AMAP AUTO)。车机直接面对各大车厂和众多设备商,环境多为定制化,构建工具链各式各样。如果针对每个车机环境都定制一套构建配置文件,那么其维护成本将非常高,所以如何用一套构建配置满足车机的多样化构建需求成为亟需解决的问题;
此外,由于历史原因,引擎库中源码和依赖库混杂,都存放于 Git 仓库中,这样会带来两个问题:
- 随着构建次数不断增加,Git 仓库越来越大,代码与依赖库检出越来越慢,极大影响本地开发以及打包效率;
- 缺乏统一管理,依赖关系混乱,经常出现因为依赖问题而导致的构建失败,或者虽然构建成功但运行时发生错误的情况;
上述的挑战和历史遗留问题严重阻碍了研发效能的提升。为此,我们对现有的构建及持续集成工具进行了深入的研究和分析,并结合自身的业务特性,最终发展出高德地图 C++ 本地构建工具 Abtor 和持续集成工具 Amap CI 。
02 本地构建
现有工具分析
C++ 是一门靠近底层的语言。不同的硬件、操作系统、编译器,再加上交叉编译,导致 C++ 构建的难度非常高。针对这些问题,C++ 社区涌现出许多优秀的构建工具,比如大名鼎鼎的 Make 和 CMake 。
Make,即 GNU Make ,于1988年发布,是一个用来执行 Makefile 的工具。Makefile 的基本语法包括目标、依赖和命令等。使用过程中,当某些文件变了,只有直接或者间接依赖这些文件的目标才需要重新构建,这样大大提升了编译速度。
Make 和 Makefile 的组合可以看作项目管理工具,但它们过于基础,在跨平台的使用方面有很高的门槛和较多的限制,此外大项目的构建还会遇到 Makefile 严重膨胀的问题。
CMake 产生于2000年,是一个跨平台的编译、测试以及打包工具。它将配置文件转化为 Makefile ,并运行 Make 命令将源码编译成可执行程序或库。CMake 属于 Make 系列,配置文件比 Makefile 具有可读性,支持跨平台构建,构建性能高。
但是 CMake 也有两项明显不足,一是配置文件的复杂度远高于其它现代语言,对于 CMake 语法初学者有一定的学习成本,二是与不同 IDE 的配合使用不够友好。
可以看出 Make 和 CMake 的抽象度还是比较低,从而对构建人员的要求过高。为了降低构建成本,C++ 社区又出现了一些新的 C++ 构建工具,现在使用较广泛的包括 Google 的 Bazel 和 Ninja ,以及 SCons 。这些工具的特点和不足如下:
经过上述对现有 C++ 构建工具的研究和分析,可以得出每个工具既有所长又有不足的结论。再考虑到高德地图引擎库工程面临的挑战和历史遗留问题,我们发现以上工具没有一个可以完美契合业务需求,且改造成本非常高,所以我们决定基于 CMake 自建 C++ 本地构建工具,即现在引擎库工程使用的 Abtor 。
Abtor
首先,我们需要解释一个问题,即 Abtor 是什么?
Abtor 是一个 C++ 跨平台构建工具。Abtor 采用 Python 编写构建脚本,生成 CMake 配置文件,并通过内置 CMake 组件生成构建文件,最终产出可执行程序或库。它抽象出构建描述,使得复杂的编译器和连接器对开发者透明;它提供强大的内置功能,从而有效的降低开发者编写构建脚本的难度。
其次,我们需要阐述一个问题,即Abtor的构建流程是什么?
如上图所示,Abtor 构建的整个流程为:
- 编写 Abtor 构建脚本;
- 解析 Abtor 构建脚本;
- 检测依赖关系,识别冲突,并从阿里 OSS 上下载所需依赖;
- 生成CMakeLists.txt,并通过内置的 CMake 生成 Makefile 文件;
- 编译,链接,生成对应平台的目标文件;
- 将目标文件发布到阿里 OSS ;
除此之外,还增加了控制访问发布库权限的功能,用于保证发布库的安全。
最后,我们需要探讨一个问题,即Abtor解决了什么?
在开篇背景中,我们提到阻碍研发效能的一些挑战和问题,这就是 Abtor 需要解决的,所以 Abtor 具备以下特点:
- 更广泛的跨平台:支持 MacOS 、iOS、Android、 Linux、Windows、QNX 等平台;
- 有效的多团队协作:较好得与 IDE 结合,并支持一套配置生成不同项目工程,从而达到工程配置一致化;
- 高定制化:支持工具链及构建参数的灵活定制,并通过内置工具链配置为车机复杂的构建提供强有力的支持;
- 源码与依赖分离:支持源码依赖与库依赖,源码通过Git管理,构建库存放于阿里云,源码与产物完全分离;
- 良好的构建性能:快速构建大型项目,从而提高开发效率;
从上述特点可看到,Abtor 有效地解决了已有的构建工具在高德业务中面临的痛点。但是冰冻三尺,非一日之寒,Abtor 也是在不断地完善中,下面重点介绍一下 Abtor 发展过程中遇到的三个问题。
工程配置一致化
在日常开发过程中,工程项目的调试工作尤为重要。高德地图客户端中的 C++ 引擎库工程的开发人员涉及几个部门和诸多小组。这些组擅长的技术栈,使用的平台和习惯的开发工具都大为不同。如果针对每一个平台都单独建立相应的工程配置,那么工作量及后续维护成本可想而知。
基于以上原因,Abtor 内置与 IDE 结合的功能,即开发者可以通过一套配置并结合 Abtor 命令一键生成工程配置,实现在不同平台的工程配置的一致化。工程配置一致化为引擎库开发带来以下几个收益:
- 命令简单,降低学习成本,开发者只需熟记 abtorw project [IDE name];
- 配置文件不会因为 IDE 的增加而迅速膨胀,开发者更换构建命令,比如 abtorw project xcode 或者abtorw project vs2015,即可生成对应的项目工程;
- 有利于部门间的协作及新人的快速融入,开发者可以根据喜好选择 IDE 进行开发,大大提高开发效率;
- 目前Abtor支持的IDE有 Xcode、Android Studio、Visual Studio、Qt Creator、CLion等。
复杂车机环境的构建
作为高德地图一条非常重要的业务线,车机面对的构建环境复杂多变,厂商往往会自行定制工具链。如果每接入一个设备,所有工程项目都需要修改配置文件,那么这个成本还是非常高的。为了解决这个问题,Abtor 提供两种做法:
- 内置工具链配置:对于开发者完全透明,他不需要修改任何配置即可构建相应平台的产物;
- 支持自定义配置插件:开发者按照规则编写配置插件,构建时 Abtor 会检测插件,并根据设置的工具链及构建参数进行构建;
除此之外,我们对所有的车机环境进行了 Docker 化处理,并通过 Docker 控制中心统一管理车机 Docker 环境的上线与下线,再利用上述 Abtor 的内置工具链配置功能内置车机构建参数,实现开发者无感知的环境切换等操作,有效地解决了复杂车机环境的构建问题。
基于 Docker 的车机构建主要步骤如下:
- 工具链安装:一般由厂商提供,我们会将该工具链安装到基础 Docker 镜像中;
- Docker 发布:将镜像发布到 Docker 仓库;
- Abtor 适配:一次性适配工具链,并内置配置,开发者可通过 Abtor 版本升级使用该配置;
- 服务配置更新:由 Jenkins 管理,支持分批更新 Abtor 版本,不影响当下编译需求;
- 服务监控: 由 Jenkins 管理,定时检测服务状态,异常态的 Docker 服务将自动被重启;