【Android构建系统】了解Soong构建系统
背景介绍
在Android7.0之前,Android使用GNU Make描述和执行build规则。Android7.0引入了Soong构建系统,弥补Make构建系统在Android层面变慢、容易出错、无法扩展且难以测试等缺点。
Soong利用Kati GNU Make克隆工具和Ninja构建系统组件来加速Android的构建。
下面从两个方面来了解Soong构建系统:
- 使用Soong构建系统构建自己模块要熟悉的两个内容,即.bp和.go。
- Soong构建系统和Make构建系统的差异。
1.Soong构建系统中.bp和.go的关系
在Android的Soong构建系统中,.bp (Blueprint)文件和.go(Golang)文件共同协作,关系大概如下:
1. .bp文件,声明式构建配置
.bp文件用于定义模块及其属性,采用类似JSON的声明式语法,描述模块的构建规则(如源文件、依赖项、编译选项等)
.bp文件不支持条件语句或控制流,仅用于静态配置。
Soong预定义了模块类型,如cc_library,cc_binary, java_library等。
例如,Android15源码中Camera HAL AIDL Demo中的一段:
//hardware/google/camera/common/hal/aidl_service/Android.bp
cc_binary {name: "android.hardware.camera.provider@2.7-service-google",defaults: ["hardware_camera_service_defaults","camera_service_eager_hal_defaults",],init_rc: ["android.hardware.camera.provider@2.7-service-google.rc],
}
2. .go文件,逻辑处理与扩展
.go文件用于实现复杂的构建逻辑,例如
- 解析.bp文件并生成Ninja构建规则
- 处理条件编译、环境变量、动态依赖等无法在.bp中直接表达的逻辑
- 扩展模块类型或自定义构建行为(如通过Go的发射机制)
使用GO语言编写,灵活性高,可调用Android构建系统的底层API。
例如,通过LoadHook动态修改模块属性:
func myHook(ctx android.LoadHookContext) {if ctx.AConfig().IsEnvTrue("USE_FEATURE_X") {ctx.AppendProperties(map[string]interface{}{"cflags": ["-DFEATURE_X"]})}
}
3.协作关系
.bp负责“做什么”(声明模块和属性), .go负责“怎么做”(实现复杂逻辑和生成构建规则)
转换流程:
- Soong解析所有.bp文件,生成模块依赖图。
- .go代码处理模块间的动态逻辑(如根据产品配置选择源码)。
- 最终转换为Ninja可执行的构建规则。
4.协作的典型例子1-条件编译
在.bp中通过arch或target(.go中预定义的变量)分平台配置,而跨模块的全局条件(如产品型号)需要在.go中实现。
cc_library {srcs: ["generic.cpp"],arch: { arm: { srcs: ["arm.cpp] } },
}
如果arch是arm, srcs是arm.cpp。arch是soong中预定义的变量,用于分平台构建。
5.协作的典型例子2-模块级别控制
例如, Android15 Camera HAL AIDL Demo构建中的一段:
gch_lazy_hal_cc_defaults {name: "camera_service_eager_hal_defaults",enabled: true,soong_config_variables: {use_lazy_hal: {enabled: false,},},
}gch_lazy_hal_cc_defaults {name: "camera_service_lazy_hal_defaults",enabled: false,soong_config_variables: {use_lazy_hal: {enabled: true,},},
}
6.总结
.bp文件 | .go文件 | |
语法 | 声明式,类JSON | 命令式,Go语言 |
灵活性 | 有限,无逻辑控制 | 高,可编程 |
适用场景 | 模块定义、静态依赖 | 动态逻辑、构建规则扩展 |
维护者 | 开发者 | 构建系统工程师/高级开发者 |
2.Soong构建系统和Make构建系统的差异
这两种构建系统在设计理念、语法结构和执行效率等方面又着显著的差异。以下从多个维度对这两种构建系统进行对比分析。
基本概念与发展背景
Make构建系统
- Android早期使用的构建系统,基于GNU Make实现
- 通过Android.mk文件定义构建规则
- 随着Android项目复杂度增加,逐渐暴露出性能瓶颈和可维护性问题
Soong构建系统
- Android 7.0(Nougat)引入的新构建系统,旨在取代Make
- 使用Android.bp文件(Blueprint格式)定义构建规则
- 采用Go语言编写,与Kati和Ninja构建系统组件配合使用
- 设计目标式解决Make系统在大型项目中的性能瓶颈
语法结构与配置方式
Make语法特点
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libexample
LOCAL_SRC_FILES := example.cpp
include $(BUILD_SHARED_LIBRARY)
- 命令式语法,包含变量定义和流程控制
- 需要手动管理依赖关系
- 基于shell命令执行构建任务
Soong语法特点
cc_library_shared {name: "libexample",srcs: ["example.cpp"],
}
- 声明式语法,类似JSON格式
- 不支持条件语句和控制流,复杂逻辑由Go代码处理
- 强类型变量和属性系统
- 更简洁直观的模块定义方式
系统架构与工作原理
Make系统架构
- 串行构建方式,依赖关系手动指定
- 扩展性差,配置复杂度随项目规模增长而急剧上升
- 增量构建能力有限
Soong系统架构
- 自动分析模块依赖关系并生成构建图
- 支持并行构建,充分利用多核CPU
- 高效的增量构建机制,仅重新编译变更部分
- 模块化设计,易于扩展
性能与效率对比
构建速度
- Make系统在大型项目上构建速度较慢,特别是全量构建时
- Soong通过并行构建和精确的依赖分析,显著提升构建速度
资源利用率
- Make系统资源利用率低,主要受限于串行执行
- Soong能充分利用现代多核处理器,实现高效资源利用
增量构建
- Make需要开发者手动维护依赖关系,增量构建不可靠
- Soong自动跟踪文件变更,增量构建精确高效
功能特性对比
特性 | Make构建系统 | Soong构建系统 |
语法类型 | 命令式 | 声明式 |
依赖管理 | 手动指定 | 自动处理 |
并行构建 | 有限支持 | 原生支持 |
增量构建 | 基本支持 | 高效支持 |
扩展性 | 有限 | 高度可扩展 |
跨平台 | 需要适配 | 原生支持 |
学习曲线 | 相对简单 | 较陡峭 |
条件编译 | 支持 | 通过Go代码实现 |
模块化 | 有限 | 高度模块化 |
与Android集成 | 逐渐淘汰 | 深度集成 |
实际应用场景
适合使用Make的场景
- 维护旧的Android项目代码
- 小型项目或原型开发
- 需要与现有Make系统集成的场景
适合使用Soong的场景
- 新的Android平台开发
- 大型复杂项目
- 需要高效并行构建的环境
- 需要精确增量构建的项目
迁移于兼容性
- Soong设计时考虑了与Make系统的兼容性
- 提供androidmk工具可将Android.mk转换为Android.bp
- 过渡期间采用混合模式:Make由Kati解析生成ninja文件,再与Soong的ninja文件合并
- 完全迁移到Soong是Android构建系统的未来方向
Make构建系统作为Android早期的构建解决方案,已经逐渐无法满足现代大型项目的需求。Soong构建系统通过声明式配置、自动依赖管理和并行构建等特性,显著提升了Android项目的构建效率和可维护性。
Make和Soong构建系统的使用建议
- 新项目应直接采用Soong构建系统
- 旧项目可逐步将Android.mk转换为Android.bp
- 复杂构建逻辑可通过Go扩展实现
- 充分利用Soong提供的工具链(bpfmt等)提高开发效率
随着Android生态的发展,Soong构建系统将持续演进,可能最终向Bazel构建系统过渡,但其核心优势仍将在Android构建领域发挥重要作用。