Home | History | Annotate | Download | only in releasetools
      1 #!/usr/bin/env python
      2 #
      3 # Copyright (C) 2018 The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #      http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 """
     18 Usage: build_super_image input_file output_dir_or_file
     19 
     20 input_file: one of the following:
     21   - directory containing extracted target files. It will load info from
     22     META/misc_info.txt and build full super image / split images using source
     23     images from IMAGES/.
     24   - target files package. Same as above, but extracts the archive before
     25     building super image.
     26   - a dictionary file containing input arguments to build. Check
     27     `dump-super-image-info' for details.
     28     In addition:
     29     - If source images should be included in the output image (for super.img
     30       and super split images), a list of "*_image" should be paths of each
     31       source images.
     32 
     33 output_dir_or_file:
     34     If a single super image is built (for super_empty.img, or super.img for
     35     launch devices), this argument is the output file.
     36     If a collection of split images are built (for retrofit devices), this
     37     argument is the output directory.
     38 """
     39 
     40 from __future__ import print_function
     41 
     42 import logging
     43 import os.path
     44 import shlex
     45 import sys
     46 import zipfile
     47 
     48 import common
     49 import sparse_img
     50 
     51 if sys.hexversion < 0x02070000:
     52   print("Python 2.7 or newer is required.", file=sys.stderr)
     53   sys.exit(1)
     54 
     55 logger = logging.getLogger(__name__)
     56 
     57 
     58 UNZIP_PATTERN = ["IMAGES/*", "META/*"]
     59 
     60 
     61 def GetArgumentsForImage(partition, group, image=None):
     62   image_size = sparse_img.GetImagePartitionSize(image) if image else 0
     63 
     64   cmd = ["--partition",
     65          "{}:readonly:{}:{}".format(partition, image_size, group)]
     66   if image:
     67     cmd += ["--image", "{}={}".format(partition, image)]
     68 
     69   return cmd
     70 
     71 
     72 def BuildSuperImageFromDict(info_dict, output):
     73 
     74   cmd = [info_dict["lpmake"],
     75          "--metadata-size", "65536",
     76          "--super-name", info_dict["super_metadata_device"]]
     77 
     78   ab_update = info_dict.get("ab_update") == "true"
     79   retrofit = info_dict.get("dynamic_partition_retrofit") == "true"
     80   block_devices = shlex.split(info_dict.get("super_block_devices", "").strip())
     81   groups = shlex.split(info_dict.get("super_partition_groups", "").strip())
     82 
     83   if ab_update and retrofit:
     84     cmd += ["--metadata-slots", "2"]
     85   elif ab_update:
     86     cmd += ["--metadata-slots", "3"]
     87   else:
     88     cmd += ["--metadata-slots", "2"]
     89 
     90   if ab_update and retrofit:
     91     cmd.append("--auto-slot-suffixing")
     92 
     93   for device in block_devices:
     94     size = info_dict["super_{}_device_size".format(device)]
     95     cmd += ["--device", "{}:{}".format(device, size)]
     96 
     97   append_suffix = ab_update and not retrofit
     98   has_image = False
     99   for group in groups:
    100     group_size = info_dict["super_{}_group_size".format(group)]
    101     if append_suffix:
    102       cmd += ["--group", "{}_a:{}".format(group, group_size),
    103               "--group", "{}_b:{}".format(group, group_size)]
    104     else:
    105       cmd += ["--group", "{}:{}".format(group, group_size)]
    106 
    107     partition_list = shlex.split(
    108         info_dict["super_{}_partition_list".format(group)].strip())
    109 
    110     for partition in partition_list:
    111       image = info_dict.get("{}_image".format(partition))
    112       if image:
    113         has_image = True
    114 
    115       if not append_suffix:
    116         cmd += GetArgumentsForImage(partition, group, image)
    117         continue
    118 
    119       # For A/B devices, super partition always contains sub-partitions in
    120       # the _a slot, because this image should only be used for
    121       # bootstrapping / initializing the device. When flashing the image,
    122       # bootloader fastboot should always mark _a slot as bootable.
    123       cmd += GetArgumentsForImage(partition + "_a", group + "_a", image)
    124 
    125       other_image = None
    126       if partition == "system" and "system_other_image" in info_dict:
    127         other_image = info_dict["system_other_image"]
    128         has_image = True
    129 
    130       cmd += GetArgumentsForImage(partition + "_b", group + "_b", other_image)
    131 
    132   if info_dict.get("build_non_sparse_super_partition") != "true":
    133     cmd.append("--sparse")
    134 
    135   cmd += ["--output", output]
    136 
    137   common.RunAndCheckOutput(cmd)
    138 
    139   if retrofit and has_image:
    140     logger.info("Done writing images to directory %s", output)
    141   else:
    142     logger.info("Done writing image %s", output)
    143 
    144   return True
    145 
    146 
    147 def BuildSuperImageFromExtractedTargetFiles(inp, out):
    148   info_dict = common.LoadInfoDict(inp)
    149   partition_list = shlex.split(
    150       info_dict.get("dynamic_partition_list", "").strip())
    151 
    152   if "system" in partition_list:
    153     image_path = os.path.join(inp, "IMAGES", "system_other.img")
    154     if os.path.isfile(image_path):
    155       info_dict["system_other_image"] = image_path
    156 
    157   missing_images = []
    158   for partition in partition_list:
    159     image_path = os.path.join(inp, "IMAGES", "{}.img".format(partition))
    160     if not os.path.isfile(image_path):
    161       missing_images.append(image_path)
    162     else:
    163       info_dict["{}_image".format(partition)] = image_path
    164   if missing_images:
    165     logger.warning("Skip building super image because the following "
    166                    "images are missing from target files:\n%s",
    167                    "\n".join(missing_images))
    168     return False
    169   return BuildSuperImageFromDict(info_dict, out)
    170 
    171 
    172 def BuildSuperImageFromTargetFiles(inp, out):
    173   input_tmp = common.UnzipTemp(inp, UNZIP_PATTERN)
    174   return BuildSuperImageFromExtractedTargetFiles(input_tmp, out)
    175 
    176 
    177 def BuildSuperImage(inp, out):
    178 
    179   if isinstance(inp, dict):
    180     logger.info("Building super image from info dict...")
    181     return BuildSuperImageFromDict(inp, out)
    182 
    183   if isinstance(inp, str):
    184     if os.path.isdir(inp):
    185       logger.info("Building super image from extracted target files...")
    186       return BuildSuperImageFromExtractedTargetFiles(inp, out)
    187 
    188     if zipfile.is_zipfile(inp):
    189       logger.info("Building super image from target files...")
    190       return BuildSuperImageFromTargetFiles(inp, out)
    191 
    192     if os.path.isfile(inp):
    193       with open(inp) as f:
    194         lines = f.read()
    195       logger.info("Building super image from info dict...")
    196       return BuildSuperImageFromDict(common.LoadDictionaryFromLines(lines.split("\n")), out)
    197 
    198   raise ValueError("{} is not a dictionary or a valid path".format(inp))
    199 
    200 
    201 def main(argv):
    202 
    203   args = common.ParseOptions(argv, __doc__)
    204 
    205   if len(args) != 2:
    206     common.Usage(__doc__)
    207     sys.exit(1)
    208 
    209   common.InitLogging()
    210 
    211   BuildSuperImage(args[0], args[1])
    212 
    213 
    214 if __name__ == "__main__":
    215   try:
    216     common.CloseInheritedPipes()
    217     main(sys.argv[1:])
    218   except common.ExternalError:
    219     logger.exception("\n   ERROR:\n")
    220     sys.exit(1)
    221   finally:
    222     common.Cleanup()
    223