blob: 0150ba15c33dfe40f1aa68c869f45ed4a694aec6 [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
Yifan Hong69e0d612019-03-11 15:55:33 -070027 `dump-super-image-info' for details.
Yifan Hong2b891ac2018-11-29 12:06:31 -080028 In addition:
Yifan Hong2b891ac2018-11-29 12:06:31 -080029 - 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
33output_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
40from __future__ import print_function
41
42import logging
43import os.path
44import shlex
45import sys
46import zipfile
47
48import common
49import sparse_img
50
51if sys.hexversion < 0x02070000:
52 print("Python 2.7 or newer is required.", file=sys.stderr)
53 sys.exit(1)
54
55logger = logging.getLogger(__name__)
56
57
58UNZIP_PATTERN = ["IMAGES/*", "META/*"]
59
60
Yifan Hongcc46eae2019-01-02 11:51:19 -080061def GetArgumentsForImage(partition, group, image=None):
Yifan Hong7ad83b62019-04-04 10:57:39 -070062 image_size = sparse_img.GetImagePartitionSize(image) if image else 0
Yifan Hongcc46eae2019-01-02 11:51:19 -080063
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
Yifan Hong2b891ac2018-11-29 12:06:31 -080072def 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:
84 cmd += ["--metadata-slots", "2"]
85 else:
86 cmd += ["--metadata-slots", "1"]
87
88 if ab_update and retrofit:
89 cmd.append("--auto-slot-suffixing")
90
91 for device in block_devices:
92 size = info_dict["super_{}_device_size".format(device)]
93 cmd += ["--device", "{}:{}".format(device, size)]
94
95 append_suffix = ab_update and not retrofit
96 has_image = False
97 for group in groups:
98 group_size = info_dict["super_{}_group_size".format(group)]
99 if append_suffix:
100 cmd += ["--group", "{}_a:{}".format(group, group_size),
101 "--group", "{}_b:{}".format(group, group_size)]
102 else:
103 cmd += ["--group", "{}:{}".format(group, group_size)]
104
105 partition_list = shlex.split(
106 info_dict["super_{}_partition_list".format(group)].strip())
107
108 for partition in partition_list:
109 image = info_dict.get("{}_image".format(partition))
Yifan Hong2b891ac2018-11-29 12:06:31 -0800110 if image:
Yifan Hong2b891ac2018-11-29 12:06:31 -0800111 has_image = True
Yifan Hongcc46eae2019-01-02 11:51:19 -0800112
113 if not append_suffix:
114 cmd += GetArgumentsForImage(partition, group, image)
115 continue
116
117 # For A/B devices, super partition always contains sub-partitions in
118 # the _a slot, because this image should only be used for
119 # bootstrapping / initializing the device. When flashing the image,
120 # bootloader fastboot should always mark _a slot as bootable.
121 cmd += GetArgumentsForImage(partition + "_a", group + "_a", image)
122
123 other_image = None
124 if partition == "system" and "system_other_image" in info_dict:
125 other_image = info_dict["system_other_image"]
126 has_image = True
127
128 cmd += GetArgumentsForImage(partition + "_b", group + "_b", other_image)
Yifan Hong2b891ac2018-11-29 12:06:31 -0800129
130 if has_image:
131 cmd.append("--sparse")
132
133 cmd += ["--output", output]
134
135 common.RunAndCheckOutput(cmd)
136
137 if retrofit and has_image:
138 logger.info("Done writing images to directory %s", output)
139 else:
140 logger.info("Done writing image %s", output)
141
Yifan Honge98427a2018-12-07 10:08:27 -0800142 return True
143
Yifan Hong2b891ac2018-11-29 12:06:31 -0800144
145def BuildSuperImageFromExtractedTargetFiles(inp, out):
146 info_dict = common.LoadInfoDict(inp)
147 partition_list = shlex.split(
148 info_dict.get("dynamic_partition_list", "").strip())
Yifan Hongcc46eae2019-01-02 11:51:19 -0800149
150 if "system" in partition_list:
151 image_path = os.path.join(inp, "IMAGES", "system_other.img")
152 if os.path.isfile(image_path):
153 info_dict["system_other_image"] = image_path
154
Yifan Honge98427a2018-12-07 10:08:27 -0800155 missing_images = []
Yifan Hong2b891ac2018-11-29 12:06:31 -0800156 for partition in partition_list:
Yifan Honge98427a2018-12-07 10:08:27 -0800157 image_path = os.path.join(inp, "IMAGES", "{}.img".format(partition))
158 if not os.path.isfile(image_path):
159 missing_images.append(image_path)
160 else:
161 info_dict["{}_image".format(partition)] = image_path
162 if missing_images:
163 logger.warning("Skip building super image because the following "
164 "images are missing from target files:\n%s",
165 "\n".join(missing_images))
166 return False
Yifan Hong2b891ac2018-11-29 12:06:31 -0800167 return BuildSuperImageFromDict(info_dict, out)
168
169
170def BuildSuperImageFromTargetFiles(inp, out):
171 input_tmp = common.UnzipTemp(inp, UNZIP_PATTERN)
172 return BuildSuperImageFromExtractedTargetFiles(input_tmp, out)
173
174
175def BuildSuperImage(inp, out):
176
177 if isinstance(inp, dict):
178 logger.info("Building super image from info dict...")
179 return BuildSuperImageFromDict(inp, out)
180
181 if isinstance(inp, str):
182 if os.path.isdir(inp):
183 logger.info("Building super image from extracted target files...")
184 return BuildSuperImageFromExtractedTargetFiles(inp, out)
185
186 if zipfile.is_zipfile(inp):
187 logger.info("Building super image from target files...")
188 return BuildSuperImageFromTargetFiles(inp, out)
189
190 if os.path.isfile(inp):
191 with open(inp) as f:
192 lines = f.read()
193 logger.info("Building super image from info dict...")
194 return BuildSuperImageFromDict(common.LoadDictionaryFromLines(lines.split("\n")), out)
195
196 raise ValueError("{} is not a dictionary or a valid path".format(inp))
197
198
199def main(argv):
200
201 args = common.ParseOptions(argv, __doc__)
202
203 if len(args) != 2:
204 common.Usage(__doc__)
205 sys.exit(1)
206
207 common.InitLogging()
208
209 BuildSuperImage(args[0], args[1])
210
211
212if __name__ == "__main__":
213 try:
214 common.CloseInheritedPipes()
215 main(sys.argv[1:])
216 except common.ExternalError:
217 logger.exception("\n ERROR:\n")
218 sys.exit(1)
219 finally:
220 common.Cleanup()