45a05bc545
am: 2725896bce
Change-Id: I5b83bc2af99d65c43660b3b764c3f7b5f2a0471a
230 lines
6.9 KiB
Python
Executable file
230 lines
6.9 KiB
Python
Executable file
#!/usr/bin/env python
|
|
#
|
|
# Copyright (C) 2018 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.
|
|
|
|
"""
|
|
Usage: build_super_image input_file output_dir_or_file
|
|
|
|
input_file: one of the following:
|
|
- directory containing extracted target files. It will load info from
|
|
META/misc_info.txt and build full super image / split images using source
|
|
images from IMAGES/.
|
|
- target files package. Same as above, but extracts the archive before
|
|
building super image.
|
|
- a dictionary file containing input arguments to build. Check
|
|
`dump-super-image-info' for details.
|
|
In addition:
|
|
- If source images should be included in the output image (for super.img
|
|
and super split images), a list of "*_image" should be paths of each
|
|
source images.
|
|
|
|
output_dir_or_file:
|
|
If a single super image is built (for super_empty.img, or super.img for
|
|
launch devices), this argument is the output file.
|
|
If a collection of split images are built (for retrofit devices), this
|
|
argument is the output directory.
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
|
|
import logging
|
|
import os.path
|
|
import shlex
|
|
import sys
|
|
import zipfile
|
|
|
|
import common
|
|
import sparse_img
|
|
|
|
if sys.hexversion < 0x02070000:
|
|
print("Python 2.7 or newer is required.", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
UNZIP_PATTERN = ["IMAGES/*", "META/*"]
|
|
|
|
|
|
def GetPartitionSizeFromImage(img):
|
|
try:
|
|
simg = sparse_img.SparseImage(img)
|
|
return simg.blocksize * simg.total_blocks
|
|
except ValueError:
|
|
return os.path.getsize(img)
|
|
|
|
|
|
def GetArgumentsForImage(partition, group, image=None):
|
|
image_size = GetPartitionSizeFromImage(image) if image else 0
|
|
|
|
cmd = ["--partition",
|
|
"{}:readonly:{}:{}".format(partition, image_size, group)]
|
|
if image:
|
|
cmd += ["--image", "{}={}".format(partition, image)]
|
|
|
|
return cmd
|
|
|
|
|
|
def BuildSuperImageFromDict(info_dict, output):
|
|
|
|
cmd = [info_dict["lpmake"],
|
|
"--metadata-size", "65536",
|
|
"--super-name", info_dict["super_metadata_device"]]
|
|
|
|
ab_update = info_dict.get("ab_update") == "true"
|
|
retrofit = info_dict.get("dynamic_partition_retrofit") == "true"
|
|
block_devices = shlex.split(info_dict.get("super_block_devices", "").strip())
|
|
groups = shlex.split(info_dict.get("super_partition_groups", "").strip())
|
|
|
|
if ab_update and retrofit:
|
|
cmd += ["--metadata-slots", "2"]
|
|
elif ab_update:
|
|
cmd += ["--metadata-slots", "3"]
|
|
else:
|
|
cmd += ["--metadata-slots", "2"]
|
|
|
|
if ab_update and retrofit:
|
|
cmd.append("--auto-slot-suffixing")
|
|
|
|
for device in block_devices:
|
|
size = info_dict["super_{}_device_size".format(device)]
|
|
cmd += ["--device", "{}:{}".format(device, size)]
|
|
|
|
append_suffix = ab_update and not retrofit
|
|
has_image = False
|
|
for group in groups:
|
|
group_size = info_dict["super_{}_group_size".format(group)]
|
|
if append_suffix:
|
|
cmd += ["--group", "{}_a:{}".format(group, group_size),
|
|
"--group", "{}_b:{}".format(group, group_size)]
|
|
else:
|
|
cmd += ["--group", "{}:{}".format(group, group_size)]
|
|
|
|
partition_list = shlex.split(
|
|
info_dict["super_{}_partition_list".format(group)].strip())
|
|
|
|
for partition in partition_list:
|
|
image = info_dict.get("{}_image".format(partition))
|
|
if image:
|
|
has_image = True
|
|
|
|
if not append_suffix:
|
|
cmd += GetArgumentsForImage(partition, group, image)
|
|
continue
|
|
|
|
# For A/B devices, super partition always contains sub-partitions in
|
|
# the _a slot, because this image should only be used for
|
|
# bootstrapping / initializing the device. When flashing the image,
|
|
# bootloader fastboot should always mark _a slot as bootable.
|
|
cmd += GetArgumentsForImage(partition + "_a", group + "_a", image)
|
|
|
|
other_image = None
|
|
if partition == "system" and "system_other_image" in info_dict:
|
|
other_image = info_dict["system_other_image"]
|
|
has_image = True
|
|
|
|
cmd += GetArgumentsForImage(partition + "_b", group + "_b", other_image)
|
|
|
|
if has_image:
|
|
cmd.append("--sparse")
|
|
|
|
cmd += ["--output", output]
|
|
|
|
common.RunAndCheckOutput(cmd)
|
|
|
|
if retrofit and has_image:
|
|
logger.info("Done writing images to directory %s", output)
|
|
else:
|
|
logger.info("Done writing image %s", output)
|
|
|
|
return True
|
|
|
|
|
|
def BuildSuperImageFromExtractedTargetFiles(inp, out):
|
|
info_dict = common.LoadInfoDict(inp)
|
|
partition_list = shlex.split(
|
|
info_dict.get("dynamic_partition_list", "").strip())
|
|
|
|
if "system" in partition_list:
|
|
image_path = os.path.join(inp, "IMAGES", "system_other.img")
|
|
if os.path.isfile(image_path):
|
|
info_dict["system_other_image"] = image_path
|
|
|
|
missing_images = []
|
|
for partition in partition_list:
|
|
image_path = os.path.join(inp, "IMAGES", "{}.img".format(partition))
|
|
if not os.path.isfile(image_path):
|
|
missing_images.append(image_path)
|
|
else:
|
|
info_dict["{}_image".format(partition)] = image_path
|
|
if missing_images:
|
|
logger.warning("Skip building super image because the following "
|
|
"images are missing from target files:\n%s",
|
|
"\n".join(missing_images))
|
|
return False
|
|
return BuildSuperImageFromDict(info_dict, out)
|
|
|
|
|
|
def BuildSuperImageFromTargetFiles(inp, out):
|
|
input_tmp = common.UnzipTemp(inp, UNZIP_PATTERN)
|
|
return BuildSuperImageFromExtractedTargetFiles(input_tmp, out)
|
|
|
|
|
|
def BuildSuperImage(inp, out):
|
|
|
|
if isinstance(inp, dict):
|
|
logger.info("Building super image from info dict...")
|
|
return BuildSuperImageFromDict(inp, out)
|
|
|
|
if isinstance(inp, str):
|
|
if os.path.isdir(inp):
|
|
logger.info("Building super image from extracted target files...")
|
|
return BuildSuperImageFromExtractedTargetFiles(inp, out)
|
|
|
|
if zipfile.is_zipfile(inp):
|
|
logger.info("Building super image from target files...")
|
|
return BuildSuperImageFromTargetFiles(inp, out)
|
|
|
|
if os.path.isfile(inp):
|
|
with open(inp) as f:
|
|
lines = f.read()
|
|
logger.info("Building super image from info dict...")
|
|
return BuildSuperImageFromDict(common.LoadDictionaryFromLines(lines.split("\n")), out)
|
|
|
|
raise ValueError("{} is not a dictionary or a valid path".format(inp))
|
|
|
|
|
|
def main(argv):
|
|
|
|
args = common.ParseOptions(argv, __doc__)
|
|
|
|
if len(args) != 2:
|
|
common.Usage(__doc__)
|
|
sys.exit(1)
|
|
|
|
common.InitLogging()
|
|
|
|
BuildSuperImage(args[0], args[1])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
common.CloseInheritedPipes()
|
|
main(sys.argv[1:])
|
|
except common.ExternalError:
|
|
logger.exception("\n ERROR:\n")
|
|
sys.exit(1)
|
|
finally:
|
|
common.Cleanup()
|