安卓系统中有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:])
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