blob: f0ae21717144712d1fe701f77d5e5d58224f88ee [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 Normane5b134a2019-04-17 14:54:06 -070016"""This script merges two partial target files packages.
Bill Peckhame9eb5f92019-02-01 15:52:10 -080017
Daniel Normand5d70ea2019-06-05 15:13:43 -070018One package contains framework files, and the other contains vendor files.
Daniel Normane5b134a2019-04-17 14:54:06 -070019It produces a complete target files package that can be used to generate an
20OTA package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -080021
22Usage: merge_target_files.py [args]
23
Daniel Normand5d70ea2019-06-05 15:13:43 -070024 --framework-target-files framework-target-files-zip-archive
25 The input target files package containing framework bits. This is a zip
Bill Peckhame9eb5f92019-02-01 15:52:10 -080026 archive.
27
Daniel Normand5d70ea2019-06-05 15:13:43 -070028 --framework-item-list framework-item-list-file
Daniel Norman2c99c5b2019-03-07 13:01:48 -080029 The optional path to a newline-separated config file that replaces the
Daniel Normand5d70ea2019-06-05 15:13:43 -070030 contents of DEFAULT_FRAMEWORK_ITEM_LIST if provided.
Daniel Norman2c99c5b2019-03-07 13:01:48 -080031
Daniel Normand5d70ea2019-06-05 15:13:43 -070032 --framework-misc-info-keys framework-misc-info-keys-file
Daniel Norman2c99c5b2019-03-07 13:01:48 -080033 The optional path to a newline-separated config file that replaces the
Daniel Normand5d70ea2019-06-05 15:13:43 -070034 contents of DEFAULT_FRAMEWORK_MISC_INFO_KEYS if provided.
Daniel Norman2c99c5b2019-03-07 13:01:48 -080035
Daniel Normand5d70ea2019-06-05 15:13:43 -070036 --vendor-target-files vendor-target-files-zip-archive
37 The input target files package containing vendor bits. This is a zip
Bill Peckhame9eb5f92019-02-01 15:52:10 -080038 archive.
39
Daniel Normand5d70ea2019-06-05 15:13:43 -070040 --vendor-item-list vendor-item-list-file
Daniel Norman2c99c5b2019-03-07 13:01:48 -080041 The optional path to a newline-separated config file that replaces the
Daniel Normand5d70ea2019-06-05 15:13:43 -070042 contents of DEFAULT_VENDOR_ITEM_LIST if provided.
Daniel Norman2c99c5b2019-03-07 13:01:48 -080043
Bill Peckhame9eb5f92019-02-01 15:52:10 -080044 --output-target-files output-target-files-package
Daniel Normanfdb38812019-04-15 09:47:24 -070045 If provided, the output merged target files package. Also a zip archive.
46
47 --output-dir output-directory
48 If provided, the destination directory for saving merged files. Requires
49 the --output-item-list flag.
50 Can be provided alongside --output-target-files, or by itself.
51
52 --output-item-list output-item-list-file.
53 The optional path to a newline-separated config file that specifies the
54 file patterns to copy into the --output-dir. Required if providing
55 the --output-dir flag.
Daniel Normana4911da2019-03-15 14:36:21 -070056
Daniel Norman3b64ce12019-04-16 16:11:35 -070057 --output-ota output-ota-package
58 The output ota package. This is a zip archive. Use of this flag may
59 require passing the --path common flag; see common.py.
60
Daniel Norman1bd2a1d2019-04-18 12:32:18 -070061 --output-img output-img-package
62 The output img package, suitable for use with 'fastboot update'. Use of
63 this flag may require passing the --path common flag; see common.py.
64
Daniel Normanf0318252019-04-15 11:34:56 -070065 --output-super-empty output-super-empty-image
66 If provided, creates a super_empty.img file from the merged target
67 files package and saves it at this path.
68
Daniel Normana4911da2019-03-15 14:36:21 -070069 --rebuild_recovery
70 Rebuild the recovery patch used by non-A/B devices and write it to the
71 system image.
Bill Peckham364c1cc2019-03-29 18:27:23 -070072
73 --keep-tmp
74 Keep tempoary files for debugging purposes.
Bill Peckhame9eb5f92019-02-01 15:52:10 -080075"""
76
77from __future__ import print_function
78
Bill Peckhame9eb5f92019-02-01 15:52:10 -080079import fnmatch
80import logging
81import os
Daniel Normanfdb38812019-04-15 09:47:24 -070082import shutil
Bill Peckham540d91a2019-04-25 14:18:16 -070083import subprocess
Bill Peckhame9eb5f92019-02-01 15:52:10 -080084import sys
85import zipfile
86
Bill Peckhame9eb5f92019-02-01 15:52:10 -080087import add_img_to_target_files
Daniel Normanf0318252019-04-15 11:34:56 -070088import build_super_image
89import common
Daniel Norman1bd2a1d2019-04-18 12:32:18 -070090import img_from_target_files
Daniel Norman3b64ce12019-04-16 16:11:35 -070091import ota_from_target_files
Bill Peckhame9eb5f92019-02-01 15:52:10 -080092
93logger = logging.getLogger(__name__)
94OPTIONS = common.OPTIONS
95OPTIONS.verbose = True
Daniel Normand5d70ea2019-06-05 15:13:43 -070096OPTIONS.framework_target_files = None
97OPTIONS.framework_item_list = None
98OPTIONS.framework_misc_info_keys = None
99OPTIONS.vendor_target_files = None
100OPTIONS.vendor_item_list = None
Bill Peckhamf753e152019-02-19 18:02:46 -0800101OPTIONS.output_target_files = None
Daniel Normanfdb38812019-04-15 09:47:24 -0700102OPTIONS.output_dir = None
103OPTIONS.output_item_list = None
Daniel Norman3b64ce12019-04-16 16:11:35 -0700104OPTIONS.output_ota = None
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700105OPTIONS.output_img = None
Daniel Normanf0318252019-04-15 11:34:56 -0700106OPTIONS.output_super_empty = None
Daniel Normana4911da2019-03-15 14:36:21 -0700107OPTIONS.rebuild_recovery = False
Bill Peckhamf753e152019-02-19 18:02:46 -0800108OPTIONS.keep_tmp = False
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800109
Daniel Normand5d70ea2019-06-05 15:13:43 -0700110# DEFAULT_FRAMEWORK_ITEM_LIST is a list of items to extract from the partial
111# framework target files package as is, meaning these items will land in the
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800112# output target files package exactly as they appear in the input partial
Daniel Normand5d70ea2019-06-05 15:13:43 -0700113# framework target files package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800114
Daniel Normand5d70ea2019-06-05 15:13:43 -0700115DEFAULT_FRAMEWORK_ITEM_LIST = (
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800116 'META/apkcerts.txt',
117 'META/filesystem_config.txt',
118 'META/root_filesystem_config.txt',
119 'META/system_manifest.xml',
120 'META/system_matrix.xml',
121 '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',
146 'avb_product_services_hashtree_enable',
147 'avb_product_services_add_hashtree_footer_args',
148 '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',
165 'META/vendor_manifest.xml',
166 'META/vendor_matrix.xml',
167 'BOOT/*',
168 'DATA/*',
169 'ODM/*',
170 'OTA/android-info.txt',
171 'PREBUILT_IMAGES/*',
172 'RADIO/*',
173 'VENDOR/*',
Daniel Normanedf12472019-05-22 10:47:08 -0700174)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800175
Daniel Normand5d70ea2019-06-05 15:13:43 -0700176# VENDOR_EXTRACT_SPECIAL_ITEM_LIST is a list of items to extract from the
177# partial vendor target files package that need some special processing, such as
178# some sort of combination with items from the partial framework target files
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800179# package.
180
Daniel Normand5d70ea2019-06-05 15:13:43 -0700181VENDOR_EXTRACT_SPECIAL_ITEM_LIST = ('META/*',)
Daniel Normanedf12472019-05-22 10:47:08 -0700182
183# The merge config lists should not attempt to extract items from both
184# builds for any of the following partitions. The partitions in
185# SINGLE_BUILD_PARTITIONS should come entirely from a single build (either
Daniel Normand5d70ea2019-06-05 15:13:43 -0700186# framework or vendor, but not both).
Daniel Normanedf12472019-05-22 10:47:08 -0700187
188SINGLE_BUILD_PARTITIONS = (
189 'BOOT/',
190 'DATA/',
191 'ODM/',
192 'PRODUCT/',
193 'PRODUCT_SERVICES/',
194 'RADIO/',
195 'RECOVERY/',
196 'ROOT/',
197 'SYSTEM/',
198 'SYSTEM_OTHER/',
199 'VENDOR/',
200)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800201
202
Chris Grossfabf50a2019-05-02 12:42:09 -0700203def write_sorted_data(data, path):
204 """Write the sorted contents of either a list or dict to file.
205
206 This function sorts the contents of the list or dict and then
207 writes the resulting sorted contents to a file specified by path.
208
209 Args:
210 data: The list or dict to sort and write.
211 path: Path to the file to write the sorted values to. The file at path will
212 be overridden if it exists.
213 """
214 with open(path, 'w') as output:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700215 for entry in sorted(data):
Chris Grossfabf50a2019-05-02 12:42:09 -0700216 out_str = '{}={}\n'.format(entry, data[entry]) if isinstance(
217 data, dict) else '{}\n'.format(entry)
218 output.write(out_str)
219
220
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800221def extract_items(target_files, target_files_temp_dir, extract_item_list):
222 """Extract items from target files to temporary directory.
223
224 This function extracts from the specified target files zip archive into the
225 specified temporary directory, the items specified in the extract item list.
226
227 Args:
228 target_files: The target files zip archive from which to extract items.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800229 target_files_temp_dir: The temporary directory where the extracted items
Daniel Normane5b134a2019-04-17 14:54:06 -0700230 will land.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800231 extract_item_list: A list of items to extract.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800232 """
233
234 logger.info('extracting from %s', target_files)
235
236 # Filter the extract_item_list to remove any items that do not exist in the
237 # zip file. Otherwise, the extraction step will fail.
238
239 with zipfile.ZipFile(
Daniel Normane5b134a2019-04-17 14:54:06 -0700240 target_files, 'r', allowZip64=True) as target_files_zipfile:
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800241 target_files_namelist = target_files_zipfile.namelist()
242
243 filtered_extract_item_list = []
244 for pattern in extract_item_list:
245 matching_namelist = fnmatch.filter(target_files_namelist, pattern)
246 if not matching_namelist:
247 logger.warning('no match for %s', pattern)
248 else:
249 filtered_extract_item_list.append(pattern)
250
Bill Peckham8ff3fbd2019-02-22 10:57:43 -0800251 # Extract from target_files into target_files_temp_dir the
252 # filtered_extract_item_list.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800253
Daniel Normane5b134a2019-04-17 14:54:06 -0700254 common.UnzipToDir(target_files, target_files_temp_dir,
255 filtered_extract_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800256
257
Daniel Normanfdb38812019-04-15 09:47:24 -0700258def copy_items(from_dir, to_dir, patterns):
259 """Similar to extract_items() except uses an input dir instead of zip."""
260 file_paths = []
261 for dirpath, _, filenames in os.walk(from_dir):
Daniel Normane5b134a2019-04-17 14:54:06 -0700262 file_paths.extend(
263 os.path.relpath(path=os.path.join(dirpath, filename), start=from_dir)
264 for filename in filenames)
Daniel Normanfdb38812019-04-15 09:47:24 -0700265
266 filtered_file_paths = set()
267 for pattern in patterns:
268 filtered_file_paths.update(fnmatch.filter(file_paths, pattern))
269
270 for file_path in filtered_file_paths:
271 original_file_path = os.path.join(from_dir, file_path)
272 copied_file_path = os.path.join(to_dir, file_path)
273 copied_file_dir = os.path.dirname(copied_file_path)
274 if not os.path.exists(copied_file_dir):
275 os.makedirs(copied_file_dir)
276 if os.path.islink(original_file_path):
277 os.symlink(os.readlink(original_file_path), copied_file_path)
278 else:
279 shutil.copyfile(original_file_path, copied_file_path)
280
281
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800282def read_config_list(config_file_path):
283 """Reads a config file into a list of strings.
284
285 Expects the file to be newline-separated.
286
287 Args:
288 config_file_path: The path to the config file to open and read.
Daniel Normane5b134a2019-04-17 14:54:06 -0700289
290 Returns:
291 The list of strings in the config file.
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800292 """
293 with open(config_file_path) as config_file:
294 return config_file.read().splitlines()
295
296
Daniel Normand5d70ea2019-06-05 15:13:43 -0700297def validate_config_lists(framework_item_list, framework_misc_info_keys,
298 vendor_item_list):
Daniel Normane5964522019-03-19 10:32:03 -0700299 """Performs validations on the merge config lists.
300
301 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700302 framework_item_list: The list of items to extract from the partial framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700303 target files package as is.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700304 framework_misc_info_keys: A list of keys to obtain from the framework
305 instance of META/misc_info.txt. The remaining keys from the vendor
306 instance.
307 vendor_item_list: The list of items to extract from the partial vendor
308 target files package as is.
Daniel Normane5964522019-03-19 10:32:03 -0700309
310 Returns:
311 False if a validation fails, otherwise true.
312 """
Daniel Normanedf12472019-05-22 10:47:08 -0700313 has_error = False
314
Daniel Normand5d70ea2019-06-05 15:13:43 -0700315 default_combined_item_set = set(DEFAULT_FRAMEWORK_ITEM_LIST)
316 default_combined_item_set.update(DEFAULT_VENDOR_ITEM_LIST)
Daniel Normane5964522019-03-19 10:32:03 -0700317
Daniel Normand5d70ea2019-06-05 15:13:43 -0700318 combined_item_set = set(framework_item_list)
319 combined_item_set.update(vendor_item_list)
Daniel Normane5964522019-03-19 10:32:03 -0700320
321 # Check that the merge config lists are not missing any item specified
322 # by the default config lists.
323 difference = default_combined_item_set.difference(combined_item_set)
324 if difference:
Daniel Normane5b134a2019-04-17 14:54:06 -0700325 logger.error('Missing merge config items: %s', list(difference))
Daniel Normane5964522019-03-19 10:32:03 -0700326 logger.error('Please ensure missing items are in either the '
Daniel Normand5d70ea2019-06-05 15:13:43 -0700327 'framework-item-list or vendor-item-list files provided to '
Daniel Normane5964522019-03-19 10:32:03 -0700328 'this script.')
Daniel Normanedf12472019-05-22 10:47:08 -0700329 has_error = True
330
331 for partition in SINGLE_BUILD_PARTITIONS:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700332 in_framework = any(
333 item.startswith(partition) for item in framework_item_list)
334 in_vendor = any(item.startswith(partition) for item in vendor_item_list)
335 if in_framework and in_vendor:
Daniel Normanedf12472019-05-22 10:47:08 -0700336 logger.error(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700337 'Cannot extract items from {0} for both the framework and vendor builds. '
Daniel Normanedf12472019-05-22 10:47:08 -0700338 'Please ensure only one merge config item list includes {0}.'.format(
339 partition))
340 has_error = True
Daniel Normane5964522019-03-19 10:32:03 -0700341
Daniel Normand5d70ea2019-06-05 15:13:43 -0700342 if ('dynamic_partition_list' in framework_misc_info_keys) or (
343 'super_partition_groups' in framework_misc_info_keys):
Daniel Norman19b9fe92019-03-19 14:48:02 -0700344 logger.error('Dynamic partition misc info keys should come from '
Daniel Normand5d70ea2019-06-05 15:13:43 -0700345 'the vendor instance of META/misc_info.txt.')
Daniel Normanedf12472019-05-22 10:47:08 -0700346 has_error = True
Daniel Norman19b9fe92019-03-19 14:48:02 -0700347
Daniel Normanedf12472019-05-22 10:47:08 -0700348 return not has_error
Daniel Normane5964522019-03-19 10:32:03 -0700349
350
Daniel Normand5d70ea2019-06-05 15:13:43 -0700351def process_ab_partitions_txt(framework_target_files_temp_dir,
352 vendor_target_files_temp_dir,
Daniel Normane5b134a2019-04-17 14:54:06 -0700353 output_target_files_temp_dir):
354 """Perform special processing for META/ab_partitions.txt.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800355
356 This function merges the contents of the META/ab_partitions.txt files from
Daniel Normand5d70ea2019-06-05 15:13:43 -0700357 the framework directory and the vendor directory, placing the merged result in
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800358 the output directory. The precondition in that the files are already
359 extracted. The post condition is that the output META/ab_partitions.txt
360 contains the merged content. The format for each ab_partitions.txt a one
361 partition name per line. The output file contains the union of the parition
362 names.
363
364 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700365 framework_target_files_temp_dir: The name of a directory containing the
366 special items extracted from the framework target files package.
367 vendor_target_files_temp_dir: The name of a directory containing the special
368 items extracted from the vendor target files package.
Daniel Normane5b134a2019-04-17 14:54:06 -0700369 output_target_files_temp_dir: The name of a directory that will be used to
370 create the output target files package after all the special cases are
371 processed.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800372 """
373
Daniel Normand5d70ea2019-06-05 15:13:43 -0700374 framework_ab_partitions_txt = os.path.join(framework_target_files_temp_dir,
375 'META', 'ab_partitions.txt')
376
377 vendor_ab_partitions_txt = os.path.join(vendor_target_files_temp_dir, 'META',
Daniel Normane5b134a2019-04-17 14:54:06 -0700378 'ab_partitions.txt')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800379
Daniel Normand5d70ea2019-06-05 15:13:43 -0700380 with open(framework_ab_partitions_txt) as f:
381 framework_ab_partitions = f.read().splitlines()
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800382
Daniel Normand5d70ea2019-06-05 15:13:43 -0700383 with open(vendor_ab_partitions_txt) as f:
384 vendor_ab_partitions = f.read().splitlines()
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800385
Daniel Normand5d70ea2019-06-05 15:13:43 -0700386 output_ab_partitions = set(framework_ab_partitions + vendor_ab_partitions)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800387
Daniel Normane5b134a2019-04-17 14:54:06 -0700388 output_ab_partitions_txt = os.path.join(output_target_files_temp_dir, 'META',
389 'ab_partitions.txt')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800390
Chris Grossfabf50a2019-05-02 12:42:09 -0700391 write_sorted_data(data=output_ab_partitions, path=output_ab_partitions_txt)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800392
393
Bill Peckham364c1cc2019-03-29 18:27:23 -0700394def append_recovery_to_filesystem_config(output_target_files_temp_dir):
Daniel Normane5b134a2019-04-17 14:54:06 -0700395 """Perform special processing for META/filesystem_config.txt.
Bill Peckham364c1cc2019-03-29 18:27:23 -0700396
397 This function appends recovery information to META/filesystem_config.txt
398 so that recovery patch regeneration will succeed.
399
400 Args:
Daniel Normane5b134a2019-04-17 14:54:06 -0700401 output_target_files_temp_dir: The name of a directory that will be used to
402 create the output target files package after all the special cases are
403 processed. We find filesystem_config.txt here.
Bill Peckham364c1cc2019-03-29 18:27:23 -0700404 """
405
Daniel Normane5b134a2019-04-17 14:54:06 -0700406 filesystem_config_txt = os.path.join(output_target_files_temp_dir, 'META',
407 'filesystem_config.txt')
Bill Peckham364c1cc2019-03-29 18:27:23 -0700408
409 with open(filesystem_config_txt, 'a') as f:
410 # TODO(bpeckham) this data is hard coded. It should be generated
411 # programmatically.
Daniel Normane5b134a2019-04-17 14:54:06 -0700412 f.write('system/bin/install-recovery.sh 0 0 750 '
413 'selabel=u:object_r:install_recovery_exec:s0 capabilities=0x0\n')
414 f.write('system/recovery-from-boot.p 0 0 644 '
415 'selabel=u:object_r:system_file:s0 capabilities=0x0\n')
416 f.write('system/etc/recovery.img 0 0 440 '
417 'selabel=u:object_r:install_recovery_exec:s0 capabilities=0x0\n')
Bill Peckham364c1cc2019-03-29 18:27:23 -0700418
419
Daniel Normand5d70ea2019-06-05 15:13:43 -0700420def merge_dynamic_partition_info_dicts(framework_dict,
421 vendor_dict,
Daniel Normana61cde02019-05-03 14:19:13 -0700422 include_dynamic_partition_list=True,
423 size_prefix='',
424 size_suffix='',
425 list_prefix='',
426 list_suffix=''):
427 """Merges dynamic partition info variables.
428
429 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700430 framework_dict: The dictionary of dynamic partition info variables from the
431 partial framework target files.
432 vendor_dict: The dictionary of dynamic partition info variables from the
433 partial vendor target files.
Daniel Normana61cde02019-05-03 14:19:13 -0700434 include_dynamic_partition_list: If true, merges the dynamic_partition_list
435 variable. Not all use cases need this variable merged.
436 size_prefix: The prefix in partition group size variables that precedes the
437 name of the partition group. For example, partition group 'group_a' with
438 corresponding size variable 'super_group_a_group_size' would have the
439 size_prefix 'super_'.
440 size_suffix: Similar to size_prefix but for the variable's suffix. For
441 example, 'super_group_a_group_size' would have size_suffix '_group_size'.
442 list_prefix: Similar to size_prefix but for the partition group's
443 partition_list variable.
444 list_suffix: Similar to size_suffix but for the partition group's
445 partition_list variable.
446
447 Returns:
448 The merged dynamic partition info dictionary.
449 """
450 merged_dict = {}
Daniel Normand5d70ea2019-06-05 15:13:43 -0700451 # Partition groups and group sizes are defined by the vendor dict because
452 # these values may vary for each board that uses a shared system image.
453 merged_dict['super_partition_groups'] = vendor_dict['super_partition_groups']
Daniel Normana61cde02019-05-03 14:19:13 -0700454 if include_dynamic_partition_list:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700455 framework_dynamic_partition_list = framework_dict.get(
456 'dynamic_partition_list', '')
457 vendor_dynamic_partition_list = vendor_dict.get('dynamic_partition_list',
Daniel Normana61cde02019-05-03 14:19:13 -0700458 '')
Daniel Normana61cde02019-05-03 14:19:13 -0700459 merged_dict['dynamic_partition_list'] = (
Daniel Normand5d70ea2019-06-05 15:13:43 -0700460 '%s %s' % (framework_dynamic_partition_list,
461 vendor_dynamic_partition_list)).strip()
Daniel Normana61cde02019-05-03 14:19:13 -0700462 for partition_group in merged_dict['super_partition_groups'].split(' '):
Daniel Normand5d70ea2019-06-05 15:13:43 -0700463 # Set the partition group's size using the value from the vendor dict.
Daniel Normana61cde02019-05-03 14:19:13 -0700464 key = '%s%s%s' % (size_prefix, partition_group, size_suffix)
Daniel Normand5d70ea2019-06-05 15:13:43 -0700465 if key not in vendor_dict:
466 raise ValueError('Vendor dict does not contain required key %s.' % key)
467 merged_dict[key] = vendor_dict[key]
Daniel Normana61cde02019-05-03 14:19:13 -0700468
469 # Set the partition group's partition list using a concatenation of the
Daniel Normand5d70ea2019-06-05 15:13:43 -0700470 # framework and vendor partition lists.
Daniel Normana61cde02019-05-03 14:19:13 -0700471 key = '%s%s%s' % (list_prefix, partition_group, list_suffix)
472 merged_dict[key] = (
Daniel Normand5d70ea2019-06-05 15:13:43 -0700473 '%s %s' %
474 (framework_dict.get(key, ''), vendor_dict.get(key, ''))).strip()
Daniel Normana61cde02019-05-03 14:19:13 -0700475 return merged_dict
476
477
Daniel Normand5d70ea2019-06-05 15:13:43 -0700478def process_misc_info_txt(framework_target_files_temp_dir,
479 vendor_target_files_temp_dir,
480 output_target_files_temp_dir,
481 framework_misc_info_keys):
Daniel Normane5b134a2019-04-17 14:54:06 -0700482 """Perform special processing for META/misc_info.txt.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800483
484 This function merges the contents of the META/misc_info.txt files from the
Daniel Normand5d70ea2019-06-05 15:13:43 -0700485 framework directory and the vendor directory, placing the merged result in the
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800486 output directory. The precondition in that the files are already extracted.
487 The post condition is that the output META/misc_info.txt contains the merged
488 content.
489
490 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700491 framework_target_files_temp_dir: The name of a directory containing the
492 special items extracted from the framework target files package.
493 vendor_target_files_temp_dir: The name of a directory containing the special
494 items extracted from the vendor target files package.
Daniel Normane5b134a2019-04-17 14:54:06 -0700495 output_target_files_temp_dir: The name of a directory that will be used to
496 create the output target files package after all the special cases are
497 processed.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700498 framework_misc_info_keys: A list of keys to obtain from the framework
499 instance of META/misc_info.txt. The remaining keys from the vendor
500 instance.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800501 """
502
503 def read_helper(d):
504 misc_info_txt = os.path.join(d, 'META', 'misc_info.txt')
505 with open(misc_info_txt) as f:
506 return list(f.read().splitlines())
507
Daniel Normand5d70ea2019-06-05 15:13:43 -0700508 framework_dict = common.LoadDictionaryFromLines(
509 read_helper(framework_target_files_temp_dir))
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800510
Daniel Normand5d70ea2019-06-05 15:13:43 -0700511 # We take most of the misc info from the vendor target files.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800512
Daniel Normand5d70ea2019-06-05 15:13:43 -0700513 merged_dict = common.LoadDictionaryFromLines(
514 read_helper(vendor_target_files_temp_dir))
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800515
Daniel Normand5d70ea2019-06-05 15:13:43 -0700516 # Replace certain values in merged_dict with values from
517 # framework_dict.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800518
Daniel Normand5d70ea2019-06-05 15:13:43 -0700519 for key in framework_misc_info_keys:
520 merged_dict[key] = framework_dict[key]
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800521
Daniel Norman19b9fe92019-03-19 14:48:02 -0700522 # Merge misc info keys used for Dynamic Partitions.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700523 if (merged_dict.get('use_dynamic_partitions') == 'true') and (
524 framework_dict.get('use_dynamic_partitions') == 'true'):
Daniel Normana61cde02019-05-03 14:19:13 -0700525 merged_dynamic_partitions_dict = merge_dynamic_partition_info_dicts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700526 framework_dict=framework_dict,
527 vendor_dict=merged_dict,
Daniel Normana61cde02019-05-03 14:19:13 -0700528 size_prefix='super_',
529 size_suffix='_group_size',
530 list_prefix='super_',
531 list_suffix='_partition_list')
Daniel Normand5d70ea2019-06-05 15:13:43 -0700532 merged_dict.update(merged_dynamic_partitions_dict)
Daniel Norman0bf940c2019-06-10 12:50:19 -0700533 # Ensure that add_img_to_target_files rebuilds super_empty.img. This flag
534 # may have been set to false in the partial builds to prevent duplicate
535 # building of super.img and super_empty.img.
536 merged_dict['build_super_partition'] = 'true'
Daniel Norman19b9fe92019-03-19 14:48:02 -0700537
Daniel Normand5d70ea2019-06-05 15:13:43 -0700538 # Replace <image>_selinux_fc values with framework or vendor file_contexts.bin
Daniel Norman72c626f2019-05-13 15:58:14 -0700539 # depending on which dictionary the key came from.
540 # Only the file basename is required because all selinux_fc properties are
541 # replaced with the full path to the file under META/ when misc_info.txt is
542 # loaded from target files for repacking. See common.py LoadInfoDict().
Daniel Normand5d70ea2019-06-05 15:13:43 -0700543 for key in merged_dict:
Daniel Norman72c626f2019-05-13 15:58:14 -0700544 if key.endswith('_selinux_fc'):
Daniel Normand5d70ea2019-06-05 15:13:43 -0700545 merged_dict[key] = 'vendor_file_contexts.bin'
546 for key in framework_dict:
Daniel Norman72c626f2019-05-13 15:58:14 -0700547 if key.endswith('_selinux_fc'):
Daniel Normand5d70ea2019-06-05 15:13:43 -0700548 merged_dict[key] = 'framework_file_contexts.bin'
Daniel Norman72c626f2019-05-13 15:58:14 -0700549
Daniel Normane5b134a2019-04-17 14:54:06 -0700550 output_misc_info_txt = os.path.join(output_target_files_temp_dir, 'META',
551 'misc_info.txt')
Daniel Normand5d70ea2019-06-05 15:13:43 -0700552 write_sorted_data(data=merged_dict, path=output_misc_info_txt)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800553
554
Daniel Normand5d70ea2019-06-05 15:13:43 -0700555def process_dynamic_partitions_info_txt(framework_target_files_dir,
556 vendor_target_files_dir,
Daniel Normana61cde02019-05-03 14:19:13 -0700557 output_target_files_dir):
558 """Perform special processing for META/dynamic_partitions_info.txt.
559
560 This function merges the contents of the META/dynamic_partitions_info.txt
Daniel Normand5d70ea2019-06-05 15:13:43 -0700561 files from the framework directory and the vendor directory, placing the
562 merged result in the output directory.
Daniel Normana61cde02019-05-03 14:19:13 -0700563
Daniel Normand5d70ea2019-06-05 15:13:43 -0700564 This function does nothing if META/dynamic_partitions_info.txt from the vendor
Daniel Normana61cde02019-05-03 14:19:13 -0700565 directory does not exist.
566
567 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700568 framework_target_files_dir: The name of a directory containing the special
569 items extracted from the framework target files package.
570 vendor_target_files_dir: The name of a directory containing the special
571 items extracted from the vendor target files package.
Daniel Normana61cde02019-05-03 14:19:13 -0700572 output_target_files_dir: The name of a directory that will be used to create
573 the output target files package after all the special cases are processed.
574 """
575
576 if not os.path.exists(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700577 os.path.join(vendor_target_files_dir, 'META',
Daniel Normana61cde02019-05-03 14:19:13 -0700578 'dynamic_partitions_info.txt')):
579 return
580
581 def read_helper(d):
582 dynamic_partitions_info_txt = os.path.join(d, 'META',
583 'dynamic_partitions_info.txt')
584 with open(dynamic_partitions_info_txt) as f:
585 return list(f.read().splitlines())
586
Daniel Normand5d70ea2019-06-05 15:13:43 -0700587 framework_dynamic_partitions_dict = common.LoadDictionaryFromLines(
588 read_helper(framework_target_files_dir))
589 vendor_dynamic_partitions_dict = common.LoadDictionaryFromLines(
590 read_helper(vendor_target_files_dir))
Daniel Normana61cde02019-05-03 14:19:13 -0700591
592 merged_dynamic_partitions_dict = merge_dynamic_partition_info_dicts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700593 framework_dict=framework_dynamic_partitions_dict,
594 vendor_dict=vendor_dynamic_partitions_dict,
Daniel Normana61cde02019-05-03 14:19:13 -0700595 # META/dynamic_partitions_info.txt does not use dynamic_partition_list.
596 include_dynamic_partition_list=False,
597 size_suffix='_size',
598 list_suffix='_partition_list')
599
600 output_dynamic_partitions_info_txt = os.path.join(
601 output_target_files_dir, 'META', 'dynamic_partitions_info.txt')
Chris Grossfabf50a2019-05-02 12:42:09 -0700602 write_sorted_data(
603 data=merged_dynamic_partitions_dict,
604 path=output_dynamic_partitions_info_txt)
605
606
Daniel Normand5d70ea2019-06-05 15:13:43 -0700607def process_apex_keys_apk_certs_common(framework_target_files_dir,
608 vendor_target_files_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700609 output_target_files_dir, file_name):
610 """Perform special processing for META/apexkeys.txt or META/apkcerts.txt.
611
612 This function merges the contents of the META/apexkeys.txt or
Daniel Normand5d70ea2019-06-05 15:13:43 -0700613 META/apkcerts.txt files from the framework directory and the vendor
614 directory, placing the merged result in the output directory. The
615 precondition in that the files are already extracted. The post condition
616 is that the output META/apexkeys.txt or META/apkcerts.txt contains the
617 merged content.
Chris Grossfabf50a2019-05-02 12:42:09 -0700618
619 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700620 framework_target_files_dir: The name of a directory containing the special
621 items extracted from the framework target files package.
622 vendor_target_files_dir: The name of a directory containing the special
623 items extracted from the vendor target files package.
Chris Grossfabf50a2019-05-02 12:42:09 -0700624 output_target_files_dir: The name of a directory that will be used to create
625 the output target files package after all the special cases are processed.
626 file_name: The name of the file to merge. One of apkcerts.txt or
627 apexkeys.txt.
628 """
629
630 def read_helper(d):
631 temp = {}
632 file_path = os.path.join(d, 'META', file_name)
633 with open(file_path) as f:
634 for line in f:
635 if line.strip():
636 temp[line.split()[0]] = line.strip()
637 return temp
638
Daniel Normand5d70ea2019-06-05 15:13:43 -0700639 framework_dict = read_helper(framework_target_files_dir)
640 vendor_dict = read_helper(vendor_target_files_dir)
Chris Grossfabf50a2019-05-02 12:42:09 -0700641
Daniel Normand5d70ea2019-06-05 15:13:43 -0700642 for key in framework_dict:
643 if key in vendor_dict and vendor_dict[key] != framework_dict[key]:
Chris Grossfabf50a2019-05-02 12:42:09 -0700644 raise ValueError('Conflicting entries found in %s:\n %s and\n %s' %
Daniel Normand5d70ea2019-06-05 15:13:43 -0700645 (file_name, framework_dict[key], vendor_dict[key]))
646 vendor_dict[key] = framework_dict[key]
Chris Grossfabf50a2019-05-02 12:42:09 -0700647
648 output_file = os.path.join(output_target_files_dir, 'META', file_name)
649
Daniel Normand5d70ea2019-06-05 15:13:43 -0700650 write_sorted_data(data=vendor_dict.values(), path=output_file)
Daniel Normana61cde02019-05-03 14:19:13 -0700651
652
Daniel Normand5d70ea2019-06-05 15:13:43 -0700653def copy_file_contexts(framework_target_files_dir, vendor_target_files_dir,
Daniel Norman72c626f2019-05-13 15:58:14 -0700654 output_target_files_dir):
655 """Creates named copies of each build's file_contexts.bin in output META/."""
Daniel Normand5d70ea2019-06-05 15:13:43 -0700656 framework_fc_path = os.path.join(framework_target_files_dir, 'META',
657 'framework_file_contexts.bin')
658 if not os.path.exists(framework_fc_path):
659 framework_fc_path = os.path.join(framework_target_files_dir, 'META',
660 'file_contexts.bin')
661 if not os.path.exists(framework_fc_path):
662 raise ValueError('Missing framework file_contexts.bin.')
663 shutil.copyfile(
664 framework_fc_path,
665 os.path.join(output_target_files_dir, 'META',
666 'framework_file_contexts.bin'))
667
668 vendor_fc_path = os.path.join(vendor_target_files_dir, 'META',
669 'vendor_file_contexts.bin')
670 if not os.path.exists(vendor_fc_path):
671 vendor_fc_path = os.path.join(vendor_target_files_dir, 'META',
Daniel Normanedf12472019-05-22 10:47:08 -0700672 'file_contexts.bin')
Daniel Normand5d70ea2019-06-05 15:13:43 -0700673 if not os.path.exists(vendor_fc_path):
674 raise ValueError('Missing vendor file_contexts.bin.')
Daniel Norman72c626f2019-05-13 15:58:14 -0700675 shutil.copyfile(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700676 vendor_fc_path,
677 os.path.join(output_target_files_dir, 'META', 'vendor_file_contexts.bin'))
Daniel Norman72c626f2019-05-13 15:58:14 -0700678
679
Daniel Normand5d70ea2019-06-05 15:13:43 -0700680def process_special_cases(framework_target_files_temp_dir,
681 vendor_target_files_temp_dir,
682 output_target_files_temp_dir,
683 framework_misc_info_keys, rebuild_recovery):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800684 """Perform special-case processing for certain target files items.
685
686 Certain files in the output target files package require special-case
687 processing. This function performs all that special-case processing.
688
689 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700690 framework_target_files_temp_dir: The name of a directory containing the
691 special items extracted from the framework target files package.
692 vendor_target_files_temp_dir: The name of a directory containing the special
693 items extracted from the vendor target files package.
Daniel Normane5b134a2019-04-17 14:54:06 -0700694 output_target_files_temp_dir: The name of a directory that will be used to
695 create the output target files package after all the special cases are
696 processed.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700697 framework_misc_info_keys: A list of keys to obtain from the framework
698 instance of META/misc_info.txt. The remaining keys from the vendor
699 instance.
Bill Peckham364c1cc2019-03-29 18:27:23 -0700700 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
Daniel Normane5b134a2019-04-17 14:54:06 -0700701 devices and write it to the system image.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800702 """
703
Daniel Normand5d70ea2019-06-05 15:13:43 -0700704 if 'ab_update' in framework_misc_info_keys:
Bill Peckham364c1cc2019-03-29 18:27:23 -0700705 process_ab_partitions_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700706 framework_target_files_temp_dir=framework_target_files_temp_dir,
707 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Bill Peckham364c1cc2019-03-29 18:27:23 -0700708 output_target_files_temp_dir=output_target_files_temp_dir)
709
710 if rebuild_recovery:
711 append_recovery_to_filesystem_config(
712 output_target_files_temp_dir=output_target_files_temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800713
Daniel Norman72c626f2019-05-13 15:58:14 -0700714 copy_file_contexts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700715 framework_target_files_dir=framework_target_files_temp_dir,
716 vendor_target_files_dir=vendor_target_files_temp_dir,
Daniel Norman72c626f2019-05-13 15:58:14 -0700717 output_target_files_dir=output_target_files_temp_dir)
718
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800719 process_misc_info_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700720 framework_target_files_temp_dir=framework_target_files_temp_dir,
721 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800722 output_target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700723 framework_misc_info_keys=framework_misc_info_keys)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800724
Daniel Normana61cde02019-05-03 14:19:13 -0700725 process_dynamic_partitions_info_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700726 framework_target_files_dir=framework_target_files_temp_dir,
727 vendor_target_files_dir=vendor_target_files_temp_dir,
Daniel Norman714bd122019-05-08 16:20:02 -0700728 output_target_files_dir=output_target_files_temp_dir)
Daniel Normana61cde02019-05-03 14:19:13 -0700729
Chris Grossfabf50a2019-05-02 12:42:09 -0700730 process_apex_keys_apk_certs_common(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700731 framework_target_files_dir=framework_target_files_temp_dir,
732 vendor_target_files_dir=vendor_target_files_temp_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700733 output_target_files_dir=output_target_files_temp_dir,
734 file_name='apkcerts.txt')
735
736 process_apex_keys_apk_certs_common(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700737 framework_target_files_dir=framework_target_files_temp_dir,
738 vendor_target_files_dir=vendor_target_files_temp_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700739 output_target_files_dir=output_target_files_temp_dir,
740 file_name='apexkeys.txt')
741
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800742
Daniel Normand5d70ea2019-06-05 15:13:43 -0700743def merge_target_files(temp_dir, framework_target_files, framework_item_list,
744 framework_misc_info_keys, vendor_target_files,
745 vendor_item_list, output_target_files, output_dir,
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700746 output_item_list, output_ota, output_img,
747 output_super_empty, rebuild_recovery):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800748 """Merge two target files packages together.
749
Daniel Normand5d70ea2019-06-05 15:13:43 -0700750 This function takes framework and vendor target files packages as input,
751 performs various file extractions, special case processing, and finally
752 creates a merged zip archive as output.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800753
754 Args:
755 temp_dir: The name of a directory we use when we extract items from the
Daniel Normane5b134a2019-04-17 14:54:06 -0700756 input target files packages, and also a scratch directory that we use for
757 temporary files.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700758 framework_target_files: The name of the zip archive containing the framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700759 partial target files package.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700760 framework_item_list: The list of items to extract from the partial framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700761 target files package as is, meaning these items will land in the output
Daniel Normand5d70ea2019-06-05 15:13:43 -0700762 target files package exactly as they appear in the input partial framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700763 target files package.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700764 framework_misc_info_keys: The list of keys to obtain from the framework
765 instance of META/misc_info.txt. The remaining keys from the vendor
766 instance.
767 vendor_target_files: The name of the zip archive containing the vendor
768 partial target files package.
769 vendor_item_list: The list of items to extract from the partial vendor
770 target files package as is, meaning these items will land in the output
771 target files package exactly as they appear in the input partial vendor
Daniel Normane5b134a2019-04-17 14:54:06 -0700772 target files package.
Daniel Normane5b134a2019-04-17 14:54:06 -0700773 output_target_files: The name of the output zip archive target files package
Daniel Normand5d70ea2019-06-05 15:13:43 -0700774 created by merging framework and vendor.
Daniel Normane5b134a2019-04-17 14:54:06 -0700775 output_dir: The destination directory for saving merged files.
776 output_item_list: The list of items to copy into the output_dir.
Daniel Norman3b64ce12019-04-16 16:11:35 -0700777 output_ota: The name of the output zip archive ota package.
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700778 output_img: The name of the output zip archive img package.
Daniel Normanf0318252019-04-15 11:34:56 -0700779 output_super_empty: If provided, creates a super_empty.img file from the
Daniel Normane5b134a2019-04-17 14:54:06 -0700780 merged target files package and saves it at this path.
Daniel Normana4911da2019-03-15 14:36:21 -0700781 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
Daniel Normane5b134a2019-04-17 14:54:06 -0700782 devices and write it to the system image.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800783 """
784
Daniel Normand5d70ea2019-06-05 15:13:43 -0700785 logger.info('starting: merge framework %s and vendor %s into output %s',
786 framework_target_files, vendor_target_files, output_target_files)
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800787
Daniel Normand5d70ea2019-06-05 15:13:43 -0700788 # Create directory names that we'll use when we extract files from framework,
789 # and vendor, and for zipping the final output.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800790
Daniel Normand5d70ea2019-06-05 15:13:43 -0700791 framework_target_files_temp_dir = os.path.join(temp_dir, 'framework')
792 vendor_target_files_temp_dir = os.path.join(temp_dir, 'vendor')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800793 output_target_files_temp_dir = os.path.join(temp_dir, 'output')
794
Daniel Normand5d70ea2019-06-05 15:13:43 -0700795 # Extract "as is" items from the input framework partial target files package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800796 # We extract them directly into the output temporary directory since the
797 # items do not need special case processing.
798
Bill Peckham889b0c62019-02-21 18:53:37 -0800799 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700800 target_files=framework_target_files,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800801 target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700802 extract_item_list=framework_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800803
Daniel Normand5d70ea2019-06-05 15:13:43 -0700804 # Extract "as is" items from the input vendor partial target files package. We
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800805 # extract them directly into the output temporary directory since the items
806 # do not need special case processing.
807
Bill Peckham889b0c62019-02-21 18:53:37 -0800808 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700809 target_files=vendor_target_files,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800810 target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700811 extract_item_list=vendor_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800812
Daniel Normand5d70ea2019-06-05 15:13:43 -0700813 # Extract "special" items from the input framework partial target files
814 # package. We extract these items to different directory since they require
815 # special processing before they will end up in the output directory.
816
817 extract_items(
818 target_files=framework_target_files,
819 target_files_temp_dir=framework_target_files_temp_dir,
820 extract_item_list=FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST)
821
822 # Extract "special" items from the input vendor partial target files package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800823 # We extract these items to different directory since they require special
824 # processing before they will end up in the output directory.
825
Bill Peckham889b0c62019-02-21 18:53:37 -0800826 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700827 target_files=vendor_target_files,
828 target_files_temp_dir=vendor_target_files_temp_dir,
829 extract_item_list=VENDOR_EXTRACT_SPECIAL_ITEM_LIST)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800830
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800831 # Now that the temporary directories contain all the extracted files, perform
832 # special case processing on any items that need it. After this function
833 # completes successfully, all the files we need to create the output target
834 # files package are in place.
835
Bill Peckham889b0c62019-02-21 18:53:37 -0800836 process_special_cases(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700837 framework_target_files_temp_dir=framework_target_files_temp_dir,
838 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800839 output_target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700840 framework_misc_info_keys=framework_misc_info_keys,
Bill Peckham364c1cc2019-03-29 18:27:23 -0700841 rebuild_recovery=rebuild_recovery)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800842
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800843 # Regenerate IMAGES in the temporary directory.
844
Daniel Normana4911da2019-03-15 14:36:21 -0700845 add_img_args = ['--verbose']
846 if rebuild_recovery:
847 add_img_args.append('--rebuild_recovery')
848 add_img_args.append(output_target_files_temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800849
850 add_img_to_target_files.main(add_img_args)
851
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700852 # Create super_empty.img using the merged misc_info.txt.
853
854 misc_info_txt = os.path.join(output_target_files_temp_dir, 'META',
855 'misc_info.txt')
856
857 def read_helper():
858 with open(misc_info_txt) as f:
859 return list(f.read().splitlines())
860
861 use_dynamic_partitions = common.LoadDictionaryFromLines(
862 read_helper()).get('use_dynamic_partitions')
863
864 if use_dynamic_partitions != 'true' and output_super_empty:
865 raise ValueError(
866 'Building super_empty.img requires use_dynamic_partitions=true.')
867 elif use_dynamic_partitions == 'true':
868 super_empty_img = os.path.join(output_target_files_temp_dir, 'IMAGES',
869 'super_empty.img')
870 build_super_image_args = [
871 misc_info_txt,
872 super_empty_img,
873 ]
874 build_super_image.main(build_super_image_args)
875
876 # Copy super_empty.img to the user-provided output_super_empty location.
877 if output_super_empty:
878 shutil.copyfile(super_empty_img, output_super_empty)
879
Daniel Normanb8a2f9d2019-04-24 12:55:51 -0700880 # Create the IMG package from the merged target files (before zipping, in
881 # order to avoid an unnecessary unzip and copy).
882
883 if output_img:
884 img_from_target_files_args = [
885 output_target_files_temp_dir,
886 output_img,
887 ]
888 img_from_target_files.main(img_from_target_files_args)
889
Daniel Normanfdb38812019-04-15 09:47:24 -0700890 # Finally, create the output target files zip archive and/or copy the
891 # output items to the output target files directory.
892
893 if output_dir:
894 copy_items(output_target_files_temp_dir, output_dir, output_item_list)
895
896 if not output_target_files:
897 return
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800898
899 output_zip = os.path.abspath(output_target_files)
900 output_target_files_list = os.path.join(temp_dir, 'output.list')
Daniel Normane5b134a2019-04-17 14:54:06 -0700901 output_target_files_meta_dir = os.path.join(output_target_files_temp_dir,
902 'META')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800903
Bill Peckham9662cfb2019-04-24 17:59:01 -0700904 find_command = [
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800905 'find',
906 output_target_files_meta_dir,
907 ]
Bill Peckham9662cfb2019-04-24 17:59:01 -0700908 find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
Daniel Normana61cde02019-05-03 14:19:13 -0700909 meta_content = common.RunAndCheckOutput(['sort'],
910 stdin=find_process.stdout,
Bill Peckham9662cfb2019-04-24 17:59:01 -0700911 verbose=False)
912
913 find_command = [
Daniel Normane5b134a2019-04-17 14:54:06 -0700914 'find', output_target_files_temp_dir, '-path',
915 output_target_files_meta_dir, '-prune', '-o', '-print'
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800916 ]
Bill Peckham9662cfb2019-04-24 17:59:01 -0700917 find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
Daniel Normana61cde02019-05-03 14:19:13 -0700918 other_content = common.RunAndCheckOutput(['sort'],
919 stdin=find_process.stdout,
Bill Peckham9662cfb2019-04-24 17:59:01 -0700920 verbose=False)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800921
922 with open(output_target_files_list, 'wb') as f:
923 f.write(meta_content)
924 f.write(other_content)
925
926 command = [
Bill Peckhamf753e152019-02-19 18:02:46 -0800927 'soong_zip',
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800928 '-d',
Daniel Normane5b134a2019-04-17 14:54:06 -0700929 '-o',
930 output_zip,
931 '-C',
932 output_target_files_temp_dir,
933 '-l',
934 output_target_files_list,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800935 ]
936 logger.info('creating %s', output_target_files)
Bill Peckham889b0c62019-02-21 18:53:37 -0800937 common.RunAndWait(command, verbose=True)
Chris Grosseab4f0e2019-06-07 10:34:13 -0700938 logger.info('finished creating %s', output_target_files)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800939
Daniel Norman3b64ce12019-04-16 16:11:35 -0700940 # Create the OTA package from the merged target files package.
941
942 if output_ota:
943 ota_from_target_files_args = [
944 output_zip,
945 output_ota,
946 ]
947 ota_from_target_files.main(ota_from_target_files_args)
948
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700949
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800950def call_func_with_temp_dir(func, keep_tmp):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800951 """Manage the creation and cleanup of the temporary directory.
952
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800953 This function calls the given function after first creating a temporary
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800954 directory. It also cleans up the temporary directory.
955
956 Args:
Daniel Normane5b134a2019-04-17 14:54:06 -0700957 func: The function to call. Should accept one parameter, the path to the
958 temporary directory.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800959 keep_tmp: Keep the temporary directory after processing is complete.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800960 """
961
962 # Create a temporary directory. This will serve as the parent of directories
963 # we use when we extract items from the input target files packages, and also
964 # a scratch directory that we use for temporary files.
965
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800966 temp_dir = common.MakeTempDir(prefix='merge_target_files_')
967
968 try:
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800969 func(temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800970 except:
971 raise
972 finally:
973 if keep_tmp:
974 logger.info('keeping %s', temp_dir)
975 else:
976 common.Cleanup()
977
978
979def main():
980 """The main function.
981
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800982 Process command line arguments, then call merge_target_files to
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800983 perform the heavy lifting.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800984 """
985
986 common.InitLogging()
987
Bill Peckhamf753e152019-02-19 18:02:46 -0800988 def option_handler(o, a):
989 if o == '--system-target-files':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700990 logger.warning(
991 '--system-target-files has been renamed to --framework-target-files')
992 OPTIONS.framework_target_files = a
993 elif o == '--framework-target-files':
994 OPTIONS.framework_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800995 elif o == '--system-item-list':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700996 logger.warning(
997 '--system-item-list has been renamed to --framework-item-list')
998 OPTIONS.framework_item_list = a
999 elif o == '--framework-item-list':
1000 OPTIONS.framework_item_list = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001001 elif o == '--system-misc-info-keys':
Daniel Normand5d70ea2019-06-05 15:13:43 -07001002 logger.warning(
1003 '--system-misc-info-keys has been renamed to --framework-misc-info-keys'
1004 )
1005 OPTIONS.framework_misc_info_keys = a
1006 elif o == '--framework-misc-info-keys':
1007 OPTIONS.framework_misc_info_keys = a
Bill Peckhamf753e152019-02-19 18:02:46 -08001008 elif o == '--other-target-files':
Daniel Normand5d70ea2019-06-05 15:13:43 -07001009 logger.warning(
1010 '--other-target-files has been renamed to --vendor-target-files')
1011 OPTIONS.vendor_target_files = a
1012 elif o == '--vendor-target-files':
1013 OPTIONS.vendor_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001014 elif o == '--other-item-list':
Daniel Normand5d70ea2019-06-05 15:13:43 -07001015 logger.warning('--other-item-list has been renamed to --vendor-item-list')
1016 OPTIONS.vendor_item_list = a
1017 elif o == '--vendor-item-list':
1018 OPTIONS.vendor_item_list = a
Bill Peckhamf753e152019-02-19 18:02:46 -08001019 elif o == '--output-target-files':
1020 OPTIONS.output_target_files = a
Daniel Normanfdb38812019-04-15 09:47:24 -07001021 elif o == '--output-dir':
1022 OPTIONS.output_dir = a
1023 elif o == '--output-item-list':
1024 OPTIONS.output_item_list = a
Daniel Norman3b64ce12019-04-16 16:11:35 -07001025 elif o == '--output-ota':
1026 OPTIONS.output_ota = a
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001027 elif o == '--output-img':
1028 OPTIONS.output_img = a
Daniel Normanf0318252019-04-15 11:34:56 -07001029 elif o == '--output-super-empty':
1030 OPTIONS.output_super_empty = a
Daniel Normana4911da2019-03-15 14:36:21 -07001031 elif o == '--rebuild_recovery':
1032 OPTIONS.rebuild_recovery = True
Bill Peckham364c1cc2019-03-29 18:27:23 -07001033 elif o == '--keep-tmp':
Bill Peckhamf753e152019-02-19 18:02:46 -08001034 OPTIONS.keep_tmp = True
1035 else:
1036 return False
1037 return True
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001038
Bill Peckhamf753e152019-02-19 18:02:46 -08001039 args = common.ParseOptions(
Daniel Normane5b134a2019-04-17 14:54:06 -07001040 sys.argv[1:],
1041 __doc__,
Bill Peckhamf753e152019-02-19 18:02:46 -08001042 extra_long_opts=[
1043 'system-target-files=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001044 'framework-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001045 'system-item-list=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001046 'framework-item-list=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001047 'system-misc-info-keys=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001048 'framework-misc-info-keys=',
Bill Peckhamf753e152019-02-19 18:02:46 -08001049 'other-target-files=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001050 'vendor-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001051 'other-item-list=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001052 'vendor-item-list=',
Bill Peckhamf753e152019-02-19 18:02:46 -08001053 'output-target-files=',
Daniel Normanfdb38812019-04-15 09:47:24 -07001054 'output-dir=',
1055 'output-item-list=',
Daniel Norman3b64ce12019-04-16 16:11:35 -07001056 'output-ota=',
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001057 'output-img=',
Daniel Normanf0318252019-04-15 11:34:56 -07001058 'output-super-empty=',
Daniel Normana4911da2019-03-15 14:36:21 -07001059 'rebuild_recovery',
Bill Peckham364c1cc2019-03-29 18:27:23 -07001060 'keep-tmp',
Bill Peckhamf753e152019-02-19 18:02:46 -08001061 ],
1062 extra_option_handler=option_handler)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001063
Daniel Normand5d70ea2019-06-05 15:13:43 -07001064 if (args or OPTIONS.framework_target_files is None or
1065 OPTIONS.vendor_target_files is None or
Daniel Normane5b134a2019-04-17 14:54:06 -07001066 (OPTIONS.output_target_files is None and OPTIONS.output_dir is None) or
1067 (OPTIONS.output_dir is not None and OPTIONS.output_item_list is None)):
Bill Peckhamf753e152019-02-19 18:02:46 -08001068 common.Usage(__doc__)
Bill Peckham889b0c62019-02-21 18:53:37 -08001069 sys.exit(1)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001070
Daniel Normand5d70ea2019-06-05 15:13:43 -07001071 if OPTIONS.framework_item_list:
1072 framework_item_list = read_config_list(OPTIONS.framework_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001073 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001074 framework_item_list = DEFAULT_FRAMEWORK_ITEM_LIST
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001075
Daniel Normand5d70ea2019-06-05 15:13:43 -07001076 if OPTIONS.framework_misc_info_keys:
1077 framework_misc_info_keys = read_config_list(
1078 OPTIONS.framework_misc_info_keys)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001079 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001080 framework_misc_info_keys = DEFAULT_FRAMEWORK_MISC_INFO_KEYS
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001081
Daniel Normand5d70ea2019-06-05 15:13:43 -07001082 if OPTIONS.vendor_item_list:
1083 vendor_item_list = read_config_list(OPTIONS.vendor_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001084 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001085 vendor_item_list = DEFAULT_VENDOR_ITEM_LIST
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001086
Daniel Normanfdb38812019-04-15 09:47:24 -07001087 if OPTIONS.output_item_list:
1088 output_item_list = read_config_list(OPTIONS.output_item_list)
1089 else:
1090 output_item_list = None
1091
Daniel Normane5964522019-03-19 10:32:03 -07001092 if not validate_config_lists(
Daniel Normand5d70ea2019-06-05 15:13:43 -07001093 framework_item_list=framework_item_list,
1094 framework_misc_info_keys=framework_misc_info_keys,
1095 vendor_item_list=vendor_item_list):
Daniel Normane5964522019-03-19 10:32:03 -07001096 sys.exit(1)
1097
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001098 call_func_with_temp_dir(
1099 lambda temp_dir: merge_target_files(
1100 temp_dir=temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -07001101 framework_target_files=OPTIONS.framework_target_files,
1102 framework_item_list=framework_item_list,
1103 framework_misc_info_keys=framework_misc_info_keys,
1104 vendor_target_files=OPTIONS.vendor_target_files,
1105 vendor_item_list=vendor_item_list,
Daniel Normana4911da2019-03-15 14:36:21 -07001106 output_target_files=OPTIONS.output_target_files,
Daniel Normanfdb38812019-04-15 09:47:24 -07001107 output_dir=OPTIONS.output_dir,
1108 output_item_list=output_item_list,
Daniel Norman3b64ce12019-04-16 16:11:35 -07001109 output_ota=OPTIONS.output_ota,
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001110 output_img=OPTIONS.output_img,
Daniel Normanf0318252019-04-15 11:34:56 -07001111 output_super_empty=OPTIONS.output_super_empty,
Daniel Normane5b134a2019-04-17 14:54:06 -07001112 rebuild_recovery=OPTIONS.rebuild_recovery), OPTIONS.keep_tmp)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001113
1114
1115if __name__ == '__main__':
Bill Peckham889b0c62019-02-21 18:53:37 -08001116 main()