背景
有时我们需要使用同一套Android源码编译生成各种差异化产品固件,比如:A产品、B产品、C产品,各产品之间大部分功能是相同的,仅个别功能定义上有差别。
方法一 Android默认的做法是在源码 device 目录下增加A、B、C产品,差异化通过 makefile 配置区分。比如:预装哪些APK,使用哪个开机动画。但这种方法有以下几个缺点:
● 每种产品都需要单独编译整个Android源码,而Android源码编译需要耗费较长时间。
● 无法决定 kernel 的差异,比如:A、B、C产品它们的硬件上有差异,需要分别编译不同的dts文件,这种方式就无法实现。
● 如果A、B、C产品之间的差异非常小,比如仅仅是开机动画的不同,也增加一个device来区分,也需要单独编译整个Android源码,就有点太耗费资源。
方法二 通过git分支来管理,每个产品单独拉一个git分支,这种方式适应于源码差异非常大,并且无法兼容,相互之间存在冲突的情况。像上面所述,如果A、B、C产品之间仅仅是开机动画的不同,也通过单独git分支来维护,自然就不妥了。另外,一旦产品数量多起来,维护各种git分支就变的相当困难。
方法三 将Android源码与产品解耦,Android源码只负责各种基础功能,产品相关的差异配置全部提取到另外一个仓库。我们在这里暂且约定Android源码为『Android仓库』,产品配置的仓库为『产品仓库』(这里的仓库指git仓库,可以理解为存在两个git仓库,一个管理Android源码,一个管理产品配置)。
我们将『Android仓库』编译后生成的相关文件打包,将其复制到特定路径下,称之为『二次开发包』。『产品仓库』中集成了二次打包相关脚步,当需要生成特定产品固件时,将『二次开发包』复制到『产品仓库』指定路径下,脚本会解压『二次开发包』并将特定产品的所有配置合入其中,从而生产最终产品固件。
这样一来,A、B、C产品共用同一份『二次开发包』,只要Android源码没有修改,就不用重新编译Android源码,而只需要执行『产品仓库』中的二次打包脚步,这往往只需要几分钟就可以打包完成,大大提高了发布产品固件的速度。
下面用一张简单的示意图来描述整个过程:
上面示意图说明『Adroid仓库』默认支持A、B、C三个功能,通过『产品仓库』进行二次打包后,支持了A、B、C、D四项功能。
文本要讲的就是上面的『方法三』,下面开始详细说明实现细节。
项目介绍
项目 Android-SecBuild 基于 方法三 在rockchip平台上实现了二次构建方案。
项目包含『Android仓库』的构建脚本和『产品仓库』的构建脚本,目录结构如下:
├─Android Android仓库
│ ├─build Android构建脚本
│ ├─device device目录
│ │ └─DEFAULT 每个目录代表一个device,里面包含config.ini用于配置uboot config、kernel config、dts
│ └─outputs 构建结果
│
└─Product 产品仓库
├─build 二次构建脚本
├─outputs 构建结果
├─platform 将二次开发包拷贝至此目录
└─product 产品目录(里面每一个目录代表一个产品)
├─DEFAULT DEFAULT产品
└─PARENT PARENT产品
Android仓库
集成构建脚本
将 Android-SecBuild 项目中『Android』目录拷贝到Android源码根目录下,并重命名为『secbuild』,如下:
├─Android
......
├─external
├─frameworks
├─hardware
├─kernel
├─uboot
├─pakcages
├─system
......
└─secbuild 新增
├─build
| ......
| ├─build.sh
| ├─full_build.sh
| ├─pack.sh
| ......
├─device
| ├─DEFAULT
| └config.ini
└─outputs
build 目录下为构建脚本,里面包含三个主要脚本(其它脚本及工具是rockchip平台相关的,请根据平台确定要否需要),分别为:full_build.sh、build.sh、pack.sh。
● full_build.sh 负责调用build.sh和pack.sh。
● build.sh 编译Android源码。
● pack.sh 将Android源码编译产物和二次打包所需工具打包成二次开发包。
device/DEFAULT 下config.ini文件用于配置uboot config、kernel config和dts,内容如下:
# u-boot config
UBOOT_DEFCONFIG=default_defconfig
# kernel config
KERNEL_DEFCONFIG=default_defconfig
# kernel dts
KERNEL_DTS=default_dts
outputs 目录为构建结果,包含Android源码编译生成的各种img和二次开发包等。
生成二次开发包
执行 secbuild/build/full_build.sh 脚本编译Android系统并打包二次开发包。
./full_build.sh -t target -d device
● target Android target
● device secbuild/device/下目录名
例子:
./full_build.sh -t rk3288 -d DEFAULT
执行完成后将在outputs目录下生成二次开发包。
二次开发包
命名
二次开发包的命名规则为
rom_分支_版本号_commit-id_日期.tar.gz
其中版本号的命名规则为:
[平台][Android版本].主版本号.commit计数
例子:
rom_master_1381.1.443_bcccadf_2020-04-24.tar.gz
| | | | | |
| | | | | 最新commit-id
| | | | commit计数(比如提交了100次,值就是100)
| | | 主版本
| | Android版本(51: Android 5.1 60: Android 6.0 71: Android 7.1 81: Android 8.1)
| 平台(比如:11: RK3128 12: RK3368 13:RK3288)
git分支
内容:
rom_master_1381.1.443_bcccadf_2020-04-24.tar.gz
├─rom_git_verson.prop 记录git仓库地址和最新Commit id
├─image Android源码编译生成的各种image
├─target_files.zip Android target files,里面包含Android各分区原始文件
└─tools 二次打包时需要的各种工具
├─bin
├─framework
├─lib64
└─releasetools
其中 target_files.zip 为二次打包的核心,这里面包含Android各分区原始文件。因此,可以通过工具对其进行重新打包生成各分区image文件。内容如下:
target_file.zip
├─BOOT
├─DATA
├─IMAGES
├─META
├─OEM
├─OTA
├─RECOVERY
├─SYSTEM
├─trust.img*
├─uboot.img*
└─VENDOR
产品仓库
将 Android-SecBuild 项目中『Product』单独新建一个git仓库,并提交。
文件解释
Product
├─product 产品目录
| ├─PARENT PARENT产品
| | ├─config.ini 产品配置
| | ├─default.dtb dtb文件(配置主板硬件特性)
| | └logo.bmp 开机logo
| ├─DEFAULT DEFAULT产品
| | ├─config.ini 产品配置
| | ├─default.dtb dtb文件(配置主板硬件特性)
| | ├─logo.bmp 开机logo
| | ├─system system分区(拷贝至android system分区,同名替换)
| | | ├─media
| | | | └bootanimation.zip 替换开机动画
| | | ├─app
| | | | ├─SougouIME 内置搜索输入法
| | | | | ├─SougouIME.apk
| | | | | ├─lib
| | | | | | ├─arm
| | | | | | | └libsogouime.so
├─platform 二次开发包将拷贝至此目录下
├─outputs 构建结果(最终image文件)
├─build 构建脚本
| ......
| ├─mkimage_o.sh 生成各分区image(平台相关:rockchip)
| ├─mkproduct.sh 主脚本,解压二次开发包,解析config.ini,替换文件,调用mkimage_o.sh、mkupdate_image.sh、mkupdate_zip.sh
| ├─mkupdate_img.sh 生成rockchip update.img烧录包(平台相关:rockchip)
| ├─mkupdate_zip_o.sh 生成ota升级包
mkproduct.sh
● 解压二次开发包。
● 替换各分区文件(system、vendor、oem、root、recovery等)。
● 解析platform目录下apk文件并内置。
● 解析 config.ini 配置,并替换属性、删除文件。
● 调用 mkimage_o.sh 生成各分区image。
● 调用 mkupdate_image.sh 生成rockchip update.img烧录包。
● 调用 mkupdate_zip_o.sh 生成ota升级包。
mkimage_o.sh
● 生成各分区image(平台相关:rockchip)。
mkupdate_img.sh
● 生成rockchip update.img烧录包(平台相关:rockchip)。
mkupdate_zip_o.sh
● 生成ota升级包。
二次打包
将『Android仓库』中构建生成的『二次开发包』拷贝到 platform 目录下。
执行 build/mkproduct.sh 脚本将会解压二次开发包,合入产品配置,并二次打包生成最终固件。
./mkproduct.sh product
product 产品名(product/路径下目录名)
例子:
./mkproduct.sh DEFAULT
执行完成后将在outputs目录下生成最终固件,如下:
rk3288_update_DEFAULT_v1.0.1(2)_ce1c332_2020-04-26.img
| | | | |
| | | | git最新commit-id
| | | build号
| | 版本号
| 产品
target(芯片平台)
产品配置
典型的产品配置目录结构如下:
├─DEFAULT DEFAULT产品
| ├─config.ini 产品配置
| ├─default.dtb dtb文件(配置主板硬件特性)
| ├─logo.bmp 开机logo
| ├─system system分区(拷贝至android system分区,同名替换)
| | ├─media
| | | └bootanimation.zip 替换开机动画
| | ├─app
| | | ├─SougouIME 内置搜索输入法
| | | | ├─SougouIME.apk
| | | | ├─lib
| | | | | ├─arm
| | | | | | └libsogouime.so
config.ini
config.ini 支持三个关键字:EXTENDS、PROP、DEL,它们各位的含义如下:
# 继承,关键字:EXTEND,值为另一个产品。
# 用于减少重复文件,减小仓库大小,公共文件只需要在父产品中放置一份即可。
EXTENDS=PARENT
# 配置属性,关键字:PROP,值为属性名@属性值,@前为属性名,@后的属性值
PROP=ro.product.name@default
# 删除文件,关键字:DEL,值为文件路径
DEL=SYSTEM/app/Launcher3
注意 EXTENDS 关键字只可以使用一次,PROP 和 DEL 可以无限使用。
例子
# 继承,关键字:EXTEND,值为另一个产品。
# 用于减少重复文件,减小仓库大小,公共文件只需要在父产品中放置一份即可。
#EXTENDS=RUNNING
# 配置属性,关键字:PROP,值为属性名@属性值,@前为属性名,@后的属性值
PROP=ro.product.name@default
PROP=ro.build.product@default
PROP=ro.product.brand@google
PROP=ro.product.device@default
PROP=ro.product.model@default
PROP=ro.product.board@rk30sdk
PROP=persist.sys.rotate.enable@0
PROP=persist.sys.sleep.enable@0
# 删除文件,关键字:DEL,值为文件路径
DEL=SYSTEM/app/Launcher3
DEL=SYSTEM/app/WallpaperPicker
DEL=SYSTEM/app/SoundRecorder
dtb
在android源码中编译对应主板的dts生成dtb文件,将其拷贝到具体产品根目录下,用于配置主板硬件差异。
logo.bmp
产品开机动画。
system
二次打包会将此目录下所有文件拷贝至android system分区,已存在的文件替换,不存在的文件增加。同理,还可以增加 vendor、oem、root、recovery 等分区,都是按路径替换文件。
因此,通过dtb可以配置硬件差异,通过属性配置功能差异,通过文件删除替换来内置或精简系统。
自动构建
将上述二次构建的框架与持续集成平台 Jenkins 结合,达到自动构建的目的。
Jenkins 配置『Android仓库』构建。
#!/bin/bash
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
export USER=$(whoami)
export HOME=/home/tomcat
rm out -rf
./full_build.sh -t rk3288 -d DEFAULT
Jenkins 配置『产品仓库』构建。
#!/bin/bash
export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar
rm -rf platform/*
cp -vf ../AndroidAutoUpgrade-Baidu/app/build/outputs/apk/release/AutoUpgrade*.apk ./platform/RomUpgrade.apk || exit $1
cp -vf ../AndroidStressTest/app/build/outputs/apk/release/AndroidStressTest*.apk ./platform/AndroidStressTest.apk || exit $1
cp -vf ../AndroidX/app/build/outputs/apk/release/AndroidX*.apk ./platform/AndroidX.apk || exit $1
cp -vf ../RK3288_ROM_DEFAULT/secbuild/outputs/rom_*.tar.gz ./platform || exit $1
cd build
./mkproduct.sh LS328-DEFAULT
cd -
构建后生成产品固件。