Image打包流程-Android10.0编译系统(四)

摘要:本节主要来进行Android10.0 Image打包流程,理解system.img是如何打包的

1 概述

  前面我们讲完了Android10.0 编译的初始化和make的完整流程,从make中我们看到了,最终编译会生成system.img、super.img、ramdisk.img等镜像文件,我们把这些镜像文件烧录到手机中,即可完成版本的替换升级。

  这一节我们来一起看看这些image是如何打包生成的

2 image打包入口

  在上一节的main.mk中,最后两步定义了需要编译的image和构建一个rom的过程.

  image构建和打包的一些依赖关系如下图所示:

 
  1. [build/make/core/main.mk]

  2. ...

  3. .PHONY: ramdisk

  4. ramdisk: $(INSTALLED_RAMDISK_TARGET)

  5. .PHONY: userdataimage

  6. userdataimage: $(INSTALLED_USERDATAIMAGE_TARGET)

  7. DATATARBALL_TARGET)

  8. .PHONY: cacheimage

  9. cacheimage: $(INSTALLED_CACHEIMAGE_TARGET)

  10. .PHONY: odmimage

  11. odmimage: $(INSTALLED_ODMIMAGE_TARGET)

  12. .PHONY: systemotherimage

  13. systemotherimage: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET)

  14. .PHONY: superimage_empty

  15. superimage_empty: $(INSTALLED_SUPERIMAGE_EMPTY_TARGET)

  16. .PHONY: bootimage

  17. bootimage: $(INSTALLED_BOOTIMAGE_TARGET)

  18. .PHONY: bootimage_debug

  19. bootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)

  20. ...

  21. .PHONY: droidcore

  22. droidcore: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) \

  23. $(INSTALLED_SYSTEMIMAGE_TARGET) \

  24. $(INSTALLED_RAMDISK_TARGET) \

  25. $(INSTALLED_BOOTIMAGE_TARGET) \

  26. $(INSTALLED_DEBUG_RAMDISK_TARGET) \

  27. $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \

  28. $(INSTALLED_RECOVERYIMAGE_TARGET) \

  29. $(INSTALLED_VBMETAIMAGE_TARGET) \

  30. $(INSTALLED_USERDATAIMAGE_TARGET) \

  31. $(INSTALLED_CACHEIMAGE_TARGET) \

  32. $(INSTALLED_BPTIMAGE_TARGET) \

  33. $(INSTALLED_VENDORIMAGE_TARGET) \

  34. $(INSTALLED_ODMIMAGE_TARGET) \

  35. $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) \

  36. ...\

  37. auxiliary \

  38. soong_docs

  39. ...

main.mk中只是做了一些定义和启动编译流程,正在的image打包在build/core/Makefile中完成

 
  1. [build/make/core/main.mk]

  2. ifdef FULL_BUILD

  3. ...

  4. # TODO: Remove the 3 places in the tree that use ALL_DEFAULT_INSTALLED_MODULES

  5. # and get rid of it from this list.

  6. modules_to_install := $(sort \

  7. $(ALL_DEFAULT_INSTALLED_MODULES) \

  8. $(product_target_FILES) \

  9. $(product_host_FILES) \

  10. $(call get-tagged-modules,$(tags_to_install)) \

  11. $(CUSTOM_MODULES) \

  12. )

  13. ...

  14. # build/make/core/Makefile contains extra stuff that we don't want to pollute this

  15. # top-level makefile with. It expects that ALL_DEFAULT_INSTALLED_MODULES

  16. # contains everything that's built during the current make, but it also further

  17. # extends ALL_DEFAULT_INSTALLED_MODULES.

  18. ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)

  19. include $(BUILD_SYSTEM)/Makefile

  20. modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))

  21. ALL_DEFAULT_INSTALLED_MODULES :=

  22. ...

  23. #endif

 在build/core/Makefile中定义了很多image的生成规则,例如:system.img,boot.img,recovery.img,vendor.img,super.img,下面我们就以system.img为例,详细的来看看image的具体打包细节。

 
  1. [build/core/Makefile]

  2. ...

  3. .PHONY: systemimage

  4. .PHONY: event-log-tags

  5. .PHONY: ramdisk-nodeps

  6. .PHONY: bootimage-nodeps

  7. .PHONY: bootimage-nodeps

  8. .PHONY: bootimage-nodeps

  9. .PHONY: bootimage-nodeps

  10. .PHONY: notice_files

  11. .PHONY: otacerts

  12. .PHONY: recoveryimage-nodeps

  13. .PHONY: recoveryimage

  14. .PHONY: ramdisk_debug-nodeps

  15. .PHONY: bootimage_debug-nodeps

  16. .PHONY: installed-file-list

  17. .PHONY: systemimage-nodeps snod

  18. .PHONY: sync syncsys

  19. .PHONY: systemtarball-nodeps

  20. .PHONY: stnod

  21. .PHONY: platform

  22. .PHONY: platform-java

  23. .PHONY: boottarball-nodeps btnod

  24. .PHONY: userdataimage-nodeps

  25. .PHONY: userdatatarball-nodeps

  26. .PHONY: bptimage-nodeps

  27. .PHONY: cacheimage-nodeps

  28. .PHONY: systemotherimage-nodeps

  29. .PHONY: vendorimage-nodeps vnod

  30. .PHONY: productimage-nodeps pnod

  31. .PHONY: productservicesimage-nodeps psnod

  32. .PHONY: odmimage-nodeps onod

  33. .PHONY: vbmetaimage-nodeps

  34. .PHONY: otatools

  35. .PHONY: otatools-package

  36. .PHONY: target-files-package

  37. .PHONY: otapackage

  38. .PHONY: otardppackage

  39. .PHONY: superimage_dist

  40. .PHONY: superimage

  41. .PHONY: superimage-nodeps supernod

  42. ...

3.systemimage 打包

  system.img打包的是system分区中的文件,相关打包内容如下:

 
  1. # Rules that need to be present for the all targets, even

  2. # if they don't do anything.

  3. .PHONY: systemimage

  4. systemimage:

  5. ...

  6. INSTALLED_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/system.img

  7. SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)

  8. $(INSTALLED_SYSTEMIMAGE_TARGET): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH)

  9. @echo "Install system fs image: $@"

  10. $(copy-file-to-target)

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

  12. systemimage: $(INSTALLED_SYSTEMIMAGE_TARGET)

  13. .PHONY: systemimage-nodeps snod

  14. systemimage-nodeps snod: $(filter-out systemimage-nodeps snod,$(MAKECMDGOALS)) \

  15. | $(INTERNAL_USERIMAGES_DEPS)

  16. @echo "make $@: ignoring dependencies"

  17. $(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE_TARGET))

  18. $(hide) $(call assert-max-image-size,$(INSTALLED_SYSTEMIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))

  19. ...

关于system.img,这里定义了两个伪目标systemimage 和 systemimage-nodeps。

systemimage表示在打包system.img之前,要根据依赖规则重新生成所有要进行打包的文件。

systemimage-nodeps则不需要根据依赖规则重新生成所有需要打包的文件而直接打包system.img文件。

systemimage 依赖于$(INSTALLED_SYSTEMIMAGE_TARGET)。

3.1 INSTALLED_SYSTEMIMAGE_TARGET

  从上面的代码看到,systemimage 依赖于INSTALLED_SYSTEMIMAGE_TARGET,最终生成目标文件

 
  1. $(PRODUCT_OUT)/system.img

  2. $(INSTALLED_SYSTEMIMAGE_TARGET): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH)

  3. @echo "Install system fs image: $@"

  4. $(copy-file-to-target)

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

其中,INSTALLED_SYSTEMIMAGE_TARGET依赖于BUILT_SYSTEMIMAGE和RECOVERY_FROM_BOOT_PATCH,再调用了函数copy-file-to-target进行文件拷贝。

3.1.1  copy-file-to-target

  copy-file-to-target 在/build/make/core/definitions.mk 中被定义,主要用于是拷贝文件,并且在拷贝的过程中会保留文件的权限和覆盖已有的文件。

  它会创建/out/target/product/xxx 目录, xxx表示产品的名称,然后把文件拷贝到该目录中

 
  1. [/build/make/core/definitions.mk]

  2. # Copy a single file from one place to another,

  3. # preserving permissions and overwriting any existing

  4. # file.

  5. # When we used acp, it could not handle high resolution timestamps

  6. # on file systems like ext4. Because of that, '-t' option was disabled

  7. # and copy-file-to-target was identical to copy-file-to-new-target.

  8. # Keep the behavior until we audit and ensure that switching this back

  9. # won't break anything.

  10. define copy-file-to-target

  11. @mkdir -p $(dir $@)

  12. $(hide) rm -f $@

  13. $(hide) cp "$<" "$@"

  14. endef

3.1.2 RECOVERY_FROM_BOOT_PATCH

  RECOVERY_FROM_BOOT_PATCH 描述的是一个patch文件,依赖规则如下所示:

 
  1. ifneq (,$(filter true, $(BOARD_BUILD_SYSTEM_ROOT_IMAGE) $(BOARD_INCLUDE_RECOVERY_DTBO) $(BOARD_INCLUDE_RECOVERY_ACPIO)))

  2. diff_tool := $(HOST_OUT_EXECUTABLES)/bsdiff

  3. else

  4. diff_tool := $(HOST_OUT_EXECUTABLES)/imgdiff

  5. endif

  6. intermediates := $(call intermediates-dir-for,PACKAGING,recovery_patch)

  7. RECOVERY_FROM_BOOT_PATCH := $(intermediates)/recovery_from_boot.p

  8. $(RECOVERY_FROM_BOOT_PATCH): PRIVATE_DIFF_TOOL := $(diff_tool)

  9. $(RECOVERY_FROM_BOOT_PATCH): \

  10. $(INSTALLED_RECOVERYIMAGE_TARGET) \

  11. $(INSTALLED_BOOTIMAGE_TARGET) \

  12. $(diff_tool)

  13. @echo "Construct recovery from boot"

  14. mkdir -p $(dir $@)

  15. $(PRIVATE_DIFF_TOOL) $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_RECOVERYIMAGE_TARGET) $@

RECOVERY_FROM_BOOT_PATCH 依赖的patch文件为:$(intermediates)/recovery_from_boot.p,表示的是recovery.img和boot.img之间的差异,存在于system分区中,可以通过boot.img和recovery_from_boot.p构造一个recovery.img。

3.1.3 BUILT_SYSTEMIMAGE

 BUILT_SYSTEMIMAGE 最终会把system.img编译到 out/target/product/generic/obj/PACKAGING/systemimage_intermediates/system.img中。

BUILT_SYSTEMIMAGE 依赖于FULL_SYSTEMIMAGE_DEPS、INSTALLED_FILES_FILE和BUILD_IMAGE_SRCS,通过调用 函数build-systemimage-target 来编译systemimage,相关依赖如下所示:

 
  1. systemimage_intermediates := \

  2. $(call intermediates-dir-for,PACKAGING,systemimage)

  3. BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img

  4. $(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) $(BUILD_IMAGE_SRCS)

  5. $(call build-systemimage-target,$@)

 BUILD_IMAGE_SRCS 在[/build/make/core/config.mk] 中定义,配置了build/make/tools/releasetools中的python脚本参与编译

 
  1. BUILD_IMAGE_SRCS := $(wildcard build/make/tools/releasetools/*.py)

 INSTALLED_FILES_FILE 依赖的是文件$(PRODUCT_OUT)/installed-files.txt,这是已安装的文件列表,这些文件要打包到system.img中,他也依赖于FULL_SYSTEMIMAGE_DEPS

 INSTALLED_FILES_FILE 的依赖描述如下所示:

 
  1. # installed file list

  2. # Depending on anything that $(BUILT_SYSTEMIMAGE) depends on.

  3. # We put installed-files.txt ahead of image itself in the dependency graph

  4. # so that we can get the size stat even if the build fails due to too large

  5. # system image.

  6. INSTALLED_FILES_FILE := $(PRODUCT_OUT)/installed-files.txt

  7. INSTALLED_FILES_JSON := $(INSTALLED_FILES_FILE:.txt=.json)

  8. $(INSTALLED_FILES_FILE): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON)

  9. $(INSTALLED_FILES_FILE): $(FULL_SYSTEMIMAGE_DEPS) $(FILESLIST)

  10. @echo Installed file list: $@

  11. @mkdir -p $(dir $@)

  12. @rm -f $@

  13. $(hide) $(FILESLIST) $(TARGET_OUT) > $(@:.txt=.json)

  14. $(hide) build/make/tools/fileslist_util.py -c $(@:.txt=.json) > $@

 FULL_SYSTEMIMAGE_DEPS依赖于INTERNAL_SYSTEMIMAGE_FILES 和INTERNAL_USERIMAGES_DEPS,列出了制作system.img所需要的工具和制作system.img所需要的文件。

 
  1. INTERNAL_USERIMAGES_DEPS := $(SIMG2IMG)

  2. INTERNAL_USERIMAGES_DEPS += $(MKEXTUSERIMG) $(MAKE_EXT4FS) $(E2FSCK) $(TUNE2FS)

  3. ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)

  4. INTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG) $(MAKE_F2FS)

  5. endif

  6. ...

  7. INTERNAL_SYSTEMIMAGE_FILES := $(sort $(filter $(TARGET_OUT)/%, \

  8. $(ALL_GENERATED_SOURCES) \

  9. $(ALL_DEFAULT_INSTALLED_MODULES) \

  10. $(PDK_FUSION_SYSIMG_FILES) \

  11. $(RECOVERY_RESOURCE_ZIP)) \

  12. $(PDK_FUSION_SYMLINK_STAMP))

  13. FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS)

INTERNAL_USERIMAGES_DEPS:列出了制作system.img所需要的工具,例如out/host/linux-x86/bin/simg2img、out/host/linux-x86/bin/mkuserimg_mke2fs 等,如果支持f2fs的文件系统,会加载out/host/linux-x86/bin/make_f2fs

INTERNAL_SYSTEMIMAGE_FILES:列出了制作system.img所需要的文件,释义如下:

ALL_GENERATED_SOURCES:描述的是要拷贝到目标设备上去的由工具自动生成的源代码文件。

ALL_DEFAULT_INSTALLED_MODULES:描述的是所有需要安装的module

PDK_FUSION_SYSIMG_FILES:是从PDK(Platform Development Kit)提取出来的相关文件

RECOVERY_RESOURCE_ZIP:描述的是Android的recovery系统要使用的资源文件,对应于/system/etc目录下的recovery-resource.dat文件。

PDK_FUSION_SYMLINK_STAMP:PDK的符号链接文件

3.1.4 build-systemimage-target

  BUILT_SYSTEMIMAGE 通过调用函数build-systemimage-target 来生成img。

  首先创进行了一些vendor\product\product_service的link操作,然后创建一个out/target/product/generic/obj/PACKAGING/systemimage_intermediates/ 目录,并先删除system_image_info.txt文件,接着调用generate-image-prop-dictionary,生成system.img的信息,保存到system_image_info.txt中。

  最后调用build/make/tools/releasetools/build_image.py来生成system.img。

  build-systemimage-target的定义如下所示:

 
  1. define build-systemimage-target

  2. @echo "Target system fs image: $(1)"

  3. $(call create-system-vendor-symlink)

  4. $(call create-system-product-symlink)

  5. $(call create-system-product_services-symlink)

  6. $(call check-apex-libs-absence-on-disk)

  7. @mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt

  8. $(call generate-image-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt,system, \

  9. skip_fsck=true)

  10. $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \

  11. build/make/tools/releasetools/build_image.py \

  12. $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \

  13. || ( mkdir -p $${DIST_DIR}; cp $(INSTALLED_FILES_FILE) $${DIST_DIR}/installed-files-rescued.txt; \

  14. exit 1 )

  15. endef

3.1.4.1 create-system-vendor-symlink

  如果存在vendor目录,就给vendor目录创建一个软连接。即/system/vendor 目录会被link到/vendor目录。

 
  1. define create-system-vendor-symlink

  2. $(hide) if [ -d $(TARGET_OUT)/vendor ] && [ ! -h $(TARGET_OUT)/vendor ]; then \

  3. echo 'Non-symlink $(TARGET_OUT)/vendor detected!' 1>&2; \

  4. echo 'You cannot install files to $(TARGET_OUT)/vendor while building a separate vendor.img!' 1>&2; \

  5. exit 1; \

  6. fi

3.1.4.2 create-system-product-symlink

  如果存在product目录,就给product目录创建一个软连接。即/system/product 目录会被link到/product目录。

 
  1. define create-system-product-symlink

  2. $(hide) if [ -d $(TARGET_OUT)/product ] && [ ! -h $(TARGET_OUT)/product ]; then \

  3. echo 'Non-symlink $(TARGET_OUT)/product detected!' 1>&2; \

  4. echo 'You cannot install files to $(TARGET_OUT)/product while building a separate product.img!' 1>&2; \

  5. exit 1; \

  6. fi

3.1.4.3 create-system-product_services-symlink

  如果存在product_services目录,就给product_services目录创建一个软连接。即/system/product_services 目录会被link到/product_services目录。

 
  1. define create-system-product_services-symlink

  2. $(hide) if [ -d $(TARGET_OUT)/product_services ] && [ ! -h $(TARGET_OUT)/product_services ]; then \

  3. echo 'Non-symlink $(TARGET_OUT)/product_services detected!' 1>&2; \

  4. echo 'You cannot install files to $(TARGET_OUT)/product_services while building a separate product_services.img!' 1>&2; \

  5. exit 1; \

  6. fi

3.2 build_image.py编译system.img

 
  1. $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \

  2. build/make/tools/releasetools/build_image.py \

  3. $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \

  4. || ( mkdir -p $${DIST_DIR}; cp $(INSTALLED_FILES_FILE) $${DIST_DIR}/installed-files-rescued.txt; \

  5. exit 1 )

执行build_image.py时,传入了4个参数:

$(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT)

$(TARGET_OUT)  :对应目录out/target/product/generic/system

$(systemimage_intermediates)/system_image_info.txt :system.img的配置文件

3.2.1 main()

  首先进行参数检查,如果参数个数小于4,直接退出,如果要生成system.img,mount指向system,调用ImagePropFromGlobalDict()来获取image的参数,再调用BuildImage()进行image的编译。

 
  1. def main(argv):

  2. if len(argv) != 4:

  3. print(__doc__)

  4. sys.exit(1)

  5. common.InitLogging()

  6. in_dir = argv[0]

  7. glob_dict_file = argv[1]

  8. out_file = argv[2]

  9. target_out = argv[3]

  10. glob_dict = LoadGlobalDict(glob_dict_file)

  11. if "mount_point" in glob_dict:

  12. # The caller knows the mount point and provides a dictionary needed by

  13. # BuildImage().

  14. image_properties = glob_dict

  15. else:

  16. image_filename = os.path.basename(out_file)

  17. mount_point = ""

  18. if image_filename == "system.img":

  19. mount_point = "system"

  20. elif image_filename == "system_other.img":

  21. mount_point = "system_other"

  22. elif image_filename == "userdata.img":

  23. mount_point = "data"

  24. elif image_filename == "cache.img":

  25. mount_point = "cache"

  26. elif image_filename == "vendor.img":

  27. mount_point = "vendor"

  28. elif image_filename == "odm.img":

  29. mount_point = "odm"

  30. elif image_filename == "oem.img":

  31. mount_point = "oem"

  32. elif image_filename == "product.img":

  33. mount_point = "product"

  34. elif image_filename == "product_services.img":

  35. mount_point = "product_services"

  36. else:

  37. logger.error("Unknown image file name %s", image_filename)

  38. sys.exit(1)

  39. image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)

  40. try:

  41. BuildImage(in_dir, image_properties, out_file, target_out)

  42. except:

  43. logger.error("Failed to build %s from %s", out_file, in_dir)

  44. raise

3.2.2 ImagePropFromGlobalDict()

  传入的mount_point为system,把类似system_reserved_size这些参数,存入system_image_info.txt中。

 
  1. def ImagePropFromGlobalDict(glob_dict, mount_point):

  2. """Build an image property dictionary from the global dictionary.

  3. Args:

  4. glob_dict: the global dictionary from the build system.

  5. mount_point: such as "system", "data" etc.

  6. """

  7. d = {}

  8. if "build.prop" in glob_dict:

  9. bp = glob_dict["build.prop"]

  10. if "ro.build.date.utc" in bp:

  11. d["timestamp"] = bp["ro.build.date.utc"]

  12. def copy_prop(src_p, dest_p):

  13. """Copy a property from the global dictionary.

  14. Args:

  15. src_p: The source property in the global dictionary.

  16. dest_p: The destination property.

  17. Returns:

  18. True if property was found and copied, False otherwise.

  19. """

  20. if src_p in glob_dict:

  21. d[dest_p] = str(glob_dict[src_p])

  22. return True

  23. return False

  24. common_props = (

  25. "extfs_sparse_flag",

  26. "squashfs_sparse_flag",

  27. "selinux_fc",

  28. "skip_fsck",

  29. "ext_mkuserimg",

  30. "verity",

  31. "verity_key",

  32. "verity_signer_cmd",

  33. "verity_fec",

  34. "verity_disable",

  35. "avb_enable",

  36. "avb_avbtool",

  37. "avb_salt",

  38. "use_dynamic_partition_size",

  39. )

  40. for p in common_props:

  41. copy_prop(p, p)

  42. d["mount_point"] = mount_point

  43. if mount_point == "system":

  44. copy_prop("avb_system_hashtree_enable", "avb_hashtree_enable")

  45. copy_prop("avb_system_add_hashtree_footer_args",

  46. "avb_add_hashtree_footer_args")

  47. copy_prop("avb_system_key_path", "avb_key_path")

  48. copy_prop("avb_system_algorithm", "avb_algorithm")

  49. copy_prop("fs_type", "fs_type")

  50. # Copy the generic system fs type first, override with specific one if

  51. # available.

  52. copy_prop("system_fs_type", "fs_type")

  53. copy_prop("system_headroom", "partition_headroom")

  54. copy_prop("system_size", "partition_size")

  55. if not copy_prop("system_journal_size", "journal_size"):

  56. d["journal_size"] = "0"

  57. copy_prop("system_verity_block_device", "verity_block_device")

  58. copy_prop("system_root_image", "system_root_image")

  59. copy_prop("root_dir", "root_dir")

  60. copy_prop("root_fs_config", "root_fs_config")

  61. copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")

  62. copy_prop("system_squashfs_compressor", "squashfs_compressor")

  63. copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")

  64. copy_prop("system_squashfs_block_size", "squashfs_block_size")

  65. copy_prop("system_squashfs_disable_4k_align", "squashfs_disable_4k_align")

  66. copy_prop("system_base_fs_file", "base_fs_file")

  67. copy_prop("system_extfs_inode_count", "extfs_inode_count")

  68. if not copy_prop("system_extfs_rsv_pct", "extfs_rsv_pct"):

  69. d["extfs_rsv_pct"] = "0"

  70. copy_prop("system_reserved_size", "partition_reserved_size")

  71. elif mount_point == "system_other":

  72. ...

  73. elif mount_point == "data":

  74. ...

  75. elif mount_point == "cache":

  76. ...

  77. elif mount_point == "vendor":

  78. ...

  79. elif mount_point == "product":

  80. ...

  81. elif mount_point == "product_services":

  82. ...

  83. elif mount_point == "odm":

  84. ...

  85. elif mount_point == "oem":

  86. ...

  87. d["partition_name"] = mount_point

  88. return d

3.2.3 BuildImage()

  上面得到system.img的参数后,接下来执行image的编译。

 
  1. def BuildImage(in_dir, prop_dict, out_file, target_out=None):

  2. """Builds an image for the files under in_dir and writes it to out_file.

  3. Args:

  4. in_dir: Path to input directory.

  5. prop_dict: A property dict that contains info like partition size. Values

  6. will be updated with computed values.

  7. out_file: The output image file.

  8. target_out: Path to the TARGET_OUT directory as in Makefile. It actually

  9. points to the /system directory under PRODUCT_OUT. fs_config (the one

  10. under system/core/libcutils) reads device specific FS config files from

  11. there.

  12. Raises:

  13. BuildImageError: On build image failures.

  14. """

  15. in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)

  16. build_command = []

  17. fs_type = prop_dict.get("fs_type", "")

  18. fs_spans_partition = True

  19. if fs_type.startswith("squash"):

  20. fs_spans_partition = False

  21. # Get a builder for creating an image that's to be verified by Verified Boot,

  22. # or None if not applicable.

  23. verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)

  24. if (prop_dict.get("use_dynamic_partition_size") == "true" and

  25. "partition_size" not in prop_dict):

  26. # If partition_size is not defined, use output of `du' + reserved_size.

  27. size = GetDiskUsage(in_dir)

  28. logger.info(

  29. "The tree size of %s is %d MB.", in_dir, size // BYTES_IN_MB)

  30. # If not specified, give us 16MB margin for GetDiskUsage error ...

  31. reserved_size = int(prop_dict.get("partition_reserved_size", BYTES_IN_MB * 16))

  32. partition_headroom = int(prop_dict.get("partition_headroom", 0))

  33. if fs_type.startswith("ext4") and partition_headroom > reserved_size:

  34. reserved_size = partition_headroom

  35. size += reserved_size

  36. # Round this up to a multiple of 4K so that avbtool works

  37. size = common.RoundUpTo4K(size)

  38. if fs_type.startswith("ext"):

  39. prop_dict["partition_size"] = str(size)

  40. prop_dict["image_size"] = str(size)

  41. if "extfs_inode_count" not in prop_dict:

  42. prop_dict["extfs_inode_count"] = str(GetInodeUsage(in_dir))

  43. logger.info(

  44. "First Pass based on estimates of %d MB and %s inodes.",

  45. size // BYTES_IN_MB, prop_dict["extfs_inode_count"])

  46. BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)

  47. sparse_image = False

  48. if "extfs_sparse_flag" in prop_dict:

  49. sparse_image = True

  50. fs_dict = GetFilesystemCharacteristics(out_file, sparse_image)

  51. os.remove(out_file)

  52. block_size = int(fs_dict.get("Block size", "4096"))

  53. free_size = int(fs_dict.get("Free blocks", "0")) * block_size

  54. reserved_size = int(prop_dict.get("partition_reserved_size", 0))

  55. partition_headroom = int(fs_dict.get("partition_headroom", 0))

  56. if fs_type.startswith("ext4") and partition_headroom > reserved_size:

  57. reserved_size = partition_headroom

  58. if free_size <= reserved_size:

  59. logger.info(

  60. "Not worth reducing image %d <= %d.", free_size, reserved_size)

  61. else:

  62. size -= free_size

  63. size += reserved_size

  64. if reserved_size == 0:

  65. # add .3% margin

  66. size = size * 1003 // 1000

  67. # Use a minimum size, otherwise we will fail to calculate an AVB footer

  68. # or fail to construct an ext4 image.

  69. size = max(size, 256 * 1024)

  70. if block_size <= 4096:

  71. size = common.RoundUpTo4K(size)

  72. else:

  73. size = ((size + block_size - 1) // block_size) * block_size

  74. extfs_inode_count = prop_dict["extfs_inode_count"]

  75. inodes = int(fs_dict.get("Inode count", extfs_inode_count))

  76. inodes -= int(fs_dict.get("Free inodes", "0"))

  77. # add .2% margin or 1 inode, whichever is greater

  78. spare_inodes = inodes * 2 // 1000

  79. min_spare_inodes = 1

  80. if spare_inodes < min_spare_inodes:

  81. spare_inodes = min_spare_inodes

  82. inodes += spare_inodes

  83. prop_dict["extfs_inode_count"] = str(inodes)

  84. prop_dict["partition_size"] = str(size)

  85. logger.info(

  86. "Allocating %d Inodes for %s.", inodes, out_file)

  87. if verity_image_builder:

  88. size = verity_image_builder.CalculateDynamicPartitionSize(size)

  89. prop_dict["partition_size"] = str(size)

  90. logger.info(

  91. "Allocating %d MB for %s.", size // BYTES_IN_MB, out_file)

  92. prop_dict["image_size"] = prop_dict["partition_size"]

  93. # Adjust the image size to make room for the hashes if this is to be verified.

  94. if verity_image_builder:

  95. max_image_size = verity_image_builder.CalculateMaxImageSize()

  96. prop_dict["image_size"] = str(max_image_size)

  97. mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)

  98. # Check if there's enough headroom space available for ext4 image.

  99. if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):

  100. CheckHeadroom(mkfs_output, prop_dict)

  101. if not fs_spans_partition and verity_image_builder:

  102. verity_image_builder.PadSparseImage(out_file)

  103. # Create the verified image if this is to be verified.

  104. if verity_image_builder:

  105. verity_image_builder.Build(out_file)

3.2.4 BuildImageMkfs()

  Android10.0 AOSP的system_image_info.txt中,几个变量如下:

 
  1. ext_mkuserimg=mkuserimg_mke2fs

  2. fs_type=ext4

  执行以下命令进行image的打包:

out/host/linux-x86/bin/mkuserimg_mke2fs '/out/soong/.temp/tmpK__WLx' 'out/target/product/generic/obj/PACKAGING/systemimage_intermediates/system.img' 'ext4' '/' '985493504' -j 0 -D 'out/target/product/generic/system' -L '/' -i 3280 -M 0 -c --inode_size 256 out/target/product/generic/obj/ETC/file_contexts.bin_intermediates/file_contexts.bin
 
  1. def BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config):

  2. ...

  3. build_command = []

  4. fs_type = prop_dict.get("fs_type", "")

  5. run_e2fsck = False

  6. if fs_type.startswith("ext"):

  7. build_command = [prop_dict["ext_mkuserimg"]]

  8. if "extfs_sparse_flag" in prop_dict:

  9. build_command.append(prop_dict["extfs_sparse_flag"])

  10. run_e2fsck = True

  11. build_command.extend([in_dir, out_file, fs_type,

  12. prop_dict["mount_point"]])

  13. build_command.append(prop_dict["image_size"])

  14. if "journal_size" in prop_dict:

  15. build_command.extend(["-j", prop_dict["journal_size"]])

  16. if "timestamp" in prop_dict:

  17. build_command.extend(["-T", str(prop_dict["timestamp"])])

  18. if fs_config:

  19. build_command.extend(["-C", fs_config])

  20. if target_out:

  21. build_command.extend(["-D", target_out])

  22. if "block_list" in prop_dict:

  23. build_command.extend(["-B", prop_dict["block_list"]])

  24. if "base_fs_file" in prop_dict:

  25. base_fs_file = ConvertBlockMapToBaseFs(prop_dict["base_fs_file"])

  26. build_command.extend(["-d", base_fs_file])

  27. build_command.extend(["-L", prop_dict["mount_point"]])

  28. if "extfs_inode_count" in prop_dict:

  29. build_command.extend(["-i", prop_dict["extfs_inode_count"]])

  30. if "extfs_rsv_pct" in prop_dict:

  31. build_command.extend(["-M", prop_dict["extfs_rsv_pct"]])

  32. if "flash_erase_block_size" in prop_dict:

  33. build_command.extend(["-e", prop_dict["flash_erase_block_size"]])

  34. if "flash_logical_block_size" in prop_dict:

  35. build_command.extend(["-o", prop_dict["flash_logical_block_size"]])

  36. # Specify UUID and hash_seed if using mke2fs.

  37. if prop_dict["ext_mkuserimg"] == "mkuserimg_mke2fs":

  38. if "uuid" in prop_dict:

  39. build_command.extend(["-U", prop_dict["uuid"]])

  40. if "hash_seed" in prop_dict:

  41. build_command.extend(["-S", prop_dict["hash_seed"]])

  42. if "ext4_share_dup_blocks" in prop_dict:

  43. build_command.append("-c")

  44. build_command.extend(["--inode_size", "256"])

  45. if "selinux_fc" in prop_dict:

  46. build_command.append(prop_dict["selinux_fc"])

  47. elif fs_type.startswith("squash"):

  48. ...

  49. elif fs_type.startswith("f2fs"):

  50. ...

  51. else:

  52. raise BuildImageError(

  53. "Error: unknown filesystem type: {}".format(fs_type))

  54. try:

  55. mkfs_output = common.RunAndCheckOutput(build_command)

  56. except:

  57. try:

  58. du = GetDiskUsage(in_dir)

  59. du_str = "{} bytes ({} MB)".format(du, du // BYTES_IN_MB)

  60. # Suppress any errors from GetDiskUsage() to avoid hiding the real errors

  61. # from common.RunAndCheckOutput().

  62. except Exception: # pylint: disable=broad-except

  63. logger.exception("Failed to compute disk usage with du")

  64. du_str = "unknown"

  65. ...

  66. raise

  67. if run_e2fsck and prop_dict.get("skip_fsck") != "true":

  68. unsparse_image = UnsparseImage(out_file, replace=False)

  69. # Run e2fsck on the inflated image file

  70. e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]

  71. try:

  72. common.RunAndCheckOutput(e2fsck_command)

  73. finally:

  74. os.remove(unsparse_image)

  75. return mkfs_output

6.总结

 至此,Android10.0中的image打包过程基本上理清了,过程比较绕,相互依赖比较多,逐层解开后,就比较好理解了。

 接下来我们再一起了解下kati、blueprint、ninja的相关知识,了解一下编译系统具体是怎么玩的。

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值