Add repack_super_image
Bug: 148839595
Test: make dist gsi_utils
Test: repack_super_image \
--ota-tools $ANDROID_PRODUCT_OUT/otatools.zip \
--misc-info ./misc_info.txt \
./super.img product= \
system=$ANDROID_PRODUCT_OUT/system.img
Change-Id: I29ff8c6f0e117d3b42cc975929bd0a0823136bc9
This commit is contained in:
33
gsi/repack_super_image/Android.bp
Normal file
33
gsi/repack_super_image/Android.bp
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright (C) 2020 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
python_binary_host {
|
||||||
|
name: "repack_super_image",
|
||||||
|
main: "repack_super_image.py",
|
||||||
|
srcs: [
|
||||||
|
"repack_super_image.py",
|
||||||
|
],
|
||||||
|
version: {
|
||||||
|
py2: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
py3: {
|
||||||
|
enabled: true,
|
||||||
|
embedded_launcher: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dist: {
|
||||||
|
targets: ["gsi_utils"],
|
||||||
|
},
|
||||||
|
}
|
||||||
206
gsi/repack_super_image/repack_super_image.py
Normal file
206
gsi/repack_super_image/repack_super_image.py
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Copyright 2020 - The Android Open Source Project
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""This script is for test infrastructure to mix images in a super image."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import stat
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
|
||||||
|
# The file extension of the unpacked images.
|
||||||
|
IMG_FILE_EXT = ".img"
|
||||||
|
|
||||||
|
# The directory containing executable files in OTA tools zip.
|
||||||
|
BIN_DIR_NAME = "bin"
|
||||||
|
|
||||||
|
|
||||||
|
def existing_abs_path(path):
|
||||||
|
"""Validates that a path exists and returns the absolute path."""
|
||||||
|
abs_path = os.path.abspath(path)
|
||||||
|
if not os.path.exists(abs_path):
|
||||||
|
raise ValueError(path + " does not exist.")
|
||||||
|
return abs_path
|
||||||
|
|
||||||
|
|
||||||
|
def partition_image(part_img):
|
||||||
|
"""Splits a string into a pair of strings by "="."""
|
||||||
|
part, sep, img = part_img.partition("=")
|
||||||
|
if not part or not sep:
|
||||||
|
raise ValueError(part_img + " is not in the format of "
|
||||||
|
"PARITITON_NAME=IMAGE_PATH.")
|
||||||
|
return part, (existing_abs_path(img) if img else "")
|
||||||
|
|
||||||
|
|
||||||
|
def unzip_ota_tools(ota_tools_zip, output_dir):
|
||||||
|
"""Unzips OTA tools and sets the files in bin/ to be executable."""
|
||||||
|
ota_tools_zip.extractall(output_dir)
|
||||||
|
permissions = (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH |
|
||||||
|
stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
|
||||||
|
for root_dir, dir_names, file_names in os.walk(
|
||||||
|
os.path.join(output_dir, BIN_DIR_NAME)):
|
||||||
|
for file_name in file_names:
|
||||||
|
file_path = os.path.join(root_dir, file_name)
|
||||||
|
file_stat = os.stat(file_path)
|
||||||
|
os.chmod(file_path, file_stat.st_mode | permissions)
|
||||||
|
|
||||||
|
|
||||||
|
def is_sparse_image(image_path):
|
||||||
|
"""Checks whether a file is a sparse image."""
|
||||||
|
with open(image_path, "rb") as image_file:
|
||||||
|
return image_file.read(4) == b"\x3a\xff\x26\xed"
|
||||||
|
|
||||||
|
|
||||||
|
def rewrite_misc_info(part_img_dict, lpmake_path, input_file, output_file):
|
||||||
|
"""Changes the lpmake path and image paths in a misc info file."""
|
||||||
|
partition_names = ()
|
||||||
|
for line in input_file:
|
||||||
|
split_line = line.strip().split("=", 1)
|
||||||
|
if len(split_line) < 2:
|
||||||
|
split_line = (split_line[0], "")
|
||||||
|
if split_line[0] == "dynamic_partition_list":
|
||||||
|
partition_names = split_line[1].split()
|
||||||
|
elif split_line[0] == "lpmake":
|
||||||
|
output_file.write("lpmake=%s\n" % lpmake_path)
|
||||||
|
continue
|
||||||
|
elif split_line[0].endswith("_image"):
|
||||||
|
continue
|
||||||
|
output_file.write(line)
|
||||||
|
|
||||||
|
for partition_name in partition_names:
|
||||||
|
if partition_name not in part_img_dict:
|
||||||
|
raise KeyError("No image for " + partition_name + " partition.")
|
||||||
|
img_path = part_img_dict[partition_name]
|
||||||
|
if img_path:
|
||||||
|
output_file.write("%s_image=%s\n" % (partition_name, img_path))
|
||||||
|
|
||||||
|
return partition_names
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="This script is for test infrastructure to mix images in a "
|
||||||
|
"super image.")
|
||||||
|
|
||||||
|
parser.add_argument("--temp-dir",
|
||||||
|
default=tempfile.gettempdir(),
|
||||||
|
type=existing_abs_path,
|
||||||
|
help="The directory where this script creates "
|
||||||
|
"temporary files.")
|
||||||
|
parser.add_argument("--ota-tools",
|
||||||
|
required=True,
|
||||||
|
type=existing_abs_path,
|
||||||
|
help="The path to the zip or directory containing OTA "
|
||||||
|
"tools.")
|
||||||
|
parser.add_argument("--misc-info",
|
||||||
|
required=True,
|
||||||
|
type=existing_abs_path,
|
||||||
|
help="The path to the misc info file.")
|
||||||
|
parser.add_argument("super_img",
|
||||||
|
metavar="SUPER_IMG",
|
||||||
|
type=existing_abs_path,
|
||||||
|
help="The path to the super image to be repacked.")
|
||||||
|
parser.add_argument("part_imgs",
|
||||||
|
metavar="PART_IMG",
|
||||||
|
nargs="*",
|
||||||
|
type=partition_image,
|
||||||
|
help="The partition and the image that will be added "
|
||||||
|
"to the super image. The format is "
|
||||||
|
"PARITITON_NAME=IMAGE_PATH. If IMAGE_PATH is "
|
||||||
|
"empty, the partition will be resized to 0.")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Convert the args.part_imgs to a dictionary.
|
||||||
|
part_img_dict = dict()
|
||||||
|
for part, img in args.part_imgs:
|
||||||
|
if part in part_img_dict:
|
||||||
|
raise ValueError(part + " partition is repeated.")
|
||||||
|
part_img_dict[part] = img
|
||||||
|
|
||||||
|
if args.temp_dir:
|
||||||
|
tempfile.tempdir = args.temp_dir
|
||||||
|
|
||||||
|
temp_dirs = []
|
||||||
|
temp_files = []
|
||||||
|
try:
|
||||||
|
if os.path.isdir(args.ota_tools):
|
||||||
|
ota_tools_dir = args.ota_tools
|
||||||
|
else:
|
||||||
|
print("Unzip OTA tools.")
|
||||||
|
temp_ota_tools_dir = tempfile.mkdtemp(prefix="ota_tools")
|
||||||
|
temp_dirs.append(temp_ota_tools_dir)
|
||||||
|
with zipfile.ZipFile(args.ota_tools) as ota_tools_zip:
|
||||||
|
unzip_ota_tools(ota_tools_zip, temp_ota_tools_dir)
|
||||||
|
ota_tools_dir = temp_ota_tools_dir
|
||||||
|
|
||||||
|
if not is_sparse_image(args.super_img):
|
||||||
|
super_img_path = args.super_img
|
||||||
|
else:
|
||||||
|
print("Convert to unsparsed super image.")
|
||||||
|
simg2img_path = os.path.join(ota_tools_dir, BIN_DIR_NAME, "simg2img")
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="wb", prefix="super", suffix=".img",
|
||||||
|
delete=False) as raw_super_img:
|
||||||
|
temp_files.append(raw_super_img.name)
|
||||||
|
super_img_path = raw_super_img.name
|
||||||
|
subprocess.check_call([simg2img_path, args.super_img, super_img_path])
|
||||||
|
|
||||||
|
print("Unpack super image.")
|
||||||
|
lpunpack_path = os.path.join(ota_tools_dir, BIN_DIR_NAME, "lpunpack")
|
||||||
|
unpack_dir = tempfile.mkdtemp(prefix="lpunpack")
|
||||||
|
temp_dirs.append(unpack_dir)
|
||||||
|
subprocess.check_call([lpunpack_path, super_img_path, unpack_dir])
|
||||||
|
for file_name in os.listdir(unpack_dir):
|
||||||
|
if file_name.endswith(IMG_FILE_EXT):
|
||||||
|
part = file_name[:-len(IMG_FILE_EXT)]
|
||||||
|
if part not in part_img_dict:
|
||||||
|
part_img_dict[part] = os.path.join(unpack_dir, file_name)
|
||||||
|
|
||||||
|
print("Create temporary misc info.")
|
||||||
|
lpmake_path = os.path.join(ota_tools_dir, BIN_DIR_NAME, "lpmake")
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="w", prefix="misc_info", suffix=".txt",
|
||||||
|
delete=False) as misc_info_file:
|
||||||
|
temp_files.append(misc_info_file.name)
|
||||||
|
misc_info_file_path = misc_info_file.name
|
||||||
|
with open(args.misc_info, "r") as misc_info:
|
||||||
|
part_list = rewrite_misc_info(part_img_dict, lpmake_path, misc_info,
|
||||||
|
misc_info_file)
|
||||||
|
|
||||||
|
# Check that all input partitions are in the partition list.
|
||||||
|
parts_not_found = set(part for part, _ in args.part_imgs) - set(part_list)
|
||||||
|
if parts_not_found:
|
||||||
|
raise ValueError("Cannot find partitions in misc info: " +
|
||||||
|
" ".join(parts_not_found))
|
||||||
|
|
||||||
|
print("Build super image.")
|
||||||
|
build_super_image_path = os.path.join(ota_tools_dir, BIN_DIR_NAME,
|
||||||
|
"build_super_image")
|
||||||
|
subprocess.check_call([build_super_image_path, misc_info_file_path,
|
||||||
|
args.super_img])
|
||||||
|
finally:
|
||||||
|
for temp_dir in temp_dirs:
|
||||||
|
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||||
|
for temp_file in temp_files:
|
||||||
|
os.remove(temp_file)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user