blob: 71437753332e40b27e98cb327b97a80d65c44267 [file] [log] [blame]
Doug Zongker3c84f562014-07-31 11:06:30 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2014 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"""
18Given a target-files zipfile that does not contain images (ie, does
19not have an IMAGES/ top-level subdirectory), produce the images and
20add them to the zipfile.
21
Tianjie Xub48589a2016-08-03 19:21:52 -070022Usage: add_img_to_target_files [flag] target_files
23
24 -a (--add_missing)
25 Build and add missing images to "IMAGES/". If this option is
26 not specified, this script will simply exit when "IMAGES/"
27 directory exists in the target file.
28
29 -r (--rebuild_recovery)
30 Rebuild the recovery patch and write it to the system image. Only
Tao Bao4978fa92019-06-04 16:26:45 -070031 meaningful when system image needs to be rebuilt and there're separate
32 boot / recovery images.
Tianjie Xub48589a2016-08-03 19:21:52 -070033
34 --replace_verity_private_key
35 Replace the private key used for verity signing. (same as the option
36 in sign_target_files_apks)
37
38 --replace_verity_public_key
39 Replace the certificate (public key) used for verity verification. (same
40 as the option in sign_target_files_apks)
41
42 --is_signing
43 Skip building & adding the images for "userdata" and "cache" if we
44 are signing the target files.
Doug Zongker3c84f562014-07-31 11:06:30 -070045"""
46
Tao Bao89fbb0f2017-01-10 10:47:58 -080047from __future__ import print_function
48
Tao Bao822f5842015-09-30 16:01:14 -070049import datetime
Tao Bao32fcdab2018-10-12 10:30:39 -070050import logging
Doug Zongker3c84f562014-07-31 11:06:30 -070051import os
David Zeuthend995f4b2016-01-29 16:59:17 -050052import shlex
Ying Wang2a048392015-06-25 13:56:53 -070053import shutil
Rupert Shuttleworth72942742020-12-08 06:18:35 +000054import stat
Tao Bao6b9fef52017-12-01 16:13:22 -080055import sys
Tao Baod86e3112017-09-22 15:45:33 -070056import uuid
Kelvin Zhang834f5d42022-01-21 12:44:44 -080057import tempfile
Doug Zongker3c84f562014-07-31 11:06:30 -070058import zipfile
59
Doug Zongker3c84f562014-07-31 11:06:30 -070060import build_image
Yifan Hong055e6cf2018-11-29 13:51:48 -080061import build_super_image
Doug Zongker3c84f562014-07-31 11:06:30 -070062import common
Hongguang Chenf23364d2020-04-27 18:36:36 -070063import verity_utils
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -050064import ota_metadata_pb2
65
Daniel Normane9af70a2021-04-15 16:39:22 -070066from apex_utils import GetApexInfoFromTargetFiles
Kelvin Zhang27324132021-03-22 15:38:38 -040067from common import AddCareMapForAbOta
Doug Zongker3c84f562014-07-31 11:06:30 -070068
Tao Bao6b9fef52017-12-01 16:13:22 -080069if sys.hexversion < 0x02070000:
70 print("Python 2.7 or newer is required.", file=sys.stderr)
71 sys.exit(1)
72
Tao Bao32fcdab2018-10-12 10:30:39 -070073logger = logging.getLogger(__name__)
Doug Zongker3c84f562014-07-31 11:06:30 -070074
Tao Bao32fcdab2018-10-12 10:30:39 -070075OPTIONS = common.OPTIONS
Michael Runge2e0d8fc2014-11-13 21:41:08 -080076OPTIONS.add_missing = False
77OPTIONS.rebuild_recovery = False
Tianjie Xu9ac4cb02017-06-09 16:58:03 -070078OPTIONS.replace_updated_files_list = []
Baligh Uddin59f4ff12015-09-16 21:20:30 -070079OPTIONS.replace_verity_public_key = False
80OPTIONS.replace_verity_private_key = False
Tianjie Xub48589a2016-08-03 19:21:52 -070081OPTIONS.is_signing = False
Doug Zongker3c84f562014-07-31 11:06:30 -070082
Bryan Henrye6d547d2018-07-31 18:32:00 -070083# Use a fixed timestamp (01/01/2009 00:00:00 UTC) for files when packaging
84# images. (b/24377993, b/80600931)
Tao Baoe30a6a62018-08-27 10:57:19 -070085FIXED_FILE_TIMESTAMP = int((
86 datetime.datetime(2009, 1, 1, 0, 0, 0, 0, None) -
87 datetime.datetime.utcfromtimestamp(0)).total_seconds())
Tao Baoa2ff4c92018-01-17 12:14:43 -080088
Bowgo Tsaid624fa62017-11-14 23:42:30 +080089
Dan Willemsen2ee00d52017-03-05 19:51:56 -080090class OutputFile(object):
Tao Bao93e7ebe2019-01-13 23:23:01 -080091 """A helper class to write a generated file to the given dir or zip.
Dan Willemsen2ee00d52017-03-05 19:51:56 -080092
Tao Bao93e7ebe2019-01-13 23:23:01 -080093 When generating images, we want the outputs to go into the given zip file, or
94 the given dir.
95
96 Attributes:
97 name: The name of the output file, regardless of the final destination.
98 """
99
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500100 def __init__(self, output_zip, input_dir, *args):
Tao Bao93e7ebe2019-01-13 23:23:01 -0800101 # We write the intermediate output file under the given input_dir, even if
102 # the final destination is a zip archive.
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500103 self.name = os.path.join(input_dir, *args)
Tao Bao93e7ebe2019-01-13 23:23:01 -0800104 self._output_zip = output_zip
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800105 if self._output_zip:
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500106 self._zip_name = os.path.join(*args)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800107
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800108 def Write(self, compress_type=None):
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800109 if self._output_zip:
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800110 common.ZipWrite(self._output_zip, self.name,
111 self._zip_name, compress_type=compress_type)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800112
Tao Baoe30a6a62018-08-27 10:57:19 -0700113
Tao Bao886d8832018-02-27 11:46:19 -0800114def AddSystem(output_zip, recovery_img=None, boot_img=None):
Doug Zongker3c84f562014-07-31 11:06:30 -0700115 """Turn the contents of SYSTEM into a system image and store it in
David Zeuthend995f4b2016-01-29 16:59:17 -0500116 output_zip. Returns the name of the system image file."""
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800117
Tao Bao886d8832018-02-27 11:46:19 -0800118 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800119 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700120 logger.info("system.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800121 return img.name
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800122
123 def output_sink(fn, data):
Tao Baoa3705452019-06-24 15:33:41 -0700124 output_file = os.path.join(OPTIONS.input_tmp, "SYSTEM", fn)
125 with open(output_file, "wb") as ofile:
126 ofile.write(data)
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800127
Daniel Normana4911da2019-03-15 14:36:21 -0700128 if output_zip:
129 arc_name = "SYSTEM/" + fn
130 if arc_name in output_zip.namelist():
131 OPTIONS.replace_updated_files_list.append(arc_name)
132 else:
Tao Baoa3705452019-06-24 15:33:41 -0700133 common.ZipWrite(output_zip, output_file, arc_name)
Tianjie Xu38af07f2017-05-25 17:38:53 -0700134
Bill Peckhame868aec2019-09-17 17:06:47 -0700135 board_uses_vendorimage = OPTIONS.info_dict.get(
136 "board_uses_vendorimage") == "true"
137
138 if (OPTIONS.rebuild_recovery and not board_uses_vendorimage and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800139 recovery_img is not None and boot_img is not None):
Bill Peckhame868aec2019-09-17 17:06:47 -0700140 logger.info("Building new recovery patch on system at system/vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700141 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
142 boot_img, info_dict=OPTIONS.info_dict)
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800143
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800144 block_list = OutputFile(output_zip, OPTIONS.input_tmp,
145 "IMAGES", "system.map")
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800146 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img,
147 block_list=block_list)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800148 return img.name
Doug Zongkerfc44a512014-08-26 13:10:25 -0700149
150
Tao Bao886d8832018-02-27 11:46:19 -0800151def AddSystemOther(output_zip):
Alex Light4e358ab2016-06-16 14:47:10 -0700152 """Turn the contents of SYSTEM_OTHER into a system_other image
153 and store it in output_zip."""
154
Tao Bao886d8832018-02-27 11:46:19 -0800155 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system_other.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800156 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700157 logger.info("system_other.img already exists; no need to rebuild...")
Alex Light4e358ab2016-06-16 14:47:10 -0700158 return
159
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800160 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img)
Alex Light4e358ab2016-06-16 14:47:10 -0700161
162
Bill Peckhame868aec2019-09-17 17:06:47 -0700163def AddVendor(output_zip, recovery_img=None, boot_img=None):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700164 """Turn the contents of VENDOR into a vendor image and store in it
165 output_zip."""
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800166
Tao Bao886d8832018-02-27 11:46:19 -0800167 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800168 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700169 logger.info("vendor.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800170 return img.name
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800171
Bill Peckhame868aec2019-09-17 17:06:47 -0700172 def output_sink(fn, data):
Iavor-Valentin Iftime756b5612022-02-09 15:56:14 +0000173 output_file = os.path.join(OPTIONS.input_tmp, "VENDOR", fn)
174 with open(output_file, "wb") as ofile:
175 ofile.write(data)
Bill Peckhame868aec2019-09-17 17:06:47 -0700176
177 if output_zip:
178 arc_name = "VENDOR/" + fn
179 if arc_name in output_zip.namelist():
180 OPTIONS.replace_updated_files_list.append(arc_name)
181 else:
Iavor-Valentin Iftime756b5612022-02-09 15:56:14 +0000182 common.ZipWrite(output_zip, output_file, arc_name)
Bill Peckhame868aec2019-09-17 17:06:47 -0700183
184 board_uses_vendorimage = OPTIONS.info_dict.get(
185 "board_uses_vendorimage") == "true"
186
187 if (OPTIONS.rebuild_recovery and board_uses_vendorimage and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800188 recovery_img is not None and boot_img is not None):
Bill Peckhame868aec2019-09-17 17:06:47 -0700189 logger.info("Building new recovery patch on vendor")
190 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
191 boot_img, info_dict=OPTIONS.info_dict)
192
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800193 block_list = OutputFile(output_zip, OPTIONS.input_tmp,
194 "IMAGES", "vendor.map")
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800195 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img,
196 block_list=block_list)
197 return img.name
Doug Zongker3c84f562014-07-31 11:06:30 -0700198
Yueyao Zhu889ee5e2017-05-12 17:50:46 -0700199
Tao Bao886d8832018-02-27 11:46:19 -0800200def AddProduct(output_zip):
201 """Turn the contents of PRODUCT into a product image and store it in
202 output_zip."""
Jaekyun Seokb7735d82017-11-27 17:04:47 +0900203
Tao Bao886d8832018-02-27 11:46:19 -0800204 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "product.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800205 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700206 logger.info("product.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800207 return img.name
Jaekyun Seokb7735d82017-11-27 17:04:47 +0900208
Tao Bao886d8832018-02-27 11:46:19 -0800209 block_list = OutputFile(
210 output_zip, OPTIONS.input_tmp, "IMAGES", "product.map")
211 CreateImage(
212 OPTIONS.input_tmp, OPTIONS.info_dict, "product", img,
213 block_list=block_list)
Jaekyun Seokb7735d82017-11-27 17:04:47 +0900214 return img.name
215
216
Justin Yun6151e3f2019-06-25 15:58:13 +0900217def AddSystemExt(output_zip):
218 """Turn the contents of SYSTEM_EXT into a system_ext image and store it in
219 output_zip."""
Dario Freni5f681e12018-05-29 13:09:01 +0100220
221 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES",
Justin Yun6151e3f2019-06-25 15:58:13 +0900222 "system_ext.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800223 if os.path.exists(img.name):
Justin Yun6151e3f2019-06-25 15:58:13 +0900224 logger.info("system_ext.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800225 return img.name
Dario Freni5f681e12018-05-29 13:09:01 +0100226
227 block_list = OutputFile(
Justin Yun6151e3f2019-06-25 15:58:13 +0900228 output_zip, OPTIONS.input_tmp, "IMAGES", "system_ext.map")
Dario Freni5f681e12018-05-29 13:09:01 +0100229 CreateImage(
Justin Yun6151e3f2019-06-25 15:58:13 +0900230 OPTIONS.input_tmp, OPTIONS.info_dict, "system_ext", img,
Dario Freni5f681e12018-05-29 13:09:01 +0100231 block_list=block_list)
232 return img.name
233
234
Bowgo Tsaid624fa62017-11-14 23:42:30 +0800235def AddOdm(output_zip):
236 """Turn the contents of ODM into an odm image and store it in output_zip."""
237
238 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800239 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700240 logger.info("odm.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800241 return img.name
Bowgo Tsaid624fa62017-11-14 23:42:30 +0800242
243 block_list = OutputFile(
244 output_zip, OPTIONS.input_tmp, "IMAGES", "odm.map")
245 CreateImage(
246 OPTIONS.input_tmp, OPTIONS.info_dict, "odm", img,
247 block_list=block_list)
248 return img.name
249
250
Yifan Hongcfb917a2020-05-07 14:58:20 -0700251def AddVendorDlkm(output_zip):
252 """Turn the contents of VENDOR_DLKM into an vendor_dlkm image and store it in output_zip."""
253
254 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.img")
255 if os.path.exists(img.name):
256 logger.info("vendor_dlkm.img already exists; no need to rebuild...")
257 return img.name
258
259 block_list = OutputFile(
260 output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.map")
261 CreateImage(
262 OPTIONS.input_tmp, OPTIONS.info_dict, "vendor_dlkm", img,
263 block_list=block_list)
264 return img.name
265
Tianjiebbde59f2021-05-03 21:18:56 -0700266
Yifan Hongf496f1b2020-07-15 16:52:59 -0700267def AddOdmDlkm(output_zip):
268 """Turn the contents of OdmDlkm into an odm_dlkm image and store it in output_zip."""
269
270 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.img")
271 if os.path.exists(img.name):
272 logger.info("odm_dlkm.img already exists; no need to rebuild...")
273 return img.name
274
275 block_list = OutputFile(
276 output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.map")
277 CreateImage(
278 OPTIONS.input_tmp, OPTIONS.info_dict, "odm_dlkm", img,
279 block_list=block_list)
280 return img.name
281
Ramji Jiyani13a41372022-01-27 07:05:08 +0000282def AddSystemDlkm(output_zip):
283 """Turn the contents of SystemDlkm into an system_dlkm image and store it in output_zip."""
284
285 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system_dlkm.img")
286 if os.path.exists(img.name):
287 logger.info("system_dlkm.img already exists; no need to rebuild...")
288 return img.name
289
290 block_list = OutputFile(
291 output_zip, OPTIONS.input_tmp, "IMAGES", "system_dlkm.map")
292 CreateImage(
293 OPTIONS.input_tmp, OPTIONS.info_dict, "system_dlkm", img,
294 block_list=block_list)
295 return img.name
296
Yifan Hongcfb917a2020-05-07 14:58:20 -0700297
Tao Bao886d8832018-02-27 11:46:19 -0800298def AddDtbo(output_zip):
Tao Baoc633ed02017-05-30 21:46:33 -0700299 """Adds the DTBO image.
300
Tao Bao886d8832018-02-27 11:46:19 -0800301 Uses the image under IMAGES/ if it already exists. Otherwise looks for the
Tao Baoc633ed02017-05-30 21:46:33 -0700302 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
303 """
Tao Bao886d8832018-02-27 11:46:19 -0800304 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "dtbo.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800305 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700306 logger.info("dtbo.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800307 return img.name
Tao Baoc633ed02017-05-30 21:46:33 -0700308
309 dtbo_prebuilt_path = os.path.join(
310 OPTIONS.input_tmp, "PREBUILT_IMAGES", "dtbo.img")
311 assert os.path.exists(dtbo_prebuilt_path)
312 shutil.copy(dtbo_prebuilt_path, img.name)
313
314 # AVB-sign the image as needed.
Bowgo Tsai3e599ea2017-05-26 18:30:04 +0800315 if OPTIONS.info_dict.get("avb_enable") == "true":
Rupert Shuttleworth72942742020-12-08 06:18:35 +0000316 # Signing requires +w
317 os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR)
318
Tao Baof88e0ce2019-03-18 14:01:38 -0700319 avbtool = OPTIONS.info_dict["avb_avbtool"]
Tao Bao3ebfdde2017-05-23 23:06:55 -0700320 part_size = OPTIONS.info_dict["dtbo_size"]
Tao Baoc633ed02017-05-30 21:46:33 -0700321 # The AVB hash footer will be replaced if already present.
322 cmd = [avbtool, "add_hash_footer", "--image", img.name,
323 "--partition_size", str(part_size), "--partition_name", "dtbo"]
Bowgo Tsai3e599ea2017-05-26 18:30:04 +0800324 common.AppendAVBSigningArgs(cmd, "dtbo")
325 args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args")
Tao Baoc633ed02017-05-30 21:46:33 -0700326 if args and args.strip():
327 cmd.extend(shlex.split(args))
Tao Bao2764aee2018-11-21 11:02:48 -0800328 common.RunAndCheckOutput(cmd)
Tao Baoc633ed02017-05-30 21:46:33 -0700329
330 img.Write()
331 return img.name
332
Tianjiebbde59f2021-05-03 21:18:56 -0700333
Andrew Sculle077cf72021-02-18 10:27:29 +0000334def AddPvmfw(output_zip):
335 """Adds the pvmfw image.
336
337 Uses the image under IMAGES/ if it already exists. Otherwise looks for the
338 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
339 """
340 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "pvmfw.img")
341 if os.path.exists(img.name):
342 logger.info("pvmfw.img already exists; no need to rebuild...")
343 return img.name
344
345 pvmfw_prebuilt_path = os.path.join(
346 OPTIONS.input_tmp, "PREBUILT_IMAGES", "pvmfw.img")
347 assert os.path.exists(pvmfw_prebuilt_path)
348 shutil.copy(pvmfw_prebuilt_path, img.name)
349
350 # AVB-sign the image as needed.
351 if OPTIONS.info_dict.get("avb_enable") == "true":
352 # Signing requires +w
353 os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR)
354
355 avbtool = OPTIONS.info_dict["avb_avbtool"]
356 part_size = OPTIONS.info_dict["pvmfw_size"]
357 # The AVB hash footer will be replaced if already present.
358 cmd = [avbtool, "add_hash_footer", "--image", img.name,
359 "--partition_size", str(part_size), "--partition_name", "pvmfw"]
360 common.AppendAVBSigningArgs(cmd, "pvmfw")
361 args = OPTIONS.info_dict.get("avb_pvmfw_add_hash_footer_args")
362 if args and args.strip():
363 cmd.extend(shlex.split(args))
364 common.RunAndCheckOutput(cmd)
365
366 img.Write()
367 return img.name
368
Tianjiebbde59f2021-05-03 21:18:56 -0700369
Hongguang Chenf23364d2020-04-27 18:36:36 -0700370def AddCustomImages(output_zip, partition_name):
371 """Adds and signs custom images in IMAGES/.
372
373 Args:
374 output_zip: The output zip file (needs to be already open), or None to
375 write images to OPTIONS.input_tmp/.
376
377 Uses the image under IMAGES/ if it already exists. Otherwise looks for the
378 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
379
380 Raises:
381 AssertionError: If image can't be found.
382 """
383
Hongguang Chenf23364d2020-04-27 18:36:36 -0700384 key_path = OPTIONS.info_dict.get("avb_{}_key_path".format(partition_name))
385 algorithm = OPTIONS.info_dict.get("avb_{}_algorithm".format(partition_name))
386 extra_args = OPTIONS.info_dict.get(
387 "avb_{}_add_hashtree_footer_args".format(partition_name))
388 partition_size = OPTIONS.info_dict.get(
389 "avb_{}_partition_size".format(partition_name))
390
391 builder = verity_utils.CreateCustomImageBuilder(
392 OPTIONS.info_dict, partition_name, partition_size,
393 key_path, algorithm, extra_args)
394
395 for img_name in OPTIONS.info_dict.get(
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800396 "avb_{}_image_list".format(partition_name)).split():
397 custom_image = OutputFile(
398 output_zip, OPTIONS.input_tmp, "IMAGES", img_name)
Hongguang Chenf23364d2020-04-27 18:36:36 -0700399 if os.path.exists(custom_image.name):
400 continue
401
402 custom_image_prebuilt_path = os.path.join(
403 OPTIONS.input_tmp, "PREBUILT_IMAGES", img_name)
404 assert os.path.exists(custom_image_prebuilt_path), \
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800405 "Failed to find %s at %s" % (img_name, custom_image_prebuilt_path)
Hongguang Chenf23364d2020-04-27 18:36:36 -0700406
407 shutil.copy(custom_image_prebuilt_path, custom_image.name)
408
409 if builder is not None:
410 builder.Build(custom_image.name)
411
412 custom_image.Write()
413
414 default = os.path.join(OPTIONS.input_tmp, "IMAGES", partition_name + ".img")
415 assert os.path.exists(default), \
416 "There should be one %s.img" % (partition_name)
417 return default
418
Doug Zongker3c84f562014-07-31 11:06:30 -0700419
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800420def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
Tao Baoa3705452019-06-24 15:33:41 -0700421 logger.info("creating %s.img...", what)
Doug Zongker3c84f562014-07-31 11:06:30 -0700422
Doug Zongker3c84f562014-07-31 11:06:30 -0700423 image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
Bryan Henrye6d547d2018-07-31 18:32:00 -0700424 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700425
Doug Zongker3c84f562014-07-31 11:06:30 -0700426 if what == "system":
427 fs_config_prefix = ""
428 else:
429 fs_config_prefix = what + "_"
430
431 fs_config = os.path.join(
432 input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
Dan Albert8b72aef2015-03-23 19:13:21 -0700433 if not os.path.exists(fs_config):
434 fs_config = None
Doug Zongker3c84f562014-07-31 11:06:30 -0700435
Ying Wanga2292c92015-03-24 19:07:40 -0700436 # Override values loaded from info_dict.
437 if fs_config:
438 image_props["fs_config"] = fs_config
Ying Wanga2292c92015-03-24 19:07:40 -0700439 if block_list:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800440 image_props["block_list"] = block_list.name
Ying Wanga2292c92015-03-24 19:07:40 -0700441
Tao Baod86e3112017-09-22 15:45:33 -0700442 # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and
Tianjiefdda51d2021-05-05 14:46:35 -0700443 # build fingerprint). Also use the legacy build id, because the vbmeta digest
444 # isn't available at this point.
445 build_info = common.BuildInfo(info_dict, use_legacy_id=True)
Yifan Hongc08cbf02020-09-15 19:07:39 +0000446 uuid_seed = what + "-" + build_info.GetPartitionFingerprint(what)
Tao Baod86e3112017-09-22 15:45:33 -0700447 image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))
448 hash_seed = "hash_seed-" + uuid_seed
449 image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed))
450
Tao Baoc6bd70a2018-09-27 16:58:00 -0700451 build_image.BuildImage(
452 os.path.join(input_dir, what.upper()), image_props, output_file.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700453
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800454 output_file.Write()
455 if block_list:
456 block_list.Write()
457
Shashikant Baviskar16a73892019-02-07 10:57:21 +0900458 # Set the '_image_size' for given image size.
Tianjie Xuf1a13182017-01-19 17:39:30 -0800459 is_verity_partition = "verity_block_device" in image_props
Tianjie Xu6b2e1552017-06-01 11:32:32 -0700460 verity_supported = (image_props.get("verity") == "true" or
Bowgo Tsai3e599ea2017-05-26 18:30:04 +0800461 image_props.get("avb_enable") == "true")
Tianjie Xu6b2e1552017-06-01 11:32:32 -0700462 is_avb_enable = image_props.get("avb_hashtree_enable") == "true"
463 if verity_supported and (is_verity_partition or is_avb_enable):
Tao Bao35f4ebc2018-09-27 15:31:11 -0700464 image_size = image_props.get("image_size")
465 if image_size:
Shashikant Baviskar16a73892019-02-07 10:57:21 +0900466 image_size_key = what + "_image_size"
467 info_dict[image_size_key] = int(image_size)
Tianjie Xuf1a13182017-01-19 17:39:30 -0800468
Yifan Hongc767f7c2018-11-08 15:41:24 -0800469 use_dynamic_size = (
Tao Bao2764aee2018-11-21 11:02:48 -0800470 info_dict.get("use_dynamic_partition_size") == "true" and
471 what in shlex.split(info_dict.get("dynamic_partition_list", "").strip()))
Yifan Hongc767f7c2018-11-08 15:41:24 -0800472 if use_dynamic_size:
473 info_dict.update(build_image.GlobalDictFromImageProp(image_props, what))
474
Doug Zongker3c84f562014-07-31 11:06:30 -0700475
Tao Bao886d8832018-02-27 11:46:19 -0800476def AddUserdata(output_zip):
Ying Wang2a048392015-06-25 13:56:53 -0700477 """Create a userdata image and store it in output_zip.
478
479 In most case we just create and store an empty userdata.img;
480 But the invoker can also request to create userdata.img with real
481 data from the target files, by setting "userdata_img_with_data=true"
482 in OPTIONS.info_dict.
483 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700484
Tao Bao886d8832018-02-27 11:46:19 -0800485 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "userdata.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800486 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700487 logger.info("userdata.img already exists; no need to rebuild...")
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800488 return
489
Elliott Hughes305b0882016-06-15 17:04:54 -0700490 # Skip userdata.img if no size.
Tao Bao2c15d9e2015-07-09 11:51:16 -0700491 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data")
Elliott Hughes305b0882016-06-15 17:04:54 -0700492 if not image_props.get("partition_size"):
Doug Zongker3c84f562014-07-31 11:06:30 -0700493 return
494
Tao Bao32fcdab2018-10-12 10:30:39 -0700495 logger.info("creating userdata.img...")
Doug Zongker3c84f562014-07-31 11:06:30 -0700496
Bryan Henrye6d547d2018-07-31 18:32:00 -0700497 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700498
Tao Baofa863c82017-05-23 23:49:03 -0700499 if OPTIONS.info_dict.get("userdata_img_with_data") == "true":
500 user_dir = os.path.join(OPTIONS.input_tmp, "DATA")
Ying Wang2a048392015-06-25 13:56:53 -0700501 else:
Tao Bao1c830bf2017-12-25 10:43:47 -0800502 user_dir = common.MakeTempDir()
Ying Wang2a048392015-06-25 13:56:53 -0700503
Tao Baoc6bd70a2018-09-27 16:58:00 -0700504 build_image.BuildImage(user_dir, image_props, img.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700505
506 common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800507 # Always use compression for useradata image.
508 # As it's likely huge and consist of lots of 0s.
509 img.Write(zipfile.ZIP_DEFLATED)
Doug Zongker3c84f562014-07-31 11:06:30 -0700510
511
Tao Bao744c4c72018-08-20 21:09:07 -0700512def AddVBMeta(output_zip, partitions, name, needed_partitions):
513 """Creates a VBMeta image and stores it in output_zip.
514
515 It generates the requested VBMeta image. The requested image could be for
516 top-level or chained VBMeta image, which is determined based on the name.
Tao Baobf70c312017-07-11 17:27:55 -0700517
518 Args:
519 output_zip: The output zip file, which needs to be already open.
520 partitions: A dict that's keyed by partition names with image paths as
Hongguang Chenf23364d2020-04-27 18:36:36 -0700521 values. Only valid partition names are accepted, as partitions listed
522 in common.AVB_PARTITIONS and custom partitions listed in
523 OPTIONS.info_dict.get("avb_custom_images_partition_list")
David Anderson7709ab22018-10-15 14:41:34 -0700524 name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
Tao Bao744c4c72018-08-20 21:09:07 -0700525 needed_partitions: Partitions whose descriptors should be included into the
526 generated VBMeta image.
527
Tao Bao71064202018-10-22 15:08:02 -0700528 Returns:
529 Path to the created image.
530
Tao Bao744c4c72018-08-20 21:09:07 -0700531 Raises:
532 AssertionError: On invalid input args.
Tao Baobf70c312017-07-11 17:27:55 -0700533 """
Tao Bao744c4c72018-08-20 21:09:07 -0700534 assert needed_partitions, "Needed partitions must be specified"
535
536 img = OutputFile(
537 output_zip, OPTIONS.input_tmp, "IMAGES", "{}.img".format(name))
Tao Bao93e7ebe2019-01-13 23:23:01 -0800538 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700539 logger.info("%s.img already exists; not rebuilding...", name)
Tao Bao93e7ebe2019-01-13 23:23:01 -0800540 return img.name
Tao Bao262bf3f2017-07-11 17:27:55 -0700541
Daniel Norman276f0622019-07-26 14:13:51 -0700542 common.BuildVBMeta(img.name, partitions, name, needed_partitions)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800543 img.Write()
Tao Bao71064202018-10-22 15:08:02 -0700544 return img.name
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400545
546
Tao Bao886d8832018-02-27 11:46:19 -0800547def AddPartitionTable(output_zip):
David Zeuthen25328622016-04-08 15:08:03 -0400548 """Create a partition table image and store it in output_zip."""
549
Tao Bao886d8832018-02-27 11:46:19 -0800550 img = OutputFile(
551 output_zip, OPTIONS.input_tmp, "IMAGES", "partition-table.img")
552 bpt = OutputFile(
Bryan Henryf130a232018-04-26 11:59:33 -0700553 output_zip, OPTIONS.input_tmp, "META", "partition-table.bpt")
David Zeuthen25328622016-04-08 15:08:03 -0400554
555 # use BPTTOOL from environ, or "bpttool" if empty or not set.
556 bpttool = os.getenv("BPTTOOL") or "bpttool"
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800557 cmd = [bpttool, "make_table", "--output_json", bpt.name,
558 "--output_gpt", img.name]
David Zeuthen25328622016-04-08 15:08:03 -0400559 input_files_str = OPTIONS.info_dict["board_bpt_input_files"]
560 input_files = input_files_str.split(" ")
561 for i in input_files:
562 cmd.extend(["--input", i])
563 disk_size = OPTIONS.info_dict.get("board_bpt_disk_size")
564 if disk_size:
565 cmd.extend(["--disk_size", disk_size])
566 args = OPTIONS.info_dict.get("board_bpt_make_table_args")
567 if args:
568 cmd.extend(shlex.split(args))
Tao Bao2764aee2018-11-21 11:02:48 -0800569 common.RunAndCheckOutput(cmd)
David Zeuthen25328622016-04-08 15:08:03 -0400570
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800571 img.Write()
572 bpt.Write()
David Zeuthen25328622016-04-08 15:08:03 -0400573
574
Tao Bao886d8832018-02-27 11:46:19 -0800575def AddCache(output_zip):
Doug Zongker3c84f562014-07-31 11:06:30 -0700576 """Create an empty cache image and store it in output_zip."""
577
Tao Bao886d8832018-02-27 11:46:19 -0800578 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "cache.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800579 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700580 logger.info("cache.img already exists; no need to rebuild...")
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800581 return
582
Tao Bao2c15d9e2015-07-09 11:51:16 -0700583 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache")
Doug Zongker3c84f562014-07-31 11:06:30 -0700584 # The build system has to explicitly request for cache.img.
585 if "fs_type" not in image_props:
586 return
587
Tao Bao32fcdab2018-10-12 10:30:39 -0700588 logger.info("creating cache.img...")
Doug Zongker3c84f562014-07-31 11:06:30 -0700589
Bryan Henrye6d547d2018-07-31 18:32:00 -0700590 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700591
Tao Bao1c830bf2017-12-25 10:43:47 -0800592 user_dir = common.MakeTempDir()
Tao Baoc6bd70a2018-09-27 16:58:00 -0700593 build_image.BuildImage(user_dir, image_props, img.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700594
595 common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800596 img.Write()
Doug Zongker3c84f562014-07-31 11:06:30 -0700597
598
Tao Bao5277d102018-04-17 23:47:21 -0700599def CheckAbOtaImages(output_zip, ab_partitions):
600 """Checks that all the listed A/B partitions have their images available.
Tao Baobea20ac2018-01-17 17:57:49 -0800601
Tao Bao5277d102018-04-17 23:47:21 -0700602 The images need to be available under IMAGES/ or RADIO/, with the former takes
603 a priority.
Tao Baobea20ac2018-01-17 17:57:49 -0800604
605 Args:
606 output_zip: The output zip file (needs to be already open), or None to
Tao Bao5277d102018-04-17 23:47:21 -0700607 find images in OPTIONS.input_tmp/.
Tao Baobea20ac2018-01-17 17:57:49 -0800608 ab_partitions: The list of A/B partitions.
609
610 Raises:
611 AssertionError: If it can't find an image.
612 """
613 for partition in ab_partitions:
Daniel Norman78554ea2021-09-14 10:29:38 -0700614 img_name = partition + ".img"
Tao Baobea20ac2018-01-17 17:57:49 -0800615
Tao Baoa2ff4c92018-01-17 12:14:43 -0800616 # Assert that the image is present under IMAGES/ now.
Tao Baobea20ac2018-01-17 17:57:49 -0800617 if output_zip:
618 # Zip spec says: All slashes MUST be forward slashes.
Tao Bao5277d102018-04-17 23:47:21 -0700619 images_path = "IMAGES/" + img_name
620 radio_path = "RADIO/" + img_name
621 available = (images_path in output_zip.namelist() or
622 radio_path in output_zip.namelist())
Tao Baobea20ac2018-01-17 17:57:49 -0800623 else:
Tao Bao5277d102018-04-17 23:47:21 -0700624 images_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
625 radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
626 available = os.path.exists(images_path) or os.path.exists(radio_path)
627
628 assert available, "Failed to find " + img_name
Tao Baobea20ac2018-01-17 17:57:49 -0800629
630
Tao Baobea20ac2018-01-17 17:57:49 -0800631def AddPackRadioImages(output_zip, images):
632 """Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/.
633
634 Args:
635 output_zip: The output zip file (needs to be already open), or None to
636 write images to OPTIONS.input_tmp/.
637 images: A list of image names.
638
639 Raises:
640 AssertionError: If a listed image can't be found.
641 """
642 for image in images:
643 img_name = image.strip()
644 _, ext = os.path.splitext(img_name)
645 if not ext:
646 img_name += ".img"
Tao Baoa2ff4c92018-01-17 12:14:43 -0800647
Tao Baobea20ac2018-01-17 17:57:49 -0800648 prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
649 if os.path.exists(prebuilt_path):
Tao Bao32fcdab2018-10-12 10:30:39 -0700650 logger.info("%s already exists, no need to overwrite...", img_name)
Tao Baobea20ac2018-01-17 17:57:49 -0800651 continue
652
653 img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
654 assert os.path.exists(img_radio_path), \
655 "Failed to find %s at %s" % (img_name, img_radio_path)
Tao Baoa2ff4c92018-01-17 12:14:43 -0800656
Tao Baobea20ac2018-01-17 17:57:49 -0800657 if output_zip:
Tao Baoa2ff4c92018-01-17 12:14:43 -0800658 common.ZipWrite(output_zip, img_radio_path, "IMAGES/" + img_name)
Tao Baobea20ac2018-01-17 17:57:49 -0800659 else:
660 shutil.copy(img_radio_path, prebuilt_path)
661
662
David Anderson1ef03e22018-08-30 13:11:47 -0700663def AddSuperEmpty(output_zip):
664 """Create a super_empty.img and store it in output_zip."""
665
666 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "super_empty.img")
Yifan Hong055e6cf2018-11-29 13:51:48 -0800667 build_super_image.BuildSuperImage(OPTIONS.info_dict, img.name)
David Anderson1ef03e22018-08-30 13:11:47 -0700668 img.Write()
669
670
Yifan Hongc767f7c2018-11-08 15:41:24 -0800671def AddSuperSplit(output_zip):
672 """Create split super_*.img and store it in output_zip."""
673
Yifan Hong055e6cf2018-11-29 13:51:48 -0800674 outdir = os.path.join(OPTIONS.input_tmp, "OTA")
Yifan Honge98427a2018-12-07 10:08:27 -0800675 built = build_super_image.BuildSuperImage(OPTIONS.input_tmp, outdir)
Yifan Hongc767f7c2018-11-08 15:41:24 -0800676
Yifan Honge98427a2018-12-07 10:08:27 -0800677 if built:
678 for dev in OPTIONS.info_dict['super_block_devices'].strip().split():
679 img = OutputFile(output_zip, OPTIONS.input_tmp, "OTA",
680 "super_" + dev + ".img")
681 img.Write()
Yifan Hongc767f7c2018-11-08 15:41:24 -0800682
683
Tianjie Xu9ac4cb02017-06-09 16:58:03 -0700684def ReplaceUpdatedFiles(zip_filename, files_list):
Tao Bao89d7ab22017-12-14 17:05:33 -0800685 """Updates all the ZIP entries listed in files_list.
Tianjie Xu38af07f2017-05-25 17:38:53 -0700686
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700687 For now the list includes META/care_map.pb, and the related files under
Tianjie Xu9ac4cb02017-06-09 16:58:03 -0700688 SYSTEM/ after rebuilding recovery.
689 """
Tao Bao89d7ab22017-12-14 17:05:33 -0800690 common.ZipDelete(zip_filename, files_list)
Tianjie Xu38af07f2017-05-25 17:38:53 -0700691 output_zip = zipfile.ZipFile(zip_filename, "a",
692 compression=zipfile.ZIP_DEFLATED,
693 allowZip64=True)
Tianjie Xu9ac4cb02017-06-09 16:58:03 -0700694 for item in files_list:
Tianjie Xu38af07f2017-05-25 17:38:53 -0700695 file_path = os.path.join(OPTIONS.input_tmp, item)
696 assert os.path.exists(file_path)
697 common.ZipWrite(output_zip, file_path, arcname=item)
698 common.ZipClose(output_zip)
699
700
Chris Gross435b8fe2020-09-15 09:53:44 -0700701def HasPartition(partition_name):
702 """Determines if the target files archive should build a given partition."""
703
704 return ((os.path.isdir(
705 os.path.join(OPTIONS.input_tmp, partition_name.upper())) and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800706 OPTIONS.info_dict.get(
707 "building_{}_image".format(partition_name)) == "true") or
708 os.path.exists(
709 os.path.join(OPTIONS.input_tmp, "IMAGES",
710 "{}.img".format(partition_name))))
Chris Gross435b8fe2020-09-15 09:53:44 -0700711
Tianjiea5fca032021-06-01 22:06:28 -0700712
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500713def AddApexInfo(output_zip):
Tianjiea5fca032021-06-01 22:06:28 -0700714 apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system',
715 compressed_only=False)
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500716 apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
717 apex_metadata_proto.apex_info.extend(apex_infos)
718 apex_info_bytes = apex_metadata_proto.SerializeToString()
719
720 output_file = os.path.join(OPTIONS.input_tmp, "META", "apex_info.pb")
721 with open(output_file, "wb") as ofile:
722 ofile.write(apex_info_bytes)
723 if output_zip:
724 arc_name = "META/apex_info.pb"
725 if arc_name in output_zip.namelist():
726 OPTIONS.replace_updated_files_list.append(arc_name)
727 else:
728 common.ZipWrite(output_zip, output_file, arc_name)
729
Chris Gross435b8fe2020-09-15 09:53:44 -0700730
Tianjiec3bf3d02021-07-14 15:56:37 -0700731def AddVbmetaDigest(output_zip):
732 """Write the vbmeta digest to the output dir and zipfile."""
733
734 # Calculate the vbmeta digest and put the result in to META/
735 boot_images = OPTIONS.info_dict.get("boot_images")
736 # Disable the digest calculation if the target_file is used as a container
Bowgo Tsaiaba5c9e2021-09-27 14:08:41 +0800737 # for boot images. A boot container might contain boot-5.4.img, boot-5.10.img
738 # etc., instead of just a boot.img and will fail in vbmeta digest calculation.
739 boot_container = boot_images and (
740 len(boot_images.split()) >= 2 or boot_images.split()[0] != 'boot.img')
Tianjiec3bf3d02021-07-14 15:56:37 -0700741 if (OPTIONS.info_dict.get("avb_enable") == "true" and not boot_container and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800742 OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true"):
Tianjiec3bf3d02021-07-14 15:56:37 -0700743 avbtool = OPTIONS.info_dict["avb_avbtool"]
744 digest = verity_utils.CalculateVbmetaDigest(OPTIONS.input_tmp, avbtool)
745 vbmeta_digest_txt = os.path.join(OPTIONS.input_tmp, "META",
746 "vbmeta_digest.txt")
747 with open(vbmeta_digest_txt, 'w') as f:
748 f.write(digest)
749 # writes to the output zipfile
750 if output_zip:
751 arc_name = "META/vbmeta_digest.txt"
752 if arc_name in output_zip.namelist():
753 OPTIONS.replace_updated_files_list.append(arc_name)
754 else:
755 common.ZipWriteStr(output_zip, arc_name, digest)
756
757
Doug Zongker3c84f562014-07-31 11:06:30 -0700758def AddImagesToTargetFiles(filename):
Tao Baoae396d92017-11-20 11:56:43 -0800759 """Creates and adds images (boot/recovery/system/...) to a target_files.zip.
760
761 It works with either a zip file (zip mode), or a directory that contains the
762 files to be packed into a target_files.zip (dir mode). The latter is used when
763 being called from build/make/core/Makefile.
764
765 The images will be created under IMAGES/ in the input target_files.zip.
766
767 Args:
Tao Baodba59ee2018-01-09 13:21:02 -0800768 filename: the target_files.zip, or the zip root directory.
Tao Baoae396d92017-11-20 11:56:43 -0800769 """
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800770 if os.path.isdir(filename):
771 OPTIONS.input_tmp = os.path.abspath(filename)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800772 else:
Tao Baodba59ee2018-01-09 13:21:02 -0800773 OPTIONS.input_tmp = common.UnzipTemp(filename)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700774
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800775 if not OPTIONS.add_missing:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800776 if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
Tao Bao32fcdab2018-10-12 10:30:39 -0700777 logger.warning("target_files appears to already contain images.")
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800778 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700779
Tao Bao410ad8b2018-08-24 12:08:38 -0700780 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, repacking=True)
Tao Baodba59ee2018-01-09 13:21:02 -0800781
782 has_recovery = OPTIONS.info_dict.get("no_recovery") != "true"
Chris Grossa784ef12019-04-22 11:09:57 -0700783 has_boot = OPTIONS.info_dict.get("no_boot") != "true"
Devin Mooreafdd7c72021-12-13 22:04:08 +0000784 has_init_boot = OPTIONS.info_dict.get("init_boot") == "true"
Steve Mucklee1b10862019-07-10 10:49:37 -0700785 has_vendor_boot = OPTIONS.info_dict.get("vendor_boot") == "true"
Tao Baodba59ee2018-01-09 13:21:02 -0800786
Ramji Jiyani13a41372022-01-27 07:05:08 +0000787 # {vendor,odm,product,system_ext,vendor_dlkm,odm_dlkm, system_dlkm, system, system_other}.img
Chris Gross435b8fe2020-09-15 09:53:44 -0700788 # can be built from source, or dropped into target_files.zip as a prebuilt blob.
789 has_vendor = HasPartition("vendor")
790 has_odm = HasPartition("odm")
791 has_vendor_dlkm = HasPartition("vendor_dlkm")
792 has_odm_dlkm = HasPartition("odm_dlkm")
Ramji Jiyani13a41372022-01-27 07:05:08 +0000793 has_system_dlkm = HasPartition("system_dlkm")
Chris Gross435b8fe2020-09-15 09:53:44 -0700794 has_product = HasPartition("product")
795 has_system_ext = HasPartition("system_ext")
796 has_system = HasPartition("system")
797 has_system_other = HasPartition("system_other")
Chris Gross203191b2020-05-30 02:39:12 +0000798 has_userdata = OPTIONS.info_dict.get("building_userdata_image") == "true"
799 has_cache = OPTIONS.info_dict.get("building_cache_image") == "true"
Doug Zongker3c84f562014-07-31 11:06:30 -0700800
Tao Baodba59ee2018-01-09 13:21:02 -0800801 # Set up the output destination. It writes to the given directory for dir
802 # mode; otherwise appends to the given ZIP.
803 if os.path.isdir(filename):
804 output_zip = None
805 else:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800806 output_zip = zipfile.ZipFile(filename, "a",
807 compression=zipfile.ZIP_DEFLATED,
808 allowZip64=True)
Tao Baoae396d92017-11-20 11:56:43 -0800809
810 # Always make input_tmp/IMAGES available, since we may stage boot / recovery
811 # images there even under zip mode. The directory will be cleaned up as part
812 # of OPTIONS.input_tmp.
813 images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
814 if not os.path.isdir(images_dir):
815 os.makedirs(images_dir)
Doug Zongker3c84f562014-07-31 11:06:30 -0700816
Tao Baobf70c312017-07-11 17:27:55 -0700817 # A map between partition names and their paths, which could be used when
818 # generating AVB vbmeta image.
Tao Bao3ed35d32019-10-07 20:48:48 -0700819 partitions = {}
Tao Baobf70c312017-07-11 17:27:55 -0700820
Doug Zongkerfc44a512014-08-26 13:10:25 -0700821 def banner(s):
Tao Baoa3705452019-06-24 15:33:41 -0700822 logger.info("\n\n++++ %s ++++\n\n", s)
Doug Zongker3c84f562014-07-31 11:06:30 -0700823
Chris Grossa784ef12019-04-22 11:09:57 -0700824 boot_image = None
Greg Kaisere086f722021-09-14 19:32:27 +0000825 if has_boot:
Chris Grossa784ef12019-04-22 11:09:57 -0700826 banner("boot")
Steve Muckle9793cf62020-04-08 18:27:00 -0700827 boot_images = OPTIONS.info_dict.get("boot_images")
828 if boot_images is None:
829 boot_images = "boot.img"
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800830 for index, b in enumerate(boot_images.split()):
Steve Muckle9793cf62020-04-08 18:27:00 -0700831 # common.GetBootableImage() returns the image directly if present.
832 boot_image = common.GetBootableImage(
833 "IMAGES/" + b, b, OPTIONS.input_tmp, "BOOT")
834 # boot.img may be unavailable in some targets (e.g. aosp_arm64).
835 if boot_image:
836 boot_image_path = os.path.join(OPTIONS.input_tmp, "IMAGES", b)
Roopesh Nataraja3e15f6e2020-06-08 19:54:13 -0700837 # Although multiple boot images can be generated, include the image
838 # descriptor of only the first boot image in vbmeta
839 if index == 0:
Steve Muckle9793cf62020-04-08 18:27:00 -0700840 partitions['boot'] = boot_image_path
841 if not os.path.exists(boot_image_path):
842 boot_image.WriteToDir(OPTIONS.input_tmp)
843 if output_zip:
844 boot_image.AddToZip(output_zip)
Doug Zongker3c84f562014-07-31 11:06:30 -0700845
Devin Mooreafdd7c72021-12-13 22:04:08 +0000846 if has_init_boot:
847 banner("init_boot")
848 init_boot_image = common.GetBootableImage(
849 "IMAGES/init_boot.img", "init_boot.img", OPTIONS.input_tmp, "INIT_BOOT")
850 if init_boot_image:
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800851 partitions['init_boot'] = os.path.join(
852 OPTIONS.input_tmp, "IMAGES", "init_boot.img")
Devin Mooreafdd7c72021-12-13 22:04:08 +0000853 if not os.path.exists(partitions['init_boot']):
854 init_boot_image.WriteToDir(OPTIONS.input_tmp)
855 if output_zip:
856 init_boot_image.AddToZip(output_zip)
857
Greg Kaisere086f722021-09-14 19:32:27 +0000858 if has_vendor_boot:
Steve Mucklee1b10862019-07-10 10:49:37 -0700859 banner("vendor_boot")
860 vendor_boot_image = common.GetVendorBootImage(
861 "IMAGES/vendor_boot.img", "vendor_boot.img", OPTIONS.input_tmp,
862 "VENDOR_BOOT")
863 if vendor_boot_image:
864 partitions['vendor_boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES",
865 "vendor_boot.img")
866 if not os.path.exists(partitions['vendor_boot']):
867 vendor_boot_image.WriteToDir(OPTIONS.input_tmp)
868 if output_zip:
869 vendor_boot_image.AddToZip(output_zip)
870
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800871 recovery_image = None
Greg Kaisere086f722021-09-14 19:32:27 +0000872 if has_recovery:
Tao Baodb45efa2015-10-27 19:25:18 -0700873 banner("recovery")
Tao Bao262bf3f2017-07-11 17:27:55 -0700874 recovery_image = common.GetBootableImage(
875 "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
876 assert recovery_image, "Failed to create recovery.img."
Tao Baobf70c312017-07-11 17:27:55 -0700877 partitions['recovery'] = os.path.join(
Tao Bao262bf3f2017-07-11 17:27:55 -0700878 OPTIONS.input_tmp, "IMAGES", "recovery.img")
Tao Baobf70c312017-07-11 17:27:55 -0700879 if not os.path.exists(partitions['recovery']):
Tao Bao262bf3f2017-07-11 17:27:55 -0700880 recovery_image.WriteToDir(OPTIONS.input_tmp)
881 if output_zip:
882 recovery_image.AddToZip(output_zip)
Doug Zongker3c84f562014-07-31 11:06:30 -0700883
Tao Baod42e97e2016-11-30 12:11:57 -0800884 banner("recovery (two-step image)")
885 # The special recovery.img for two-step package use.
886 recovery_two_step_image = common.GetBootableImage(
Tao Bao04808502019-07-25 23:11:41 -0700887 "OTA/recovery-two-step.img", "recovery-two-step.img",
Tao Baod42e97e2016-11-30 12:11:57 -0800888 OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
Tao Bao262bf3f2017-07-11 17:27:55 -0700889 assert recovery_two_step_image, "Failed to create recovery-two-step.img."
890 recovery_two_step_image_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -0700891 OPTIONS.input_tmp, "OTA", "recovery-two-step.img")
Tao Bao262bf3f2017-07-11 17:27:55 -0700892 if not os.path.exists(recovery_two_step_image_path):
893 recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800894 if output_zip:
895 recovery_two_step_image.AddToZip(output_zip)
Tao Baod42e97e2016-11-30 12:11:57 -0800896
Daniel Norman78554ea2021-09-14 10:29:38 -0700897 def add_partition(partition, has_partition, add_func, add_args):
898 if has_partition:
899 banner(partition)
900 partitions[partition] = add_func(output_zip, *add_args)
Tao Baobf70c312017-07-11 17:27:55 -0700901
Daniel Norman78554ea2021-09-14 10:29:38 -0700902 add_partition_calls = (
903 ("system", has_system, AddSystem, [recovery_image, boot_image]),
904 ("vendor", has_vendor, AddVendor, [recovery_image, boot_image]),
905 ("product", has_product, AddProduct, []),
906 ("system_ext", has_system_ext, AddSystemExt, []),
907 ("odm", has_odm, AddOdm, []),
908 ("vendor_dlkm", has_vendor_dlkm, AddVendorDlkm, []),
909 ("odm_dlkm", has_odm_dlkm, AddOdmDlkm, []),
Ramji Jiyani13a41372022-01-27 07:05:08 +0000910 ("system_dlkm", has_system_dlkm, AddSystemDlkm, []),
Daniel Norman78554ea2021-09-14 10:29:38 -0700911 ("system_other", has_system_other, AddSystemOther, []),
912 )
913 for call in add_partition_calls:
914 add_partition(*call)
Tao Baobf70c312017-07-11 17:27:55 -0700915
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500916 AddApexInfo(output_zip)
917
Tianjie Xub48589a2016-08-03 19:21:52 -0700918 if not OPTIONS.is_signing:
Greg Kaisere086f722021-09-14 19:32:27 +0000919 banner("userdata")
920 AddUserdata(output_zip)
921 banner("cache")
922 AddCache(output_zip)
Tao Baoc633ed02017-05-30 21:46:33 -0700923
924 if OPTIONS.info_dict.get("board_bpt_enable") == "true":
David Zeuthen25328622016-04-08 15:08:03 -0400925 banner("partition-table")
926 AddPartitionTable(output_zip)
Tao Baoc633ed02017-05-30 21:46:33 -0700927
Daniel Norman78554ea2021-09-14 10:29:38 -0700928 add_partition("dtbo",
929 OPTIONS.info_dict.get("has_dtbo") == "true", AddDtbo, [])
930 add_partition("pvmfw",
931 OPTIONS.info_dict.get("has_pvmfw") == "true", AddPvmfw, [])
Andrew Sculle077cf72021-02-18 10:27:29 +0000932
Hongguang Chenf23364d2020-04-27 18:36:36 -0700933 # Custom images.
934 custom_partitions = OPTIONS.info_dict.get(
935 "avb_custom_images_partition_list", "").strip().split()
936 for partition_name in custom_partitions:
937 partition_name = partition_name.strip()
938 banner("custom images for " + partition_name)
939 partitions[partition_name] = AddCustomImages(output_zip, partition_name)
940
Bowgo Tsai3e599ea2017-05-26 18:30:04 +0800941 if OPTIONS.info_dict.get("avb_enable") == "true":
Tao Bao744c4c72018-08-20 21:09:07 -0700942 # vbmeta_partitions includes the partitions that should be included into
943 # top-level vbmeta.img, which are the ones that are not included in any
944 # chained VBMeta image plus the chained VBMeta images themselves.
Hongguang Chenf23364d2020-04-27 18:36:36 -0700945 # Currently custom_partitions are all chained to VBMeta image.
946 vbmeta_partitions = common.AVB_PARTITIONS[:] + tuple(custom_partitions)
Tao Bao744c4c72018-08-20 21:09:07 -0700947
David Anderson7709ab22018-10-15 14:41:34 -0700948 vbmeta_system = OPTIONS.info_dict.get("avb_vbmeta_system", "").strip()
Greg Kaisere086f722021-09-14 19:32:27 +0000949 if vbmeta_system:
David Anderson7709ab22018-10-15 14:41:34 -0700950 banner("vbmeta_system")
Tao Bao71064202018-10-22 15:08:02 -0700951 partitions["vbmeta_system"] = AddVBMeta(
David Anderson7709ab22018-10-15 14:41:34 -0700952 output_zip, partitions, "vbmeta_system", vbmeta_system.split())
Tao Bao744c4c72018-08-20 21:09:07 -0700953 vbmeta_partitions = [
954 item for item in vbmeta_partitions
David Anderson7709ab22018-10-15 14:41:34 -0700955 if item not in vbmeta_system.split()]
956 vbmeta_partitions.append("vbmeta_system")
Tao Bao744c4c72018-08-20 21:09:07 -0700957
958 vbmeta_vendor = OPTIONS.info_dict.get("avb_vbmeta_vendor", "").strip()
Greg Kaisere086f722021-09-14 19:32:27 +0000959 if vbmeta_vendor:
Tao Bao744c4c72018-08-20 21:09:07 -0700960 banner("vbmeta_vendor")
Tao Bao71064202018-10-22 15:08:02 -0700961 partitions["vbmeta_vendor"] = AddVBMeta(
Tao Bao744c4c72018-08-20 21:09:07 -0700962 output_zip, partitions, "vbmeta_vendor", vbmeta_vendor.split())
963 vbmeta_partitions = [
964 item for item in vbmeta_partitions
965 if item not in vbmeta_vendor.split()]
966 vbmeta_partitions.append("vbmeta_vendor")
967
Greg Kaisere086f722021-09-14 19:32:27 +0000968 if OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true":
Bowgo Tsai82182252020-11-13 11:28:17 +0800969 banner("vbmeta")
970 AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions)
Doug Zongker3c84f562014-07-31 11:06:30 -0700971
Tao Bao48a2feb2019-06-28 11:00:05 -0700972 if OPTIONS.info_dict.get("use_dynamic_partitions") == "true":
Greg Kaisere086f722021-09-14 19:32:27 +0000973 if OPTIONS.info_dict.get("build_super_empty_partition") == "true":
Yo Chiange86bab42021-03-25 10:12:28 +0000974 banner("super_empty")
975 AddSuperEmpty(output_zip)
David Anderson1ef03e22018-08-30 13:11:47 -0700976
Greg Kaisere086f722021-09-14 19:32:27 +0000977 if OPTIONS.info_dict.get("build_super_partition") == "true":
Tao Bao519d1822018-12-27 12:47:23 -0800978 if OPTIONS.info_dict.get(
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800979 "build_retrofit_dynamic_partitions_ota_package") == "true":
Yifan Hongc767f7c2018-11-08 15:41:24 -0800980 banner("super split images")
981 AddSuperSplit(output_zip)
Yifan Hongc767f7c2018-11-08 15:41:24 -0800982
Tianjie Xuaaca4212016-06-28 14:34:03 -0700983 banner("radio")
Tao Baobea20ac2018-01-17 17:57:49 -0800984 ab_partitions_txt = os.path.join(OPTIONS.input_tmp, "META",
985 "ab_partitions.txt")
986 if os.path.exists(ab_partitions_txt):
Tao Baoa3705452019-06-24 15:33:41 -0700987 with open(ab_partitions_txt) as f:
Daniel Norman78554ea2021-09-14 10:29:38 -0700988 ab_partitions = f.read().splitlines()
Tianjie Xucfa86222016-03-07 16:31:19 -0800989
Greg Kaisere086f722021-09-14 19:32:27 +0000990 # For devices using A/B update, make sure we have all the needed images
991 # ready under IMAGES/ or RADIO/.
992 CheckAbOtaImages(output_zip, ab_partitions)
Tianjie Xuaaca4212016-06-28 14:34:03 -0700993
Greg Kaisere086f722021-09-14 19:32:27 +0000994 # Generate care_map.pb for ab_partitions, then write this file to
995 # target_files package.
996 output_care_map = os.path.join(OPTIONS.input_tmp, "META", "care_map.pb")
997 AddCareMapForAbOta(output_zip if output_zip else output_care_map,
998 ab_partitions, partitions)
Tianjie Xucfa86222016-03-07 16:31:19 -0800999
Tao Bao95a95c32017-06-16 15:30:23 -07001000 # Radio images that need to be packed into IMAGES/, and product-img.zip.
Tao Baobea20ac2018-01-17 17:57:49 -08001001 pack_radioimages_txt = os.path.join(
Tao Bao95a95c32017-06-16 15:30:23 -07001002 OPTIONS.input_tmp, "META", "pack_radioimages.txt")
Tao Baobea20ac2018-01-17 17:57:49 -08001003 if os.path.exists(pack_radioimages_txt):
Tao Baoa3705452019-06-24 15:33:41 -07001004 with open(pack_radioimages_txt) as f:
Tao Baobea20ac2018-01-17 17:57:49 -08001005 AddPackRadioImages(output_zip, f.readlines())
Tao Bao95a95c32017-06-16 15:30:23 -07001006
Greg Kaisere086f722021-09-14 19:32:27 +00001007 AddVbmetaDigest(output_zip)
Tianjiebbde59f2021-05-03 21:18:56 -07001008
Dan Willemsen2ee00d52017-03-05 19:51:56 -08001009 if output_zip:
1010 common.ZipClose(output_zip)
Tianjie Xu9ac4cb02017-06-09 16:58:03 -07001011 if OPTIONS.replace_updated_files_list:
1012 ReplaceUpdatedFiles(output_zip.filename,
1013 OPTIONS.replace_updated_files_list)
Tianjie Xu38af07f2017-05-25 17:38:53 -07001014
Doug Zongker3c84f562014-07-31 11:06:30 -07001015
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001016def OptimizeCompressedEntries(zipfile_path):
1017 """Convert files that do not compress well to uncompressed storage
1018
1019 EROFS images tend to be compressed already, so compressing them again
1020 yields little space savings. Leaving them uncompressed will make
1021 downstream tooling's job easier, and save compute time.
1022 """
1023 if not zipfile.is_zipfile(zipfile_path):
1024 return
1025 entries_to_store = []
1026 with tempfile.TemporaryDirectory() as tmpdir:
1027 with zipfile.ZipFile(zipfile_path, "r", allowZip64=True) as zfp:
1028 for zinfo in zfp.filelist:
1029 if not zinfo.filename.startswith("IMAGES/") and not zinfo.filename.startswith("META"):
Kelvin Zhang9d021e92022-02-07 16:37:15 -08001030 continue
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001031 # Don't try to store userdata.img uncompressed, it's usually huge.
1032 if zinfo.filename.endswith("userdata.img"):
Kelvin Zhang9d021e92022-02-07 16:37:15 -08001033 continue
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001034 if zinfo.compress_size > zinfo.file_size * 0.80 and zinfo.compress_type != zipfile.ZIP_STORED:
1035 entries_to_store.append(zinfo)
1036 zfp.extract(zinfo, tmpdir)
1037 # Remove these entries, then re-add them as ZIP_STORED
1038 common.RunAndCheckOutput(
1039 ["zip", "-d", zipfile_path] + [entry.filename for entry in entries_to_store])
1040 with zipfile.ZipFile(zipfile_path, "a", allowZip64=True) as zfp:
1041 for entry in entries_to_store:
1042 zfp.write(os.path.join(tmpdir, entry.filename), entry.filename, compress_type=zipfile.ZIP_STORED)
1043
1044
Doug Zongker3c84f562014-07-31 11:06:30 -07001045def main(argv):
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001046 def option_handler(o, a):
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001047 if o in ("-a", "--add_missing"):
1048 OPTIONS.add_missing = True
1049 elif o in ("-r", "--rebuild_recovery",):
1050 OPTIONS.rebuild_recovery = True
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001051 elif o == "--replace_verity_private_key":
1052 OPTIONS.replace_verity_private_key = (True, a)
1053 elif o == "--replace_verity_public_key":
1054 OPTIONS.replace_verity_public_key = (True, a)
Tianjie Xub48589a2016-08-03 19:21:52 -07001055 elif o == "--is_signing":
1056 OPTIONS.is_signing = True
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001057 else:
1058 return False
1059 return True
1060
Dan Albert8b72aef2015-03-23 19:13:21 -07001061 args = common.ParseOptions(
1062 argv, __doc__, extra_opts="ar",
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001063 extra_long_opts=["add_missing", "rebuild_recovery",
1064 "replace_verity_public_key=",
1065 "replace_verity_private_key=",
Greg Kaisere086f722021-09-14 19:32:27 +00001066 "is_signing"],
Dan Albert8b72aef2015-03-23 19:13:21 -07001067 extra_option_handler=option_handler)
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001068
Doug Zongker3c84f562014-07-31 11:06:30 -07001069 if len(args) != 1:
1070 common.Usage(__doc__)
1071 sys.exit(1)
1072
Tao Bao32fcdab2018-10-12 10:30:39 -07001073 common.InitLogging()
1074
Doug Zongker3c84f562014-07-31 11:06:30 -07001075 AddImagesToTargetFiles(args[0])
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001076 OptimizeCompressedEntries(args[0])
Tao Bao32fcdab2018-10-12 10:30:39 -07001077 logger.info("done.")
Doug Zongker3c84f562014-07-31 11:06:30 -07001078
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001079
Doug Zongker3c84f562014-07-31 11:06:30 -07001080if __name__ == '__main__':
1081 try:
1082 common.CloseInheritedPipes()
1083 main(sys.argv[1:])
Doug Zongkerfc44a512014-08-26 13:10:25 -07001084 finally:
1085 common.Cleanup()