build_image.py進(jìn)行簡(jiǎn)單分析。
編譯系統(tǒng)中調(diào)用build_image.py
生成系統(tǒng)鏡像的target在build\core\Makefile中,
#----------build\core\Makefile-------------
# $(1): output file
define build-systemimage-target
@echo "Target system fs image: $(1)"
@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)
endef
其中,可以看出來(lái)主要是借助了build_image.py去生成鏡像,/build/tools/releasetools/build_image.py (systemimage_intermediates)/system_image_info.txt $(1) , 有三個(gè)參數(shù),一個(gè)是作為輸入的文件目錄,system_image_info.txt,和最終生成的鏡像名,例如system.img。
build_image.py分析
由于上面makefile中是在shell中直接調(diào)用build_image.py(不是import 模塊),所以name為main,會(huì)直接調(diào)用main函數(shù),
#參數(shù)格式必須為3個(gè)
# $(TARGET_OUT) system_image_info.txt output_file
def main(argv):
if len(argv) != 3:
print __doc__
sys.exit(1)
in_dir = argv[0]
glob_dict_file = argv[1]
out_file = argv[2]
#將system_image_info.txt中的內(nèi)容保存到字典中,例如{'fs_type':'ext4',.....}
glob_dict = LoadGlobalDict(glob_dict_file)
#basename獲取文件名(不包括路徑),這里為system.img
image_filename = os.path.basename(out_file)
mount_point = ""
if image_filename == "system.img":
mount_point = "system"
elif image_filename == "userdata.img":
mount_point = "data"
elif image_filename == "cache.img":
mount_point = "cache"
elif image_filename == "vendor.img":
mount_point = "vendor"
else:
print >> sys.stderr, "error: unknown image file name ", image_filename
exit(1)
#print >>
#the first expression after the >> must evaluate to a “file-like” object,
#輸入為system_image_info.txt內(nèi)容字典和system
image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)
if not BuildImage(in_dir, image_properties, out_file):
print >> sys.stderr, "error: failed to build %s from %s" % (out_file, in_dir)
exit(1)
#當(dāng)被直接python build_image.py執(zhí)行時(shí) __name__為__main__
#當(dāng)被其他import時(shí),__name__是模塊的名字,build_image
#argv[0]是腳本名
if __name__ == '__main__':
main(sys.argv[1:])
其中,system_image_info.txt一個(gè)例子如下所示,一些參數(shù)及其值,
fs_type=ext4
system_size=1288491008
userdata_size=5835325440
cache_fs_type=ext4
cache_size=268435456
extfs_sparse_flag=-s
selinux_fc=out/target/product/msm8916_64/root/file_contexts
verity=true
verity_key=build/target/product/security/verity
verity_signer_cmd=out/host/linux-x86/bin/verity_signer
system_verity_block_device=/dev/block/bootdevice/by-name/system
skip_fsck=true
LoadGlobalDict函數(shù)如下,將system_image_info.txt中的內(nèi)容保存到字典中,例如{‘fs_type’:’ext4’,…..}
def LoadGlobalDict(filename):
"""Load "name=value" pairs from filename"""
d = {}
f = open(filename)
#對(duì)文件對(duì)象的迭代
#文件中#不處理,是注釋
#strip去除字符串兩頭,不包括內(nèi)部的空格
#split將字符串分割成序列 str.split(sep=None, maxsplit=-1)
#If maxsplit is given, at most maxsplit splits are done (thus, the list will have at most maxsplit+1 elements).
#>>>'1,2,3'.split(',', maxsplit=1)
#['1', '2,3']
#所以用=分割,只有=前面和后面2個(gè)元素
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
k, v = line.split("=", 1)
d[k] = v
f.close()
return d
ImagePropFromGlobalDict函數(shù),從全局字典(system_image_info.txt中有些東西不是都有用)取出需要的東西,返回一個(gè)新字典。
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 = {}
def copy_prop(src_p, dest_p):
if src_p in glob_dict:
d[dest_p] = str(glob_dict[src_p])
#extfs_sparse_flag=-s
#skip_fsck=true
#selinux_fc=out/target/product/msm8916_64/root/file_contexts
#將common_props屬性中的值保存到新建的字典d中,
common_props = (
"extfs_sparse_flag",
"mkyaffs2_extra_flags",
"selinux_fc",
"skip_fsck",
)
for p in common_props:
copy_prop(p, p)
#添加'mount_point':'system'
d["mount_point"] = mount_point
if mount_point == "system":
#'fs_type':'ext4'
#'partition_size':'1288491008'
copy_prop("fs_type", "fs_type")
copy_prop("system_size", "partition_size")
elif mount_point == "data":
copy_prop("fs_type", "fs_type")
copy_prop("userdata_size", "partition_size")
elif mount_point == "cache":
copy_prop("cache_fs_type", "fs_type")
copy_prop("cache_size", "partition_size")
elif mount_point == "vendor":
copy_prop("vendor_fs_type", "fs_type")
copy_prop("vendor_size", "partition_size")
return d
最后調(diào)用BuildImage函數(shù),其實(shí)就是組了一個(gè)命令,然后執(zhí)行該命令(mkuserimg.sh -s in_dir out_file ext4 system 1288491008 out/target/product/msm8916_64/root/file_contexts)。
def BuildImage(in_dir, prop_dict, out_file):
"""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.
Returns:
True iff the image is built successfully.
"""
build_command = []
#字典的get方法,如果鍵不存在,沒有任何異常,而得到None
fs_type = prop_dict.get("fs_type", "")
run_fsck = False
#文件系統(tǒng)以ext開頭
#這里是ext4,走這里,其實(shí)就是組一個(gè)要執(zhí)行的命令 mkuserimg.sh -s in_dir out_file ext4 system 1288491008 out/target/product/msm8916_64/root/file_contexts
if fs_type.startswith("ext"):
build_command = ["mkuserimg.sh"]
if "extfs_sparse_flag" in prop_dict:
build_command.append(prop_dict["extfs_sparse_flag"])
run_fsck = True
build_command.extend([in_dir, out_file, fs_type,
prop_dict["mount_point"]])
if "partition_size" in prop_dict:
build_command.append(prop_dict["partition_size"])
if "selinux_fc" in prop_dict:
build_command.append(prop_dict["selinux_fc"])
else:
build_command = ["mkyaffs2image", "-f"]
#split用于將字符串分割成list,沒有參數(shù)表示用空格分割
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)
if "selinux_fc" in prop_dict:
build_command.append(prop_dict["selinux_fc"])
build_command.append(prop_dict["mount_point"])
#執(zhí)行該命令mkuserimg.sh -s in_dir out_file ext4 system
exit_code = RunCommand(build_command)
if exit_code != 0:
return False
# >>> os.path.dirname('c:\\Python\\a.txt') 返回目錄
# 'c:\\Python'
# >>> os.path.basename('c:\\Python\\a.txt') 返回文件名
# 'a.txt'
# os.path.join連接目錄與文件名或目錄
#skip_fsck=true,不走這里
if run_fsck and prop_dict.get("skip_fsck") != "true":
# Inflate the sparse image
unsparse_image = os.path.join(
os.path.dirname(out_file), "unsparse_" + os.path.basename(out_file))
inflate_command = ["simg2img", out_file, unsparse_image]
exit_code = RunCommand(inflate_command)
#This function is semantically identical to unlink().
#remove和unlink一樣
if exit_code != 0:
os.remove(unsparse_image)
return False
# Run e2fsck on the inflated image file
e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]
exit_code = RunCommand(e2fsck_command)
os.remove(unsparse_image)
return exit_code == 0
其中,RunCommand其實(shí)就類似于c語(yǔ)言中的system(),執(zhí)行一段命令,獲取返回值,用popen實(shí)現(xiàn)的。
def RunCommand(cmd):
""" Echo and run the given command
Args:
cmd: the command represented as a list of strings.
Returns:
The exit code.
"""
print "Running: ", " ".join(cmd)
#創(chuàng)建子進(jìn)程
p = subprocess.Popen(cmd)
#等待子進(jìn)程執(zhí)行完
p.communicate()
return p.returncode
最終其實(shí)是調(diào)用了mkuserimg.sh腳本,腳本核心是調(diào)用make_ext4fs 應(yīng)用程序
#system\extras\ext4_utils\mkuserimg.sh
function usage() {
cat<<EOT
Usage:
mkuserimg.sh [-s] SRC_DIR OUTPUT_FILE EXT_VARIANT MOUNT_POINT SIZE [FILE_CONTEXTS]
EOT
}
echo "in mkuserimg.sh PATH=$PATH"
ENABLE_SPARSE_IMAGE=
if [ "$1" = "-s" ]; then
ENABLE_SPARSE_IMAGE="-s"
shift
fi
if [ $# -ne 5 -a $# -ne 6 ]; then
usage
exit 1
fi
SRC_DIR=$1
if [ ! -d $SRC_DIR ]; then
echo "Can not find directory $SRC_DIR!"
exit 2
fi
OUTPUT_FILE=$2
EXT_VARIANT=$3
MOUNT_POINT=$4
SIZE=$5
FC=$6
case $EXT_VARIANT in
ext4) ;;
*) echo "Only ext4 is supported!"; exit 3 ;;
esac
if [ -z $MOUNT_POINT ]; then
echo "Mount point is required"
exit 2
fi
if [ -z $SIZE ]; then
echo "Need size of filesystem"
exit 2
fi
if [ -n "$FC" ]; then
FCOPT="-S $FC"
fi
#核心是make_ext4fs 應(yīng)用程序
MAKE_EXT4FS_CMD="make_ext4fs $ENABLE_SPARSE_IMAGE $FCOPT -l $SIZE -a $MOUNT_POINT $OUTPUT_FILE $SRC_DIR"
echo $MAKE_EXT4FS_CMD
$MAKE_EXT4FS_CMD
if [ $? -ne 0 ]; then
exit 4
fi
而make_ext4fs 應(yīng)用程序具體在system\extras\ext4_utils中生成,makefile如下,其中makefile中有兩個(gè)make_ext4fs的module,一個(gè)是host(BUILD_HOST_EXECUTABLE),一個(gè)是target的(BUILD_EXECUTABLE),這個(gè)命令是在編譯機(jī)調(diào)用,也就是host上而不是target上。
include $(CLEAR_VARS)
LOCAL_SRC_FILES := make_ext4fs_main.c
LOCAL_MODULE := make_ext4fs
LOCAL_STATIC_LIBRARIES += \
libext4_utils_host \
libsparse_host \
libz
ifeq ($(HOST_OS),windows)
LOCAL_LDLIBS += -lws2_32
else
LOCAL_STATIC_LIBRARIES += libselinux
LOCAL_CFLAGS := -DHOST
endif
include $(BUILD_HOST_EXECUTABLE)