blob: b7a5ad8b74cfca03fcb4ebab7d2247c54df770e0 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 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"""
Tao Bao2aac9c92019-08-02 15:50:32 -070018Given an input target-files, produces an image zipfile suitable for use with
19'fastboot update'.
Doug Zongkereef39442009-04-02 12:14:19 -070020
21Usage: img_from_target_files [flags] input_target_files output_image_zip
22
Tao Bao2aac9c92019-08-02 15:50:32 -070023input_target_files: Path to the input target_files zip.
Daniel Normanb8a2f9d2019-04-24 12:55:51 -070024
25Flags:
Doug Zongker55d93282011-01-25 17:03:34 -080026 -z (--bootable_zip)
27 Include only the bootable images (eg 'boot' and 'recovery') in
28 the output.
29
Tao Bao57f8ed62019-08-14 23:08:18 -070030 --additional <filespec>
31 Include an additional entry into the generated zip file. The filespec is
32 in a format that's accepted by zip2zip (e.g.
33 'OTA/android-info.txt:android-info.txt', to copy `OTA/android-info.txt`
34 from input_file into output_file as `android-info.txt`. Refer to the
35 `filespec` arg in zip2zip's help message). The option can be repeated to
36 include multiple entries.
37
Doug Zongkereef39442009-04-02 12:14:19 -070038"""
39
Tao Bao89fbb0f2017-01-10 10:47:58 -080040from __future__ import print_function
41
Tao Bao32fcdab2018-10-12 10:30:39 -070042import logging
Tao Bao76def242017-11-21 09:25:31 -080043import os
Doug Zongkereef39442009-04-02 12:14:19 -070044import sys
Tao Bao76def242017-11-21 09:25:31 -080045import zipfile
46
47import common
Yifan Hong0e97dbb2019-04-17 14:28:52 -070048from build_super_image import BuildSuperImage
Doug Zongkereef39442009-04-02 12:14:19 -070049
Doug Zongkercf6d5a92014-02-18 10:57:07 -080050if sys.hexversion < 0x02070000:
Tao Baoac63a9d2019-08-26 20:33:11 -070051 print('Python 2.7 or newer is required.', file=sys.stderr)
Doug Zongkereef39442009-04-02 12:14:19 -070052 sys.exit(1)
53
Tao Bao32fcdab2018-10-12 10:30:39 -070054logger = logging.getLogger(__name__)
Doug Zongkereef39442009-04-02 12:14:19 -070055
56OPTIONS = common.OPTIONS
57
Tao Bao57f8ed62019-08-14 23:08:18 -070058OPTIONS.additional_entries = []
Tao Baoac63a9d2019-08-26 20:33:11 -070059OPTIONS.bootable_only = False
60OPTIONS.put_super = None
Ram Muthiah0c4a3522020-08-27 00:54:13 +000061OPTIONS.put_bootloader = None
Tao Baoac63a9d2019-08-26 20:33:11 -070062OPTIONS.dynamic_partition_list = None
63OPTIONS.super_device_list = None
64OPTIONS.retrofit_dap = None
65OPTIONS.build_super = None
66OPTIONS.sparse_userimages = None
Daniel Zheng89a85ba2023-09-11 09:49:56 -070067OPTIONS.use_fastboot_info = True
David Iserovichbabdafe2023-08-10 17:58:31 +000068OPTIONS.build_super_image = None
Ying Wanga0febe52013-03-20 11:02:05 -070069
Kelvin Zhangd7924a52023-10-11 19:47:28 -070070
Yifan Hong0e97dbb2019-04-17 14:28:52 -070071def LoadOptions(input_file):
Tao Bao2aac9c92019-08-02 15:50:32 -070072 """Loads information from input_file to OPTIONS.
Yifan Hong0e97dbb2019-04-17 14:28:52 -070073
74 Args:
Tao Bao57f8ed62019-08-14 23:08:18 -070075 input_file: Path to the input target_files zip file.
Yifan Hong0e97dbb2019-04-17 14:28:52 -070076 """
Tao Bao57f8ed62019-08-14 23:08:18 -070077 with zipfile.ZipFile(input_file) as input_zip:
78 info = OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Yifan Hong0e97dbb2019-04-17 14:28:52 -070079
Tao Baoac63a9d2019-08-26 20:33:11 -070080 OPTIONS.put_super = info.get('super_image_in_update_package') == 'true'
Ram Muthiah0c4a3522020-08-27 00:54:13 +000081 OPTIONS.put_bootloader = info.get('bootloader_in_update_package') == 'true'
Tao Baoac63a9d2019-08-26 20:33:11 -070082 OPTIONS.dynamic_partition_list = info.get('dynamic_partition_list',
83 '').strip().split()
84 OPTIONS.super_device_list = info.get('super_block_devices',
85 '').strip().split()
86 OPTIONS.retrofit_dap = info.get('dynamic_partition_retrofit') == 'true'
87 OPTIONS.build_super = info.get('build_super_partition') == 'true'
88 OPTIONS.sparse_userimages = bool(info.get('extfs_sparse_flag'))
Yifan Hong0e97dbb2019-04-17 14:28:52 -070089
90
Tao Bao57f8ed62019-08-14 23:08:18 -070091def CopyZipEntries(input_file, output_file, entries):
92 """Copies ZIP entries between input and output files.
Yifan Hong0e97dbb2019-04-17 14:28:52 -070093
94 Args:
Tao Bao57f8ed62019-08-14 23:08:18 -070095 input_file: Path to the input target_files zip.
96 output_file: Output filename.
97 entries: A list of entries to copy, in a format that's accepted by zip2zip
98 (e.g. 'OTA/android-info.txt:android-info.txt', which copies
99 `OTA/android-info.txt` from input_file into output_file as
100 `android-info.txt`. Refer to the `filespec` arg in zip2zip's help
101 message).
102 """
103 logger.info('Writing %d entries to archive...', len(entries))
104 cmd = ['zip2zip', '-i', input_file, '-o', output_file]
105 cmd.extend(entries)
106 common.RunAndCheckOutput(cmd)
107
108
Kelvin Zhangd7924a52023-10-11 19:47:28 -0700109def LocatePartitionEntry(partition_name, namelist):
110 for subdir in ["IMAGES", "PREBUILT_IMAGES", "RADIO"]:
111 entry_name = os.path.join(subdir, partition_name + ".img")
112 if entry_name in namelist:
113 return entry_name
114
115
Tao Bao57f8ed62019-08-14 23:08:18 -0700116def EntriesForUserImages(input_file):
117 """Returns the user images entries to be copied.
118
119 Args:
120 input_file: Path to the input target_files zip file.
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700121 """
Tao Baoac63a9d2019-08-26 20:33:11 -0700122 dynamic_images = [p + '.img' for p in OPTIONS.dynamic_partition_list]
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700123
124 # Filter out system_other for launch DAP devices because it is in super image.
Tao Baoac63a9d2019-08-26 20:33:11 -0700125 if not OPTIONS.retrofit_dap and 'system' in OPTIONS.dynamic_partition_list:
126 dynamic_images.append('system_other.img')
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700127
Tao Bao57f8ed62019-08-14 23:08:18 -0700128 entries = [
129 'OTA/android-info.txt:android-info.txt',
130 ]
Daniel Zhengee10d072023-06-01 14:18:18 -0700131 if OPTIONS.use_fastboot_info:
132 entries.append('META/fastboot-info.txt:fastboot-info.txt')
Kelvin Zhangd7924a52023-10-11 19:47:28 -0700133 ab_partitions = []
Tao Bao57f8ed62019-08-14 23:08:18 -0700134 with zipfile.ZipFile(input_file) as input_zip:
135 namelist = input_zip.namelist()
Kelvin Zhangd7924a52023-10-11 19:47:28 -0700136 if "META/ab_partitions.txt" in namelist:
137 ab_partitions = input_zip.read(
138 "META/ab_partitions.txt").decode().strip().split()
Kelvin Zhangdecee4a2023-05-19 12:33:06 -0700139 if 'PREBUILT_IMAGES/kernel_16k' in namelist:
140 entries.append('PREBUILT_IMAGES/kernel_16k:kernel_16k')
141 if 'PREBUILT_IMAGES/ramdisk_16k.img' in namelist:
142 entries.append('PREBUILT_IMAGES/ramdisk_16k.img:ramdisk_16k.img')
Tao Bao57f8ed62019-08-14 23:08:18 -0700143
Kelvin Zhangd7924a52023-10-11 19:47:28 -0700144 visited_partitions = set(OPTIONS.dynamic_partition_list)
Tao Bao57f8ed62019-08-14 23:08:18 -0700145 for image_path in [name for name in namelist if name.startswith('IMAGES/')]:
146 image = os.path.basename(image_path)
Kelvin Zhangdecee4a2023-05-19 12:33:06 -0700147 if OPTIONS.bootable_only and image not in ('boot.img', 'recovery.img', 'bootloader', 'init_boot.img'):
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700148 continue
Ram Muthiah0c4a3522020-08-27 00:54:13 +0000149 if not image.endswith('.img') and image != 'bootloader':
150 continue
151 if image == 'bootloader' and not OPTIONS.put_bootloader:
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700152 continue
Tao Bao57f8ed62019-08-14 23:08:18 -0700153 # Filter out super_empty and the images that are already in super partition.
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700154 if OPTIONS.put_super:
Tao Baoac63a9d2019-08-26 20:33:11 -0700155 if image == 'super_empty.img':
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700156 continue
157 if image in dynamic_images:
158 continue
Kelvin Zhangd7924a52023-10-11 19:47:28 -0700159 partition_name = image.rstrip(".img")
160 visited_partitions.add(partition_name)
Tao Bao57f8ed62019-08-14 23:08:18 -0700161 entries.append('{}:{}'.format(image_path, image))
Kelvin Zhangd7924a52023-10-11 19:47:28 -0700162 for part in [part for part in ab_partitions if part not in visited_partitions]:
163 entry = LocatePartitionEntry(part, namelist)
164 image = os.path.basename(entry)
165 if entry is not None:
166 entries.append('{}:{}'.format(entry, image))
Tao Bao57f8ed62019-08-14 23:08:18 -0700167 return entries
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700168
169
Tao Bao57f8ed62019-08-14 23:08:18 -0700170def EntriesForSplitSuperImages(input_file):
171 """Returns the entries for split super images.
Tao Bao2aac9c92019-08-02 15:50:32 -0700172
Tao Bao57f8ed62019-08-14 23:08:18 -0700173 This is only done for retrofit dynamic partition devices.
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700174
175 Args:
Tao Bao57f8ed62019-08-14 23:08:18 -0700176 input_file: Path to the input target_files zip file.
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700177 """
Tao Bao57f8ed62019-08-14 23:08:18 -0700178 with zipfile.ZipFile(input_file) as input_zip:
179 namelist = input_zip.namelist()
180 entries = []
181 for device in OPTIONS.super_device_list:
182 image = 'OTA/super_{}.img'.format(device)
183 assert image in namelist, 'Failed to find {}'.format(image)
184 entries.append('{}:{}'.format(image, os.path.basename(image)))
185 return entries
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700186
Tao Bao57f8ed62019-08-14 23:08:18 -0700187
188def RebuildAndWriteSuperImages(input_file, output_file):
189 """Builds and writes super images to the output file."""
190 logger.info('Building super image...')
191
192 # We need files under IMAGES/, OTA/, META/ for img_from_target_files.py.
193 # However, common.LoadInfoDict() may read additional files under BOOT/,
194 # RECOVERY/ and ROOT/. So unzip everything from the target_files.zip.
195 input_tmp = common.UnzipTemp(input_file)
196
197 super_file = common.MakeTempFile('super_', '.img')
David Iserovichbabdafe2023-08-10 17:58:31 +0000198
199 # Allow overriding the BUILD_SUPER_IMAGE binary
200 if OPTIONS.build_super_image:
201 command = [OPTIONS.build_super_image, input_tmp, super_file]
202 common.RunAndCheckOutput(command)
203 else:
204 BuildSuperImage(input_tmp, super_file)
Tao Bao57f8ed62019-08-14 23:08:18 -0700205
206 logger.info('Writing super.img to archive...')
207 with zipfile.ZipFile(
Kelvin Zhangdecee4a2023-05-19 12:33:06 -0700208 output_file, 'a', compression=zipfile.ZIP_DEFLATED,
209 allowZip64=True) as output_zip:
Tao Baoac63a9d2019-08-26 20:33:11 -0700210 common.ZipWrite(output_zip, super_file, 'super.img')
Yifan Hong0e97dbb2019-04-17 14:28:52 -0700211
212
Tao Bao2aac9c92019-08-02 15:50:32 -0700213def ImgFromTargetFiles(input_file, output_file):
214 """Creates an image archive from the input target_files zip.
215
216 Args:
217 input_file: Path to the input target_files zip.
218 output_file: Output filename.
219
220 Raises:
221 ValueError: On invalid input.
222 """
jiajia tang92be6ee2021-04-21 15:49:51 +0800223 if not os.path.exists(input_file):
224 raise ValueError('%s is not exist' % input_file)
225
Tao Bao2aac9c92019-08-02 15:50:32 -0700226 if not zipfile.is_zipfile(input_file):
Tao Baoac63a9d2019-08-26 20:33:11 -0700227 raise ValueError('%s is not a valid zipfile' % input_file)
Tao Bao2aac9c92019-08-02 15:50:32 -0700228
Tao Baoac63a9d2019-08-26 20:33:11 -0700229 logger.info('Building image zip from target files zip.')
Tao Bao2aac9c92019-08-02 15:50:32 -0700230
Tao Bao57f8ed62019-08-14 23:08:18 -0700231 LoadOptions(input_file)
Tao Bao2aac9c92019-08-02 15:50:32 -0700232
Tao Bao57f8ed62019-08-14 23:08:18 -0700233 # Entries to be copied into the output file.
234 entries = EntriesForUserImages(input_file)
Tao Bao2aac9c92019-08-02 15:50:32 -0700235
Tao Bao57f8ed62019-08-14 23:08:18 -0700236 # Only for devices that retrofit dynamic partitions there're split super
237 # images available in the target_files.zip.
238 rebuild_super = False
239 if OPTIONS.build_super and OPTIONS.put_super:
240 if OPTIONS.retrofit_dap:
241 entries += EntriesForSplitSuperImages(input_file)
242 else:
243 rebuild_super = True
244
245 # Any additional entries provided by caller.
246 entries += OPTIONS.additional_entries
247
248 CopyZipEntries(input_file, output_file, entries)
249
250 if rebuild_super:
251 RebuildAndWriteSuperImages(input_file, output_file)
Tao Bao2aac9c92019-08-02 15:50:32 -0700252
253
Doug Zongkereef39442009-04-02 12:14:19 -0700254def main(argv):
255
Tao Bao57f8ed62019-08-14 23:08:18 -0700256 def option_handler(o, a):
Tao Baoac63a9d2019-08-26 20:33:11 -0700257 if o in ('-z', '--bootable_zip'):
258 OPTIONS.bootable_only = True
Tao Bao57f8ed62019-08-14 23:08:18 -0700259 elif o == '--additional':
260 OPTIONS.additional_entries.append(a)
David Iserovichbabdafe2023-08-10 17:58:31 +0000261 elif o == '--build_super_image':
262 OPTIONS.build_super_image = a
Doug Zongkereef39442009-04-02 12:14:19 -0700263 else:
264 return False
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700265 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700266
267 args = common.ParseOptions(argv, __doc__,
Tao Baoac63a9d2019-08-26 20:33:11 -0700268 extra_opts='z',
Tao Bao57f8ed62019-08-14 23:08:18 -0700269 extra_long_opts=[
270 'additional=',
271 'bootable_zip',
David Iserovichbabdafe2023-08-10 17:58:31 +0000272 'build_super_image=',
Tao Bao57f8ed62019-08-14 23:08:18 -0700273 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700274 extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -0700275 if len(args) != 2:
276 common.Usage(__doc__)
277 sys.exit(1)
278
Tao Bao32fcdab2018-10-12 10:30:39 -0700279 common.InitLogging()
280
Tao Bao2aac9c92019-08-02 15:50:32 -0700281 ImgFromTargetFiles(args[0], args[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700282
Tao Baoac63a9d2019-08-26 20:33:11 -0700283 logger.info('done.')
Doug Zongkereef39442009-04-02 12:14:19 -0700284
285
286if __name__ == '__main__':
287 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -0800288 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -0700289 main(sys.argv[1:])
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700290 finally:
291 common.Cleanup()