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