28、构建系统最佳实践:代码组织、命名规范、构建文件维护、团队协作
各位同学,今天我们来聊聊构建系统的「软实力」。
说实话,很多团队能把 Soong 和 Bazel 用起来,但项目依然乱成一锅粥。为什么?因为工具只是骨架,规范和习惯才是血肉。我在 AOSP 大厂待了几年,见过太多「能跑但没法维护」的构建系统。今天就把我踩过的坑和总结的经验,一次性倒给你们。
一、代码组织:别让模块「裸奔」
代码怎么放,直接决定了构建系统的复杂度。我个人习惯遵循一个原则:一个模块一个目录,目录深度不超过 4 层。
1.1 模块目录结构模板
packages/apps/MyApp/
├── Android.bp
├── src/
│ ├── main/
│ │ ├── java/
│ │ └── res/
│ └── test/
├── libs/
│ └── mylib/
│ └── Android.bp
└── tests/
└── Android.bp
你想想看,如果每个模块都这么放,构建文件查找起来多省心。我在项目中遇到过有人把 5 个模块塞进同一个目录,结果 Android.bp 里写了 200 行条件判断——那简直是灾难。
我曾经接手过一个项目,所有模块的 Android.bp 都放在根目录下。每次改一个模块,都要全局搜索依赖关系。后来我花了整整两天才把它们拆开。记住:Android.bp 应该和模块代码放在一起,别偷懒。
二、命名规范:让构建文件「自解释」
命名这件事,说白了就是「让别人一眼看懂你在干什么」。我见过太多 my_module、test_lib 这种名字,过两周自己都忘了是啥。
2.1 模块命名规则
| 类型 | 命名格式 | 示例 |
|---|---|---|
| 应用模块 | app_<名称> |
app_settings |
| 静态库 | lib_<名称>_static |
lib_network_static |
| 共享库 | lib_<名称> |
lib_crypto |
| 测试模块 | test_<模块名> |
test_app_settings |
| 头文件库 | headers_<名称> |
headers_common |
嗯,这里要注意:不要用缩写。比如 lib_comm 到底是 communication 还是 common?我吃过这个亏,后来统一要求全拼。
2.2 变量和属性命名
- 布尔变量:用
enable_或use_开头,比如enable_debug - 路径变量:用
_dir或_path结尾,比如output_dir - 版本号:统一用
_version,别混用_ver或_v
在 Android.bp 文件头部加一段注释,写明这个模块的用途、依赖方和维护人。比如:
// Module: app_settings - 系统设置应用,依赖 lib_common, lib_ui这样任何人打开文件,3 秒内就能知道全貌。
三、构建文件维护:别让 Android.bp 变成「屎山」
构建文件也是代码,需要持续重构。我见过最夸张的一个 Android.bp 有 800 行,里面塞满了条件判断和重复定义。
3.1 保持文件短小
一个 Android.bp 文件建议控制在 100 行以内。如果超过,说明模块职责不单一。拆!
3.2 使用模板减少重复
// 定义通用模板
cc_defaults {
name: "my_lib_defaults",
cflags: ["-Wall", "-Werror"],
include_dirs: ["include"],
shared_libs: ["liblog"],
}
// 实际模块引用模板
cc_library {
name: "lib_foo",
defaults: ["my_lib_defaults"],
srcs: ["foo.c"],
}
cc_library {
name: "lib_bar",
defaults: ["my_lib_defaults"],
srcs: ["bar.c"],
}
你看,这样改一个地方,所有模块都生效。我在项目中用这个模式,把 300 行的构建文件压缩到了 80 行。
3.3 版本控制与变更记录
每次修改 Android.bp,都要在 commit message 里写明:改了哪个模块、为什么改、影响范围。我见过有人只写「fix build」,结果回滚时根本不知道改了什么。
用
build/make/tools/buildbot/check_build_file.py 做静态检查。它能发现未使用的变量、重复的依赖、格式问题。我每次提交前都会跑一遍,省了不少事。
四、团队协作:构建系统是「公共厕所」
这个比喻可能不太雅,但道理一样:每个人都要用,但没人愿意打扫。所以必须定规矩。
4.1 代码审查中的构建文件检查
- 必须审查 Android.bp:不能只看 Java/C++ 代码,构建文件改了什么也要看
- 检查依赖是否合理:有没有引入不必要的模块?有没有循环依赖?
- 确认命名规范:新模块名字是否符合团队约定?
4.2 构建缓存与增量编译
团队协作时,最怕「我本地能编译,你那边就不行」。我建议统一使用 ccache 和 distcc,并共享构建缓存目录。
# 在 .bashrc 中设置
export USE_CCACHE=1
export CCACHE_DIR=/mnt/ssd/ccache
ccache -M 50G # 设置缓存大小
说实话,这个配置能节省 30% 以上的编译时间。尤其是团队里有人频繁改头文件时,效果更明显。
4.3 构建失败的处理流程
- 先看自己的修改:是不是改了 Android.bp 但没同步?
- 检查依赖模块:是不是别人改了你的依赖?
- 清理重试:
make clean && make能解决 80% 的玄学问题 - 看构建日志:别只看最后几行,从第一个 error 开始看
有一次团队里有人改了公共头文件,但没更新 Android.bp 中的
export_include_dirs。结果 5 个人同时编译失败,排查了整整半天。后来我们规定:改公共头文件必须同步更新构建文件,并在代码审查中强制检查。
五、知识体系总览
下面这张图,是我对本章内容的总结。你可以把它当作「构建系统最佳实践」的检查清单。
好了,以上就是构建系统最佳实践的全部内容。说白了,这些经验都是我用加班和踩坑换来的。你如果能从一开始就养成这些习惯,后面会省很多事。
记住:好的构建系统,是让团队感觉不到它的存在。它默默工作,不拖后腿,不制造惊喜。嗯,这就是我们追求的境界。