Android system.img编译过程(1)

这里主要梳理Android build system.img的make流程,在梳理流程之前,先了解下makefile的一些特性:

https://blog.csdn.net/Thanksgining/article/details/83385445

编译Android系统三部曲:

  • source build/envsetup.sh
  • lunch full-eng
  • make -j24

make其实就是执行Makefile文件,在没有指定Makefile的情况下,执行当前路径下的Makefile文件。Android系统执行make命令就是source code跟目录下Makefile文件,而此Makefile文件的内容只有一句,include另外一个main.mk文件

### DO NOT EDIT THIS FILE ###
include build/make/core/main.mk
### DO NOT EDIT THIS FILE ###

根据Makefile书写规则,在main.mk开始不久,就出现了一个droid伪目标,也是默认目标(default target)。这个默认目标是一个伪目标,make工具遇到伪目标以后,会检查解析伪目标的依赖,如果伪目标存在依赖,就会检查这些依赖,如果这些依赖是伪目标,继续检查这个伪目标的依赖,如果不是伪目标,就会生成这个目标。

最终目标droid

默认目标droid依赖于droid_targets,根据build/make/core/main.mk文件可知完整编译Android系统时,droid_targets的依赖于droidcode和dist_files两大伪目标

BUILD_SYSTEM := $(TOPDIR)build/make/core

# This is the default target.  It must be the first declared target.
.PHONY: droid
DEFAULT_GOAL := droid
$(DEFAULT_GOAL): droid_targets

.PHONY: droid_targets
droid_targets:

# Set up various standard variables based on configuration
# and host information.
include $(BUILD_SYSTEM)/config.mk

droid_targets依赖

根据上面代码可知默认目标droid依赖于droid_targets,根据build/make/core/main.mk文件可知完整编译Android系统时,droid_targets的依赖于droidcode和dist_files两大伪目标

ifneq ($(TARGET_BUILD_APPS),)
......
.PHONY: apps_only
apps_only: $(unbundled_build_modules)
droid_targets: apps_only
......
else # TARGET_BUILD_APPS
......
# Building a full system-- the default is to build droidcore
droid_targets: droidcore dist_files
......
endif # TARGET_BUILD_APPS

droid_code依赖

droidcode目标依赖files、systemimage、INSTALLED_BOOTIMAGE_TARGET等目标,dist_files没有看到依赖情况;

# $(INSTALLED_BOOTIMAGE_TARGET)取INSTALLED_BOOTIMAGE_TARGET的环境变量值
# Build files and then package it into the rom formats
.PHONY: droidcore
droidcore: files \
	systemimage \
	$(INSTALLED_BOOTIMAGE_TARGET) \
	$(MTK_BOOTIMAGE_TARGET) \
	$(INSTALLED_RECOVERYIMAGE_TARGET) \
	$(INSTALLED_VBMETAIMAGE_TARGET) \
	$(INSTALLED_USERDATAIMAGE_TARGET) \
	$(INSTALLED_CACHEIMAGE_TARGET) \
	$(INSTALLED_TRANFSIMAGE_TARGET) \
	$(INSTALLED_BPTIMAGE_TARGET) \
	$(INSTALLED_VENDORIMAGE_TARGET) \
    $(INSTALLED_PRODUCTIMAGE_TARGET) \
	$(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \
	$(INSTALLED_FILES_FILE) \
	$(INSTALLED_FILES_FILE_VENDOR) \
	$(INSTALLED_FILES_FILE_PRODUCT) \
	$(INSTALLED_FILES_FILE_SYSTEMOTHER) \
	soong_docs

# dist_files only for putting your library into the dist directory with a full build.
.PHONY: dist_files

files依赖

files目标依赖modules_to_install和INSTALLED_ANDROID_INFO_TXT_TARGET目标;

# All the droid stuff, in directories
.PHONY: files
files: $(modules_to_install) \
       $(INSTALLED_ANDROID_INFO_TXT_TARGET)

INSTALLED_SYSTEMIMAGE依赖

build/make/core/main.mk文件包含如下.mk和Makefile文件

systemimage目标依赖在build/make/core/Makefile中说明,可知systemimage目标依赖INSTALLED_SYSTEMIMAGE目标;INSTALLED_SYSTEMIMAGE赋值为$(PRODUCT_OUT)/system.img,即$(PRODUCT_OUT)/system.img目标依赖:

  • BUILT_SYSTEMIMAGE
  • RECOVERY_FROM_BOOT_PATCH
INSTALLED_SYSTEMIMAGE := $(PRODUCT_OUT)/system.img
SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)
......
# Rules that need to be present for the all targets, even
# if they don't do anything.
.PHONY: systemimage
systemimage:

# build/make/core/config.mk定义了hide := @,即$(hide)表示在命令前加“@”
# 在命令前加@表示不回显命令
# call 一个可以用来创建新的参数化的函数
$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH)
	@echo "Install system fs image: $@"
	$(copy-file-to-target)
	$(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))

systemimage: $(INSTALLED_SYSTEMIMAGE)

#QEMU表示软件化模拟器
ifeq ($(BUILD_QEMU_IMAGES),true)
......
systemimage: $(INSTALLED_QEMU_SYSTEMIMAGE)
......
endif

BUILT_SYSTEMIMAGE赋值为$(systemimage_intermediates)/system.img,其依赖:

  • FULL_SYSTEMIMAGE_DEPS
  • INSTALLED_FILES_FILE
  • BUILD_IMAGE_SRCS
systemimage_intermediates := \
    $(call intermediates-dir-for,PACKAGING,systemimage)
BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img
......
$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) $(BUILD_IMAGE_SRCS)
	$(call build-systemimage-target,$@)

先不分析BUILT_SYSTEMIMAGE是如何生成,BUILT_SYSTEMIMAGE就是$(systemimage_intermediates)/system.img。

目标INSTALLED_SYSTEMIMAGE执行两个动作,分别是:

  • copy-file-to-target,此命令在build/make/core/definitions.mk文件定义了,可知此命令就是将第一个依赖项拷贝到目标处,即$(PRODUCT_OUT)/system.img是由拷贝$(systemimage_intermediates)/system.img过来的。
# $@ --> 表示目标
# $< --> 表示第一个依赖项
# mkdir -p --> 可创建多级文件目录 如:mkdir -p a/b/c
define copy-file-to-target
@mkdir -p $(dir $@)
$(hide) rm -f $@
$(hide) cp "$<" "$@"
endef
  • assert-max-image-size,此命令在build/make/core/definitions.mk文件定义了
# build/make/core/combo/HOST_linux-x86.mk中定义了get_file-size
# $(1): The file to check
define get-file-size
stat --format "%s" "$(1)" | tr -d '\n'
endef

# build/make/core/definitions.mk
# $(1): The file(s) to check (often $@)
# $(2): The partition size.
define assert-max-image-size
$(if $(2), \
  size=$$(for i in $(1); do $(call get-file-size,$$i); echo +; done; echo 0); \
  total=$$(( $$( echo "$$size" ) )); \
  printname=$$(echo -n "$(1)" | tr " " +); \
  maxsize=$$(($(2))); \
  if [ "$$total" -gt "$$maxsize" ]; then \
    echo "error: $$printname too large ($$total > $$maxsize)"; \
    false; \
  elif [ "$$total" -gt $$((maxsize - 32768)) ]; then \
    echo "WARNING: $$printname approaching size limit ($$total now; limit $$maxsize)"; \
  fi \
 , \
  true \
 )
endef

在分析assert-max-image-size之前,先看看RECOVERY_FROM_BOOT_PATCH和BOARD_SYSTEMIMAGE_PARTITION_SIZE

,RECOVERY_FROM_BOOT_PATCH描述的是一个patch文件,这个patch文件的名称为recovery_from_boot.p,保存在设备上system分区中,描述recovery.img与boot.img之间的差异。也就是说,在设备上,可以通过boot.img和recovery_from_boot.p文件生成一个recovery.img文件,使得设备可以进入recovery模式。好比OTA通过recovery_from_boot.p方式升级recovery.img(OTA卡包里面必须要有boot.img、recovery-from-boot.p、install-recovery.sh三个文件缺一不可),在OTA升级完之后,系统重启时会去升级recovery.img(通过boot.img与recovery-from-boot.p生成的)。

RECOVERY_FROM_BOOT_PATCH依赖规则如下:

# The system partition needs room for the recovery image as well.  We
# now store the recovery image as a binary patch using the boot image
# as the source (since they are very similar).  Generate the patch so
# we can see how big it's going to be, and include that in the system
# image size check calculation.
ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
ifneq ($(BOARD_USES_FULL_RECOVERY_IMAGE),true)
ifneq (,$(filter true, $(BOARD_BUILD_SYSTEM_ROOT_IMAGE) $(BOARD_INCLUDE_RECOVERY_DTBO)))
diff_tool := $(HOST_OUT_EXECUTABLES)/bsdiff
else
diff_tool := $(HOST_OUT_EXECUTABLES)/imgdiff
endif
intermediates := $(call intermediates-dir-for,PACKAGING,recovery_patch)
RECOVERY_FROM_BOOT_PATCH := $(intermediates)/recovery_from_boot.p
$(RECOVERY_FROM_BOOT_PATCH): PRIVATE_DIFF_TOOL := $(diff_tool)
$(RECOVERY_FROM_BOOT_PATCH): \
		$(INSTALLED_RECOVERYIMAGE_TARGET) \
		$(INSTALLED_BOOTIMAGE_TARGET) \
		$(diff_tool)
	@echo "Construct recovery from boot"
	mkdir -p $(dir $@)
	$(PRIVATE_DIFF_TOOL) $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_RECOVERYIMAGE_TARGET) $@
else # $(BOARD_USES_FULL_RECOVERY_IMAGE) == true
RECOVERY_FROM_BOOT_PATCH := $(INSTALLED_RECOVERYIMAGE_TARGET)
endif
endif

BOARD_SYSTEMIMAGE_PARTITION_SIZE是system分区大小,在对应产品的Makefile文件中定义了

BOARD_SYSTEMIMAGE_PARTITION_SIZE:=838860800

如下操作是将“$(PRODUCT_OUT)/system.img recovery-from-boot.p”作为一组参数$1,“分区size”作为一组参数$2,传入assert-max-image-size中,再根据assert-max-image-size的定义,我们可知以下操作就是在比较镜像文件总和是否大于分区大小,如果大于允许的最大分区的大小,这里就会报错。因此,assert-max-image-size函数可以理解为检查system.img size的合法性。

$(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))

通过编译log也可以看到如下信息:

[100% 81510/81510] Install system fs image: out/target/product/${project}/system.img
out/target/product/${project}/system.img+out/target/product/${project}/obj/PACKAGING/recovery_patch_intermediates/recovery_from_boot.p maxsize=2415114240 blocksize=4224 total=1860325777 reserve=24397824

至此,$(PRODUCT_OUT)/system.img就生成了,至于它所依赖的$(systemimage_intermediates)/system.img是如何生成流程将在下一篇blog里记录Android system.img编译过程(2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值