安卓镜像文件编译打包过程

安卓系统中有system.img,boot.img,userdata.img等镜像文件,那么这些镜像文件是怎么形成的呢?下面我们以system.img为例来描述系统镜像文件的编译打包过程。

(一)system.img的编译生成过程

build/core/Makefile中相关变量的定义:

INSTALLED_SYSTEMIMAGE := $(PRODUCT_OUT)/system.img
							// system.img实际产生的位置,即out/target/product/~/system.img

编译的target :

$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH) | $(ACP)
 $(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))
 $(copy-file-to-target)
 @echo "Install system fs image: $@"

systemimage: $(INSTALLED_SYSTEMIMAGE)


安卓系统编译时,会including 相关的MK文件,因此可以通过make systemimage来单独编译system.img。当$(INSTALLED_SYSTEMIMAGE)执行时,会先进行$(BUILT_SYSTEMIMAGE)的编译,即:

$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
 $(call build-systemimage-target,$@)
(1)FULL_SYSTEMIMAGE_DEPS
FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS)

INTERNAL_SYSTEMIMAGE_FILES := $(filter $(TARGET_OUT)/%, \
    $(ALL_DEFAULT_INSTALLED_MODULES) \
    $(RECOVERY_RESOURCE_ZIP))
    $(PDK_FUSION_SYSIMG_FILES) \
    $(ALL_PREBUILT) \
    $(ALL_GENERATED_SOURCES) \


这部分基本就是system所包含的内容了,有兴趣的可以根据TARGET跟下去。

编译过程中在Linux的终端上可以看到"Install system fs image:" 的输出,在上面两个TARGET执行后,会调用build-systemimage-target方法,如下:

define build-systemimage-target
  @echo "Target system fs image: $(1)"
  $(call create-system-vendor-symlink)
  @mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt
  $(call generate-userimage-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt, \
      skip_fsck=true)
  $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
      ./build/tools/releasetools/build_image.py \
      $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \
      || ( echo "Out of space? the tree size of $(TARGET_OUT) is (MB): " 1>&2 ;\
           du -sm $(TARGET_OUT) 1>&2;\
           if [ "$(INTERNAL_USERIMAGES_EXT_VARIANT)" == "ext4" ]; then \
              maxsize=$(BOARD_SYSTEMIMAGE_PARTITION_SIZE); \
               if [ "$(BOARD_HAS_EXT4_RESERVED_BLOCKS)" == "true" ]; then \
                   maxsize=$$((maxsize - 4096 * 4096)); \
               fi; \
               echo "The max is $$(( maxsize / 1048576 )) MB." 1>&2 ;\
           else \
               echo "The max is $$(( $(BOARD_SYSTEMIMAGE_PARTITION_SIZE) / 1048576 )) MB." 1>&2 ;\
           fi; \
           mkdir -p $(DIST_DIR); cp $(INSTALLED_FILES_FILE) $(DIST_DIR)/installed-files-rescued.txt; \
          exit 1 )
endef


build-systemimage-target方法中会进行如下几件事情:

create-system-vendor-symlink:将system/vendor软连接到vendor下面

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

然后创建systemimage_intermediates目录,强制删除目录下面的system_image_info.txt文件后重新生成该文件(即更新文件)。接下来就是使用python脚本将已经生成好的system目录整体打包了。

(二)system.img打包过程

$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
      ./build/tools/releasetools/build_image.py \
      $(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) 

在执行build_image.py文件时,需要传递四个参数 $(TARGET_OUT)    $(systemimage_intermediates)/system_image_info.txt   $(1)   $(TARGET_OUT)

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

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

build/tools/releasetools/build_image.py在执行的时候会先检查传入的参数是否满足条件:

def main(argv):
  if len(argv) != 4:
    print __doc__
    sys.exit(1)

  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 dictionay 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 == "oem.img":
      mount_point = "oem"
    else:
      print >> sys.stderr, "error: unknown image file name ", image_filename
      exit(1)

    image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)

  if not BuildImage(in_dir, image_properties, out_file, target_out):
    print >> sys.stderr, "error: failed to build %s from %s" % (out_file,
                                                                in_dir)
    exit(1)

if __name__ == '__main__':
  main(sys.argv[1:])


传给build_image.py的参数必须是四个,数目不对程序会自动退出。然后进行目录的判断,如果是system.img就将挂载点指向system。这里global_dict_file是指system_image_info.txt文件。调用ImagePropFromGlobalDict()方法获取img的配置参数。

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):
    if src_p in glob_dict:
      d[dest_p] = str(glob_dict[src_p])

  common_props = (
      "extfs_sparse_flag",
      "squashfs_sparse_flag",
      "mkyaffs2_extra_flags",
      "selinux_fc",
      "skip_fsck",
      "verity",
      "verity_key",
      "verity_signer_cmd",
      "verity_fec"
      )
  for p in common_props:
    copy_prop(p, p)

  d["mount_point"] = mount_point
  if mount_point == "system":
    copy_prop("fs_type", "fs_type")
    # Copy the generic sysetem fs type first, override with specific one if
    # available.
    copy_prop("system_fs_type", "fs_type")
    copy_prop("system_size", "partition_size")
    copy_prop("system_journal_size", "journal_size")
    copy_prop("system_verity_block_device", "verity_block_device")
    copy_prop("system_root_image", "system_root_image")
    copy_prop("ramdisk_dir", "ramdisk_dir")
    copy_prop("ramdisk_fs_config", "ramdisk_fs_config")
    copy_prop("has_ext4_reserved_blocks", "has_ext4_reserved_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")
     ......

  return 


得到system.img的参数后通过BuildImage()方法进行打包。


def BuildImage(in_dir, prop_dict, out_file, target_out=None):
  """Build an image to out_file from in_dir with property prop_dict.

  Args:
    in_dir: path of input directory.
    prop_dict: property dictionary.
    out_file: path of the output image file.
    target_out: path of the product out directory to read device specific FS config files.
    ......
  build_command = []
  fs_type = prop_dict.get("fs_type", "")
		 			
if fs_type.startswith("ext"):
    build_command = ["mkuserimg.sh"]
    if "extfs_sparse_flag" in prop_dict:
     build_command.append(prop_dict["extfs_sparse_flag"])
                      prop_dict["mount_point"]])
    build_command.append(prop_dict["partition_size"]
    ......
   else:
       build_command = ["mkyaffs2image", "-f"]    if prop_dict.get("mkyaffs2_extra_flags", None):
       build_command.extend(prop_dict["mkyaffs2_extra_flags"].split())
       build_command.append(in_dir)
       build_command.append(out_file)
  try:
    if reserved_blocks and fs_type.startswith("ext4"):
      (ext4fs_output, exit_code) = RunCommand(build_command)
    else:
      (_, exit_code) = RunCommand(build_command)
  finally:
    if in_dir != origin_in:
      # Clean up temporary directories and files.
      shutil.rmtree(in_dir, ignore_errors=True)
      if fs_config:
        os.remove(fs_config)
    if base_fs_file is not None:
      os.remove(base_fs_file)
  if exit_code != 0:
    return False


获取在文件形同中的类型,并根据类型配置相应的打包命令

工程项目编译完成后,镜像文件在终端上可以直接通过命令打包(Linux环境中,需要sudo权限):


out/host/linux-x86/bin/make_ext4fs -s  -l 300M -a system/ test/system.img system


-s 是指ext4的s模式制作

-l 300M 是指镜像文件的大小

-a 是指镜像文件用于安卓系统

挂在点是/system


将镜像文件解包可以用如下命令:

simg2img A.img A.img.raw
sudo mount -t ext4 -o loop A.img.raw A


你可能感兴趣的:(综合篇)