[Android]基于AOSP源码为Pixel3编译boot.img(中)
[Android]基于AOSP源码为Pixel3编译boot.img(中)
前言
在上一节我们提到,本系列主要是要实现将Android Kernel源码并入AOSP源码,使后者的编译框架可以直接从源码编译boot.img这一目标,而改造工作大致需要如下几步:
- 下载源码——下载Android Kernel源码;
- 编译代码——编译一次,确认环境与代码状态正确;
- 代码迁移与合并——将目录结构合入AOSP,并复用AOSP的编译工具链(但此时仍然为独立编译方式,仅仅工具链复用);
- 编译规则修改——在AOSP侧编写编译规则,使其可以从源码编译Android Kernel;
- 确认产物、刷机验证;
在上一节中,我们已经完成上面的步骤1、2、3;
本篇继续从4、5继续;
环境
台式机
Ubuntu16.04
AOSP版本
9.0(android-9.0.0_r46)
内核版本
4.9(android-msm-crosshatch-4.9-pie-qpr2)
Pixel型号
Pixel3 (4GB + 64GB)
正文
编译规则修改
确认当前规则
依旧以Pixel3为例,在原生AOSP的设计中,kernel是以二进制形式保存在device/google/crosshatch-kernel/Image.lz4-dtb
,在编译时通过拷贝的形式,重命名,并放置到out/target/product/blueline/kernel
:
# device/google/crosshatch/device.mk:
...
ifeq ($(TARGET_PREBUILT_KERNEL),)
LOCAL_KERNEL := device/google/crosshatch-kernel/Image.lz4-dtb
else
LOCAL_KERNEL := $(TARGET_PREBUILT_KERNEL)
endif
...
PRODUCT_COPY_FILES += \
$(LOCAL_KERNEL):kernel \
...
如果要实现从源码编译出kernel的目标,这部分代码需要去掉;
并且,我们要编写自己的编译规则,来生成out/target/product/blueline/kernel
选定修改方案
要实现上述目标,目前可参考的方案有如下几个:
- 参考CAF(CodeAuroraForum, 现更改为:CodeLinaro) 中SDM845(Pixel3 SoC) + Android 9.0的基线上的编译规则编写;
- 参考LineageOS 16(Android 9.0)上Pixel3(blueline)对应基线的编译规则编写;
- 自行实现:按照编译Android Kernel的命令行添加编译规则;
事实上,前两个方案我也充分评估,甚至尝试过,但是由于各种比较恼人的报错,最终放弃,最终采用方案3来实现;(另外两个方案的评估过程,优劣分析会在下一篇详细介绍,这里为了阅读连贯性,暂不展开)
方案具体实施
确认使用方案3后,我们先来罗列下在当前代码结构下,编译Android Kernel的步骤:
$ source build/envsetup.sh
$ lunch aosp_blueline-userdebug
$ cd kernel/
$ ./build/build.sh
$ cp out/android-msm-bluecross-4.9/dist/*.ko ../out/target/product/blueline/vendor/lib/modules/
$ cp out/android-msm-bluecross-4.9/dist/dtbo.img ../out/target/product/blueline/dtbo.img
$ cp out/android-msm-bluecross-4.9/dist/Image.lz4-dtb ../out/target/product/blueline/kernel
$ cd ../
首先,前两句是不需要的,因为在调用make
之前,必然已经执行过了;
其次,由于Makefile是逐行执行的,也就是说每一行的$(PWD)都是不会变的,因此cd kernel/
与./build/build.sh
合并为一句命令;
同理,后续拷贝的相对相对路径也需要做相应调整,且最后一句也不需要;
按照这个方向,我们可以编写规则如下:
# 由于打包boot.img明确依赖out/target/product/blueline/kernel,因此需要将其提取成一个目标
# 由于编译Android Kernel最后需要依赖soong_zip,因此需要在此声明依赖;
# 否则new build时可能会出现打包失败的问题(kernel在soong_zip之前编译);
$(PRODUCT_OUT)/kernel: $(SOONG_ZIP)
@echo "Making kernel"
# 合并为一行执行
cd kernel && bash build/build.sh
# 由于AOSP指定了OUT_DIR(=out),因此目录不再以分支名创建(out/android-msm-bluecross-4.9)
# 拷贝所有生成的ko文件
cp kernel/out/dist/*.ko $(TARGET_OUT_VENDOR)/lib/modules/
# 拷贝dtbo.img文件
cp kernel/out/dist/dtbo.img $(PRODUCT_OUT)/dtbo.img
# 拷贝kernel文件
cp kernel/out/dist/Image.lz4-dtb $(PRODUCT_OUT)/kernel
# 定义一个名为kernel的伪目标,方便通过命令make kernel来快速验证;
.PHONY: kernel
kernel: $(PRODUCT_OUT)/kernel
同时,为了反复快速验证,再创建一个kernelclean
来进行重置操作:
.PHONY: kernelclean
kernelclean:
if [ -f $(PRODUCT_OUT)/dtbo.img ]; then rm $(PRODUCT_OUT)/dtbo.img; fi
if [ -f $(PRODUCT_OUT)/kernel ]; then rm $(PRODUCT_OUT)/kernel; fi
if [ -d kernel/out ]; then rm -rf kernel/out; fi
@echo "kernelclean done"
- 新建一个mk文件(随便取,此处以
kernel.mk
为例),保存上述内容; - 将
kernel.mk
放置到可以自动引入的目录下;
AOSP预定义了一些路径,在编译初期会自动引入,因此放到以下提到的任何目录下均可:# build/make/core/Makefile: ... ifneq ($(dont_bother),true) include $(sort $(wildcard $(BUILD_SYSTEM)/tasks/*.mk)) -include $(sort $(wildcard vendor/*/build/tasks/*.mk)) -include $(sort $(wildcard device/*/build/tasks/*.mk)) -include $(sort $(wildcard product/*/build/tasks/*.mk)) # Also the project-specific tasks -include $(sort $(wildcard vendor/*/*6. 去掉/build/tasks/*.mk)) -include $(sort $(wildcard device/*/*/build/tasks/*.mk)) -include $(sort $(wildcard product/*/*/build/tasks/*.mk)) # Also add test specifc tasks include $(sort $(wildcard platform_testing/build/tasks/*.mk)) include $(sort $(wildcard test/vts/tools/build/tasks/*.mk)) endif ...
- 编译:
make kernel
如果你严格按照上述步骤,那么使用make kernel
编译成功是必然的,但是为了处理与原生AOSP编译逻辑的冲突,我们还需要修改:
- 去掉上面提到的
device/google/crosshatch/device.mk
中拷贝device/google/crosshatch-kernel/Image.lz4-dtb
到out/target/product/blueline/kernel
的规则; - 去掉
device/google/crosshatch/BoardConfig-common.mk
中BOARD_PREBUILT_DTBOIMAGE
与BOARD_VENDOR_KERNEL_MODULES
两个宏的所有赋值逻辑; - 去掉
device/google/crosshatch/AndroidBoard.mk
中kernel-modules:
这个伪目标的定义,已经INSTALLED_KERNEL_MODULES
与INTERNAL_KERNEL_MODULES
的宏定义;
确认产物、刷机验证
编译验证
建议删除out全编译一次,避免一些编译依赖问题在增量编译的环境下被掩盖;
编译成功后则可以进行刷机验证了;
刷机验证
由于AVB的机制,在刷入boot.img
的同时,建议同时刷入:
vbmeta.img
vendor.img
dtbo.img
以上步骤都是我实际操作过的,经验证,手机在刷入编译出的product.img/system_other.img/system.img/boot.img/vendor.img/dtbo.img/vbmeta.img
后可正常开机,相机、传感器、WLAN、音频均工作正常;
若读者在参考本文操作过程中遇到问题,欢迎留言讨论;
后记
截至目前,我们已经成功实现使用make命令,从源码编译boot.img的目标,但其实仍然存在一定的局限性和优化空间:
- 编译依赖
kernel/build
,生成产物目录与AOSP不相同:目前只是将编译命令脚本化,并没有实现编译环境的完全整合,比较明显的一个现象就是,各自的out
目录并不相同; - 编译过程仍然有目录建拷贝的过程:同上,这种拷贝实际上也是没有完全整合的产物;
- 编译目标粒度过于粗糙:
make kernel
只有一个目标,因此在其成功、失败之前,是没有日志输出的;
因此下一篇,会进行编译规则的优化,使其与AOSP实现真正意义上的融合;
同时,会详细讨论下上述提到的其他几家方案实现的优劣;