美文网首页
Android 编译系统--04:Image打包过程

Android 编译系统--04:Image打包过程

作者: DarcyZhou | 来源:发表于2023-12-05 08:43 被阅读0次

    本文转载自:Image打包流程-Android10.0编译系统(四)

    本文基于Android 10.0源码分析

    1.概述

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

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

    2.image打包入口

      在上一节的main.mk中,最后两步定义了需要编译的image和构建一个rom的过程。image构建和打包的一些依赖关系如下图所示:

    编译系统4-1.PNG
    # build/make/core/main.mk
           ...
    .PHONY: ramdisk
    ramdisk: $(INSTALLED_RAMDISK_TARGET)
    
    .PHONY: userdataimage
    userdataimage: $(INSTALLED_USERDATAIMAGE_TARGET)
    DATATARBALL_TARGET)
    
    .PHONY: cacheimage
    cacheimage: $(INSTALLED_CACHEIMAGE_TARGET)
    
    .PHONY: odmimage
    odmimage: $(INSTALLED_ODMIMAGE_TARGET)
    
    .PHONY: systemotherimage
    systemotherimage: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET)
    
    .PHONY: superimage_empty
    superimage_empty: $(INSTALLED_SUPERIMAGE_EMPTY_TARGET)
    
    .PHONY: bootimage
    bootimage: $(INSTALLED_BOOTIMAGE_TARGET)
    
    .PHONY: bootimage_debug
    bootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)
    
    ...
    
    .PHONY: droidcore
    droidcore: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) \
        $(INSTALLED_SYSTEMIMAGE_TARGET) \
        $(INSTALLED_RAMDISK_TARGET) \
        $(INSTALLED_BOOTIMAGE_TARGET) \
        $(INSTALLED_DEBUG_RAMDISK_TARGET) \
        $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \
        $(INSTALLED_RECOVERYIMAGE_TARGET) \
        $(INSTALLED_VBMETAIMAGE_TARGET) \
        $(INSTALLED_USERDATAIMAGE_TARGET) \
        $(INSTALLED_CACHEIMAGE_TARGET) \
        $(INSTALLED_BPTIMAGE_TARGET) \
        $(INSTALLED_VENDORIMAGE_TARGET) \
        $(INSTALLED_ODMIMAGE_TARGET) \
        $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) \
        ...\
        auxiliary \
        soong_docs
    ...
    

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

    # build/make/core/main.mk
    ifdef FULL_BUILD
      ...
    
    # TODO: Remove the 3 places in the tree that use ALL_DEFAULT_INSTALLED_MODULES
    # and get rid of it from this list.
    modules_to_install := $(sort \
        $(ALL_DEFAULT_INSTALLED_MODULES) \
        $(product_target_FILES) \
        $(product_host_FILES) \
        $(call get-tagged-modules,$(tags_to_install)) \
        $(CUSTOM_MODULES) \
      )
    ...
    # build/make/core/Makefile contains extra stuff that we don't want to pollute this
    # top-level makefile with.  It expects that ALL_DEFAULT_INSTALLED_MODULES
    # contains everything that's built during the current make, but it also further
    # extends ALL_DEFAULT_INSTALLED_MODULES.
    ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)
    include $(BUILD_SYSTEM)/Makefile # 导入Makefile文件
    modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))
    ALL_DEFAULT_INSTALLED_MODULES :=
    
    ...
    #endif
    

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

    # build/core/Makefile
    ...
    .PHONY: systemimage
    .PHONY: event-log-tags
    .PHONY: ramdisk-nodeps
    .PHONY: bootimage-nodeps
    .PHONY: bootimage-nodeps
    .PHONY: bootimage-nodeps
    .PHONY: bootimage-nodeps
    .PHONY: notice_files
    .PHONY: otacerts
    .PHONY: recoveryimage-nodeps
    .PHONY: recoveryimage
    .PHONY: ramdisk_debug-nodeps
    .PHONY: bootimage_debug-nodeps
    .PHONY: installed-file-list
    .PHONY: systemimage-nodeps snod
    .PHONY: sync syncsys
    .PHONY: systemtarball-nodeps
    .PHONY: stnod
    .PHONY: platform
    .PHONY: platform-java
    .PHONY: boottarball-nodeps btnod
    .PHONY: userdataimage-nodeps
    .PHONY: userdatatarball-nodeps
    .PHONY: bptimage-nodeps
    .PHONY: cacheimage-nodeps
    .PHONY: systemotherimage-nodeps
    .PHONY: vendorimage-nodeps vnod
    .PHONY: productimage-nodeps pnod
    .PHONY: productservicesimage-nodeps psnod
    .PHONY: odmimage-nodeps onod
    .PHONY: vbmetaimage-nodeps
    .PHONY: otatools
    .PHONY: otatools-package
    .PHONY: target-files-package
    .PHONY: otapackage
    .PHONY: otardppackage
    .PHONY: superimage_dist
    .PHONY: superimage
    .PHONY: superimage-nodeps supernod
    ...
    

    3.systemimage打包

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

    # Rules that need to be present for the all targets, even
    # if they don't do anything.
    .PHONY: systemimage
    systemimage:
    
    ...
    INSTALLED_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/system.img
    SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)
    
    $(INSTALLED_SYSTEMIMAGE_TARGET): $(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_TARGET)
    
    .PHONY: systemimage-nodeps snod
    systemimage-nodeps snod: $(filter-out systemimage-nodeps snod,$(MAKECMDGOALS)) \
                       | $(INTERNAL_USERIMAGES_DEPS)
           @echo "make $@: ignoring dependencies"
           $(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE_TARGET))
           $(hide) $(call assert-max-image-size,$(INSTALLED_SYSTEMIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))
    ...
    

    关于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,最终生成目标文件。

    $(PRODUCT_OUT)/system.img
    $(INSTALLED_SYSTEMIMAGE_TARGET): $(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))
    

    其中,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表示产品的名称,然后把文件拷贝到该目录中

    ## /build/make/core/definitions.mk
    # Copy a single file from one place to another,
    # preserving permissions and overwriting any existing
    # file.
    # When we used acp, it could not handle high resolution timestamps
    # on file systems like ext4\. Because of that, '-t' option was disabled
    # and copy-file-to-target was identical to copy-file-to-new-target.
    # Keep the behavior until we audit and ensure that switching this back
    # won't break anything.
    define copy-file-to-target
    @mkdir -p $(dir $@)
    $(hide) rm -f $@
    $(hide) cp "$<" "$@"
    endef
    

    3.1.2 RECOVERY_FROM_BOOT_PATCH

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

    ifneq (,$(filter true, $(BOARD_BUILD_SYSTEM_ROOT_IMAGE) $(BOARD_INCLUDE_RECOVERY_DTBO) $(BOARD_INCLUDE_RECOVERY_ACPIO)))
    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) $@
    

    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,相关依赖如下所示:

    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,$@)
    

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

    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 的依赖描述如下所示:

    # installed file list
    # Depending on anything that $(BUILT_SYSTEMIMAGE) depends on.
    # We put installed-files.txt ahead of image itself in the dependency graph
    # so that we can get the size stat even if the build fails due to too large
    # system image.
    INSTALLED_FILES_FILE := $(PRODUCT_OUT)/installed-files.txt
    INSTALLED_FILES_JSON := $(INSTALLED_FILES_FILE:.txt=.json)
    $(INSTALLED_FILES_FILE): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON)
    $(INSTALLED_FILES_FILE): $(FULL_SYSTEMIMAGE_DEPS) $(FILESLIST)
           @echo Installed file list: $@
           @mkdir -p $(dir $@)
           @rm -f $@
           $(hide) $(FILESLIST) $(TARGET_OUT) > $(@:.txt=.json)
           $(hide) build/make/tools/fileslist_util.py -c $(@:.txt=.json) > $@
    

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

    INTERNAL_USERIMAGES_DEPS := $(SIMG2IMG)
    INTERNAL_USERIMAGES_DEPS += $(MKEXTUSERIMG) $(MAKE_EXT4FS) $(E2FSCK) $(TUNE2FS)
    ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
    INTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG) $(MAKE_F2FS)
    endif
    ...
    INTERNAL_SYSTEMIMAGE_FILES := $(sort $(filter $(TARGET_OUT)/%, \
        $(ALL_GENERATED_SOURCES) \
        $(ALL_DEFAULT_INSTALLED_MODULES) \
        $(PDK_FUSION_SYSIMG_FILES) \
        $(RECOVERY_RESOURCE_ZIP)) \
        $(PDK_FUSION_SYMLINK_STAMP))
    
    FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS)
    
    1. 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;

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

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

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

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

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

      5. 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的定义如下所示:

    define build-systemimage-target
      @echo "Target system fs image: $(1)"
      $(call create-system-vendor-symlink)
      $(call create-system-product-symlink)
      $(call create-system-product_services-symlink)
      $(call check-apex-libs-absence-on-disk)
      @mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt
      $(call generate-image-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt,system, \
          skip_fsck=true)
      $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
          build/make/tools/releasetools/build_image.py \
          $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \
          || ( mkdir -p $${DIST_DIR}; cp $(INSTALLED_FILES_FILE) $${DIST_DIR}/installed-files-rescued.txt; \
               exit 1 )
    endef
    

    3.1.4.1 create-system-vendor-symlink

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

    define create-system-vendor-symlink
    $(hide) if [ -d $(TARGET_OUT)/vendor ] && [ ! -h $(TARGET_OUT)/vendor ]; then \
      echo 'Non-symlink $(TARGET_OUT)/vendor detected!' 1>&2; \
      echo 'You cannot install files to $(TARGET_OUT)/vendor while building a separate vendor.img!' 1>&2; \
      exit 1; \
    fi
    

    3.1.4.2 create-system-product-symlink

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

    define create-system-product-symlink
    $(hide) if [ -d $(TARGET_OUT)/product ] && [ ! -h $(TARGET_OUT)/product ]; then \
      echo 'Non-symlink $(TARGET_OUT)/product detected!' 1>&2; \
      echo 'You cannot install files to $(TARGET_OUT)/product while building a separate product.img!' 1>&2; \
      exit 1; \
    fi
    

    3.1.4.3 create-system-product_services-symlink

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

    define create-system-product_services-symlink
    $(hide) if [ -d $(TARGET_OUT)/product_services ] && [ ! -h $(TARGET_OUT)/product_services ]; then \
      echo 'Non-symlink $(TARGET_OUT)/product_services detected!' 1>&2; \
      echo 'You cannot install files to $(TARGET_OUT)/product_services while building a separate product_services.img!' 1>&2; \
      exit 1; \
    fi
    

    3.2 build_image.py编译system.img

     $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
          build/make/tools/releasetools/build_image.py \
          $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \
          || ( mkdir -p $${DIST_DIR}; cp $(INSTALLED_FILES_FILE) $${DIST_DIR}/installed-files-rescued.txt; \
               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的编译。

    ## build/make/tools/releasetools/build_image.py
    def main(argv):
      if len(argv) != 4:
        print(__doc__)
        sys.exit(1)
    
      common.InitLogging()
    
      in_dir = argv[0]
      glob_dict_file = argv[1]
      out_file = argv[2]
      target_out = argv[3]
    
      glob_dict = LoadGlobalDict(glob_dict_file)
      if "mount_point" in glob_dict:
        # The caller knows the mount point and provides a dictionary needed by
        # BuildImage().
        image_properties = glob_dict
      else:
        image_filename = os.path.basename(out_file)
        mount_point = ""
        if image_filename == "system.img":
          mount_point = "system"
        elif image_filename == "system_other.img":
          mount_point = "system_other"
        elif image_filename == "userdata.img":
          mount_point = "data"
        elif image_filename == "cache.img":
          mount_point = "cache"
        elif image_filename == "vendor.img":
          mount_point = "vendor"
        elif image_filename == "odm.img":
          mount_point = "odm"
        elif image_filename == "oem.img":
          mount_point = "oem"
        elif image_filename == "product.img":
          mount_point = "product"
        elif image_filename == "product_services.img":
          mount_point = "product_services"
        else:
          logger.error("Unknown image file name %s", image_filename)
          sys.exit(1)
    
        image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)
    
      try:
        BuildImage(in_dir, image_properties, out_file, target_out)
      except:
        logger.error("Failed to build %s from %s", out_file, in_dir)
        raise
    

    3.2.2 ImagePropFromGlobalDict()

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

    def ImagePropFromGlobalDict(glob_dict, mount_point):
      """Build an image property dictionary from the global dictionary.
      Args:
        glob_dict: the global dictionary from the build system.
        mount_point: such as "system", "data" etc.
      """
      d = {}
    
      if "build.prop" in glob_dict:
        bp = glob_dict["build.prop"]
        if "ro.build.date.utc" in bp:
          d["timestamp"] = bp["ro.build.date.utc"]
    
      def copy_prop(src_p, dest_p):
        """Copy a property from the global dictionary.
        Args:
          src_p: The source property in the global dictionary.
          dest_p: The destination property.
        Returns:
          True if property was found and copied, False otherwise.
        """
        if src_p in glob_dict:
          d[dest_p] = str(glob_dict[src_p])
          return True
        return False
    
      common_props = (
          "extfs_sparse_flag",
          "squashfs_sparse_flag",
          "selinux_fc",
          "skip_fsck",
          "ext_mkuserimg",
          "verity",
          "verity_key",
          "verity_signer_cmd",
          "verity_fec",
          "verity_disable",
          "avb_enable",
          "avb_avbtool",
          "avb_salt",
          "use_dynamic_partition_size",
      )
      for p in common_props:
        copy_prop(p, p)
    
      d["mount_point"] = mount_point
      if mount_point == "system":
        copy_prop("avb_system_hashtree_enable", "avb_hashtree_enable")
        copy_prop("avb_system_add_hashtree_footer_args",
                  "avb_add_hashtree_footer_args")
        copy_prop("avb_system_key_path", "avb_key_path")
        copy_prop("avb_system_algorithm", "avb_algorithm")
        copy_prop("fs_type", "fs_type")
        # Copy the generic system fs type first, override with specific one if
        # available.
        copy_prop("system_fs_type", "fs_type")
        copy_prop("system_headroom", "partition_headroom")
        copy_prop("system_size", "partition_size")
        if not copy_prop("system_journal_size", "journal_size"):
          d["journal_size"] = "0"
        copy_prop("system_verity_block_device", "verity_block_device")
        copy_prop("system_root_image", "system_root_image")
        copy_prop("root_dir", "root_dir")
        copy_prop("root_fs_config", "root_fs_config")
        copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")
        copy_prop("system_squashfs_compressor", "squashfs_compressor")
        copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")
        copy_prop("system_squashfs_block_size", "squashfs_block_size")
        copy_prop("system_squashfs_disable_4k_align", "squashfs_disable_4k_align")
        copy_prop("system_base_fs_file", "base_fs_file")
        copy_prop("system_extfs_inode_count", "extfs_inode_count")
        if not copy_prop("system_extfs_rsv_pct", "extfs_rsv_pct"):
          d["extfs_rsv_pct"] = "0"
        copy_prop("system_reserved_size", "partition_reserved_size")
      elif mount_point == "system_other":
        ...
      elif mount_point == "data":
        ...
      elif mount_point == "cache":
        ...
      elif mount_point == "vendor":
        ...
      elif mount_point == "product":
        ...
      elif mount_point == "product_services":
        ...
      elif mount_point == "odm":
        ...
      elif mount_point == "oem":
        ...
      d["partition_name"] = mount_point
      return d
    

    3.2.3 BuildImage()

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

    def BuildImage(in_dir, prop_dict, out_file, target_out=None):
      """Builds an image for the files under in_dir and writes it to out_file.
      Args:
        in_dir: Path to input directory.
        prop_dict: A property dict that contains info like partition size. Values
            will be updated with computed values.
        out_file: The output image file.
        target_out: Path to the TARGET_OUT directory as in Makefile. It actually
            points to the /system directory under PRODUCT_OUT. fs_config (the one
            under system/core/libcutils) reads device specific FS config files from
            there.
      Raises:
        BuildImageError: On build image failures.
      """
      in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)
    
      build_command = []
      fs_type = prop_dict.get("fs_type", "")
    
      fs_spans_partition = True
      if fs_type.startswith("squash"):
        fs_spans_partition = False
    
      # Get a builder for creating an image that's to be verified by Verified Boot,
      # or None if not applicable.
      verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)
      if (prop_dict.get("use_dynamic_partition_size") == "true" and
          "partition_size" not in prop_dict):
        # If partition_size is not defined, use output of `du' + reserved_size.
        size = GetDiskUsage(in_dir)
        logger.info(
            "The tree size of %s is %d MB.", in_dir, size // BYTES_IN_MB)
        # If not specified, give us 16MB margin for GetDiskUsage error ...
        reserved_size = int(prop_dict.get("partition_reserved_size", BYTES_IN_MB * 16))
        partition_headroom = int(prop_dict.get("partition_headroom", 0))
        if fs_type.startswith("ext4") and partition_headroom > reserved_size:
          reserved_size = partition_headroom
        size += reserved_size
        # Round this up to a multiple of 4K so that avbtool works
        size = common.RoundUpTo4K(size)
        if fs_type.startswith("ext"):
          prop_dict["partition_size"] = str(size)
          prop_dict["image_size"] = str(size)
          if "extfs_inode_count" not in prop_dict:
            prop_dict["extfs_inode_count"] = str(GetInodeUsage(in_dir))
          logger.info(
              "First Pass based on estimates of %d MB and %s inodes.",
              size // BYTES_IN_MB, prop_dict["extfs_inode_count"])
          BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
          sparse_image = False
          if "extfs_sparse_flag" in prop_dict:
            sparse_image = True
          fs_dict = GetFilesystemCharacteristics(out_file, sparse_image)
          os.remove(out_file)
          block_size = int(fs_dict.get("Block size", "4096"))
          free_size = int(fs_dict.get("Free blocks", "0")) * block_size
          reserved_size = int(prop_dict.get("partition_reserved_size", 0))
          partition_headroom = int(fs_dict.get("partition_headroom", 0))
          if fs_type.startswith("ext4") and partition_headroom > reserved_size:
            reserved_size = partition_headroom
          if free_size <= reserved_size:
            logger.info(
                "Not worth reducing image %d <= %d.", free_size, reserved_size)
          else:
            size -= free_size
            size += reserved_size
            if reserved_size == 0:
              # add .3% margin
              size = size * 1003 // 1000
            # Use a minimum size, otherwise we will fail to calculate an AVB footer
            # or fail to construct an ext4 image.
            size = max(size, 256 * 1024)
            if block_size <= 4096:
              size = common.RoundUpTo4K(size)
            else:
              size = ((size + block_size - 1) // block_size) * block_size
          extfs_inode_count = prop_dict["extfs_inode_count"]
          inodes = int(fs_dict.get("Inode count", extfs_inode_count))
          inodes -= int(fs_dict.get("Free inodes", "0"))
          # add .2% margin or 1 inode, whichever is greater
          spare_inodes = inodes * 2 // 1000
          min_spare_inodes = 1
          if spare_inodes < min_spare_inodes:
            spare_inodes = min_spare_inodes
          inodes += spare_inodes
          prop_dict["extfs_inode_count"] = str(inodes)
          prop_dict["partition_size"] = str(size)
          logger.info(
              "Allocating %d Inodes for %s.", inodes, out_file)
        if verity_image_builder:
          size = verity_image_builder.CalculateDynamicPartitionSize(size)
        prop_dict["partition_size"] = str(size)
        logger.info(
            "Allocating %d MB for %s.", size // BYTES_IN_MB, out_file)
    
      prop_dict["image_size"] = prop_dict["partition_size"]
    
      # Adjust the image size to make room for the hashes if this is to be verified.
      if verity_image_builder:
        max_image_size = verity_image_builder.CalculateMaxImageSize()
        prop_dict["image_size"] = str(max_image_size)
    
      mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
    
      # Check if there's enough headroom space available for ext4 image.
      if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):
        CheckHeadroom(mkfs_output, prop_dict)
      if not fs_spans_partition and verity_image_builder:
        verity_image_builder.PadSparseImage(out_file)
      # Create the verified image if this is to be verified.
      if verity_image_builder:
        verity_image_builder.Build(out_file)
    

    3.2.4 BuildImageMkfs()

      Android 10.0 AOSP的system_image_info.txt中,几个变量如下:

    ext_mkuserimg=mkuserimg_mke2fs
    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
    
    def BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config):
      ...
      build_command = []
      fs_type = prop_dict.get("fs_type", "")
      run_e2fsck = False
    
      if fs_type.startswith("ext"):
        build_command = [prop_dict["ext_mkuserimg"]]
        if "extfs_sparse_flag" in prop_dict:
          build_command.append(prop_dict["extfs_sparse_flag"])
          run_e2fsck = True
        build_command.extend([in_dir, out_file, fs_type,
                              prop_dict["mount_point"]])
        build_command.append(prop_dict["image_size"])
        if "journal_size" in prop_dict:
          build_command.extend(["-j", prop_dict["journal_size"]])
        if "timestamp" in prop_dict:
          build_command.extend(["-T", str(prop_dict["timestamp"])])
        if fs_config:
          build_command.extend(["-C", fs_config])
        if target_out:
          build_command.extend(["-D", target_out])
        if "block_list" in prop_dict:
          build_command.extend(["-B", prop_dict["block_list"]])
        if "base_fs_file" in prop_dict:
          base_fs_file = ConvertBlockMapToBaseFs(prop_dict["base_fs_file"])
          build_command.extend(["-d", base_fs_file])
        build_command.extend(["-L", prop_dict["mount_point"]])
        if "extfs_inode_count" in prop_dict:
          build_command.extend(["-i", prop_dict["extfs_inode_count"]])
        if "extfs_rsv_pct" in prop_dict:
          build_command.extend(["-M", prop_dict["extfs_rsv_pct"]])
        if "flash_erase_block_size" in prop_dict:
          build_command.extend(["-e", prop_dict["flash_erase_block_size"]])
        if "flash_logical_block_size" in prop_dict:
          build_command.extend(["-o", prop_dict["flash_logical_block_size"]])
        # Specify UUID and hash_seed if using mke2fs.
        if prop_dict["ext_mkuserimg"] == "mkuserimg_mke2fs":
          if "uuid" in prop_dict:
            build_command.extend(["-U", prop_dict["uuid"]])
          if "hash_seed" in prop_dict:
            build_command.extend(["-S", prop_dict["hash_seed"]])
        if "ext4_share_dup_blocks" in prop_dict:
          build_command.append("-c")
        build_command.extend(["--inode_size", "256"])
        if "selinux_fc" in prop_dict:
          build_command.append(prop_dict["selinux_fc"])
      elif fs_type.startswith("squash"):
        ...
      elif fs_type.startswith("f2fs"):
        ...
      else:
        raise BuildImageError(
            "Error: unknown filesystem type: {}".format(fs_type))
    
      try:
        mkfs_output = common.RunAndCheckOutput(build_command)
      except:
        try:
          du = GetDiskUsage(in_dir)
          du_str = "{} bytes ({} MB)".format(du, du // BYTES_IN_MB)
        # Suppress any errors from GetDiskUsage() to avoid hiding the real errors
        # from common.RunAndCheckOutput().
        except Exception:  # pylint: disable=broad-except
          logger.exception("Failed to compute disk usage with du")
          du_str = "unknown"
        ...
        raise
    
      if run_e2fsck and prop_dict.get("skip_fsck") != "true":
        unsparse_image = UnsparseImage(out_file, replace=False)
    
        # Run e2fsck on the inflated image file
        e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]
        try:
          common.RunAndCheckOutput(e2fsck_command)
        finally:
          os.remove(unsparse_image)
    
      return mkfs_output
    

    4.总结

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

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

    相关文章

      网友评论

          本文标题:Android 编译系统--04:Image打包过程

          本文链接:https://www.haomeiwen.com/subject/hlyxidtx.html