blob: bacbfd0d81bf1544da6e82ed00b2d9e2a31709e4 [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 Norman19b9fe92019-03-19 14:48:02 -0700533
Daniel Normand5d70ea2019-06-05 15:13:43 -0700534 # Replace <image>_selinux_fc values with framework or vendor file_contexts.bin
Daniel Norman72c626f2019-05-13 15:58:14 -0700535 # depending on which dictionary the key came from.
536 # Only the file basename is required because all selinux_fc properties are
537 # replaced with the full path to the file under META/ when misc_info.txt is
538 # loaded from target files for repacking. See common.py LoadInfoDict().
Daniel Normand5d70ea2019-06-05 15:13:43 -0700539 for key in merged_dict:
Daniel Norman72c626f2019-05-13 15:58:14 -0700540 if key.endswith('_selinux_fc'):
Daniel Normand5d70ea2019-06-05 15:13:43 -0700541 merged_dict[key] = 'vendor_file_contexts.bin'
542 for key in framework_dict:
Daniel Norman72c626f2019-05-13 15:58:14 -0700543 if key.endswith('_selinux_fc'):
Daniel Normand5d70ea2019-06-05 15:13:43 -0700544 merged_dict[key] = 'framework_file_contexts.bin'
Daniel Norman72c626f2019-05-13 15:58:14 -0700545
Daniel Normane5b134a2019-04-17 14:54:06 -0700546 output_misc_info_txt = os.path.join(output_target_files_temp_dir, 'META',
547 'misc_info.txt')
Daniel Normand5d70ea2019-06-05 15:13:43 -0700548 write_sorted_data(data=merged_dict, path=output_misc_info_txt)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800549
550
Daniel Normand5d70ea2019-06-05 15:13:43 -0700551def process_dynamic_partitions_info_txt(framework_target_files_dir,
552 vendor_target_files_dir,
Daniel Normana61cde02019-05-03 14:19:13 -0700553 output_target_files_dir):
554 """Perform special processing for META/dynamic_partitions_info.txt.
555
556 This function merges the contents of the META/dynamic_partitions_info.txt
Daniel Normand5d70ea2019-06-05 15:13:43 -0700557 files from the framework directory and the vendor directory, placing the
558 merged result in the output directory.
Daniel Normana61cde02019-05-03 14:19:13 -0700559
Daniel Normand5d70ea2019-06-05 15:13:43 -0700560 This function does nothing if META/dynamic_partitions_info.txt from the vendor
Daniel Normana61cde02019-05-03 14:19:13 -0700561 directory does not exist.
562
563 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700564 framework_target_files_dir: The name of a directory containing the special
565 items extracted from the framework target files package.
566 vendor_target_files_dir: The name of a directory containing the special
567 items extracted from the vendor target files package.
Daniel Normana61cde02019-05-03 14:19:13 -0700568 output_target_files_dir: The name of a directory that will be used to create
569 the output target files package after all the special cases are processed.
570 """
571
572 if not os.path.exists(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700573 os.path.join(vendor_target_files_dir, 'META',
Daniel Normana61cde02019-05-03 14:19:13 -0700574 'dynamic_partitions_info.txt')):
575 return
576
577 def read_helper(d):
578 dynamic_partitions_info_txt = os.path.join(d, 'META',
579 'dynamic_partitions_info.txt')
580 with open(dynamic_partitions_info_txt) as f:
581 return list(f.read().splitlines())
582
Daniel Normand5d70ea2019-06-05 15:13:43 -0700583 framework_dynamic_partitions_dict = common.LoadDictionaryFromLines(
584 read_helper(framework_target_files_dir))
585 vendor_dynamic_partitions_dict = common.LoadDictionaryFromLines(
586 read_helper(vendor_target_files_dir))
Daniel Normana61cde02019-05-03 14:19:13 -0700587
588 merged_dynamic_partitions_dict = merge_dynamic_partition_info_dicts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700589 framework_dict=framework_dynamic_partitions_dict,
590 vendor_dict=vendor_dynamic_partitions_dict,
Daniel Normana61cde02019-05-03 14:19:13 -0700591 # META/dynamic_partitions_info.txt does not use dynamic_partition_list.
592 include_dynamic_partition_list=False,
593 size_suffix='_size',
594 list_suffix='_partition_list')
595
596 output_dynamic_partitions_info_txt = os.path.join(
597 output_target_files_dir, 'META', 'dynamic_partitions_info.txt')
Chris Grossfabf50a2019-05-02 12:42:09 -0700598 write_sorted_data(
599 data=merged_dynamic_partitions_dict,
600 path=output_dynamic_partitions_info_txt)
601
602
Daniel Normand5d70ea2019-06-05 15:13:43 -0700603def process_apex_keys_apk_certs_common(framework_target_files_dir,
604 vendor_target_files_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700605 output_target_files_dir, file_name):
606 """Perform special processing for META/apexkeys.txt or META/apkcerts.txt.
607
608 This function merges the contents of the META/apexkeys.txt or
Daniel Normand5d70ea2019-06-05 15:13:43 -0700609 META/apkcerts.txt files from the framework directory and the vendor
610 directory, placing the merged result in the output directory. The
611 precondition in that the files are already extracted. The post condition
612 is that the output META/apexkeys.txt or META/apkcerts.txt contains the
613 merged content.
Chris Grossfabf50a2019-05-02 12:42:09 -0700614
615 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700616 framework_target_files_dir: The name of a directory containing the special
617 items extracted from the framework target files package.
618 vendor_target_files_dir: The name of a directory containing the special
619 items extracted from the vendor target files package.
Chris Grossfabf50a2019-05-02 12:42:09 -0700620 output_target_files_dir: The name of a directory that will be used to create
621 the output target files package after all the special cases are processed.
622 file_name: The name of the file to merge. One of apkcerts.txt or
623 apexkeys.txt.
624 """
625
626 def read_helper(d):
627 temp = {}
628 file_path = os.path.join(d, 'META', file_name)
629 with open(file_path) as f:
630 for line in f:
631 if line.strip():
632 temp[line.split()[0]] = line.strip()
633 return temp
634
Daniel Normand5d70ea2019-06-05 15:13:43 -0700635 framework_dict = read_helper(framework_target_files_dir)
636 vendor_dict = read_helper(vendor_target_files_dir)
Chris Grossfabf50a2019-05-02 12:42:09 -0700637
Daniel Normand5d70ea2019-06-05 15:13:43 -0700638 for key in framework_dict:
639 if key in vendor_dict and vendor_dict[key] != framework_dict[key]:
Chris Grossfabf50a2019-05-02 12:42:09 -0700640 raise ValueError('Conflicting entries found in %s:\n %s and\n %s' %
Daniel Normand5d70ea2019-06-05 15:13:43 -0700641 (file_name, framework_dict[key], vendor_dict[key]))
642 vendor_dict[key] = framework_dict[key]
Chris Grossfabf50a2019-05-02 12:42:09 -0700643
644 output_file = os.path.join(output_target_files_dir, 'META', file_name)
645
Daniel Normand5d70ea2019-06-05 15:13:43 -0700646 write_sorted_data(data=vendor_dict.values(), path=output_file)
Daniel Normana61cde02019-05-03 14:19:13 -0700647
648
Daniel Normand5d70ea2019-06-05 15:13:43 -0700649def copy_file_contexts(framework_target_files_dir, vendor_target_files_dir,
Daniel Norman72c626f2019-05-13 15:58:14 -0700650 output_target_files_dir):
651 """Creates named copies of each build's file_contexts.bin in output META/."""
Daniel Normand5d70ea2019-06-05 15:13:43 -0700652 framework_fc_path = os.path.join(framework_target_files_dir, 'META',
653 'framework_file_contexts.bin')
654 if not os.path.exists(framework_fc_path):
655 framework_fc_path = os.path.join(framework_target_files_dir, 'META',
656 'file_contexts.bin')
657 if not os.path.exists(framework_fc_path):
658 raise ValueError('Missing framework file_contexts.bin.')
659 shutil.copyfile(
660 framework_fc_path,
661 os.path.join(output_target_files_dir, 'META',
662 'framework_file_contexts.bin'))
663
664 vendor_fc_path = os.path.join(vendor_target_files_dir, 'META',
665 'vendor_file_contexts.bin')
666 if not os.path.exists(vendor_fc_path):
667 vendor_fc_path = os.path.join(vendor_target_files_dir, 'META',
Daniel Normanedf12472019-05-22 10:47:08 -0700668 'file_contexts.bin')
Daniel Normand5d70ea2019-06-05 15:13:43 -0700669 if not os.path.exists(vendor_fc_path):
670 raise ValueError('Missing vendor file_contexts.bin.')
Daniel Norman72c626f2019-05-13 15:58:14 -0700671 shutil.copyfile(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700672 vendor_fc_path,
673 os.path.join(output_target_files_dir, 'META', 'vendor_file_contexts.bin'))
Daniel Norman72c626f2019-05-13 15:58:14 -0700674
675
Daniel Normand5d70ea2019-06-05 15:13:43 -0700676def process_special_cases(framework_target_files_temp_dir,
677 vendor_target_files_temp_dir,
678 output_target_files_temp_dir,
679 framework_misc_info_keys, rebuild_recovery):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800680 """Perform special-case processing for certain target files items.
681
682 Certain files in the output target files package require special-case
683 processing. This function performs all that special-case processing.
684
685 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700686 framework_target_files_temp_dir: The name of a directory containing the
687 special items extracted from the framework target files package.
688 vendor_target_files_temp_dir: The name of a directory containing the special
689 items extracted from the vendor target files package.
Daniel Normane5b134a2019-04-17 14:54:06 -0700690 output_target_files_temp_dir: The name of a directory that will be used to
691 create the output target files package after all the special cases are
692 processed.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700693 framework_misc_info_keys: A list of keys to obtain from the framework
694 instance of META/misc_info.txt. The remaining keys from the vendor
695 instance.
Bill Peckham364c1cc2019-03-29 18:27:23 -0700696 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
Daniel Normane5b134a2019-04-17 14:54:06 -0700697 devices and write it to the system image.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800698 """
699
Daniel Normand5d70ea2019-06-05 15:13:43 -0700700 if 'ab_update' in framework_misc_info_keys:
Bill Peckham364c1cc2019-03-29 18:27:23 -0700701 process_ab_partitions_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700702 framework_target_files_temp_dir=framework_target_files_temp_dir,
703 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Bill Peckham364c1cc2019-03-29 18:27:23 -0700704 output_target_files_temp_dir=output_target_files_temp_dir)
705
706 if rebuild_recovery:
707 append_recovery_to_filesystem_config(
708 output_target_files_temp_dir=output_target_files_temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800709
Daniel Norman72c626f2019-05-13 15:58:14 -0700710 copy_file_contexts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700711 framework_target_files_dir=framework_target_files_temp_dir,
712 vendor_target_files_dir=vendor_target_files_temp_dir,
Daniel Norman72c626f2019-05-13 15:58:14 -0700713 output_target_files_dir=output_target_files_temp_dir)
714
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800715 process_misc_info_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700716 framework_target_files_temp_dir=framework_target_files_temp_dir,
717 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800718 output_target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700719 framework_misc_info_keys=framework_misc_info_keys)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800720
Daniel Normana61cde02019-05-03 14:19:13 -0700721 process_dynamic_partitions_info_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700722 framework_target_files_dir=framework_target_files_temp_dir,
723 vendor_target_files_dir=vendor_target_files_temp_dir,
Daniel Norman714bd122019-05-08 16:20:02 -0700724 output_target_files_dir=output_target_files_temp_dir)
Daniel Normana61cde02019-05-03 14:19:13 -0700725
Chris Grossfabf50a2019-05-02 12:42:09 -0700726 process_apex_keys_apk_certs_common(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700727 framework_target_files_dir=framework_target_files_temp_dir,
728 vendor_target_files_dir=vendor_target_files_temp_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700729 output_target_files_dir=output_target_files_temp_dir,
730 file_name='apkcerts.txt')
731
732 process_apex_keys_apk_certs_common(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700733 framework_target_files_dir=framework_target_files_temp_dir,
734 vendor_target_files_dir=vendor_target_files_temp_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700735 output_target_files_dir=output_target_files_temp_dir,
736 file_name='apexkeys.txt')
737
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800738
Daniel Normand5d70ea2019-06-05 15:13:43 -0700739def merge_target_files(temp_dir, framework_target_files, framework_item_list,
740 framework_misc_info_keys, vendor_target_files,
741 vendor_item_list, output_target_files, output_dir,
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700742 output_item_list, output_ota, output_img,
743 output_super_empty, rebuild_recovery):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800744 """Merge two target files packages together.
745
Daniel Normand5d70ea2019-06-05 15:13:43 -0700746 This function takes framework and vendor target files packages as input,
747 performs various file extractions, special case processing, and finally
748 creates a merged zip archive as output.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800749
750 Args:
751 temp_dir: The name of a directory we use when we extract items from the
Daniel Normane5b134a2019-04-17 14:54:06 -0700752 input target files packages, and also a scratch directory that we use for
753 temporary files.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700754 framework_target_files: The name of the zip archive containing the framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700755 partial target files package.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700756 framework_item_list: The list of items to extract from the partial framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700757 target files package as is, meaning these items will land in the output
Daniel Normand5d70ea2019-06-05 15:13:43 -0700758 target files package exactly as they appear in the input partial framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700759 target files package.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700760 framework_misc_info_keys: The list of keys to obtain from the framework
761 instance of META/misc_info.txt. The remaining keys from the vendor
762 instance.
763 vendor_target_files: The name of the zip archive containing the vendor
764 partial target files package.
765 vendor_item_list: The list of items to extract from the partial vendor
766 target files package as is, meaning these items will land in the output
767 target files package exactly as they appear in the input partial vendor
Daniel Normane5b134a2019-04-17 14:54:06 -0700768 target files package.
Daniel Normane5b134a2019-04-17 14:54:06 -0700769 output_target_files: The name of the output zip archive target files package
Daniel Normand5d70ea2019-06-05 15:13:43 -0700770 created by merging framework and vendor.
Daniel Normane5b134a2019-04-17 14:54:06 -0700771 output_dir: The destination directory for saving merged files.
772 output_item_list: The list of items to copy into the output_dir.
Daniel Norman3b64ce12019-04-16 16:11:35 -0700773 output_ota: The name of the output zip archive ota package.
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700774 output_img: The name of the output zip archive img package.
Daniel Normanf0318252019-04-15 11:34:56 -0700775 output_super_empty: If provided, creates a super_empty.img file from the
Daniel Normane5b134a2019-04-17 14:54:06 -0700776 merged target files package and saves it at this path.
Daniel Normana4911da2019-03-15 14:36:21 -0700777 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
Daniel Normane5b134a2019-04-17 14:54:06 -0700778 devices and write it to the system image.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800779 """
780
Daniel Normand5d70ea2019-06-05 15:13:43 -0700781 logger.info('starting: merge framework %s and vendor %s into output %s',
782 framework_target_files, vendor_target_files, output_target_files)
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800783
Daniel Normand5d70ea2019-06-05 15:13:43 -0700784 # Create directory names that we'll use when we extract files from framework,
785 # and vendor, and for zipping the final output.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800786
Daniel Normand5d70ea2019-06-05 15:13:43 -0700787 framework_target_files_temp_dir = os.path.join(temp_dir, 'framework')
788 vendor_target_files_temp_dir = os.path.join(temp_dir, 'vendor')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800789 output_target_files_temp_dir = os.path.join(temp_dir, 'output')
790
Daniel Normand5d70ea2019-06-05 15:13:43 -0700791 # Extract "as is" items from the input framework partial target files package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800792 # We extract them directly into the output temporary directory since the
793 # items do not need special case processing.
794
Bill Peckham889b0c62019-02-21 18:53:37 -0800795 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700796 target_files=framework_target_files,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800797 target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700798 extract_item_list=framework_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800799
Daniel Normand5d70ea2019-06-05 15:13:43 -0700800 # Extract "as is" items from the input vendor partial target files package. We
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800801 # extract them directly into the output temporary directory since the items
802 # do not need special case processing.
803
Bill Peckham889b0c62019-02-21 18:53:37 -0800804 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700805 target_files=vendor_target_files,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800806 target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700807 extract_item_list=vendor_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800808
Daniel Normand5d70ea2019-06-05 15:13:43 -0700809 # Extract "special" items from the input framework partial target files
810 # package. We extract these items to different directory since they require
811 # special processing before they will end up in the output directory.
812
813 extract_items(
814 target_files=framework_target_files,
815 target_files_temp_dir=framework_target_files_temp_dir,
816 extract_item_list=FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST)
817
818 # Extract "special" items from the input vendor partial target files package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800819 # We extract these items to different directory since they require special
820 # processing before they will end up in the output directory.
821
Bill Peckham889b0c62019-02-21 18:53:37 -0800822 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700823 target_files=vendor_target_files,
824 target_files_temp_dir=vendor_target_files_temp_dir,
825 extract_item_list=VENDOR_EXTRACT_SPECIAL_ITEM_LIST)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800826
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800827 # Now that the temporary directories contain all the extracted files, perform
828 # special case processing on any items that need it. After this function
829 # completes successfully, all the files we need to create the output target
830 # files package are in place.
831
Bill Peckham889b0c62019-02-21 18:53:37 -0800832 process_special_cases(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700833 framework_target_files_temp_dir=framework_target_files_temp_dir,
834 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800835 output_target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700836 framework_misc_info_keys=framework_misc_info_keys,
Bill Peckham364c1cc2019-03-29 18:27:23 -0700837 rebuild_recovery=rebuild_recovery)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800838
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800839 # Regenerate IMAGES in the temporary directory.
840
Daniel Normana4911da2019-03-15 14:36:21 -0700841 add_img_args = ['--verbose']
842 if rebuild_recovery:
843 add_img_args.append('--rebuild_recovery')
844 add_img_args.append(output_target_files_temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800845
846 add_img_to_target_files.main(add_img_args)
847
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700848 # Create super_empty.img using the merged misc_info.txt.
849
850 misc_info_txt = os.path.join(output_target_files_temp_dir, 'META',
851 'misc_info.txt')
852
853 def read_helper():
854 with open(misc_info_txt) as f:
855 return list(f.read().splitlines())
856
857 use_dynamic_partitions = common.LoadDictionaryFromLines(
858 read_helper()).get('use_dynamic_partitions')
859
860 if use_dynamic_partitions != 'true' and output_super_empty:
861 raise ValueError(
862 'Building super_empty.img requires use_dynamic_partitions=true.')
863 elif use_dynamic_partitions == 'true':
864 super_empty_img = os.path.join(output_target_files_temp_dir, 'IMAGES',
865 'super_empty.img')
866 build_super_image_args = [
867 misc_info_txt,
868 super_empty_img,
869 ]
870 build_super_image.main(build_super_image_args)
871
872 # Copy super_empty.img to the user-provided output_super_empty location.
873 if output_super_empty:
874 shutil.copyfile(super_empty_img, output_super_empty)
875
Daniel Normanb8a2f9d2019-04-24 12:55:51 -0700876 # Create the IMG package from the merged target files (before zipping, in
877 # order to avoid an unnecessary unzip and copy).
878
879 if output_img:
880 img_from_target_files_args = [
881 output_target_files_temp_dir,
882 output_img,
883 ]
884 img_from_target_files.main(img_from_target_files_args)
885
Daniel Normanfdb38812019-04-15 09:47:24 -0700886 # Finally, create the output target files zip archive and/or copy the
887 # output items to the output target files directory.
888
889 if output_dir:
890 copy_items(output_target_files_temp_dir, output_dir, output_item_list)
891
892 if not output_target_files:
893 return
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800894
895 output_zip = os.path.abspath(output_target_files)
896 output_target_files_list = os.path.join(temp_dir, 'output.list')
Daniel Normane5b134a2019-04-17 14:54:06 -0700897 output_target_files_meta_dir = os.path.join(output_target_files_temp_dir,
898 'META')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800899
Bill Peckham9662cfb2019-04-24 17:59:01 -0700900 find_command = [
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800901 'find',
902 output_target_files_meta_dir,
903 ]
Bill Peckham9662cfb2019-04-24 17:59:01 -0700904 find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
Daniel Normana61cde02019-05-03 14:19:13 -0700905 meta_content = common.RunAndCheckOutput(['sort'],
906 stdin=find_process.stdout,
Bill Peckham9662cfb2019-04-24 17:59:01 -0700907 verbose=False)
908
909 find_command = [
Daniel Normane5b134a2019-04-17 14:54:06 -0700910 'find', output_target_files_temp_dir, '-path',
911 output_target_files_meta_dir, '-prune', '-o', '-print'
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800912 ]
Bill Peckham9662cfb2019-04-24 17:59:01 -0700913 find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
Daniel Normana61cde02019-05-03 14:19:13 -0700914 other_content = common.RunAndCheckOutput(['sort'],
915 stdin=find_process.stdout,
Bill Peckham9662cfb2019-04-24 17:59:01 -0700916 verbose=False)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800917
918 with open(output_target_files_list, 'wb') as f:
919 f.write(meta_content)
920 f.write(other_content)
921
922 command = [
Bill Peckhamf753e152019-02-19 18:02:46 -0800923 'soong_zip',
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800924 '-d',
Daniel Normane5b134a2019-04-17 14:54:06 -0700925 '-o',
926 output_zip,
927 '-C',
928 output_target_files_temp_dir,
929 '-l',
930 output_target_files_list,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800931 ]
932 logger.info('creating %s', output_target_files)
Bill Peckham889b0c62019-02-21 18:53:37 -0800933 common.RunAndWait(command, verbose=True)
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:
938 ota_from_target_files_args = [
939 output_zip,
940 output_ota,
941 ]
942 ota_from_target_files.main(ota_from_target_files_args)
943
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700944
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800945def call_func_with_temp_dir(func, keep_tmp):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800946 """Manage the creation and cleanup of the temporary directory.
947
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800948 This function calls the given function after first creating a temporary
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800949 directory. It also cleans up the temporary directory.
950
951 Args:
Daniel Normane5b134a2019-04-17 14:54:06 -0700952 func: The function to call. Should accept one parameter, the path to the
953 temporary directory.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800954 keep_tmp: Keep the temporary directory after processing is complete.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800955 """
956
957 # Create a temporary directory. This will serve as the parent of directories
958 # we use when we extract items from the input target files packages, and also
959 # a scratch directory that we use for temporary files.
960
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800961 temp_dir = common.MakeTempDir(prefix='merge_target_files_')
962
963 try:
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800964 func(temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800965 except:
966 raise
967 finally:
968 if keep_tmp:
969 logger.info('keeping %s', temp_dir)
970 else:
971 common.Cleanup()
972
973
974def main():
975 """The main function.
976
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800977 Process command line arguments, then call merge_target_files to
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800978 perform the heavy lifting.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800979 """
980
981 common.InitLogging()
982
Bill Peckhamf753e152019-02-19 18:02:46 -0800983 def option_handler(o, a):
984 if o == '--system-target-files':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700985 logger.warning(
986 '--system-target-files has been renamed to --framework-target-files')
987 OPTIONS.framework_target_files = a
988 elif o == '--framework-target-files':
989 OPTIONS.framework_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800990 elif o == '--system-item-list':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700991 logger.warning(
992 '--system-item-list has been renamed to --framework-item-list')
993 OPTIONS.framework_item_list = a
994 elif o == '--framework-item-list':
995 OPTIONS.framework_item_list = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800996 elif o == '--system-misc-info-keys':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700997 logger.warning(
998 '--system-misc-info-keys has been renamed to --framework-misc-info-keys'
999 )
1000 OPTIONS.framework_misc_info_keys = a
1001 elif o == '--framework-misc-info-keys':
1002 OPTIONS.framework_misc_info_keys = a
Bill Peckhamf753e152019-02-19 18:02:46 -08001003 elif o == '--other-target-files':
Daniel Normand5d70ea2019-06-05 15:13:43 -07001004 logger.warning(
1005 '--other-target-files has been renamed to --vendor-target-files')
1006 OPTIONS.vendor_target_files = a
1007 elif o == '--vendor-target-files':
1008 OPTIONS.vendor_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001009 elif o == '--other-item-list':
Daniel Normand5d70ea2019-06-05 15:13:43 -07001010 logger.warning('--other-item-list has been renamed to --vendor-item-list')
1011 OPTIONS.vendor_item_list = a
1012 elif o == '--vendor-item-list':
1013 OPTIONS.vendor_item_list = a
Bill Peckhamf753e152019-02-19 18:02:46 -08001014 elif o == '--output-target-files':
1015 OPTIONS.output_target_files = a
Daniel Normanfdb38812019-04-15 09:47:24 -07001016 elif o == '--output-dir':
1017 OPTIONS.output_dir = a
1018 elif o == '--output-item-list':
1019 OPTIONS.output_item_list = a
Daniel Norman3b64ce12019-04-16 16:11:35 -07001020 elif o == '--output-ota':
1021 OPTIONS.output_ota = a
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001022 elif o == '--output-img':
1023 OPTIONS.output_img = a
Daniel Normanf0318252019-04-15 11:34:56 -07001024 elif o == '--output-super-empty':
1025 OPTIONS.output_super_empty = a
Daniel Normana4911da2019-03-15 14:36:21 -07001026 elif o == '--rebuild_recovery':
1027 OPTIONS.rebuild_recovery = True
Bill Peckham364c1cc2019-03-29 18:27:23 -07001028 elif o == '--keep-tmp':
Bill Peckhamf753e152019-02-19 18:02:46 -08001029 OPTIONS.keep_tmp = True
1030 else:
1031 return False
1032 return True
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001033
Bill Peckhamf753e152019-02-19 18:02:46 -08001034 args = common.ParseOptions(
Daniel Normane5b134a2019-04-17 14:54:06 -07001035 sys.argv[1:],
1036 __doc__,
Bill Peckhamf753e152019-02-19 18:02:46 -08001037 extra_long_opts=[
1038 'system-target-files=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001039 'framework-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001040 'system-item-list=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001041 'framework-item-list=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001042 'system-misc-info-keys=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001043 'framework-misc-info-keys=',
Bill Peckhamf753e152019-02-19 18:02:46 -08001044 'other-target-files=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001045 'vendor-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001046 'other-item-list=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001047 'vendor-item-list=',
Bill Peckhamf753e152019-02-19 18:02:46 -08001048 'output-target-files=',
Daniel Normanfdb38812019-04-15 09:47:24 -07001049 'output-dir=',
1050 'output-item-list=',
Daniel Norman3b64ce12019-04-16 16:11:35 -07001051 'output-ota=',
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001052 'output-img=',
Daniel Normanf0318252019-04-15 11:34:56 -07001053 'output-super-empty=',
Daniel Normana4911da2019-03-15 14:36:21 -07001054 'rebuild_recovery',
Bill Peckham364c1cc2019-03-29 18:27:23 -07001055 'keep-tmp',
Bill Peckhamf753e152019-02-19 18:02:46 -08001056 ],
1057 extra_option_handler=option_handler)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001058
Daniel Normand5d70ea2019-06-05 15:13:43 -07001059 if (args or OPTIONS.framework_target_files is None or
1060 OPTIONS.vendor_target_files is None or
Daniel Normane5b134a2019-04-17 14:54:06 -07001061 (OPTIONS.output_target_files is None and OPTIONS.output_dir is None) or
1062 (OPTIONS.output_dir is not None and OPTIONS.output_item_list is None)):
Bill Peckhamf753e152019-02-19 18:02:46 -08001063 common.Usage(__doc__)
Bill Peckham889b0c62019-02-21 18:53:37 -08001064 sys.exit(1)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001065
Daniel Normand5d70ea2019-06-05 15:13:43 -07001066 if OPTIONS.framework_item_list:
1067 framework_item_list = read_config_list(OPTIONS.framework_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001068 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001069 framework_item_list = DEFAULT_FRAMEWORK_ITEM_LIST
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001070
Daniel Normand5d70ea2019-06-05 15:13:43 -07001071 if OPTIONS.framework_misc_info_keys:
1072 framework_misc_info_keys = read_config_list(
1073 OPTIONS.framework_misc_info_keys)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001074 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001075 framework_misc_info_keys = DEFAULT_FRAMEWORK_MISC_INFO_KEYS
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001076
Daniel Normand5d70ea2019-06-05 15:13:43 -07001077 if OPTIONS.vendor_item_list:
1078 vendor_item_list = read_config_list(OPTIONS.vendor_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001079 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001080 vendor_item_list = DEFAULT_VENDOR_ITEM_LIST
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001081
Daniel Normanfdb38812019-04-15 09:47:24 -07001082 if OPTIONS.output_item_list:
1083 output_item_list = read_config_list(OPTIONS.output_item_list)
1084 else:
1085 output_item_list = None
1086
Daniel Normane5964522019-03-19 10:32:03 -07001087 if not validate_config_lists(
Daniel Normand5d70ea2019-06-05 15:13:43 -07001088 framework_item_list=framework_item_list,
1089 framework_misc_info_keys=framework_misc_info_keys,
1090 vendor_item_list=vendor_item_list):
Daniel Normane5964522019-03-19 10:32:03 -07001091 sys.exit(1)
1092
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001093 call_func_with_temp_dir(
1094 lambda temp_dir: merge_target_files(
1095 temp_dir=temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -07001096 framework_target_files=OPTIONS.framework_target_files,
1097 framework_item_list=framework_item_list,
1098 framework_misc_info_keys=framework_misc_info_keys,
1099 vendor_target_files=OPTIONS.vendor_target_files,
1100 vendor_item_list=vendor_item_list,
Daniel Normana4911da2019-03-15 14:36:21 -07001101 output_target_files=OPTIONS.output_target_files,
Daniel Normanfdb38812019-04-15 09:47:24 -07001102 output_dir=OPTIONS.output_dir,
1103 output_item_list=output_item_list,
Daniel Norman3b64ce12019-04-16 16:11:35 -07001104 output_ota=OPTIONS.output_ota,
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001105 output_img=OPTIONS.output_img,
Daniel Normanf0318252019-04-15 11:34:56 -07001106 output_super_empty=OPTIONS.output_super_empty,
Daniel Normane5b134a2019-04-17 14:54:06 -07001107 rebuild_recovery=OPTIONS.rebuild_recovery), OPTIONS.keep_tmp)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001108
1109
1110if __name__ == '__main__':
Bill Peckham889b0c62019-02-21 18:53:37 -08001111 main()