Android 构建系统详解

Android 构建系统详解

深入理解 Android 构建系统是 Framework 开发的必备技能。本文详细讲解了 Android.mk 与 Android.bp 的语法、用户和迁移方案

一、构建系统的演变历史

1.1、Android 构建系统发展

版本 时间 构建系统
Android 1-6 2008-2015 纯 Make (Android.mk)
Android 7 2016 引入 Soong (Android.bp),与 Make 并存
Android 8 2017 Soong 成为主要构建系统
Android 10 2019 大部门模块迁移到 bp
Android 12 2021 bp 为主,mk 逐渐被废弃
Android 13+ 2022+ 引入 Bazel (实验性)

1.2、为什么从 mk 迁移到 bp

Make 的问题

  • ❌ 构建速度慢 - Make 是递归解析,效率低
  • ❌ 难以并行 - 依赖关系复杂,并行度受限
  • ❌ 语法复杂 - Makefile 语法晦涩,维护困难
  • ❌ 调试困难 - 错误信息不友好
  • ❌ 缺乏类型检查 - 容易出现拼写错误

Soong(bp)的优势

  • ✓ 构建速度快 - 使用 Ninja 作为后端
  • ✓ 高度并行 - 更好的依赖分析
  • ✓ 语法简洁 - 类似 JSON,易读易写
  • ✓ 类型安全 - 属性有类型检查
  • ✓ 更好的模块化 - 更清晰的依赖管理

1.3、构建系统架构

二、Android.mk 详解

2.1、基本结构

makefile 复制代码
# 每个 Android.mk 必须以此开头
LOCAL_PATH := $(callmy-dir)

# 清除之前的变量定义
include $(CLEAR_VARS)

# 模块名称
LOCAL_MODULE := my_module

# 源文件
LOCAL_SRC_FILES := \
	src/file1.cpp \
	src/file2.cpp

# 依赖的共享库
LOCAL_SHARED_LIBRARIES := \
	liblog \
	libutils
	
# 编译选项
LOCAL_CFLAGS := -Wall -Werror

# 构建目标类型
include $(BUILD_SHARED_LIBRARY)

2.2、核心变量讲解

变量 说明
LOCAL_PATH 当前目录路径,必须首先定义
LOCAL_MODULE 模块名称,唯一标识
LOCAL_SRC_FILES 源文件列表
LOCAL_C_INCLUDES C/C++ 头文件路径
LOCAL_CFLAGS C 编译选项
LOCAL_SHARED_LIBRARIES 依赖的动态库
LOCAL_STATIC_LIBRARIES 依赖的静态库
LOCAL_CERTIFICATE 签名证书:platform, shared, media
LOCAL_PRIVILEGED_MODULE 是否为特权应用

2.3、构建目标类型

目标 说明
BUILD_SHARED_LIBRARY 编译成动态库(.so)
BUILD_STATIC_LIBRARY 编译成静态库(.a)
BUILD_EXECUTABLE 编译成可执行文件
BUILD_PACKAGE 编译为 APK
BUILD_JAVA_LIBRARY 编译为 Java 库 (.jar)
BUILD_PREBUILT 预编译模块

2.4、完整示例

示例1:编译动态库(.so)

makefile 复制代码
LOCAL_PATH := $(callmy-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := libmyutils
LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := \
	src/utils.cpp \
	src/helper.cpp
	
LOCAL_C_INCLUDES := 、
	$(LOCAL_PATH)/include
	
LOCAL_SHARED_LIBRARIES := \
	liblog \
	libutils
	
LOCAL_CFLAGS := -Wall -Werror
LOCAL_CPPFLAGS := -std=c++17

include $(BUILD_SHARED_LIBRARY)

示例二:编译 APK 应用

make 复制代码
LOCAL_PATH := $(callmy-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := MyApp
LOCAL_SRC_FILES := $(callall-java-files-under, src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_MANIFEST_FILE := AndroidManifest.xml
	
# 使用平台签名
LOCAL_CERTIFICATE := platform
# 放入 priv-app 目录
LOCAL_PRIVILGED_MODULE := true
# 使用平台私有 API
LOCAL_PRIVATE_PLATFORM_APIS := true

include $(BUILD_PACKAGE)

2.5、常用函数

函数 说明
$(call my-dir) 返回当前目录路径
$(call all-java-files-under, dir) 获取目录下所有 Java 文件
$(call all-cpp-files-under, dir) 获取目录下所有 C++ 文件
$(wildcard pattern) 通配符匹配文件

参考: https://blog.csdn.net/qq_45146250/article/details/134327011?spm=1001.2014.3001.5502

三、Android.bp 详解

3.1、基本语法

Android.bp 使用类似 JSON 的语法(Blueprint 语言),更简洁易读

makefile 复制代码
// 模块定义
module_type {
	name:"module_name",
	property1:"value1",
	property2:["list", "of", "values"],
	property3:true,
	nested_property: {
		key: "value1",
	}
}

3.2、核心模块类型

模块类型 说明
C/C++模块
cc_library_shared C/C++动态库(.so)
cc_library_static C/C++静态库(.a)
cc_binary C/C++可执行文件
cc_library_headers 仅头文件库
Java 模块
java_library Java库
java_binary Java可执行文件

Android 模块 android_app Android 应用(APK) android_library Android库(AAR) 其他 filegroup 文件分组 genrule 自定义生成规则

3.3、常用属性详解

属性 说明
name 模块名称(必需,全局唯一)
srcs 源文件列表
local_include_dirs 本地头文件目录
export_include_dirs 导出的头文件目录
shared_libs 依赖的动态库
static_libs 依赖的静态库
clfags C 编译选项
defaults 继承的默认配置
certificate 签名证书(APK)
platform_apis 使用平台 API(APK)
privileged 特权应用(APK)

3.4、完整示例

示例1:编译动态库 (cc_library_shared)

makefile 复制代码
cc_library_shared {
    name:"libmyutils",

    srcs: [
        "src/utils.cpp",
        "src/helper.cpp",
    ],

    local_include_dirs: ["include"],
    export_include_dirs: ["include"],

    shared_libs: [
        "liblog",
        "libutils",
        "libbase",
    ],

    cflags: ["-Wall","-Werror"],
    cppflags: ["-std=c++17"],

    vendor_available:true,
}

示例2:编译 Android 应用 (android_app)

makefile 复制代码
android_app {
    name:"MySystemApp",

    srcs: ["src/**/*.java"],
    resource_dirs: ["res"],
    manifest:"AndroidManifest.xml",

    // 使用平台签名
    certificate:"platform",
    // 特权应用
    privileged:true,
    // 使用平台私有 API
    platform_apis:true,

    static_libs: [
        "androidx.appcompat_appcompat",
    ],
}

示例3:使用 defaults 简化配置

makefile 复制代码
// 定义默认配置
cc_defaults{
    name:"my_project_defaults",
    cflags: ["-Wall","-Werror"],
    shared_libs: ["liblog","libutils"],
}

// 使用默认配置
cc_library_shared{
    name:"libmodule1",
    defaults: ["my_project_defaults"],
    srcs: ["module1.cpp"],
}

3.5、多条件配置

makefile 复制代码
cc_library_shared {
    name:"libconditional",
    srcs: ["common.cpp"],

    // 针对不同架构
    arch: {
        arm: {srcs: ["arm/*.cpp"] },
        arm64: {srcs: ["arm64/*.cpp"] },
        x86_64: {srcs: ["x86_64/*.cpp"] },
    },

    // 针对不同目标
    target: {
        android: {cflags: ["-DANDROID"] },
        host: {cflags: ["-DHOST"] },
    },
}

四、mk 与 bp 对比

4.1、语法对比

Android.mk Android.bp
LOCAL_PATH := $(call my-dir) // 不需要,自动推断
include $(CLEAR_VARS) // 不需要
LOCAL_MODULE := mylib name: "mylib",
LOCAL_SRC_FILES := a.cpp b.cpp srcs: ["a.cpp", "b.cpp"],
LOCAL_SHARED_LIBRARIES := liblog shared_libs: ["liblog"],
LOCAL_CFLAGS := -Wall cflags: ["-Wall"],
include $(BUILD_SHARED_LIBRARY) cc_library_shared {...}

4.2、变量名映射表

Android.mk Android.bp
LOCAL_MODULE name
LOCAL_SRC_FILES srcs
LOCAL_C_INCLUDES local_include_dirs
LOCAL_EXPORT_C_INCLUDE_DIRS export_include_dirs
LOCAL_SHARED_LIBRARIES shared_libs
LOCAL_STATIC_LIBRARIES static_libs
LOCAL_CERTIFICATE certificate
LOCAL_PRIVILEGED_MODULE privileged
LOCAL_PRIVATE_PLATFORM_APIS platform_apis
LOCAL_INIT_RC init_rc

五、常用模块类型

5.1、C/C++ 模块

makefile 复制代码
// 仅编译动态库
cc_library_shared{name:"libfoo_shared",srcs: ["foo.cpp"], }

// 仅编译静态库
cc_library_static{name:"libfoo_static",srcs: ["foo.cpp"], }

// 同时编译动态库和静态库
cc_library{name:"libfoo",srcs: ["foo.cpp"], }

// 仅头文件库
cc_library_headers{name:"libfoo_headers",export_include_dirs: ["include"], }

// 可执行文件
cc_binary{name:"foo_bin",srcs: ["main.cpp"], }

5.2、Android 应用模块

makefile 复制代码
// 系统特权应用
android_app{
    name:"MySystemApp",
    srcs: ["src/**/*.java"],
    resource_dirs: ["res"],
    manifest:"AndroidManifest.xml",
    platform_apis:true,
    certificate:"platform",
    privileged:true,
    system_ext_specific:true,
}

5.3、预编译模块

makefile 复制代码
// 预编译动态库
cc_prebuilt_library_shared{
    name:"libprebuilt",
    srcs: ["libs/libprebuilt.so"],
}

// 预编译 JAR
java_import{
    name:"prebuilt-jar",
    jars: ["libs/library.jar"],
}

// 预编译配置文件
prebuilt_etc{
    name:"my_config",
    src:"config/my.conf",
    sub_dir:"myapp",
}

六、实例

案例一:添加新的系统服务

frameworks/base/services/core/java/.../myservice/ ├── Android.bp ├── MyService.java └── IMyService.aidl

makefile 复制代码
java_library {
    name: "services.myservice",
    srcs: [
        "java/**/*.java",
        ":myservice-aidl",
    ],
    libs: ["services.core", "framework"],
}

filegroup {
    name: "myservice-aidl",
    srcs: ["aidl/**/*.aidl"],
}

案例二:添加新的 Native 服务

makefile 复制代码
cc_binary {
    name: "myservice",
    srcs: ["MyService.cpp", "main.cpp"],

    shared_libs: [
        "libbase",
        "liblog",
        "libutils",
        "libbinder",
    ],
    
    init_rc: ["myservice.rc"],
    vendor:true,
}

七、迁移指南

7.1、使用 androidmk 工具自动转换

makefile 复制代码
# 编译 androidmk 工具
cd $AOSP_ROOT
source build/envsetup.sh
m androidmk

# 使用工具转换
androidmk Android.mk > Android.bp

# 工具位置
out/host/linux-x86/bin/androidmk

7.2、手动迁移注意事项

条件编译转换

mk: ifeq ($(TARGET_ARCH), arm64)

bp: arch: { arm64: { ... } }

不能转换的情况

• 复杂的条件逻辑

• 动态生成源文件列表

• 外部脚本调用 → 使用 genrule

八、调试技巧

8.1 常用调试命令

makefile 复制代码
# 显示详细命令
m showcommands

# 单独编译某个模块,显示详细信息
m <module_name> showcommands 2>&1 | tee build.log

# 检查 bp 语法错误
out/soong/host/linux-x86/bin/bpfmt -d Android.bp

8.2 常见错误处理

错误 解决方案
module 'xxx' not found 检查依赖模块名是否正确
property 'xxx' is not supported 检查属性名是否正确
duplicate module 'xxx' 模块名全局唯一,检查重复
unrecognized property 属性名拼写错误或不支持
  1. Android 12 以 Android.bp 为主Android.mk 逐渐废弃

  2. bp 语法类似 JSON,更简洁、更易维护

  3. 模块类型决定编译输出:cc_library_shared → .so

  4. 使用 defaults 复用配置,减少重复

  5. 条件编译使用 arch/target 等属性块

  6. 可使用 androidmk 工具辅助迁移

记住这个对应关系

.so 库 cc_library_shared
.a 库 cc_library_static
可执行文件 cc_binary
APK android_app
JAR java_library
相关推荐
安卓程序员_谢伟光2 小时前
安卓内存分析
android·jvm
符哥20088 小时前
新能源智能充电桩与 Android/iOS App 蓝牙通信协议
android·ios
JMchen1239 小时前
自定义View性能优化:从60fps到120fps的进阶之路
android·经验分享·性能优化·kotlin·自定义view
vistaup9 小时前
DevEco Studio 鸿蒙 HAR本地引入相互依赖问题解决
android·华为·harmonyos
常利兵10 小时前
Android 开发秘籍:用Tint为Icon动态变色
android
奔跑吧 android10 小时前
【车载audio】【CarAudioService 05】【车载 Android 系统调试深度指南:解析 dumpsys car_service】
android·audio·audioflinger·aosp15·车载音频·车载audio·car_service
shuangrenlong10 小时前
androidstudio gradle文件报红
android
Digitally10 小时前
如何通过蓝牙将 iPhone 上的照片传输到 Android
android·ios·iphone
常利兵10 小时前
Android Intent.setAction失效报错排查与修复全方案
android