深入解析RK Android构建系统,从环境搭建到模块编译全掌握

电子说

1.4w人已加入

描述

今天给大家带来一份超详细的Android构建系统实战指南,不管是刚接触Android编译的新手,还是想优化编译流程的老司机,这份内容都能帮你理清思路、避坑提效。话不多说,直接上干货!

本节学习目标

学完本章后,你将能够回答:

Android 构建系统的三个核心组件是什么?

./build.sh -UKAop 每个参数的含义是什么?

如何初始化编译环境?

useruserdebugeng 三种构建变体的区别?

如何单独编译一个模块?

如何用 ccache 加速编译?

1.1 构建系统是什么

1.1.1 概念:构建系统的作用

构建系统就是把我们的源码变成可运行的镜像文件的完整工具链。

1.1.2 Android 构建系统的演进

Android

三者关系图

Android

简单理解:

Android.bp = 新版菜谱(语法简洁,推荐)

Android.mk = 旧版菜谱(兼容旧代码)

Ninja = 高效厨师(替代了 GNU Make

Soong = 菜谱翻译器(把 Android.bp 翻译给 Ninja

1.1.3 Android 编译产物

编译完成后,你会得到以下文件(在out/target/product/rk3576_u/ 目录下):

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
treeout/target/product/rk3576_u/├── boot.img               内核 + ramdisk (启动必需)├── dtbo.img               设备树 Overlay├── vbmeta.img             AVB 验证元数据├── super.img              system + vendor + odm + product 的合集   ├── system.img         Android Framework系统应用   ├── vendor.img         HALvendor 库Init 脚本   ├── odm.img            ODM 定制内容   └── product.img        产品特定内容└── userdata.img           用户数据分区 (f2fs)

这些镜像文件最终会被rockdev/Image-rk3576_u/ 目录链接或复制,用于烧写到设备。

实战:查看编译产物

  •  
  •  
  •  
  •  
  •  
  •  
# 进入输出目录cd out/target/product/rk3576_u/# 查看镜像文件大小ls -lh *.img# 查看 system.img 的文件系统类型file system.img

1.2 编译环境准备

1.2.1 系统要求

Android

涉及的文件:

build.sh:99 - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64

1.2.2 安装依赖包

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# Ubuntu 20.04/22.04 依赖安装sudo apt-get updatesudo apt-get install -y     git-core gnupg flex bison build-essential     zip curl zlib1g-dev libc6-dev libncurses5-dev     x11proto-core-dev libx11-dev lib32z1-dev     libgl1-mesa-dev libxml2-utils xsltproc unzip     python3 python-is-python3 openjdk-8-jdk     bc ccache# 验证 Java 安装java -version# 应该输出 openjdk version "1.8.x"# 验证 Python 安装python3 --version

1.2.3 常见错误及解决

错误信息

原因

解决

build/envsetup.sh: line xx: java: command not found

未安装 JDK

sudo apt-get install openjdk-8-jdk

flex: command not found

缺少 flex

sudo apt-get install flex

No space left on device

磁盘空间不足

清理磁盘或扩展分区

Out of memory error

内存不足

减少 -J 参数,如 ./build.sh -A -J8

1.3 build.sh 主构建脚本

1.3.1 build.sh 是什么

build.sh  Rockchip 封装的编译入口脚本,它:

1.解析命令行参数

2.调用 U-BootKernelAndroid 各自的编译流程

3.打包最终镜像

涉及的文件:

build.sh - 主构建脚本

1.3.2 参数逐一解析

build.sh 接受的参数如下:

  •  
./build.sh [-U] [-C] [-K] [-A] [-B] [-p] [-o] [-u] [-v VERSION] [-d DTS] [-V VERSION] [-J JOBS]

参数

含义

触发编译

说明

-U

编译 U-Boot

u-boot/

Bootloader

-C

 Clang 编译内核

kernel-6.1/

默认 Clang

-K

编译内核

kernel-6.1/

包含 Clang

-A

编译 Android

frameworks/packages/hardware/

Framework + HAL + 应用

-B

编译 A/B 镜像

-

 A/B 分区设备使用

-p

打包镜像

-

 out/ 下的镜像复制到 rockdev/Image-xxx/

-o

编译 OTA 

-

生成 OTA 升级包

-u

编译 update.img

-

生成 RKDevTool 烧写包

-v

构建变体

-

user userdebug

-d

内核设备树

-

指定 DTS 文件名

-V

构建版本

-

版本号字符串

-J

并行任务数

-

默认 16

重要提示:这些参数可以组合使用!比如:

-UK = 编译 U-Boot + 内核

-UKA = 编译 U-Boot + 内核 + Android

-UKAp = 编译全部 打包镜像

1.3.3 实战:首次完整编译

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 步骤 1: 进入源码根目录cd z:/RK72/rk3576_android15# 步骤 2: 初始化环境source build/envsetup.sh# 步骤 3: 选择构建目标lunch rk3576_u-userdebug# 步骤 4: 开始完整编译./build.sh -UKAop# 等待编译完成... 首次编译可能需要 2-4 小时

1.3.4 常见编译场景

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 场景 1: 首次完整编译(所有组件)./build.sh -UKAop# 场景 2: 只修改了内核代码,重新编译内核./build.sh -Kp# 场景 3: 只修改了 Framework/HAL 代码,重新编译 Android./build.sh -Ap# 场景 4: 完整编译 + OTA + update.img./build.sh -UKAopu# 场景 5: 指定构建变体和 DTS./build.sh -UKAp -v userdebug -d rk3576-evb1-v10

1.3.5 编译日志解读

编译过程中,日志会输出到终端。关键信息:

  •  
  •  
  •  
[  0% 1/23456] ...                     ← 进度指示 (百分比 + 文件序号/总数)ninja: build stopped: subcommand failed.  ← 编译失败build completed successfully          ← 编译成功

如果编译失败,看最后几行输出:

error: 后面跟的是错误原因

ninja: 后面跟的是失败的构建规则

1.3.6 案例分析:编译内存不足

症状:编译到 80% 时突然被 Killed,没有任何错误信息。

原因:系统内存不足,Linux 的 OOM Killer 杀掉了编译进程。

解决

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 方法 1: 减少并行任务数./build.sh -A -J8     # 用 8 个并行任务替代默认的 16 个# 方法 2: 增加 swapsudo fallocate -l 8G /swapfilesudo chmod 600 /swapfilesudo mkswap /swapfilesudo swapon /swapfile# 方法 3: 编译单个模块代替全量编译mmm hardware/rockchip/audio/

1.4 envsetup.sh + lunch

1.4.1 概念:环境初始化做了什么?

  •  
  •  
  •  
  •  
  •  
  •  
# 第一步:加载构建环境source build/envsetup.sh# 输出类似:# including device/rockchip/rk3576/rk3576_u/vendorsetup.sh# including device/rockchip/common/vendorsetup.sh# ...

envsetup.sh 做了两件事:

1.加载所有 vendorsetup.sh - 每个产品的 vendorsetup.sh 把自己的产品注册到 lunch 菜单

2.注册辅助命令 - 如 mmmmmmcrootcgrepgodir 

验证

  •  
  •  
# 查看注册了哪些辅助命令help | grep "^ - "

1.4.2 lunch 命令做了什么?

  •  
  •  
# 第二步:选择构建目标lunch rk3576_u-ap4a-userdebug

lunch 命令设置了以下环境变量(打印输出可以看到):

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
============================================PLATFORM_VERSION_CODENAME=RELPLATFORM_VERSION=15TARGET_PRODUCT=rk3576_uTARGET_BUILD_VARIANT=userdebugTARGET_BUILD_TYPE=releaseTARGET_ARCH=arm64TARGET_ARCH_VARIANT=armv8-aTARGET_CPU_VARIANT=genericTARGET_CPU_VARIANT_RUNTIME=cortex-a72TARGET_2ND_ARCH=TARGET_2ND_ARCH_VARIANT=HOST_ARCH=x86_64BUILD_ID=AP3A.240905.015OUT_DIR=outPRODUCT_OUT=out/target/product/rk3576_u============================================

关键变量解读

变量

含义

TARGET_PRODUCT

rk3576_u

产品名称,决定加载哪些配置

TARGET_BUILD_VARIANT

userdebug

构建变体(调试版本)

TARGET_ARCH

arm64

CPU 架构

TARGET_CPU_VARIANT_RUNTIME

cortex-a72

运行时 CPU 优化目标

PRODUCT_OUT

out/target/product/rk3576_u

编译输出目录

涉及的文件:

build/envsetup.sh - 环境初始化脚本

device/rockchip/rk3576/rk3576_u/rk3576_u.mk - 产品主配置

1.4.3 构建变体的区别

特性

user

userdebug

eng

用途

生产发布

开发调试

工程测试

root 权限

 (adb root)

ADB 调试

受限

完整

完整

调试符号

剥离

保留

保留

模块编译

精简

包含更多

全部

logcat

受限

完整

完整

编译优化

最优

较优

较少

SELinux

enforcing

enforcing

permissive

推荐:日常开发使用userdebug

1.4.4 常见错误

错误

原因

解决

lunch: command not found

未执行source build/envsetup.sh

先执行source build/envsetup.sh

lunch 找不到rk3576_u

产品配置未加载

检查vendorsetup.sh 是否存在

编译到一半变体不对

lunch 选错了

重新lunch rk3576_u-userdebug

1.5 模块编译

1.5.1 m/mm/mmm 命令区别

envsetup.sh 注册了三个编译命令:

命令

用法

说明

m

m -j16

从任意目录编译整个项目(类似make

mm

cd xxx && mm

编译当前目录的模块

mmm

mmm path/to/module

编译指定目录的模块

实战对比

  •  
  •  
  •  
  •  
  •  
# 方法 1: 使用 mmm 编译(推荐)mmm hardware/rockchip/audio/# 方法 2: 使用 mm 编译cd hardware/rockchip/audio/mm

1.5.2 实战:编译单个 HAL 模块

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 确保已初始化环境source build/envsetup.shlunch rk3576_u-userdebug# 编译音频 HALmmm hardware/rockchip/audio/# 编译 Camera HAL (AIDL)mmm hardware/rockchip/camera_aidl/# 编译显示合成器mmm hardware/rockchip/hwcomposer/# 编译电源 HALmmm hardware/rockchip/power_aidl/# 编译传感器 HALmmm hardware/rockchip/sensor/# 编译 Grallocmmm hardware/rockchip/libgralloc/

编译输出

  •  
  •  
95% 123/130] Linking C++ shared library out/.../audio.primary.rk30board.so[100% 130/130] Install: out/target/product/rk3576_u/vendor/lib64/hw/audio.primary.rk30board.so

1.5.3 编译后快速部署

修改了 HAL 代码后,不需要全量编译 烧写整个系统:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 步骤 1: 编译模块mmm hardware/rockchip/audio/# 步骤 2: 重新打包镜像(不重新编译)make snod          # 快速重新打包 system.imgmake vendorimage   # 重新打包 vendor.img# 步骤 3: 部署到设备adb rootadb remountadb push out/target/product/rk3576_u/vendor/lib64/hw/audio.primary.rk30board.so /vendor/lib64/hw/# 步骤 4: 重启服务adb shell stopadb shell start

1.5.4 案例分析:如何快速迭代 HAL 开发

场景:你在修改音频 HAL,需要反复编译、部署、测试。

低效做法(每次 30+ 分钟):

  •  
  •  
  •  
  •  
./build.sh -Ap          # 编译全部 Android → 慢./build.sh -p           # 打包镜像 → 慢烧写 vendor.img         # 烧写 → 慢重启设备                # 重启 → 慢

高效做法(每次 1-2 分钟):

  •  
  •  
  •  
  •  
mmm hardware/rockchip/audio/    # 只编译音频 HAL → 快adb root && adb remount         # 获取写入权限adb push out/.../audio.primary.rk30board.so /vendor/lib64/hw/  # 推送 → 快adb shell stop && adb shell start   # 重启服务 → 快

1.6 编译加速

1.6.1 ccache 原理和配置

ccache 是什么?
ccache (Compiler Cache) 会缓存编译结果。当你重新编译相同的文件时,直接使用缓存而不是重新编译。

效果

首次编译:无加速(需要建立缓存)

二次编译:加速 3-10 倍(取决于代码变更量)

配置步骤

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 启用 ccacheexport USE_CCACHE=1export CCACHE_EXEC=/usr/bin/ccache# 设置缓存大小(50GB)prebuilts/misc/linux-x86/ccache/ccache -M 50G# 查看 ccache 状态ccache -s# 输出示例:# cache directory                     /home/user/.ccache# cache hit (direct)                  12345          ← 直接命中# cache hit (preprocessed)            6789           ← 预处理后命中# cache miss                          1234           ← 未命中# cache hit rate                      90.0%          ← 命中率

1.6.2 增量编译技巧

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 技巧 1: 只编译修改的模块mmm # 技巧 2: 减少并行任务数(内存不足时避免 OOM)./build.sh -A -J8# 技巧 3: 使用 Ninja 而非 Make(Android 默认使用 Ninja)export USE_NINJA=true# 技巧 4: 跳过不需要编译的组件#   不修改 U-Boot 时:不用 -U#   不修改内核时:不用 -K

1.6.3 编译问题排查

问题 1: 内存不足

症状:编译过程中被 Killed

排查

  •  
  •  
  •  
  •  
  •  
# 查看系统内存free -h# 查看是否有 OOM 记录dmesg | grep -i "out of memory"dmesg | grep -i "killed process"

解决

  •  
  •  
  •  
# 减少并行任务数./build.sh -A -J8# 或者增加 swap(见 1.3.6)

问题 2: 头文件缺失

症状fatal error: xxx.h: No such file or directory

排查

  •  
  •  
# 搜索头文件位置find . -name "xxx.h" -type f 2>/dev/null | head -5

解决:检查 Android.bp 中的 include_dirs shared_libs 是否遗漏了依赖。

问题 3: 符号未定义

症状undefined reference to 'xxx'

排查

  •  
  •  
# 搜索符号定义grep -r "xxx" hardware/rockchip/ --include="*.cpp" --include="*.h" -l

解决:检查static_libs / shared_libs 是否遗漏了包含该符号的库。

问题 4: lunch 失败

症状lunch 找不到rk3576_u

排查

  •  
  •  
  •  
  •  
# 检查产品配置是否存在ls device/rockchip/rk3576/rk3576_u/# 检查 vendorsetup.shcat device/rockchip/rk3576/rk3576_u/vendorsetup.sh 2>/dev/null

解决:确保source build/envsetup.sh 执行成功,没有报错。

1.7 配置链深度解析

1.7.1 rk3576_u.mk 的 include 关系

这是一个配置继承链,类似面向对象编程中的继承:

Android

实际文件路径

device/rockchip/rk3576/rk3576_u/rk3576_u.mk

device/rockchip/rk3576/rk3576_u/BoardConfig.mk

device/rockchip/rk3576/BoardConfig.mk

device/rockchip/common/BoardConfig.mk

device/rockchip/rk3576/device.mk

device/rockchip/common/device.mk

1.7.2 BOARD_ 变量 vs PRODUCT_ 变量

Android

1.7.3 案例分析:为什么我的包没被安装?

场景:在device.mk 中添加了PRODUCT_PACKAGES += MyService,编译后设备上没有。

排查步骤

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 步骤 1: 确认文件在正确的目录下cat device/rockchip/rk3576/device.mk | grep "MyService"# 步骤 2: 确认模块定义正确# 检查 MyService 的 Android.bp 是否有:# name: "MyService"# 步骤 3: 重新编译并打包mmm make vendorimage# 步骤 4: 检查输出目录ls out/target/product/rk3576_u/vendor/bin/ | grep MyService# 步骤 5: 检查设备adb shell ls /vendor/bin/ | grep MyService

常见原因

1.PRODUCT_PACKAGES 写错了包名(与 Android.bp 的 name 不一致)

2.没有重新编译(只修改了 device.mk 但没有触发重新编译)

3.镜像没有重新打包或没有烧写到设备

1.8 获取 SDK 源码(官方指南)

1.8.1 repo 同步(Rockchip Gerrit

Rockchip Android15 SDK 使用 Gerrit 进行代码管理和分发。

完整同步(在线)

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 安装 repo 工具git clone https://gerrit.rock-chips.com:8443/repo-release/tools/repo# 初始化 manifest./repo/repo init --repo-url https://gerrit.rock-chips.com:8443/repo-release/tools/repo     -u https://gerrit.rock-chips.com:8443/Android_15/manifests     -b master -m Android15.xml# 同步所有仓库.repo/repo/repo sync -c# Express 版本(精简版,下载更快)./repo/repo init --repo-url https://gerrit.rock-chips.com:8443/repo-release/tools/repo     -u https://gerrit.rock-chips.com:8443/Android_15/manifests     -b master -m Android15_Express.xml

镜像仓库同步(适合内部团队部署私有镜像):

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 1. 创建镜像仓库mkdir RK_Android15_mirrorcd RK_Android15_mirror# 2. 初始化镜像../repo/repo init --repo-url https://gerrit.rock-chips.com:8443/repo-release/tools/repo     -u https://gerrit.rock-chips.com:8443/Android_15/manifests     -b master -m Android15.xml --mirror# 3. 同步.repo/repo/repo sync -c# 4. 团队从镜像同步mkdir Android15cd Android15~/repo/repo init -u ssh://git@10.10.10.206/Android_15/manifests_xxx.git -m Android15.xml

从压缩包恢复(离线场景):

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 解压分卷压缩包mkdir Rockchip_Android15.0_SDK_RELEASEcat Rockchip_Android15.0_SDK_RELEASE.tar.gz.* | tar -zx -C Rockchip_Android15.0_SDK_RELEASEcd Rockchip_Android15.0_SDK_RELEASE# 本地同步.repo/repo/repo sync -l# 更新到最新.repo/repo/repo sync -c

1.8.2 切换内核分支

开发中经常需要切换内核分支进行调试:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 1. 进入内核目录cd kernel-6.1# 2. 从 remote 创建本地分支git checkout remotes/m/master -b xxx_branch# 3. 推送到远程git push rk29 xxx_branch:xxx_branch# 4. 修改 manifest 指向新分支cd .repo/manifests# 编辑 include/rk_modules_repository.xml,修改 kernel 的 revisiongit add include/rk_modules_repository.xmlgit commit -m "change kernel branch on xxx_branch"git push origin default:master

1.9 内核独立编译

除了build.sh -K 外,内核也可以独立编译,这在调试内核问题时更高效。

配置编译环境

  •  
  •  
  •  
  •  
  •  
  •  
  •  
cd kernel-6.1# 设置 Clang 工具链export PATH=../prebuilts/clang/host/linux-x86/clang-r487747c/bin:$PATHalias msk='make CROSS_COMPILE=aarch64-linux-gnu- LLVM=1 LLVM_IAS=1'# RK3576: 配置 + 编译msk ARCH=arm64 rockchip_defconfig rk3576-ftrace-debug.configmsk ARCH=arm64 BOOT_IMG=../rockdev/Image-rk3576_u/boot.img rk3576-evb1-v10.img -j32

产物说明

编译方式

产物

用途

build.sh -K

kernel.img resource.img

传统烧写方式

独立编译 + BOOT_IMG

boot.img

直接烧写 boot 分区

1.10 fastbootd 烧写与 GSI

1.10.1 fastbootd vs bootloader 模式

Android 15 支持两种 fastboot 模式:

模式

进入方式

可烧写分区

用途

bootloader

adb reboot bootloader

bootdtbovbmetarecovery

底层分区烧写

fastbootd

adb reboot fastboot

systemvendorodmproduct

动态分区烧写

1.10.2 烧写动态分区

  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 1. 进入 fastbootd 模式adb reboot fastboot# 2. 烧写各个分区fastboot flash vendor vendor.imgfastboot flash system system.imgfastboot flash odm odm.imgfastboot flash product product.img

1.10.3 GSI 烧写流程

GSI (Generic System Image) 用于测试纯 AOSP 系统:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# 1. 解锁 AVB(首次)adb reboot bootloaderfastboot oem at-unlock-vboot# 2. 进入 fastbootd 模式fastboot flash misc misc.imgfastboot reboot fastboot# 3. 删除 product 分区(GSI 不需要)fastboot delete-logical-partition product# 4. 烧写 GSI systemfastboot flash system system.img# 5. 重启fastboot reboot

1.10.4 DSUDynamic System Updates

DSU 允许在不影响当前系统的情况下测试 GSI,最低需要 1GB DDR

使用场景:VTS 测试、CTS-ON-GSI 测试、Google-signed GSI 测试

需要使用boot-debug.img 替代boot.img

1.11 新增产品配置

如何在 SDK 中新增一个产品(以 rk3562_new_u 为例):

步骤 1:注册产品

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
# device/rockchip/rk3562/AndroidProducts.mkPRODUCT_MAKEFILES :=     $(LOCAL_DIR)/rk3562_u/rk3562_u.mk     $(LOCAL_DIR)/rk3562_new_u/rk3562_new_u.mk COMMON_LUNCH_CHOICES :=     rk3562_u-userdebug     rk3562_u-user     rk3562_new_u-userdebug     rk3562_new_u-user 

步骤 2:复制现有产品配置

  •  
  •  
  •  
  •  
  •  
cd device/rockchip/rk3562cp -rf rk3562_u rk3562_new_ucd rk3562_new_u# 修改产品名称sed -i 's/rk3562_u/rk3562_new_u/g' rk3562_new_u.mk BoardConfig.mk

步骤 3:编译

  •  
  •  
  •  
source build/envsetup.shlunch rk3562_new_u-userdebug./build.sh -UKAop

本章小结

Android

 最后想说:Android构建系统看似复杂,但只要理清核心流程和关键命令,就能大幅提升开发效率。如果这篇内容对你有帮助,记得点赞+收藏,后续还会分享更多Android底层开发干货~

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分