blob: 5dc4cee6b32aa9e5e13574f0e5abfe9f7bd423d4 [file] [log] [blame]
Yifan Hong2b891ac2018-11-29 12:06:31 -08001#!/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"""
18Usage: build_super_image input_file output_dir_or_file
19
20input_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_dynamic_partitions_info' for details.
28 In addition:
29 - "ab_update" needs to be true for A/B devices.
30 - If source images should be included in the output image (for super.img
31 and super split images), a list of "*_image" should be paths of each
32 source images.
33
34output_dir_or_file:
35 If a single super image is built (for super_empty.img, or super.img for
36 launch devices), this argument is the output file.
37 If a collection of split images are built (for retrofit devices), this
38 argument is the output directory.
39"""
40
41from __future__ import print_function
42
43import logging
44import os.path
45import shlex
46import sys
47import zipfile
48
49import common
50import sparse_img
51
52if sys.hexversion < 0x02070000:
53 print("Python 2.7 or newer is required.", file=sys.stderr)
54 sys.exit(1)
55
56logger = logging.getLogger(__name__)
57
58
59UNZIP_PATTERN = ["IMAGES/*", "META/*"]
60
61
62def GetPartitionSizeFromImage(img):
63 try:
64 simg = sparse_img.SparseImage(img)
65 return simg.blocksize * simg.total_blocks
66 except ValueError:
67 return os.path.getsize(img)
68
69
Yifan Hongcc46eae2019-01-02 11:51:19 -080070def GetArgumentsForImage(partition, group, image=None):
71 image_size = GetPartitionSizeFromImage(image) if image else 0
72
73 cmd = ["--partition",
74 "{}:readonly:{}:{}".format(partition, image_size, group)]
75 if image:
76 cmd += ["--image", "{}={}".format(partition, image)]
77
78 return cmd
79
80
Yifan Hong2b891ac2018-11-29 12:06:31 -080081def BuildSuperImageFromDict(info_dict, output):
82
83 cmd = [info_dict["lpmake"],
84 "--metadata-size", "65536",
85 "--super-name", info_dict["super_metadata_device"]]
86
87 ab_update = info_dict.get("ab_update") == "true"
88 retrofit = info_dict.get("dynamic_partition_retrofit") == "true"
89 block_devices = shlex.split(info_dict.get("super_block_devices", "").strip())
90 groups = shlex.split(info_dict.get("super_partition_groups", "").strip())
91
David Anderson212e5df2018-12-17 12:52:25 -080092 if ab_update and retrofit:
Yifan Hong2b891ac2018-11-29 12:06:31 -080093 cmd += ["--metadata-slots", "2"]
David Anderson212e5df2018-12-17 12:52:25 -080094 elif ab_update:
95 cmd += ["--metadata-slots", "3"]
Yifan Hong2b891ac2018-11-29 12:06:31 -080096 else:
David Anderson212e5df2018-12-17 12:52:25 -080097 cmd += ["--metadata-slots", "2"]
Yifan Hong2b891ac2018-11-29 12:06:31 -080098
99 if ab_update and retrofit:
100 cmd.append("--auto-slot-suffixing")
101
102 for device in block_devices:
103 size = info_dict["super_{}_device_size".format(device)]
104 cmd += ["--device", "{}:{}".format(device, size)]
105
106 append_suffix = ab_update and not retrofit
107 has_image = False
108 for group in groups:
109 group_size = info_dict["super_{}_group_size".format(group)]
110 if append_suffix:
111 cmd += ["--group", "{}_a:{}".format(group, group_size),
112 "--group", "{}_b:{}".format(group, group_size)]
113 else:
114 cmd += ["--group", "{}:{}".format(group, group_size)]
115
116 partition_list = shlex.split(
117 info_dict["super_{}_partition_list".format(group)].strip())
118
119 for partition in partition_list:
120 image = info_dict.get("{}_image".format(partition))
Yifan Hong2b891ac2018-11-29 12:06:31 -0800121 if image:
Yifan Hong2b891ac2018-11-29 12:06:31 -0800122 has_image = True
Yifan Hongcc46eae2019-01-02 11:51:19 -0800123
124 if not append_suffix:
125 cmd += GetArgumentsForImage(partition, group, image)
126 continue
127
128 # For A/B devices, super partition always contains sub-partitions in
129 # the _a slot, because this image should only be used for
130 # bootstrapping / initializing the device. When flashing the image,
131 # bootloader fastboot should always mark _a slot as bootable.
132 cmd += GetArgumentsForImage(partition + "_a", group + "_a", image)
133
134 other_image = None
135 if partition == "system" and "system_other_image" in info_dict:
136 other_image = info_dict["system_other_image"]
137 has_image = True
138
139 cmd += GetArgumentsForImage(partition + "_b", group + "_b", other_image)
Yifan Hong2b891ac2018-11-29 12:06:31 -0800140
141 if has_image:
142 cmd.append("--sparse")
143
144 cmd += ["--output", output]
145
146 common.RunAndCheckOutput(cmd)
147
148 if retrofit and has_image:
149 logger.info("Done writing images to directory %s", output)
150 else:
151 logger.info("Done writing image %s", output)
152
Yifan Honge98427a2018-12-07 10:08:27 -0800153 return True
154
Yifan Hong2b891ac2018-11-29 12:06:31 -0800155
156def BuildSuperImageFromExtractedTargetFiles(inp, out):
157 info_dict = common.LoadInfoDict(inp)
158 partition_list = shlex.split(
159 info_dict.get("dynamic_partition_list", "").strip())
Yifan Hongcc46eae2019-01-02 11:51:19 -0800160
161 if "system" in partition_list:
162 image_path = os.path.join(inp, "IMAGES", "system_other.img")
163 if os.path.isfile(image_path):
164 info_dict["system_other_image"] = image_path
165
Yifan Honge98427a2018-12-07 10:08:27 -0800166 missing_images = []
Yifan Hong2b891ac2018-11-29 12:06:31 -0800167 for partition in partition_list:
Yifan Honge98427a2018-12-07 10:08:27 -0800168 image_path = os.path.join(inp, "IMAGES", "{}.img".format(partition))
169 if not os.path.isfile(image_path):
170 missing_images.append(image_path)
171 else:
172 info_dict["{}_image".format(partition)] = image_path
173 if missing_images:
174 logger.warning("Skip building super image because the following "
175 "images are missing from target files:\n%s",
176 "\n".join(missing_images))
177 return False
Yifan Hong2b891ac2018-11-29 12:06:31 -0800178 return BuildSuperImageFromDict(info_dict, out)
179
180
181def BuildSuperImageFromTargetFiles(inp, out):
182 input_tmp = common.UnzipTemp(inp, UNZIP_PATTERN)
183 return BuildSuperImageFromExtractedTargetFiles(input_tmp, out)
184
185
186def BuildSuperImage(inp, out):
187
188 if isinstance(inp, dict):
189 logger.info("Building super image from info dict...")
190 return BuildSuperImageFromDict(inp, out)
191
192 if isinstance(inp, str):
193 if os.path.isdir(inp):
194 logger.info("Building super image from extracted target files...")
195 return BuildSuperImageFromExtractedTargetFiles(inp, out)
196
197 if zipfile.is_zipfile(inp):
198 logger.info("Building super image from target files...")
199 return BuildSuperImageFromTargetFiles(inp, out)
200
201 if os.path.isfile(inp):
202 with open(inp) as f:
203 lines = f.read()
204 logger.info("Building super image from info dict...")
205 return BuildSuperImageFromDict(common.LoadDictionaryFromLines(lines.split("\n")), out)
206
207 raise ValueError("{} is not a dictionary or a valid path".format(inp))
208
209
210def main(argv):
211
212 args = common.ParseOptions(argv, __doc__)
213
214 if len(args) != 2:
215 common.Usage(__doc__)
216 sys.exit(1)
217
218 common.InitLogging()
219
220 BuildSuperImage(args[0], args[1])
221
222
223if __name__ == "__main__":
224 try:
225 common.CloseInheritedPipes()
226 main(sys.argv[1:])
227 except common.ExternalError:
228 logger.exception("\n ERROR:\n")
229 sys.exit(1)
230 finally:
231 common.Cleanup()