blob: 61c4f4ecc90493ef843ae0c0800c43c28f5b9bf6 [file] [log] [blame]
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001#!/usr/bin/env python
2#
3# Copyright (C) 2019 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# 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, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
Daniel Norman4cc9df62019-07-18 10:11:07 -070016#
17"""This script merges two partial target files packages.
Bill Peckhame9eb5f92019-02-01 15:52:10 -080018
Daniel Normand5d70ea2019-06-05 15:13:43 -070019One package contains framework files, and the other contains vendor files.
Daniel Normane5b134a2019-04-17 14:54:06 -070020It produces a complete target files package that can be used to generate an
21OTA package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -080022
23Usage: merge_target_files.py [args]
24
Daniel Normand5d70ea2019-06-05 15:13:43 -070025 --framework-target-files framework-target-files-zip-archive
26 The input target files package containing framework bits. This is a zip
Bill Peckhame9eb5f92019-02-01 15:52:10 -080027 archive.
28
Daniel Normand5d70ea2019-06-05 15:13:43 -070029 --framework-item-list framework-item-list-file
Daniel Norman2c99c5b2019-03-07 13:01:48 -080030 The optional path to a newline-separated config file that replaces the
Daniel Normand5d70ea2019-06-05 15:13:43 -070031 contents of DEFAULT_FRAMEWORK_ITEM_LIST if provided.
Daniel Norman2c99c5b2019-03-07 13:01:48 -080032
Daniel Normand5d70ea2019-06-05 15:13:43 -070033 --framework-misc-info-keys framework-misc-info-keys-file
Daniel Norman2c99c5b2019-03-07 13:01:48 -080034 The optional path to a newline-separated config file that replaces the
Daniel Normand5d70ea2019-06-05 15:13:43 -070035 contents of DEFAULT_FRAMEWORK_MISC_INFO_KEYS if provided.
Daniel Norman2c99c5b2019-03-07 13:01:48 -080036
Daniel Normand5d70ea2019-06-05 15:13:43 -070037 --vendor-target-files vendor-target-files-zip-archive
38 The input target files package containing vendor bits. This is a zip
Bill Peckhame9eb5f92019-02-01 15:52:10 -080039 archive.
40
Daniel Normand5d70ea2019-06-05 15:13:43 -070041 --vendor-item-list vendor-item-list-file
Daniel Norman2c99c5b2019-03-07 13:01:48 -080042 The optional path to a newline-separated config file that replaces the
Daniel Normand5d70ea2019-06-05 15:13:43 -070043 contents of DEFAULT_VENDOR_ITEM_LIST if provided.
Daniel Norman2c99c5b2019-03-07 13:01:48 -080044
Bill Peckhame9eb5f92019-02-01 15:52:10 -080045 --output-target-files output-target-files-package
Daniel Normanfdb38812019-04-15 09:47:24 -070046 If provided, the output merged target files package. Also a zip archive.
47
48 --output-dir output-directory
49 If provided, the destination directory for saving merged files. Requires
50 the --output-item-list flag.
51 Can be provided alongside --output-target-files, or by itself.
52
53 --output-item-list output-item-list-file.
54 The optional path to a newline-separated config file that specifies the
55 file patterns to copy into the --output-dir. Required if providing
56 the --output-dir flag.
Daniel Normana4911da2019-03-15 14:36:21 -070057
Daniel Norman3b64ce12019-04-16 16:11:35 -070058 --output-ota output-ota-package
59 The output ota package. This is a zip archive. Use of this flag may
60 require passing the --path common flag; see common.py.
61
Daniel Norman1bd2a1d2019-04-18 12:32:18 -070062 --output-img output-img-package
63 The output img package, suitable for use with 'fastboot update'. Use of
64 this flag may require passing the --path common flag; see common.py.
65
Daniel Normanf0318252019-04-15 11:34:56 -070066 --output-super-empty output-super-empty-image
67 If provided, creates a super_empty.img file from the merged target
68 files package and saves it at this path.
69
Daniel Normana4911da2019-03-15 14:36:21 -070070 --rebuild_recovery
71 Rebuild the recovery patch used by non-A/B devices and write it to the
72 system image.
Bill Peckham364c1cc2019-03-29 18:27:23 -070073
74 --keep-tmp
75 Keep tempoary files for debugging purposes.
Bill Peckhame9eb5f92019-02-01 15:52:10 -080076"""
77
78from __future__ import print_function
79
Bill Peckhame9eb5f92019-02-01 15:52:10 -080080import fnmatch
81import logging
82import os
Daniel Normanfdb38812019-04-15 09:47:24 -070083import shutil
Bill Peckham540d91a2019-04-25 14:18:16 -070084import subprocess
Bill Peckhame9eb5f92019-02-01 15:52:10 -080085import sys
86import zipfile
87
Bill Peckhame9eb5f92019-02-01 15:52:10 -080088import add_img_to_target_files
Daniel Normanf0318252019-04-15 11:34:56 -070089import build_super_image
Yifan Hongade0d3f2019-08-21 16:37:11 -070090import check_target_files_vintf
Daniel Normanf0318252019-04-15 11:34:56 -070091import common
Daniel Norman1bd2a1d2019-04-18 12:32:18 -070092import img_from_target_files
Daniel Norman3b64ce12019-04-16 16:11:35 -070093import ota_from_target_files
Bill Peckhame9eb5f92019-02-01 15:52:10 -080094
95logger = logging.getLogger(__name__)
Tao Bao2ad4b822019-06-27 16:52:12 -070096
Bill Peckhame9eb5f92019-02-01 15:52:10 -080097OPTIONS = common.OPTIONS
Daniel Normand5d70ea2019-06-05 15:13:43 -070098OPTIONS.framework_target_files = None
99OPTIONS.framework_item_list = None
100OPTIONS.framework_misc_info_keys = None
101OPTIONS.vendor_target_files = None
102OPTIONS.vendor_item_list = None
Bill Peckhamf753e152019-02-19 18:02:46 -0800103OPTIONS.output_target_files = None
Daniel Normanfdb38812019-04-15 09:47:24 -0700104OPTIONS.output_dir = None
105OPTIONS.output_item_list = None
Daniel Norman3b64ce12019-04-16 16:11:35 -0700106OPTIONS.output_ota = None
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700107OPTIONS.output_img = None
Daniel Normanf0318252019-04-15 11:34:56 -0700108OPTIONS.output_super_empty = None
Daniel Normana4911da2019-03-15 14:36:21 -0700109OPTIONS.rebuild_recovery = False
Bill Peckhamf753e152019-02-19 18:02:46 -0800110OPTIONS.keep_tmp = False
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800111
Daniel Normand5d70ea2019-06-05 15:13:43 -0700112# DEFAULT_FRAMEWORK_ITEM_LIST is a list of items to extract from the partial
113# framework target files package as is, meaning these items will land in the
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800114# output target files package exactly as they appear in the input partial
Daniel Normand5d70ea2019-06-05 15:13:43 -0700115# framework target files package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800116
Daniel Normand5d70ea2019-06-05 15:13:43 -0700117DEFAULT_FRAMEWORK_ITEM_LIST = (
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800118 'META/apkcerts.txt',
119 'META/filesystem_config.txt',
120 'META/root_filesystem_config.txt',
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800121 'META/update_engine_config.txt',
122 'PRODUCT/*',
123 'ROOT/*',
124 'SYSTEM/*',
Daniel Normanedf12472019-05-22 10:47:08 -0700125)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800126
Daniel Normand5d70ea2019-06-05 15:13:43 -0700127# FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST is a list of items to extract from the
128# partial framework target files package that need some special processing, such
129# as some sort of combination with items from the partial vendor target files
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800130# package.
131
Daniel Normand5d70ea2019-06-05 15:13:43 -0700132FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST = ('META/*',)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800133
Daniel Normand5d70ea2019-06-05 15:13:43 -0700134# DEFAULT_FRAMEWORK_MISC_INFO_KEYS is a list of keys to obtain from the
135# framework instance of META/misc_info.txt. The remaining keys from the
136# vendor instance.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800137
Daniel Normand5d70ea2019-06-05 15:13:43 -0700138DEFAULT_FRAMEWORK_MISC_INFO_KEYS = (
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800139 'avb_system_hashtree_enable',
140 'avb_system_add_hashtree_footer_args',
141 'avb_system_key_path',
142 'avb_system_algorithm',
143 'avb_system_rollback_index_location',
144 'avb_product_hashtree_enable',
145 'avb_product_add_hashtree_footer_args',
Justin Yun6151e3f2019-06-25 15:58:13 +0900146 'avb_system_ext_hashtree_enable',
147 'avb_system_ext_add_hashtree_footer_args',
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800148 'system_root_image',
149 'root_dir',
150 'ab_update',
151 'default_system_dev_certificate',
152 'system_size',
Daniel Normanedf12472019-05-22 10:47:08 -0700153)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800154
Daniel Normand5d70ea2019-06-05 15:13:43 -0700155# DEFAULT_VENDOR_ITEM_LIST is a list of items to extract from the partial
156# vendor target files package as is, meaning these items will land in the output
157# target files package exactly as they appear in the input partial vendor target
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800158# files package.
159
Daniel Normand5d70ea2019-06-05 15:13:43 -0700160DEFAULT_VENDOR_ITEM_LIST = (
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800161 'META/boot_filesystem_config.txt',
162 'META/otakeys.txt',
163 'META/releasetools.py',
164 'META/vendor_filesystem_config.txt',
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800165 'BOOT/*',
166 'DATA/*',
167 'ODM/*',
168 'OTA/android-info.txt',
169 'PREBUILT_IMAGES/*',
170 'RADIO/*',
171 'VENDOR/*',
Daniel Normanedf12472019-05-22 10:47:08 -0700172)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800173
Daniel Normand5d70ea2019-06-05 15:13:43 -0700174# VENDOR_EXTRACT_SPECIAL_ITEM_LIST is a list of items to extract from the
175# partial vendor target files package that need some special processing, such as
176# some sort of combination with items from the partial framework target files
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800177# package.
178
Daniel Normand5d70ea2019-06-05 15:13:43 -0700179VENDOR_EXTRACT_SPECIAL_ITEM_LIST = ('META/*',)
Daniel Normanedf12472019-05-22 10:47:08 -0700180
181# The merge config lists should not attempt to extract items from both
182# builds for any of the following partitions. The partitions in
183# SINGLE_BUILD_PARTITIONS should come entirely from a single build (either
Daniel Normand5d70ea2019-06-05 15:13:43 -0700184# framework or vendor, but not both).
Daniel Normanedf12472019-05-22 10:47:08 -0700185
186SINGLE_BUILD_PARTITIONS = (
187 'BOOT/',
188 'DATA/',
189 'ODM/',
190 'PRODUCT/',
Justin Yun6151e3f2019-06-25 15:58:13 +0900191 'SYSTEM_EXT/',
Daniel Normanedf12472019-05-22 10:47:08 -0700192 'RADIO/',
193 'RECOVERY/',
194 'ROOT/',
195 'SYSTEM/',
196 'SYSTEM_OTHER/',
197 'VENDOR/',
198)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800199
200
Chris Grossfabf50a2019-05-02 12:42:09 -0700201def write_sorted_data(data, path):
Tao Bao2ad4b822019-06-27 16:52:12 -0700202 """Writes the sorted contents of either a list or dict to file.
Chris Grossfabf50a2019-05-02 12:42:09 -0700203
Tao Bao2ad4b822019-06-27 16:52:12 -0700204 This function sorts the contents of the list or dict and then writes the
205 resulting sorted contents to a file specified by path.
Chris Grossfabf50a2019-05-02 12:42:09 -0700206
207 Args:
208 data: The list or dict to sort and write.
209 path: Path to the file to write the sorted values to. The file at path will
210 be overridden if it exists.
211 """
212 with open(path, 'w') as output:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700213 for entry in sorted(data):
Chris Grossfabf50a2019-05-02 12:42:09 -0700214 out_str = '{}={}\n'.format(entry, data[entry]) if isinstance(
215 data, dict) else '{}\n'.format(entry)
216 output.write(out_str)
217
218
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800219def extract_items(target_files, target_files_temp_dir, extract_item_list):
Tao Bao2ad4b822019-06-27 16:52:12 -0700220 """Extracts items from target files to temporary directory.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800221
222 This function extracts from the specified target files zip archive into the
223 specified temporary directory, the items specified in the extract item list.
224
225 Args:
226 target_files: The target files zip archive from which to extract items.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800227 target_files_temp_dir: The temporary directory where the extracted items
Daniel Normane5b134a2019-04-17 14:54:06 -0700228 will land.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800229 extract_item_list: A list of items to extract.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800230 """
231
232 logger.info('extracting from %s', target_files)
233
234 # Filter the extract_item_list to remove any items that do not exist in the
235 # zip file. Otherwise, the extraction step will fail.
236
Daniel Norman4cc9df62019-07-18 10:11:07 -0700237 with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zipfile:
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800238 target_files_namelist = target_files_zipfile.namelist()
239
240 filtered_extract_item_list = []
241 for pattern in extract_item_list:
242 matching_namelist = fnmatch.filter(target_files_namelist, pattern)
243 if not matching_namelist:
244 logger.warning('no match for %s', pattern)
245 else:
246 filtered_extract_item_list.append(pattern)
247
Bill Peckham8ff3fbd2019-02-22 10:57:43 -0800248 # Extract from target_files into target_files_temp_dir the
249 # filtered_extract_item_list.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800250
Daniel Normane5b134a2019-04-17 14:54:06 -0700251 common.UnzipToDir(target_files, target_files_temp_dir,
252 filtered_extract_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800253
254
Daniel Normanfdb38812019-04-15 09:47:24 -0700255def copy_items(from_dir, to_dir, patterns):
256 """Similar to extract_items() except uses an input dir instead of zip."""
257 file_paths = []
258 for dirpath, _, filenames in os.walk(from_dir):
Daniel Normane5b134a2019-04-17 14:54:06 -0700259 file_paths.extend(
260 os.path.relpath(path=os.path.join(dirpath, filename), start=from_dir)
261 for filename in filenames)
Daniel Normanfdb38812019-04-15 09:47:24 -0700262
263 filtered_file_paths = set()
264 for pattern in patterns:
265 filtered_file_paths.update(fnmatch.filter(file_paths, pattern))
266
267 for file_path in filtered_file_paths:
268 original_file_path = os.path.join(from_dir, file_path)
269 copied_file_path = os.path.join(to_dir, file_path)
270 copied_file_dir = os.path.dirname(copied_file_path)
271 if not os.path.exists(copied_file_dir):
272 os.makedirs(copied_file_dir)
273 if os.path.islink(original_file_path):
274 os.symlink(os.readlink(original_file_path), copied_file_path)
275 else:
276 shutil.copyfile(original_file_path, copied_file_path)
277
278
Daniel Normand5d70ea2019-06-05 15:13:43 -0700279def validate_config_lists(framework_item_list, framework_misc_info_keys,
280 vendor_item_list):
Daniel Normane5964522019-03-19 10:32:03 -0700281 """Performs validations on the merge config lists.
282
283 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700284 framework_item_list: The list of items to extract from the partial framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700285 target files package as is.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700286 framework_misc_info_keys: A list of keys to obtain from the framework
287 instance of META/misc_info.txt. The remaining keys from the vendor
288 instance.
289 vendor_item_list: The list of items to extract from the partial vendor
290 target files package as is.
Daniel Normane5964522019-03-19 10:32:03 -0700291
292 Returns:
293 False if a validation fails, otherwise true.
294 """
Daniel Normanedf12472019-05-22 10:47:08 -0700295 has_error = False
296
Daniel Normand5d70ea2019-06-05 15:13:43 -0700297 default_combined_item_set = set(DEFAULT_FRAMEWORK_ITEM_LIST)
298 default_combined_item_set.update(DEFAULT_VENDOR_ITEM_LIST)
Daniel Normane5964522019-03-19 10:32:03 -0700299
Daniel Normand5d70ea2019-06-05 15:13:43 -0700300 combined_item_set = set(framework_item_list)
301 combined_item_set.update(vendor_item_list)
Daniel Normane5964522019-03-19 10:32:03 -0700302
303 # Check that the merge config lists are not missing any item specified
304 # by the default config lists.
305 difference = default_combined_item_set.difference(combined_item_set)
306 if difference:
Daniel Normane5b134a2019-04-17 14:54:06 -0700307 logger.error('Missing merge config items: %s', list(difference))
Daniel Normane5964522019-03-19 10:32:03 -0700308 logger.error('Please ensure missing items are in either the '
Daniel Normand5d70ea2019-06-05 15:13:43 -0700309 'framework-item-list or vendor-item-list files provided to '
Daniel Normane5964522019-03-19 10:32:03 -0700310 'this script.')
Daniel Normanedf12472019-05-22 10:47:08 -0700311 has_error = True
312
313 for partition in SINGLE_BUILD_PARTITIONS:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700314 in_framework = any(
315 item.startswith(partition) for item in framework_item_list)
316 in_vendor = any(item.startswith(partition) for item in vendor_item_list)
317 if in_framework and in_vendor:
Daniel Normanedf12472019-05-22 10:47:08 -0700318 logger.error(
Tao Bao2ad4b822019-06-27 16:52:12 -0700319 'Cannot extract items from %s for both the framework and vendor'
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900320 ' builds. Please ensure only one merge config item list'
Tao Bao2ad4b822019-06-27 16:52:12 -0700321 ' includes %s.', partition, partition)
Daniel Normanedf12472019-05-22 10:47:08 -0700322 has_error = True
Daniel Normane5964522019-03-19 10:32:03 -0700323
Daniel Normand5d70ea2019-06-05 15:13:43 -0700324 if ('dynamic_partition_list' in framework_misc_info_keys) or (
325 'super_partition_groups' in framework_misc_info_keys):
Daniel Norman19b9fe92019-03-19 14:48:02 -0700326 logger.error('Dynamic partition misc info keys should come from '
Daniel Normand5d70ea2019-06-05 15:13:43 -0700327 'the vendor instance of META/misc_info.txt.')
Daniel Normanedf12472019-05-22 10:47:08 -0700328 has_error = True
Daniel Norman19b9fe92019-03-19 14:48:02 -0700329
Daniel Normanedf12472019-05-22 10:47:08 -0700330 return not has_error
Daniel Normane5964522019-03-19 10:32:03 -0700331
332
Daniel Normand5d70ea2019-06-05 15:13:43 -0700333def process_ab_partitions_txt(framework_target_files_temp_dir,
334 vendor_target_files_temp_dir,
Daniel Normane5b134a2019-04-17 14:54:06 -0700335 output_target_files_temp_dir):
Tao Bao2ad4b822019-06-27 16:52:12 -0700336 """Performs special processing for META/ab_partitions.txt.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800337
Tao Bao2ad4b822019-06-27 16:52:12 -0700338 This function merges the contents of the META/ab_partitions.txt files from the
339 framework directory and the vendor directory, placing the merged result in the
340 output directory. The precondition in that the files are already extracted.
341 The post condition is that the output META/ab_partitions.txt contains the
342 merged content. The format for each ab_partitions.txt a one partition name per
343 line. The output file contains the union of the parition names.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800344
345 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700346 framework_target_files_temp_dir: The name of a directory containing the
347 special items extracted from the framework target files package.
348 vendor_target_files_temp_dir: The name of a directory containing the special
349 items extracted from the vendor target files package.
Daniel Normane5b134a2019-04-17 14:54:06 -0700350 output_target_files_temp_dir: The name of a directory that will be used to
351 create the output target files package after all the special cases are
352 processed.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800353 """
354
Daniel Normand5d70ea2019-06-05 15:13:43 -0700355 framework_ab_partitions_txt = os.path.join(framework_target_files_temp_dir,
356 'META', 'ab_partitions.txt')
357
358 vendor_ab_partitions_txt = os.path.join(vendor_target_files_temp_dir, 'META',
Daniel Normane5b134a2019-04-17 14:54:06 -0700359 'ab_partitions.txt')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800360
Daniel Normand5d70ea2019-06-05 15:13:43 -0700361 with open(framework_ab_partitions_txt) as f:
362 framework_ab_partitions = f.read().splitlines()
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800363
Daniel Normand5d70ea2019-06-05 15:13:43 -0700364 with open(vendor_ab_partitions_txt) as f:
365 vendor_ab_partitions = f.read().splitlines()
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800366
Daniel Normand5d70ea2019-06-05 15:13:43 -0700367 output_ab_partitions = set(framework_ab_partitions + vendor_ab_partitions)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800368
Daniel Normane5b134a2019-04-17 14:54:06 -0700369 output_ab_partitions_txt = os.path.join(output_target_files_temp_dir, 'META',
370 'ab_partitions.txt')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800371
Chris Grossfabf50a2019-05-02 12:42:09 -0700372 write_sorted_data(data=output_ab_partitions, path=output_ab_partitions_txt)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800373
374
Bill Peckham364c1cc2019-03-29 18:27:23 -0700375def append_recovery_to_filesystem_config(output_target_files_temp_dir):
Tao Bao2ad4b822019-06-27 16:52:12 -0700376 """Performs special processing for META/filesystem_config.txt.
Bill Peckham364c1cc2019-03-29 18:27:23 -0700377
Tao Bao2ad4b822019-06-27 16:52:12 -0700378 This function appends recovery information to META/filesystem_config.txt so
379 that recovery patch regeneration will succeed.
Bill Peckham364c1cc2019-03-29 18:27:23 -0700380
381 Args:
Daniel Normane5b134a2019-04-17 14:54:06 -0700382 output_target_files_temp_dir: The name of a directory that will be used to
383 create the output target files package after all the special cases are
384 processed. We find filesystem_config.txt here.
Bill Peckham364c1cc2019-03-29 18:27:23 -0700385 """
386
Daniel Normane5b134a2019-04-17 14:54:06 -0700387 filesystem_config_txt = os.path.join(output_target_files_temp_dir, 'META',
388 'filesystem_config.txt')
Bill Peckham364c1cc2019-03-29 18:27:23 -0700389
390 with open(filesystem_config_txt, 'a') as f:
391 # TODO(bpeckham) this data is hard coded. It should be generated
392 # programmatically.
Daniel Normane5b134a2019-04-17 14:54:06 -0700393 f.write('system/bin/install-recovery.sh 0 0 750 '
394 'selabel=u:object_r:install_recovery_exec:s0 capabilities=0x0\n')
395 f.write('system/recovery-from-boot.p 0 0 644 '
396 'selabel=u:object_r:system_file:s0 capabilities=0x0\n')
397 f.write('system/etc/recovery.img 0 0 440 '
398 'selabel=u:object_r:install_recovery_exec:s0 capabilities=0x0\n')
Bill Peckham364c1cc2019-03-29 18:27:23 -0700399
400
Daniel Normand5d70ea2019-06-05 15:13:43 -0700401def process_misc_info_txt(framework_target_files_temp_dir,
402 vendor_target_files_temp_dir,
403 output_target_files_temp_dir,
404 framework_misc_info_keys):
Tao Bao2ad4b822019-06-27 16:52:12 -0700405 """Performs special processing for META/misc_info.txt.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800406
407 This function merges the contents of the META/misc_info.txt files from the
Daniel Normand5d70ea2019-06-05 15:13:43 -0700408 framework directory and the vendor directory, placing the merged result in the
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800409 output directory. The precondition in that the files are already extracted.
410 The post condition is that the output META/misc_info.txt contains the merged
411 content.
412
413 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700414 framework_target_files_temp_dir: The name of a directory containing the
415 special items extracted from the framework target files package.
416 vendor_target_files_temp_dir: The name of a directory containing the special
417 items extracted from the vendor target files package.
Daniel Normane5b134a2019-04-17 14:54:06 -0700418 output_target_files_temp_dir: The name of a directory that will be used to
419 create the output target files package after all the special cases are
420 processed.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700421 framework_misc_info_keys: A list of keys to obtain from the framework
422 instance of META/misc_info.txt. The remaining keys from the vendor
423 instance.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800424 """
425
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900426 misc_info_path = ['META', 'misc_info.txt']
427 framework_dict = common.LoadDictionaryFromFile(
428 os.path.join(framework_target_files_temp_dir, *misc_info_path))
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800429
Daniel Normand5d70ea2019-06-05 15:13:43 -0700430 # We take most of the misc info from the vendor target files.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800431
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900432 merged_dict = common.LoadDictionaryFromFile(
433 os.path.join(vendor_target_files_temp_dir, *misc_info_path))
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800434
Daniel Normand5d70ea2019-06-05 15:13:43 -0700435 # Replace certain values in merged_dict with values from
436 # framework_dict.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800437
Daniel Normand5d70ea2019-06-05 15:13:43 -0700438 for key in framework_misc_info_keys:
439 merged_dict[key] = framework_dict[key]
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800440
Daniel Norman19b9fe92019-03-19 14:48:02 -0700441 # Merge misc info keys used for Dynamic Partitions.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700442 if (merged_dict.get('use_dynamic_partitions') == 'true') and (
443 framework_dict.get('use_dynamic_partitions') == 'true'):
Daniel Normanbfc51ef2019-07-24 14:34:54 -0700444 merged_dynamic_partitions_dict = common.MergeDynamicPartitionInfoDicts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700445 framework_dict=framework_dict,
446 vendor_dict=merged_dict,
Daniel Normana61cde02019-05-03 14:19:13 -0700447 size_prefix='super_',
448 size_suffix='_group_size',
449 list_prefix='super_',
450 list_suffix='_partition_list')
Daniel Normand5d70ea2019-06-05 15:13:43 -0700451 merged_dict.update(merged_dynamic_partitions_dict)
Tao Bao48a2feb2019-06-28 11:00:05 -0700452 # Ensure that add_img_to_target_files rebuilds super split images for
453 # devices that retrofit dynamic partitions. This flag may have been set to
454 # false in the partial builds to prevent duplicate building of super.img.
Daniel Norman0bf940c2019-06-10 12:50:19 -0700455 merged_dict['build_super_partition'] = 'true'
Daniel Norman19b9fe92019-03-19 14:48:02 -0700456
Daniel Normand5d70ea2019-06-05 15:13:43 -0700457 # Replace <image>_selinux_fc values with framework or vendor file_contexts.bin
Daniel Norman72c626f2019-05-13 15:58:14 -0700458 # depending on which dictionary the key came from.
459 # Only the file basename is required because all selinux_fc properties are
460 # replaced with the full path to the file under META/ when misc_info.txt is
461 # loaded from target files for repacking. See common.py LoadInfoDict().
Daniel Normand5d70ea2019-06-05 15:13:43 -0700462 for key in merged_dict:
Daniel Norman72c626f2019-05-13 15:58:14 -0700463 if key.endswith('_selinux_fc'):
Daniel Normand5d70ea2019-06-05 15:13:43 -0700464 merged_dict[key] = 'vendor_file_contexts.bin'
465 for key in framework_dict:
Daniel Norman72c626f2019-05-13 15:58:14 -0700466 if key.endswith('_selinux_fc'):
Daniel Normand5d70ea2019-06-05 15:13:43 -0700467 merged_dict[key] = 'framework_file_contexts.bin'
Daniel Norman72c626f2019-05-13 15:58:14 -0700468
Daniel Normane5b134a2019-04-17 14:54:06 -0700469 output_misc_info_txt = os.path.join(output_target_files_temp_dir, 'META',
470 'misc_info.txt')
Daniel Normand5d70ea2019-06-05 15:13:43 -0700471 write_sorted_data(data=merged_dict, path=output_misc_info_txt)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800472
473
Daniel Normand5d70ea2019-06-05 15:13:43 -0700474def process_dynamic_partitions_info_txt(framework_target_files_dir,
475 vendor_target_files_dir,
Daniel Normana61cde02019-05-03 14:19:13 -0700476 output_target_files_dir):
Tao Bao2ad4b822019-06-27 16:52:12 -0700477 """Performs special processing for META/dynamic_partitions_info.txt.
Daniel Normana61cde02019-05-03 14:19:13 -0700478
479 This function merges the contents of the META/dynamic_partitions_info.txt
Daniel Normand5d70ea2019-06-05 15:13:43 -0700480 files from the framework directory and the vendor directory, placing the
481 merged result in the output directory.
Daniel Normana61cde02019-05-03 14:19:13 -0700482
Daniel Normand5d70ea2019-06-05 15:13:43 -0700483 This function does nothing if META/dynamic_partitions_info.txt from the vendor
Daniel Normana61cde02019-05-03 14:19:13 -0700484 directory does not exist.
485
486 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700487 framework_target_files_dir: The name of a directory containing the special
488 items extracted from the framework target files package.
489 vendor_target_files_dir: The name of a directory containing the special
490 items extracted from the vendor target files package.
Daniel Normana61cde02019-05-03 14:19:13 -0700491 output_target_files_dir: The name of a directory that will be used to create
492 the output target files package after all the special cases are processed.
493 """
494
495 if not os.path.exists(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700496 os.path.join(vendor_target_files_dir, 'META',
Daniel Normana61cde02019-05-03 14:19:13 -0700497 'dynamic_partitions_info.txt')):
498 return
499
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900500 dynamic_partitions_info_path = ['META', 'dynamic_partitions_info.txt']
Daniel Normana61cde02019-05-03 14:19:13 -0700501
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900502 framework_dynamic_partitions_dict = common.LoadDictionaryFromFile(
503 os.path.join(framework_target_files_dir, *dynamic_partitions_info_path))
504 vendor_dynamic_partitions_dict = common.LoadDictionaryFromFile(
505 os.path.join(vendor_target_files_dir, *dynamic_partitions_info_path))
Daniel Normana61cde02019-05-03 14:19:13 -0700506
Daniel Normanbfc51ef2019-07-24 14:34:54 -0700507 merged_dynamic_partitions_dict = common.MergeDynamicPartitionInfoDicts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700508 framework_dict=framework_dynamic_partitions_dict,
509 vendor_dict=vendor_dynamic_partitions_dict,
Daniel Normana61cde02019-05-03 14:19:13 -0700510 # META/dynamic_partitions_info.txt does not use dynamic_partition_list.
511 include_dynamic_partition_list=False,
512 size_suffix='_size',
513 list_suffix='_partition_list')
514
515 output_dynamic_partitions_info_txt = os.path.join(
516 output_target_files_dir, 'META', 'dynamic_partitions_info.txt')
Chris Grossfabf50a2019-05-02 12:42:09 -0700517 write_sorted_data(
518 data=merged_dynamic_partitions_dict,
519 path=output_dynamic_partitions_info_txt)
520
521
Daniel Normand5d70ea2019-06-05 15:13:43 -0700522def process_apex_keys_apk_certs_common(framework_target_files_dir,
523 vendor_target_files_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700524 output_target_files_dir, file_name):
Tao Bao2ad4b822019-06-27 16:52:12 -0700525 """Performs special processing for META/apexkeys.txt or META/apkcerts.txt.
Chris Grossfabf50a2019-05-02 12:42:09 -0700526
527 This function merges the contents of the META/apexkeys.txt or
Tao Bao2ad4b822019-06-27 16:52:12 -0700528 META/apkcerts.txt files from the framework directory and the vendor directory,
529 placing the merged result in the output directory. The precondition in that
530 the files are already extracted. The post condition is that the output
531 META/apexkeys.txt or META/apkcerts.txt contains the merged content.
Chris Grossfabf50a2019-05-02 12:42:09 -0700532
533 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700534 framework_target_files_dir: The name of a directory containing the special
535 items extracted from the framework target files package.
536 vendor_target_files_dir: The name of a directory containing the special
537 items extracted from the vendor target files package.
Chris Grossfabf50a2019-05-02 12:42:09 -0700538 output_target_files_dir: The name of a directory that will be used to create
539 the output target files package after all the special cases are processed.
540 file_name: The name of the file to merge. One of apkcerts.txt or
541 apexkeys.txt.
542 """
543
544 def read_helper(d):
545 temp = {}
546 file_path = os.path.join(d, 'META', file_name)
547 with open(file_path) as f:
548 for line in f:
549 if line.strip():
550 temp[line.split()[0]] = line.strip()
551 return temp
552
Daniel Normand5d70ea2019-06-05 15:13:43 -0700553 framework_dict = read_helper(framework_target_files_dir)
554 vendor_dict = read_helper(vendor_target_files_dir)
Chris Grossfabf50a2019-05-02 12:42:09 -0700555
Daniel Normand5d70ea2019-06-05 15:13:43 -0700556 for key in framework_dict:
557 if key in vendor_dict and vendor_dict[key] != framework_dict[key]:
Chris Grossfabf50a2019-05-02 12:42:09 -0700558 raise ValueError('Conflicting entries found in %s:\n %s and\n %s' %
Daniel Normand5d70ea2019-06-05 15:13:43 -0700559 (file_name, framework_dict[key], vendor_dict[key]))
560 vendor_dict[key] = framework_dict[key]
Chris Grossfabf50a2019-05-02 12:42:09 -0700561
562 output_file = os.path.join(output_target_files_dir, 'META', file_name)
563
Daniel Normand5d70ea2019-06-05 15:13:43 -0700564 write_sorted_data(data=vendor_dict.values(), path=output_file)
Daniel Normana61cde02019-05-03 14:19:13 -0700565
566
Daniel Normand5d70ea2019-06-05 15:13:43 -0700567def copy_file_contexts(framework_target_files_dir, vendor_target_files_dir,
Daniel Norman72c626f2019-05-13 15:58:14 -0700568 output_target_files_dir):
569 """Creates named copies of each build's file_contexts.bin in output META/."""
Daniel Normand5d70ea2019-06-05 15:13:43 -0700570 framework_fc_path = os.path.join(framework_target_files_dir, 'META',
571 'framework_file_contexts.bin')
572 if not os.path.exists(framework_fc_path):
573 framework_fc_path = os.path.join(framework_target_files_dir, 'META',
574 'file_contexts.bin')
575 if not os.path.exists(framework_fc_path):
576 raise ValueError('Missing framework file_contexts.bin.')
577 shutil.copyfile(
578 framework_fc_path,
579 os.path.join(output_target_files_dir, 'META',
580 'framework_file_contexts.bin'))
581
582 vendor_fc_path = os.path.join(vendor_target_files_dir, 'META',
583 'vendor_file_contexts.bin')
584 if not os.path.exists(vendor_fc_path):
585 vendor_fc_path = os.path.join(vendor_target_files_dir, 'META',
Daniel Normanedf12472019-05-22 10:47:08 -0700586 'file_contexts.bin')
Daniel Normand5d70ea2019-06-05 15:13:43 -0700587 if not os.path.exists(vendor_fc_path):
588 raise ValueError('Missing vendor file_contexts.bin.')
Daniel Norman72c626f2019-05-13 15:58:14 -0700589 shutil.copyfile(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700590 vendor_fc_path,
591 os.path.join(output_target_files_dir, 'META', 'vendor_file_contexts.bin'))
Daniel Norman72c626f2019-05-13 15:58:14 -0700592
593
Daniel Normand5d70ea2019-06-05 15:13:43 -0700594def process_special_cases(framework_target_files_temp_dir,
595 vendor_target_files_temp_dir,
596 output_target_files_temp_dir,
597 framework_misc_info_keys, rebuild_recovery):
Tao Bao2ad4b822019-06-27 16:52:12 -0700598 """Performs special-case processing for certain target files items.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800599
600 Certain files in the output target files package require special-case
601 processing. This function performs all that special-case processing.
602
603 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700604 framework_target_files_temp_dir: The name of a directory containing the
605 special items extracted from the framework target files package.
606 vendor_target_files_temp_dir: The name of a directory containing the special
607 items extracted from the vendor target files package.
Daniel Normane5b134a2019-04-17 14:54:06 -0700608 output_target_files_temp_dir: The name of a directory that will be used to
609 create the output target files package after all the special cases are
610 processed.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700611 framework_misc_info_keys: A list of keys to obtain from the framework
612 instance of META/misc_info.txt. The remaining keys from the vendor
613 instance.
Bill Peckham364c1cc2019-03-29 18:27:23 -0700614 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
Daniel Normane5b134a2019-04-17 14:54:06 -0700615 devices and write it to the system image.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800616 """
617
Daniel Normand5d70ea2019-06-05 15:13:43 -0700618 if 'ab_update' in framework_misc_info_keys:
Bill Peckham364c1cc2019-03-29 18:27:23 -0700619 process_ab_partitions_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700620 framework_target_files_temp_dir=framework_target_files_temp_dir,
621 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Bill Peckham364c1cc2019-03-29 18:27:23 -0700622 output_target_files_temp_dir=output_target_files_temp_dir)
623
624 if rebuild_recovery:
625 append_recovery_to_filesystem_config(
626 output_target_files_temp_dir=output_target_files_temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800627
Daniel Norman72c626f2019-05-13 15:58:14 -0700628 copy_file_contexts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700629 framework_target_files_dir=framework_target_files_temp_dir,
630 vendor_target_files_dir=vendor_target_files_temp_dir,
Daniel Norman72c626f2019-05-13 15:58:14 -0700631 output_target_files_dir=output_target_files_temp_dir)
632
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800633 process_misc_info_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700634 framework_target_files_temp_dir=framework_target_files_temp_dir,
635 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800636 output_target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700637 framework_misc_info_keys=framework_misc_info_keys)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800638
Daniel Normana61cde02019-05-03 14:19:13 -0700639 process_dynamic_partitions_info_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700640 framework_target_files_dir=framework_target_files_temp_dir,
641 vendor_target_files_dir=vendor_target_files_temp_dir,
Daniel Norman714bd122019-05-08 16:20:02 -0700642 output_target_files_dir=output_target_files_temp_dir)
Daniel Normana61cde02019-05-03 14:19:13 -0700643
Chris Grossfabf50a2019-05-02 12:42:09 -0700644 process_apex_keys_apk_certs_common(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700645 framework_target_files_dir=framework_target_files_temp_dir,
646 vendor_target_files_dir=vendor_target_files_temp_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700647 output_target_files_dir=output_target_files_temp_dir,
648 file_name='apkcerts.txt')
649
650 process_apex_keys_apk_certs_common(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700651 framework_target_files_dir=framework_target_files_temp_dir,
652 vendor_target_files_dir=vendor_target_files_temp_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700653 output_target_files_dir=output_target_files_temp_dir,
654 file_name='apexkeys.txt')
655
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800656
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900657def files_from_path(target_path, extra_args=None):
Tao Bao2ad4b822019-06-27 16:52:12 -0700658 """Gets files under given path.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800659
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900660 Get (sub)files from given target path and return sorted list.
661
662 Args:
663 target_path: Target path to get subfiles.
664 extra_args: List of extra argument for find command. Optional.
665
666 Returns:
667 Sorted files and directories list.
668 """
669
670 find_command = ['find', target_path] + (extra_args or [])
671 find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
Daniel Norman4cc9df62019-07-18 10:11:07 -0700672 return common.RunAndCheckOutput(['sort'],
673 stdin=find_process.stdout,
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900674 verbose=False)
675
676
677def create_merged_package(temp_dir, framework_target_files, framework_item_list,
678 vendor_target_files, vendor_item_list,
Daniel Norman4cc9df62019-07-18 10:11:07 -0700679 framework_misc_info_keys, rebuild_recovery):
Tao Bao2ad4b822019-06-27 16:52:12 -0700680 """Merges two target files packages into one target files structure.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800681
682 Args:
683 temp_dir: The name of a directory we use when we extract items from the
Daniel Normane5b134a2019-04-17 14:54:06 -0700684 input target files packages, and also a scratch directory that we use for
685 temporary files.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700686 framework_target_files: The name of the zip archive containing the framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700687 partial target files package.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700688 framework_item_list: The list of items to extract from the partial framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700689 target files package as is, meaning these items will land in the output
Daniel Normand5d70ea2019-06-05 15:13:43 -0700690 target files package exactly as they appear in the input partial framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700691 target files package.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700692 vendor_target_files: The name of the zip archive containing the vendor
693 partial target files package.
694 vendor_item_list: The list of items to extract from the partial vendor
695 target files package as is, meaning these items will land in the output
696 target files package exactly as they appear in the input partial vendor
Daniel Normane5b134a2019-04-17 14:54:06 -0700697 target files package.
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900698 framework_misc_info_keys: The list of keys to obtain from the framework
699 instance of META/misc_info.txt. The remaining keys from the vendor
700 instance.
Daniel Normana4911da2019-03-15 14:36:21 -0700701 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
Daniel Normane5b134a2019-04-17 14:54:06 -0700702 devices and write it to the system image.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800703
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900704 Returns:
705 Path to merged package under temp directory.
706 """
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800707
Daniel Normand5d70ea2019-06-05 15:13:43 -0700708 # Create directory names that we'll use when we extract files from framework,
709 # and vendor, and for zipping the final output.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800710
Daniel Normand5d70ea2019-06-05 15:13:43 -0700711 framework_target_files_temp_dir = os.path.join(temp_dir, 'framework')
712 vendor_target_files_temp_dir = os.path.join(temp_dir, 'vendor')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800713 output_target_files_temp_dir = os.path.join(temp_dir, 'output')
714
Daniel Normand5d70ea2019-06-05 15:13:43 -0700715 # Extract "as is" items from the input framework partial target files package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800716 # We extract them directly into the output temporary directory since the
717 # items do not need special case processing.
718
Bill Peckham889b0c62019-02-21 18:53:37 -0800719 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700720 target_files=framework_target_files,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800721 target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700722 extract_item_list=framework_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800723
Daniel Normand5d70ea2019-06-05 15:13:43 -0700724 # Extract "as is" items from the input vendor partial target files package. We
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800725 # extract them directly into the output temporary directory since the items
726 # do not need special case processing.
727
Bill Peckham889b0c62019-02-21 18:53:37 -0800728 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700729 target_files=vendor_target_files,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800730 target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700731 extract_item_list=vendor_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800732
Daniel Normand5d70ea2019-06-05 15:13:43 -0700733 # Extract "special" items from the input framework partial target files
734 # package. We extract these items to different directory since they require
735 # special processing before they will end up in the output directory.
736
737 extract_items(
738 target_files=framework_target_files,
739 target_files_temp_dir=framework_target_files_temp_dir,
740 extract_item_list=FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST)
741
742 # Extract "special" items from the input vendor partial target files package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800743 # We extract these items to different directory since they require special
744 # processing before they will end up in the output directory.
745
Bill Peckham889b0c62019-02-21 18:53:37 -0800746 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700747 target_files=vendor_target_files,
748 target_files_temp_dir=vendor_target_files_temp_dir,
749 extract_item_list=VENDOR_EXTRACT_SPECIAL_ITEM_LIST)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800750
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800751 # Now that the temporary directories contain all the extracted files, perform
752 # special case processing on any items that need it. After this function
753 # completes successfully, all the files we need to create the output target
754 # files package are in place.
755
Bill Peckham889b0c62019-02-21 18:53:37 -0800756 process_special_cases(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700757 framework_target_files_temp_dir=framework_target_files_temp_dir,
758 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800759 output_target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700760 framework_misc_info_keys=framework_misc_info_keys,
Bill Peckham364c1cc2019-03-29 18:27:23 -0700761 rebuild_recovery=rebuild_recovery)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800762
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900763 return output_target_files_temp_dir
764
765
766def generate_images(target_files_dir, rebuild_recovery):
767 """Generate images from target files.
768
769 This function takes merged output temporary directory and create images
770 from it.
771
772 Args:
773 target_files_dir: Path to merged temp directory.
774 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
775 devices and write it to the system image.
776 """
777
778 # Regenerate IMAGES in the target directory.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800779
Daniel Normana4911da2019-03-15 14:36:21 -0700780 add_img_args = ['--verbose']
Paul Trautrimbc3600c2019-08-13 18:07:03 +0900781 add_img_args.append('--add_missing')
Daniel Normana4911da2019-03-15 14:36:21 -0700782 if rebuild_recovery:
783 add_img_args.append('--rebuild_recovery')
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900784 add_img_args.append(target_files_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800785
786 add_img_to_target_files.main(add_img_args)
787
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900788
789def generate_super_empty_image(target_dir, output_super_empty):
Tao Bao2ad4b822019-06-27 16:52:12 -0700790 """Generates super_empty image from target package.
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900791
792 Args:
793 target_dir: Path to the target file package which contains misc_info.txt for
794 detailed information for super image.
795 output_super_empty: If provided, copies a super_empty.img file from the
796 target files package to this path.
797 """
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700798 # Create super_empty.img using the merged misc_info.txt.
799
Daniel Norman4cc9df62019-07-18 10:11:07 -0700800 misc_info_txt = os.path.join(target_dir, 'META', 'misc_info.txt')
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700801
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900802 use_dynamic_partitions = common.LoadDictionaryFromFile(misc_info_txt).get(
803 'use_dynamic_partitions')
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700804
805 if use_dynamic_partitions != 'true' and output_super_empty:
806 raise ValueError(
807 'Building super_empty.img requires use_dynamic_partitions=true.')
808 elif use_dynamic_partitions == 'true':
Daniel Norman4cc9df62019-07-18 10:11:07 -0700809 super_empty_img = os.path.join(target_dir, 'IMAGES', 'super_empty.img')
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700810 build_super_image_args = [
811 misc_info_txt,
812 super_empty_img,
813 ]
814 build_super_image.main(build_super_image_args)
815
816 # Copy super_empty.img to the user-provided output_super_empty location.
817 if output_super_empty:
818 shutil.copyfile(super_empty_img, output_super_empty)
819
Daniel Normanb8a2f9d2019-04-24 12:55:51 -0700820
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900821def create_target_files_archive(output_file, source_dir, temp_dir):
Tao Bao2ad4b822019-06-27 16:52:12 -0700822 """Creates archive from target package.
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900823
824 Args:
825 output_file: The name of the zip archive target files package.
826 source_dir: The target directory contains package to be archived.
827 temp_dir: Path to temporary directory for any intermediate files.
828 """
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800829 output_target_files_list = os.path.join(temp_dir, 'output.list')
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900830 output_zip = os.path.abspath(output_file)
Daniel Norman4cc9df62019-07-18 10:11:07 -0700831 output_target_files_meta_dir = os.path.join(source_dir, 'META')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800832
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900833 meta_content = files_from_path(output_target_files_meta_dir)
Daniel Norman4cc9df62019-07-18 10:11:07 -0700834 other_content = files_from_path(
835 source_dir,
836 ['-path', output_target_files_meta_dir, '-prune', '-o', '-print'])
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800837
Tao Bao2ad4b822019-06-27 16:52:12 -0700838 with open(output_target_files_list, 'w') as f:
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800839 f.write(meta_content)
840 f.write(other_content)
841
842 command = [
Bill Peckhamf753e152019-02-19 18:02:46 -0800843 'soong_zip',
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800844 '-d',
Daniel Normane5b134a2019-04-17 14:54:06 -0700845 '-o',
846 output_zip,
847 '-C',
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900848 source_dir,
Daniel Normane5b134a2019-04-17 14:54:06 -0700849 '-l',
850 output_target_files_list,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800851 ]
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900852
853 logger.info('creating %s', output_file)
Bill Peckham889b0c62019-02-21 18:53:37 -0800854 common.RunAndWait(command, verbose=True)
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900855 logger.info('finished creating %s', output_file)
856
857 return output_zip
858
859
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900860def merge_target_files(temp_dir, framework_target_files, framework_item_list,
861 framework_misc_info_keys, vendor_target_files,
862 vendor_item_list, output_target_files, output_dir,
863 output_item_list, output_ota, output_img,
864 output_super_empty, rebuild_recovery):
Tao Bao2ad4b822019-06-27 16:52:12 -0700865 """Merges two target files packages together.
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900866
867 This function takes framework and vendor target files packages as input,
868 performs various file extractions, special case processing, and finally
869 creates a merged zip archive as output.
870
871 Args:
872 temp_dir: The name of a directory we use when we extract items from the
873 input target files packages, and also a scratch directory that we use for
874 temporary files.
875 framework_target_files: The name of the zip archive containing the framework
876 partial target files package.
877 framework_item_list: The list of items to extract from the partial framework
878 target files package as is, meaning these items will land in the output
879 target files package exactly as they appear in the input partial framework
880 target files package.
881 framework_misc_info_keys: The list of keys to obtain from the framework
882 instance of META/misc_info.txt. The remaining keys from the vendor
883 instance.
884 vendor_target_files: The name of the zip archive containing the vendor
885 partial target files package.
886 vendor_item_list: The list of items to extract from the partial vendor
887 target files package as is, meaning these items will land in the output
888 target files package exactly as they appear in the input partial vendor
889 target files package.
890 output_target_files: The name of the output zip archive target files package
891 created by merging framework and vendor.
892 output_dir: The destination directory for saving merged files.
893 output_item_list: The list of items to copy into the output_dir.
894 output_ota: The name of the output zip archive ota package.
895 output_img: The name of the output zip archive img package.
896 output_super_empty: If provided, creates a super_empty.img file from the
897 merged target files package and saves it at this path.
898 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
899 devices and write it to the system image.
900 """
901
902 logger.info('starting: merge framework %s and vendor %s into output %s',
903 framework_target_files, vendor_target_files, output_target_files)
904
905 output_target_files_temp_dir = create_merged_package(
906 temp_dir, framework_target_files, framework_item_list,
907 vendor_target_files, vendor_item_list, framework_misc_info_keys,
908 rebuild_recovery)
909
Yifan Hongade0d3f2019-08-21 16:37:11 -0700910 if not check_target_files_vintf.CheckVintf(output_target_files_temp_dir):
911 raise RuntimeError("Incompatible VINTF metadata")
912
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900913 generate_images(output_target_files_temp_dir, rebuild_recovery)
914
915 generate_super_empty_image(output_target_files_temp_dir, output_super_empty)
916
917 if output_img:
918 # Create the IMG package from the merged target files (before zipping, in
919 # order to avoid an unnecessary unzip and copy).
Daniel Norman4cc9df62019-07-18 10:11:07 -0700920 img_from_target_files.main([output_target_files_temp_dir, output_img])
Kiyoung Kim7cbeda72019-06-28 13:26:04 +0900921
922 # Finally, create the output target files zip archive and/or copy the
923 # output items to the output target files directory.
924
925 if output_dir:
926 copy_items(output_target_files_temp_dir, output_dir, output_item_list)
927
928 if not output_target_files:
929 return
930
931 output_zip = create_target_files_archive(output_target_files,
932 output_target_files_temp_dir,
933 temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800934
Daniel Norman3b64ce12019-04-16 16:11:35 -0700935 # Create the OTA package from the merged target files package.
936
937 if output_ota:
Daniel Norman4cc9df62019-07-18 10:11:07 -0700938 ota_from_target_files.main([output_zip, output_ota])
Daniel Norman3b64ce12019-04-16 16:11:35 -0700939
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700940
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800941def call_func_with_temp_dir(func, keep_tmp):
Tao Bao2ad4b822019-06-27 16:52:12 -0700942 """Manages the creation and cleanup of the temporary directory.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800943
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800944 This function calls the given function after first creating a temporary
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800945 directory. It also cleans up the temporary directory.
946
947 Args:
Daniel Normane5b134a2019-04-17 14:54:06 -0700948 func: The function to call. Should accept one parameter, the path to the
949 temporary directory.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800950 keep_tmp: Keep the temporary directory after processing is complete.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800951 """
952
953 # Create a temporary directory. This will serve as the parent of directories
954 # we use when we extract items from the input target files packages, and also
955 # a scratch directory that we use for temporary files.
956
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800957 temp_dir = common.MakeTempDir(prefix='merge_target_files_')
958
959 try:
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800960 func(temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800961 finally:
962 if keep_tmp:
963 logger.info('keeping %s', temp_dir)
964 else:
965 common.Cleanup()
966
967
968def main():
969 """The main function.
970
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800971 Process command line arguments, then call merge_target_files to
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800972 perform the heavy lifting.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800973 """
974
975 common.InitLogging()
976
Bill Peckhamf753e152019-02-19 18:02:46 -0800977 def option_handler(o, a):
978 if o == '--system-target-files':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700979 logger.warning(
980 '--system-target-files has been renamed to --framework-target-files')
981 OPTIONS.framework_target_files = a
982 elif o == '--framework-target-files':
983 OPTIONS.framework_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800984 elif o == '--system-item-list':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700985 logger.warning(
986 '--system-item-list has been renamed to --framework-item-list')
987 OPTIONS.framework_item_list = a
988 elif o == '--framework-item-list':
989 OPTIONS.framework_item_list = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800990 elif o == '--system-misc-info-keys':
Daniel Norman4cc9df62019-07-18 10:11:07 -0700991 logger.warning('--system-misc-info-keys has been renamed to '
992 '--framework-misc-info-keys')
Daniel Normand5d70ea2019-06-05 15:13:43 -0700993 OPTIONS.framework_misc_info_keys = a
994 elif o == '--framework-misc-info-keys':
995 OPTIONS.framework_misc_info_keys = a
Bill Peckhamf753e152019-02-19 18:02:46 -0800996 elif o == '--other-target-files':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700997 logger.warning(
998 '--other-target-files has been renamed to --vendor-target-files')
999 OPTIONS.vendor_target_files = a
1000 elif o == '--vendor-target-files':
1001 OPTIONS.vendor_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001002 elif o == '--other-item-list':
Daniel Normand5d70ea2019-06-05 15:13:43 -07001003 logger.warning('--other-item-list has been renamed to --vendor-item-list')
1004 OPTIONS.vendor_item_list = a
1005 elif o == '--vendor-item-list':
1006 OPTIONS.vendor_item_list = a
Bill Peckhamf753e152019-02-19 18:02:46 -08001007 elif o == '--output-target-files':
1008 OPTIONS.output_target_files = a
Daniel Normanfdb38812019-04-15 09:47:24 -07001009 elif o == '--output-dir':
1010 OPTIONS.output_dir = a
1011 elif o == '--output-item-list':
1012 OPTIONS.output_item_list = a
Daniel Norman3b64ce12019-04-16 16:11:35 -07001013 elif o == '--output-ota':
1014 OPTIONS.output_ota = a
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001015 elif o == '--output-img':
1016 OPTIONS.output_img = a
Daniel Normanf0318252019-04-15 11:34:56 -07001017 elif o == '--output-super-empty':
1018 OPTIONS.output_super_empty = a
Daniel Normana4911da2019-03-15 14:36:21 -07001019 elif o == '--rebuild_recovery':
1020 OPTIONS.rebuild_recovery = True
Bill Peckham364c1cc2019-03-29 18:27:23 -07001021 elif o == '--keep-tmp':
Bill Peckhamf753e152019-02-19 18:02:46 -08001022 OPTIONS.keep_tmp = True
1023 else:
1024 return False
1025 return True
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001026
Bill Peckhamf753e152019-02-19 18:02:46 -08001027 args = common.ParseOptions(
Daniel Normane5b134a2019-04-17 14:54:06 -07001028 sys.argv[1:],
1029 __doc__,
Bill Peckhamf753e152019-02-19 18:02:46 -08001030 extra_long_opts=[
1031 'system-target-files=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001032 'framework-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001033 'system-item-list=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001034 'framework-item-list=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001035 'system-misc-info-keys=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001036 'framework-misc-info-keys=',
Bill Peckhamf753e152019-02-19 18:02:46 -08001037 'other-target-files=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001038 'vendor-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001039 'other-item-list=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001040 'vendor-item-list=',
Bill Peckhamf753e152019-02-19 18:02:46 -08001041 'output-target-files=',
Daniel Normanfdb38812019-04-15 09:47:24 -07001042 'output-dir=',
1043 'output-item-list=',
Daniel Norman3b64ce12019-04-16 16:11:35 -07001044 'output-ota=',
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001045 'output-img=',
Daniel Normanf0318252019-04-15 11:34:56 -07001046 'output-super-empty=',
Daniel Normana4911da2019-03-15 14:36:21 -07001047 'rebuild_recovery',
Bill Peckham364c1cc2019-03-29 18:27:23 -07001048 'keep-tmp',
Bill Peckhamf753e152019-02-19 18:02:46 -08001049 ],
1050 extra_option_handler=option_handler)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001051
Tao Bao2ad4b822019-06-27 16:52:12 -07001052 # pylint: disable=too-many-boolean-expressions
Daniel Normand5d70ea2019-06-05 15:13:43 -07001053 if (args or OPTIONS.framework_target_files is None or
1054 OPTIONS.vendor_target_files is None or
Daniel Normane5b134a2019-04-17 14:54:06 -07001055 (OPTIONS.output_target_files is None and OPTIONS.output_dir is None) or
1056 (OPTIONS.output_dir is not None and OPTIONS.output_item_list is None)):
Bill Peckhamf753e152019-02-19 18:02:46 -08001057 common.Usage(__doc__)
Bill Peckham889b0c62019-02-21 18:53:37 -08001058 sys.exit(1)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001059
Tao Baoabb806b2019-08-06 10:32:32 -07001060 # Always turn on verbose logging.
1061 OPTIONS.verbose = True
1062
Daniel Normand5d70ea2019-06-05 15:13:43 -07001063 if OPTIONS.framework_item_list:
Daniel Norman4cc9df62019-07-18 10:11:07 -07001064 framework_item_list = common.LoadListFromFile(OPTIONS.framework_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001065 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001066 framework_item_list = DEFAULT_FRAMEWORK_ITEM_LIST
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001067
Daniel Normand5d70ea2019-06-05 15:13:43 -07001068 if OPTIONS.framework_misc_info_keys:
Daniel Norman4cc9df62019-07-18 10:11:07 -07001069 framework_misc_info_keys = common.LoadListFromFile(
Daniel Normand5d70ea2019-06-05 15:13:43 -07001070 OPTIONS.framework_misc_info_keys)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001071 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001072 framework_misc_info_keys = DEFAULT_FRAMEWORK_MISC_INFO_KEYS
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001073
Daniel Normand5d70ea2019-06-05 15:13:43 -07001074 if OPTIONS.vendor_item_list:
Daniel Norman4cc9df62019-07-18 10:11:07 -07001075 vendor_item_list = common.LoadListFromFile(OPTIONS.vendor_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001076 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001077 vendor_item_list = DEFAULT_VENDOR_ITEM_LIST
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001078
Daniel Normanfdb38812019-04-15 09:47:24 -07001079 if OPTIONS.output_item_list:
Daniel Norman4cc9df62019-07-18 10:11:07 -07001080 output_item_list = common.LoadListFromFile(OPTIONS.output_item_list)
Daniel Normanfdb38812019-04-15 09:47:24 -07001081 else:
1082 output_item_list = None
1083
Daniel Normane5964522019-03-19 10:32:03 -07001084 if not validate_config_lists(
Daniel Normand5d70ea2019-06-05 15:13:43 -07001085 framework_item_list=framework_item_list,
1086 framework_misc_info_keys=framework_misc_info_keys,
1087 vendor_item_list=vendor_item_list):
Daniel Normane5964522019-03-19 10:32:03 -07001088 sys.exit(1)
1089
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001090 call_func_with_temp_dir(
1091 lambda temp_dir: merge_target_files(
1092 temp_dir=temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -07001093 framework_target_files=OPTIONS.framework_target_files,
1094 framework_item_list=framework_item_list,
1095 framework_misc_info_keys=framework_misc_info_keys,
1096 vendor_target_files=OPTIONS.vendor_target_files,
1097 vendor_item_list=vendor_item_list,
Daniel Normana4911da2019-03-15 14:36:21 -07001098 output_target_files=OPTIONS.output_target_files,
Daniel Normanfdb38812019-04-15 09:47:24 -07001099 output_dir=OPTIONS.output_dir,
1100 output_item_list=output_item_list,
Daniel Norman3b64ce12019-04-16 16:11:35 -07001101 output_ota=OPTIONS.output_ota,
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001102 output_img=OPTIONS.output_img,
Daniel Normanf0318252019-04-15 11:34:56 -07001103 output_super_empty=OPTIONS.output_super_empty,
Daniel Normane5b134a2019-04-17 14:54:06 -07001104 rebuild_recovery=OPTIONS.rebuild_recovery), OPTIONS.keep_tmp)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001105
1106
1107if __name__ == '__main__':
Bill Peckham889b0c62019-02-21 18:53:37 -08001108 main()