blob: e2b0e3d29b9894ac767ef7218f8d2f44b48f4444 [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(
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900337 'Cannot extract items from {0} for both the framework and vendor'
338 ' builds. Please ensure only one merge config item list'
339 ' includes {0}.'.format(partition))
Daniel Normanedf12472019-05-22 10:47:08 -0700340 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
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900503 misc_info_path = ['META', 'misc_info.txt']
504 framework_dict = common.LoadDictionaryFromFile(
505 os.path.join(framework_target_files_temp_dir, *misc_info_path))
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800506
Daniel Normand5d70ea2019-06-05 15:13:43 -0700507 # We take most of the misc info from the vendor target files.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800508
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900509 merged_dict = common.LoadDictionaryFromFile(
510 os.path.join(vendor_target_files_temp_dir, *misc_info_path))
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800511
Daniel Normand5d70ea2019-06-05 15:13:43 -0700512 # Replace certain values in merged_dict with values from
513 # framework_dict.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800514
Daniel Normand5d70ea2019-06-05 15:13:43 -0700515 for key in framework_misc_info_keys:
516 merged_dict[key] = framework_dict[key]
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800517
Daniel Norman19b9fe92019-03-19 14:48:02 -0700518 # Merge misc info keys used for Dynamic Partitions.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700519 if (merged_dict.get('use_dynamic_partitions') == 'true') and (
520 framework_dict.get('use_dynamic_partitions') == 'true'):
Daniel Normana61cde02019-05-03 14:19:13 -0700521 merged_dynamic_partitions_dict = merge_dynamic_partition_info_dicts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700522 framework_dict=framework_dict,
523 vendor_dict=merged_dict,
Daniel Normana61cde02019-05-03 14:19:13 -0700524 size_prefix='super_',
525 size_suffix='_group_size',
526 list_prefix='super_',
527 list_suffix='_partition_list')
Daniel Normand5d70ea2019-06-05 15:13:43 -0700528 merged_dict.update(merged_dynamic_partitions_dict)
Daniel Norman0bf940c2019-06-10 12:50:19 -0700529 # Ensure that add_img_to_target_files rebuilds super_empty.img. This flag
530 # may have been set to false in the partial builds to prevent duplicate
531 # building of super.img and super_empty.img.
532 merged_dict['build_super_partition'] = 'true'
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
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900577 dynamic_partitions_info_path = ['META', 'dynamic_partitions_info.txt']
Daniel Normana61cde02019-05-03 14:19:13 -0700578
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900579 framework_dynamic_partitions_dict = common.LoadDictionaryFromFile(
580 os.path.join(framework_target_files_dir, *dynamic_partitions_info_path))
581 vendor_dynamic_partitions_dict = common.LoadDictionaryFromFile(
582 os.path.join(vendor_target_files_dir, *dynamic_partitions_info_path))
Daniel Normana61cde02019-05-03 14:19:13 -0700583
584 merged_dynamic_partitions_dict = merge_dynamic_partition_info_dicts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700585 framework_dict=framework_dynamic_partitions_dict,
586 vendor_dict=vendor_dynamic_partitions_dict,
Daniel Normana61cde02019-05-03 14:19:13 -0700587 # META/dynamic_partitions_info.txt does not use dynamic_partition_list.
588 include_dynamic_partition_list=False,
589 size_suffix='_size',
590 list_suffix='_partition_list')
591
592 output_dynamic_partitions_info_txt = os.path.join(
593 output_target_files_dir, 'META', 'dynamic_partitions_info.txt')
Chris Grossfabf50a2019-05-02 12:42:09 -0700594 write_sorted_data(
595 data=merged_dynamic_partitions_dict,
596 path=output_dynamic_partitions_info_txt)
597
598
Daniel Normand5d70ea2019-06-05 15:13:43 -0700599def process_apex_keys_apk_certs_common(framework_target_files_dir,
600 vendor_target_files_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700601 output_target_files_dir, file_name):
602 """Perform special processing for META/apexkeys.txt or META/apkcerts.txt.
603
604 This function merges the contents of the META/apexkeys.txt or
Daniel Normand5d70ea2019-06-05 15:13:43 -0700605 META/apkcerts.txt files from the framework directory and the vendor
606 directory, placing the merged result in the output directory. The
607 precondition in that the files are already extracted. The post condition
608 is that the output META/apexkeys.txt or META/apkcerts.txt contains the
609 merged content.
Chris Grossfabf50a2019-05-02 12:42:09 -0700610
611 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700612 framework_target_files_dir: The name of a directory containing the special
613 items extracted from the framework target files package.
614 vendor_target_files_dir: The name of a directory containing the special
615 items extracted from the vendor target files package.
Chris Grossfabf50a2019-05-02 12:42:09 -0700616 output_target_files_dir: The name of a directory that will be used to create
617 the output target files package after all the special cases are processed.
618 file_name: The name of the file to merge. One of apkcerts.txt or
619 apexkeys.txt.
620 """
621
622 def read_helper(d):
623 temp = {}
624 file_path = os.path.join(d, 'META', file_name)
625 with open(file_path) as f:
626 for line in f:
627 if line.strip():
628 temp[line.split()[0]] = line.strip()
629 return temp
630
Daniel Normand5d70ea2019-06-05 15:13:43 -0700631 framework_dict = read_helper(framework_target_files_dir)
632 vendor_dict = read_helper(vendor_target_files_dir)
Chris Grossfabf50a2019-05-02 12:42:09 -0700633
Daniel Normand5d70ea2019-06-05 15:13:43 -0700634 for key in framework_dict:
635 if key in vendor_dict and vendor_dict[key] != framework_dict[key]:
Chris Grossfabf50a2019-05-02 12:42:09 -0700636 raise ValueError('Conflicting entries found in %s:\n %s and\n %s' %
Daniel Normand5d70ea2019-06-05 15:13:43 -0700637 (file_name, framework_dict[key], vendor_dict[key]))
638 vendor_dict[key] = framework_dict[key]
Chris Grossfabf50a2019-05-02 12:42:09 -0700639
640 output_file = os.path.join(output_target_files_dir, 'META', file_name)
641
Daniel Normand5d70ea2019-06-05 15:13:43 -0700642 write_sorted_data(data=vendor_dict.values(), path=output_file)
Daniel Normana61cde02019-05-03 14:19:13 -0700643
644
Daniel Normand5d70ea2019-06-05 15:13:43 -0700645def copy_file_contexts(framework_target_files_dir, vendor_target_files_dir,
Daniel Norman72c626f2019-05-13 15:58:14 -0700646 output_target_files_dir):
647 """Creates named copies of each build's file_contexts.bin in output META/."""
Daniel Normand5d70ea2019-06-05 15:13:43 -0700648 framework_fc_path = os.path.join(framework_target_files_dir, 'META',
649 'framework_file_contexts.bin')
650 if not os.path.exists(framework_fc_path):
651 framework_fc_path = os.path.join(framework_target_files_dir, 'META',
652 'file_contexts.bin')
653 if not os.path.exists(framework_fc_path):
654 raise ValueError('Missing framework file_contexts.bin.')
655 shutil.copyfile(
656 framework_fc_path,
657 os.path.join(output_target_files_dir, 'META',
658 'framework_file_contexts.bin'))
659
660 vendor_fc_path = os.path.join(vendor_target_files_dir, 'META',
661 'vendor_file_contexts.bin')
662 if not os.path.exists(vendor_fc_path):
663 vendor_fc_path = os.path.join(vendor_target_files_dir, 'META',
Daniel Normanedf12472019-05-22 10:47:08 -0700664 'file_contexts.bin')
Daniel Normand5d70ea2019-06-05 15:13:43 -0700665 if not os.path.exists(vendor_fc_path):
666 raise ValueError('Missing vendor file_contexts.bin.')
Daniel Norman72c626f2019-05-13 15:58:14 -0700667 shutil.copyfile(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700668 vendor_fc_path,
669 os.path.join(output_target_files_dir, 'META', 'vendor_file_contexts.bin'))
Daniel Norman72c626f2019-05-13 15:58:14 -0700670
671
Daniel Normand5d70ea2019-06-05 15:13:43 -0700672def process_special_cases(framework_target_files_temp_dir,
673 vendor_target_files_temp_dir,
674 output_target_files_temp_dir,
675 framework_misc_info_keys, rebuild_recovery):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800676 """Perform special-case processing for certain target files items.
677
678 Certain files in the output target files package require special-case
679 processing. This function performs all that special-case processing.
680
681 Args:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700682 framework_target_files_temp_dir: The name of a directory containing the
683 special items extracted from the framework target files package.
684 vendor_target_files_temp_dir: The name of a directory containing the special
685 items extracted from the vendor target files package.
Daniel Normane5b134a2019-04-17 14:54:06 -0700686 output_target_files_temp_dir: The name of a directory that will be used to
687 create the output target files package after all the special cases are
688 processed.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700689 framework_misc_info_keys: A list of keys to obtain from the framework
690 instance of META/misc_info.txt. The remaining keys from the vendor
691 instance.
Bill Peckham364c1cc2019-03-29 18:27:23 -0700692 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
Daniel Normane5b134a2019-04-17 14:54:06 -0700693 devices and write it to the system image.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800694 """
695
Daniel Normand5d70ea2019-06-05 15:13:43 -0700696 if 'ab_update' in framework_misc_info_keys:
Bill Peckham364c1cc2019-03-29 18:27:23 -0700697 process_ab_partitions_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700698 framework_target_files_temp_dir=framework_target_files_temp_dir,
699 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Bill Peckham364c1cc2019-03-29 18:27:23 -0700700 output_target_files_temp_dir=output_target_files_temp_dir)
701
702 if rebuild_recovery:
703 append_recovery_to_filesystem_config(
704 output_target_files_temp_dir=output_target_files_temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800705
Daniel Norman72c626f2019-05-13 15:58:14 -0700706 copy_file_contexts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700707 framework_target_files_dir=framework_target_files_temp_dir,
708 vendor_target_files_dir=vendor_target_files_temp_dir,
Daniel Norman72c626f2019-05-13 15:58:14 -0700709 output_target_files_dir=output_target_files_temp_dir)
710
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800711 process_misc_info_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700712 framework_target_files_temp_dir=framework_target_files_temp_dir,
713 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800714 output_target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700715 framework_misc_info_keys=framework_misc_info_keys)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800716
Daniel Normana61cde02019-05-03 14:19:13 -0700717 process_dynamic_partitions_info_txt(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700718 framework_target_files_dir=framework_target_files_temp_dir,
719 vendor_target_files_dir=vendor_target_files_temp_dir,
Daniel Norman714bd122019-05-08 16:20:02 -0700720 output_target_files_dir=output_target_files_temp_dir)
Daniel Normana61cde02019-05-03 14:19:13 -0700721
Chris Grossfabf50a2019-05-02 12:42:09 -0700722 process_apex_keys_apk_certs_common(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700723 framework_target_files_dir=framework_target_files_temp_dir,
724 vendor_target_files_dir=vendor_target_files_temp_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700725 output_target_files_dir=output_target_files_temp_dir,
726 file_name='apkcerts.txt')
727
728 process_apex_keys_apk_certs_common(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700729 framework_target_files_dir=framework_target_files_temp_dir,
730 vendor_target_files_dir=vendor_target_files_temp_dir,
Chris Grossfabf50a2019-05-02 12:42:09 -0700731 output_target_files_dir=output_target_files_temp_dir,
732 file_name='apexkeys.txt')
733
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800734
Daniel Normand5d70ea2019-06-05 15:13:43 -0700735def merge_target_files(temp_dir, framework_target_files, framework_item_list,
736 framework_misc_info_keys, vendor_target_files,
737 vendor_item_list, output_target_files, output_dir,
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700738 output_item_list, output_ota, output_img,
739 output_super_empty, rebuild_recovery):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800740 """Merge two target files packages together.
741
Daniel Normand5d70ea2019-06-05 15:13:43 -0700742 This function takes framework and vendor target files packages as input,
743 performs various file extractions, special case processing, and finally
744 creates a merged zip archive as output.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800745
746 Args:
747 temp_dir: The name of a directory we use when we extract items from the
Daniel Normane5b134a2019-04-17 14:54:06 -0700748 input target files packages, and also a scratch directory that we use for
749 temporary files.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700750 framework_target_files: The name of the zip archive containing the framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700751 partial target files package.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700752 framework_item_list: The list of items to extract from the partial framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700753 target files package as is, meaning these items will land in the output
Daniel Normand5d70ea2019-06-05 15:13:43 -0700754 target files package exactly as they appear in the input partial framework
Daniel Normane5b134a2019-04-17 14:54:06 -0700755 target files package.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700756 framework_misc_info_keys: The list of keys to obtain from the framework
757 instance of META/misc_info.txt. The remaining keys from the vendor
758 instance.
759 vendor_target_files: The name of the zip archive containing the vendor
760 partial target files package.
761 vendor_item_list: The list of items to extract from the partial vendor
762 target files package as is, meaning these items will land in the output
763 target files package exactly as they appear in the input partial vendor
Daniel Normane5b134a2019-04-17 14:54:06 -0700764 target files package.
Daniel Normane5b134a2019-04-17 14:54:06 -0700765 output_target_files: The name of the output zip archive target files package
Daniel Normand5d70ea2019-06-05 15:13:43 -0700766 created by merging framework and vendor.
Daniel Normane5b134a2019-04-17 14:54:06 -0700767 output_dir: The destination directory for saving merged files.
768 output_item_list: The list of items to copy into the output_dir.
Daniel Norman3b64ce12019-04-16 16:11:35 -0700769 output_ota: The name of the output zip archive ota package.
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700770 output_img: The name of the output zip archive img package.
Daniel Normanf0318252019-04-15 11:34:56 -0700771 output_super_empty: If provided, creates a super_empty.img file from the
Daniel Normane5b134a2019-04-17 14:54:06 -0700772 merged target files package and saves it at this path.
Daniel Normana4911da2019-03-15 14:36:21 -0700773 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
Daniel Normane5b134a2019-04-17 14:54:06 -0700774 devices and write it to the system image.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800775 """
776
Daniel Normand5d70ea2019-06-05 15:13:43 -0700777 logger.info('starting: merge framework %s and vendor %s into output %s',
778 framework_target_files, vendor_target_files, output_target_files)
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800779
Daniel Normand5d70ea2019-06-05 15:13:43 -0700780 # Create directory names that we'll use when we extract files from framework,
781 # and vendor, and for zipping the final output.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800782
Daniel Normand5d70ea2019-06-05 15:13:43 -0700783 framework_target_files_temp_dir = os.path.join(temp_dir, 'framework')
784 vendor_target_files_temp_dir = os.path.join(temp_dir, 'vendor')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800785 output_target_files_temp_dir = os.path.join(temp_dir, 'output')
786
Daniel Normand5d70ea2019-06-05 15:13:43 -0700787 # Extract "as is" items from the input framework partial target files package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800788 # We extract them directly into the output temporary directory since the
789 # items do not need special case processing.
790
Bill Peckham889b0c62019-02-21 18:53:37 -0800791 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700792 target_files=framework_target_files,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800793 target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700794 extract_item_list=framework_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800795
Daniel Normand5d70ea2019-06-05 15:13:43 -0700796 # Extract "as is" items from the input vendor partial target files package. We
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800797 # extract them directly into the output temporary directory since the items
798 # do not need special case processing.
799
Bill Peckham889b0c62019-02-21 18:53:37 -0800800 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700801 target_files=vendor_target_files,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800802 target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700803 extract_item_list=vendor_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800804
Daniel Normand5d70ea2019-06-05 15:13:43 -0700805 # Extract "special" items from the input framework partial target files
806 # package. We extract these items to different directory since they require
807 # special processing before they will end up in the output directory.
808
809 extract_items(
810 target_files=framework_target_files,
811 target_files_temp_dir=framework_target_files_temp_dir,
812 extract_item_list=FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST)
813
814 # Extract "special" items from the input vendor partial target files package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800815 # We extract these items to different directory since they require special
816 # processing before they will end up in the output directory.
817
Bill Peckham889b0c62019-02-21 18:53:37 -0800818 extract_items(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700819 target_files=vendor_target_files,
820 target_files_temp_dir=vendor_target_files_temp_dir,
821 extract_item_list=VENDOR_EXTRACT_SPECIAL_ITEM_LIST)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800822
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800823 # Now that the temporary directories contain all the extracted files, perform
824 # special case processing on any items that need it. After this function
825 # completes successfully, all the files we need to create the output target
826 # files package are in place.
827
Bill Peckham889b0c62019-02-21 18:53:37 -0800828 process_special_cases(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700829 framework_target_files_temp_dir=framework_target_files_temp_dir,
830 vendor_target_files_temp_dir=vendor_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800831 output_target_files_temp_dir=output_target_files_temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -0700832 framework_misc_info_keys=framework_misc_info_keys,
Bill Peckham364c1cc2019-03-29 18:27:23 -0700833 rebuild_recovery=rebuild_recovery)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800834
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800835 # Regenerate IMAGES in the temporary directory.
836
Daniel Normana4911da2019-03-15 14:36:21 -0700837 add_img_args = ['--verbose']
838 if rebuild_recovery:
839 add_img_args.append('--rebuild_recovery')
840 add_img_args.append(output_target_files_temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800841
842 add_img_to_target_files.main(add_img_args)
843
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700844 # Create super_empty.img using the merged misc_info.txt.
845
846 misc_info_txt = os.path.join(output_target_files_temp_dir, 'META',
847 'misc_info.txt')
848
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900849 use_dynamic_partitions = common.LoadDictionaryFromFile(misc_info_txt).get(
850 'use_dynamic_partitions')
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700851
852 if use_dynamic_partitions != 'true' and output_super_empty:
853 raise ValueError(
854 'Building super_empty.img requires use_dynamic_partitions=true.')
855 elif use_dynamic_partitions == 'true':
856 super_empty_img = os.path.join(output_target_files_temp_dir, 'IMAGES',
857 'super_empty.img')
858 build_super_image_args = [
859 misc_info_txt,
860 super_empty_img,
861 ]
862 build_super_image.main(build_super_image_args)
863
864 # Copy super_empty.img to the user-provided output_super_empty location.
865 if output_super_empty:
866 shutil.copyfile(super_empty_img, output_super_empty)
867
Daniel Normanb8a2f9d2019-04-24 12:55:51 -0700868 # Create the IMG package from the merged target files (before zipping, in
869 # order to avoid an unnecessary unzip and copy).
870
871 if output_img:
872 img_from_target_files_args = [
873 output_target_files_temp_dir,
874 output_img,
875 ]
876 img_from_target_files.main(img_from_target_files_args)
877
Daniel Normanfdb38812019-04-15 09:47:24 -0700878 # Finally, create the output target files zip archive and/or copy the
879 # output items to the output target files directory.
880
881 if output_dir:
882 copy_items(output_target_files_temp_dir, output_dir, output_item_list)
883
884 if not output_target_files:
885 return
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800886
887 output_zip = os.path.abspath(output_target_files)
888 output_target_files_list = os.path.join(temp_dir, 'output.list')
Daniel Normane5b134a2019-04-17 14:54:06 -0700889 output_target_files_meta_dir = os.path.join(output_target_files_temp_dir,
890 'META')
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800891
Bill Peckham9662cfb2019-04-24 17:59:01 -0700892 find_command = [
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800893 'find',
894 output_target_files_meta_dir,
895 ]
Bill Peckham9662cfb2019-04-24 17:59:01 -0700896 find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
Daniel Normana61cde02019-05-03 14:19:13 -0700897 meta_content = common.RunAndCheckOutput(['sort'],
898 stdin=find_process.stdout,
Bill Peckham9662cfb2019-04-24 17:59:01 -0700899 verbose=False)
900
901 find_command = [
Daniel Normane5b134a2019-04-17 14:54:06 -0700902 'find', output_target_files_temp_dir, '-path',
903 output_target_files_meta_dir, '-prune', '-o', '-print'
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800904 ]
Bill Peckham9662cfb2019-04-24 17:59:01 -0700905 find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False)
Daniel Normana61cde02019-05-03 14:19:13 -0700906 other_content = common.RunAndCheckOutput(['sort'],
907 stdin=find_process.stdout,
Bill Peckham9662cfb2019-04-24 17:59:01 -0700908 verbose=False)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800909
910 with open(output_target_files_list, 'wb') as f:
911 f.write(meta_content)
912 f.write(other_content)
913
914 command = [
Bill Peckhamf753e152019-02-19 18:02:46 -0800915 'soong_zip',
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800916 '-d',
Daniel Normane5b134a2019-04-17 14:54:06 -0700917 '-o',
918 output_zip,
919 '-C',
920 output_target_files_temp_dir,
921 '-l',
922 output_target_files_list,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800923 ]
924 logger.info('creating %s', output_target_files)
Bill Peckham889b0c62019-02-21 18:53:37 -0800925 common.RunAndWait(command, verbose=True)
Chris Grosseab4f0e2019-06-07 10:34:13 -0700926 logger.info('finished creating %s', output_target_files)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800927
Daniel Norman3b64ce12019-04-16 16:11:35 -0700928 # Create the OTA package from the merged target files package.
929
930 if output_ota:
931 ota_from_target_files_args = [
932 output_zip,
933 output_ota,
934 ]
935 ota_from_target_files.main(ota_from_target_files_args)
936
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700937
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800938def call_func_with_temp_dir(func, keep_tmp):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800939 """Manage the creation and cleanup of the temporary directory.
940
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800941 This function calls the given function after first creating a temporary
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800942 directory. It also cleans up the temporary directory.
943
944 Args:
Daniel Normane5b134a2019-04-17 14:54:06 -0700945 func: The function to call. Should accept one parameter, the path to the
946 temporary directory.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800947 keep_tmp: Keep the temporary directory after processing is complete.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800948 """
949
950 # Create a temporary directory. This will serve as the parent of directories
951 # we use when we extract items from the input target files packages, and also
952 # a scratch directory that we use for temporary files.
953
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800954 temp_dir = common.MakeTempDir(prefix='merge_target_files_')
955
956 try:
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800957 func(temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800958 except:
959 raise
960 finally:
961 if keep_tmp:
962 logger.info('keeping %s', temp_dir)
963 else:
964 common.Cleanup()
965
966
967def main():
968 """The main function.
969
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800970 Process command line arguments, then call merge_target_files to
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800971 perform the heavy lifting.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800972 """
973
974 common.InitLogging()
975
Bill Peckhamf753e152019-02-19 18:02:46 -0800976 def option_handler(o, a):
977 if o == '--system-target-files':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700978 logger.warning(
979 '--system-target-files has been renamed to --framework-target-files')
980 OPTIONS.framework_target_files = a
981 elif o == '--framework-target-files':
982 OPTIONS.framework_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800983 elif o == '--system-item-list':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700984 logger.warning(
985 '--system-item-list has been renamed to --framework-item-list')
986 OPTIONS.framework_item_list = a
987 elif o == '--framework-item-list':
988 OPTIONS.framework_item_list = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800989 elif o == '--system-misc-info-keys':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700990 logger.warning(
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900991 '--system-misc-info-keys has been renamed to '
992 '--framework-misc-info-keys'
Daniel Normand5d70ea2019-06-05 15:13:43 -0700993 )
994 OPTIONS.framework_misc_info_keys = a
995 elif o == '--framework-misc-info-keys':
996 OPTIONS.framework_misc_info_keys = a
Bill Peckhamf753e152019-02-19 18:02:46 -0800997 elif o == '--other-target-files':
Daniel Normand5d70ea2019-06-05 15:13:43 -0700998 logger.warning(
999 '--other-target-files has been renamed to --vendor-target-files')
1000 OPTIONS.vendor_target_files = a
1001 elif o == '--vendor-target-files':
1002 OPTIONS.vendor_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001003 elif o == '--other-item-list':
Daniel Normand5d70ea2019-06-05 15:13:43 -07001004 logger.warning('--other-item-list has been renamed to --vendor-item-list')
1005 OPTIONS.vendor_item_list = a
1006 elif o == '--vendor-item-list':
1007 OPTIONS.vendor_item_list = a
Bill Peckhamf753e152019-02-19 18:02:46 -08001008 elif o == '--output-target-files':
1009 OPTIONS.output_target_files = a
Daniel Normanfdb38812019-04-15 09:47:24 -07001010 elif o == '--output-dir':
1011 OPTIONS.output_dir = a
1012 elif o == '--output-item-list':
1013 OPTIONS.output_item_list = a
Daniel Norman3b64ce12019-04-16 16:11:35 -07001014 elif o == '--output-ota':
1015 OPTIONS.output_ota = a
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001016 elif o == '--output-img':
1017 OPTIONS.output_img = a
Daniel Normanf0318252019-04-15 11:34:56 -07001018 elif o == '--output-super-empty':
1019 OPTIONS.output_super_empty = a
Daniel Normana4911da2019-03-15 14:36:21 -07001020 elif o == '--rebuild_recovery':
1021 OPTIONS.rebuild_recovery = True
Bill Peckham364c1cc2019-03-29 18:27:23 -07001022 elif o == '--keep-tmp':
Bill Peckhamf753e152019-02-19 18:02:46 -08001023 OPTIONS.keep_tmp = True
1024 else:
1025 return False
1026 return True
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001027
Bill Peckhamf753e152019-02-19 18:02:46 -08001028 args = common.ParseOptions(
Daniel Normane5b134a2019-04-17 14:54:06 -07001029 sys.argv[1:],
1030 __doc__,
Bill Peckhamf753e152019-02-19 18:02:46 -08001031 extra_long_opts=[
1032 'system-target-files=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001033 'framework-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001034 'system-item-list=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001035 'framework-item-list=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001036 'system-misc-info-keys=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001037 'framework-misc-info-keys=',
Bill Peckhamf753e152019-02-19 18:02:46 -08001038 'other-target-files=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001039 'vendor-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001040 'other-item-list=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001041 'vendor-item-list=',
Bill Peckhamf753e152019-02-19 18:02:46 -08001042 'output-target-files=',
Daniel Normanfdb38812019-04-15 09:47:24 -07001043 'output-dir=',
1044 'output-item-list=',
Daniel Norman3b64ce12019-04-16 16:11:35 -07001045 'output-ota=',
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001046 'output-img=',
Daniel Normanf0318252019-04-15 11:34:56 -07001047 'output-super-empty=',
Daniel Normana4911da2019-03-15 14:36:21 -07001048 'rebuild_recovery',
Bill Peckham364c1cc2019-03-29 18:27:23 -07001049 'keep-tmp',
Bill Peckhamf753e152019-02-19 18:02:46 -08001050 ],
1051 extra_option_handler=option_handler)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001052
Daniel Normand5d70ea2019-06-05 15:13:43 -07001053 if (args or OPTIONS.framework_target_files is None or
1054 OPTIONS.vendor_target_files is None or
Daniel Normane5b134a2019-04-17 14:54:06 -07001055 (OPTIONS.output_target_files is None and OPTIONS.output_dir is None) or
1056 (OPTIONS.output_dir is not None and OPTIONS.output_item_list is None)):
Bill Peckhamf753e152019-02-19 18:02:46 -08001057 common.Usage(__doc__)
Bill Peckham889b0c62019-02-21 18:53:37 -08001058 sys.exit(1)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001059
Daniel Normand5d70ea2019-06-05 15:13:43 -07001060 if OPTIONS.framework_item_list:
1061 framework_item_list = read_config_list(OPTIONS.framework_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001062 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001063 framework_item_list = DEFAULT_FRAMEWORK_ITEM_LIST
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001064
Daniel Normand5d70ea2019-06-05 15:13:43 -07001065 if OPTIONS.framework_misc_info_keys:
1066 framework_misc_info_keys = read_config_list(
1067 OPTIONS.framework_misc_info_keys)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001068 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001069 framework_misc_info_keys = DEFAULT_FRAMEWORK_MISC_INFO_KEYS
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001070
Daniel Normand5d70ea2019-06-05 15:13:43 -07001071 if OPTIONS.vendor_item_list:
1072 vendor_item_list = read_config_list(OPTIONS.vendor_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001073 else:
Daniel Normand5d70ea2019-06-05 15:13:43 -07001074 vendor_item_list = DEFAULT_VENDOR_ITEM_LIST
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001075
Daniel Normanfdb38812019-04-15 09:47:24 -07001076 if OPTIONS.output_item_list:
1077 output_item_list = read_config_list(OPTIONS.output_item_list)
1078 else:
1079 output_item_list = None
1080
Daniel Normane5964522019-03-19 10:32:03 -07001081 if not validate_config_lists(
Daniel Normand5d70ea2019-06-05 15:13:43 -07001082 framework_item_list=framework_item_list,
1083 framework_misc_info_keys=framework_misc_info_keys,
1084 vendor_item_list=vendor_item_list):
Daniel Normane5964522019-03-19 10:32:03 -07001085 sys.exit(1)
1086
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001087 call_func_with_temp_dir(
1088 lambda temp_dir: merge_target_files(
1089 temp_dir=temp_dir,
Daniel Normand5d70ea2019-06-05 15:13:43 -07001090 framework_target_files=OPTIONS.framework_target_files,
1091 framework_item_list=framework_item_list,
1092 framework_misc_info_keys=framework_misc_info_keys,
1093 vendor_target_files=OPTIONS.vendor_target_files,
1094 vendor_item_list=vendor_item_list,
Daniel Normana4911da2019-03-15 14:36:21 -07001095 output_target_files=OPTIONS.output_target_files,
Daniel Normanfdb38812019-04-15 09:47:24 -07001096 output_dir=OPTIONS.output_dir,
1097 output_item_list=output_item_list,
Daniel Norman3b64ce12019-04-16 16:11:35 -07001098 output_ota=OPTIONS.output_ota,
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001099 output_img=OPTIONS.output_img,
Daniel Normanf0318252019-04-15 11:34:56 -07001100 output_super_empty=OPTIONS.output_super_empty,
Daniel Normane5b134a2019-04-17 14:54:06 -07001101 rebuild_recovery=OPTIONS.rebuild_recovery), OPTIONS.keep_tmp)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001102
1103
1104if __name__ == '__main__':
Bill Peckham889b0c62019-02-21 18:53:37 -08001105 main()