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_moduletest_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 构建缓存与增量编译

团队协作时,最怕「我本地能编译,你那边就不行」。我建议统一使用 ccachedistcc,并共享构建缓存目录。

# 在 .bashrc 中设置
export USE_CCACHE=1
export CCACHE_DIR=/mnt/ssd/ccache
ccache -M 50G  # 设置缓存大小

说实话,这个配置能节省 30% 以上的编译时间。尤其是团队里有人频繁改头文件时,效果更明显。

4.3 构建失败的处理流程

  1. 先看自己的修改:是不是改了 Android.bp 但没同步?
  2. 检查依赖模块:是不是别人改了你的依赖?
  3. 清理重试make clean && make 能解决 80% 的玄学问题
  4. 看构建日志:别只看最后几行,从第一个 error 开始看
⚠️ 我曾经踩过的坑
有一次团队里有人改了公共头文件,但没更新 Android.bp 中的 export_include_dirs。结果 5 个人同时编译失败,排查了整整半天。后来我们规定:改公共头文件必须同步更新构建文件,并在代码审查中强制检查。

五、知识体系总览

下面这张图,是我对本章内容的总结。你可以把它当作「构建系统最佳实践」的检查清单。

构建系统最佳实践 代码组织 模块独立目录 深度 ≤ 4 层 Android.bp 就近存放 命名规范 模块名有前缀 变量名有后缀 禁止缩写 构建文件维护 文件 ≤ 100 行 使用模板减少重复 提交信息写清楚 团队协作 审查构建文件 共享缓存 失败处理流程 核心原则 1. 模块自治:一个模块一个目录,构建文件随代码走 2. 命名即文档:让名字自己说话,减少注释依赖 3. 持续重构:构建文件也要定期清理,别等变成屎山 4. 团队共识:定好规矩,用工具强制检查

好了,以上就是构建系统最佳实践的全部内容。说白了,这些经验都是我用加班和踩坑换来的。你如果能从一开始就养成这些习惯,后面会省很多事。

记住:好的构建系统,是让团队感觉不到它的存在。它默默工作,不拖后腿,不制造惊喜。嗯,这就是我们追求的境界。


公众号:蓝海资料掘金营,微信deep3321