1#!/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. 16# 17"""This script merges two partial target files packages. 18 19One package contains framework files, and the other contains vendor files. 20It produces a complete target files package that can be used to generate an 21OTA package. 22 23Usage: merge_target_files.py [args] 24 25 --framework-target-files framework-target-files-zip-archive 26 The input target files package containing framework bits. This is a zip 27 archive. 28 29 --framework-item-list framework-item-list-file 30 The optional path to a newline-separated config file that replaces the 31 contents of DEFAULT_FRAMEWORK_ITEM_LIST if provided. 32 33 --framework-misc-info-keys framework-misc-info-keys-file 34 The optional path to a newline-separated config file that replaces the 35 contents of DEFAULT_FRAMEWORK_MISC_INFO_KEYS if provided. 36 37 --vendor-target-files vendor-target-files-zip-archive 38 The input target files package containing vendor bits. This is a zip 39 archive. 40 41 --vendor-item-list vendor-item-list-file 42 The optional path to a newline-separated config file that replaces the 43 contents of DEFAULT_VENDOR_ITEM_LIST if provided. 44 45 --output-target-files output-target-files-package 46 If provided, the output merged target files package. Also a zip archive. 47 48 --output-dir output-directory 49 If provided, the destination directory for saving merged files. Requires 50 the --output-item-list flag. 51 Can be provided alongside --output-target-files, or by itself. 52 53 --output-item-list output-item-list-file. 54 The optional path to a newline-separated config file that specifies the 55 file patterns to copy into the --output-dir. Required if providing 56 the --output-dir flag. 57 58 --output-ota output-ota-package 59 The output ota package. This is a zip archive. Use of this flag may 60 require passing the --path common flag; see common.py. 61 62 --output-img output-img-package 63 The output img package, suitable for use with 'fastboot update'. Use of 64 this flag may require passing the --path common flag; see common.py. 65 66 --output-super-empty output-super-empty-image 67 If provided, creates a super_empty.img file from the merged target 68 files package and saves it at this path. 69 70 --rebuild_recovery 71 Deprecated; does nothing. 72 73 --keep-tmp 74 Keep tempoary files for debugging purposes. 75""" 76 77from __future__ import print_function 78 79import fnmatch 80import logging 81import os 82import re 83import shutil 84import subprocess 85import sys 86import zipfile 87 88import add_img_to_target_files 89import build_super_image 90import check_target_files_vintf 91import common 92import img_from_target_files 93import ota_from_target_files 94 95logger = logging.getLogger(__name__) 96 97OPTIONS = common.OPTIONS 98# Always turn on verbose logging. 99OPTIONS.verbose = True 100OPTIONS.framework_target_files = None 101OPTIONS.framework_item_list = None 102OPTIONS.framework_misc_info_keys = None 103OPTIONS.vendor_target_files = None 104OPTIONS.vendor_item_list = None 105OPTIONS.output_target_files = None 106OPTIONS.output_dir = None 107OPTIONS.output_item_list = None 108OPTIONS.output_ota = None 109OPTIONS.output_img = None 110OPTIONS.output_super_empty = None 111# TODO(b/132730255): Remove this option. 112OPTIONS.rebuild_recovery = False 113OPTIONS.keep_tmp = False 114 115# In an item list (framework or vendor), we may see entries that select whole 116# partitions. Such an entry might look like this 'SYSTEM/*' (e.g., for the 117# system partition). The following regex matches this and extracts the 118# partition name. 119 120PARTITION_ITEM_PATTERN = re.compile(r'^([A-Z_]+)/\*$') 121 122# In apexkeys.txt or apkcerts.txt, we will find partition tags on each entry in 123# the file. We use these partition tags to filter the entries in those files 124# from the two different target files packages to produce a merged apexkeys.txt 125# or apkcerts.txt file. A partition tag (e.g., for the product partition) looks 126# like this: 'partition="product"'. We use the group syntax grab the value of 127# the tag. We use non-greedy matching in case there are other fields on the 128# same line. 129 130PARTITION_TAG_PATTERN = re.compile(r'partition="(.*?)"') 131 132# The sorting algorithm for apexkeys.txt and apkcerts.txt does not include the 133# ".apex" or ".apk" suffix, so we use the following pattern to extract a key. 134 135MODULE_KEY_PATTERN = re.compile(r'name="(.+)\.(apex|apk)"') 136 137# DEFAULT_FRAMEWORK_ITEM_LIST is a list of items to extract from the partial 138# framework target files package as is, meaning these items will land in the 139# output target files package exactly as they appear in the input partial 140# framework target files package. 141 142DEFAULT_FRAMEWORK_ITEM_LIST = ( 143 'META/apkcerts.txt', 144 'META/filesystem_config.txt', 145 'META/root_filesystem_config.txt', 146 'META/update_engine_config.txt', 147 'PRODUCT/*', 148 'ROOT/*', 149 'SYSTEM/*', 150) 151 152# FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST is a list of items to extract from the 153# partial framework target files package that need some special processing, such 154# as some sort of combination with items from the partial vendor target files 155# package. 156 157FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST = ('META/*',) 158 159# DEFAULT_FRAMEWORK_MISC_INFO_KEYS is a list of keys to obtain from the 160# framework instance of META/misc_info.txt. The remaining keys from the 161# vendor instance. 162 163DEFAULT_FRAMEWORK_MISC_INFO_KEYS = ( 164 'avb_system_hashtree_enable', 165 'avb_system_add_hashtree_footer_args', 166 'avb_system_key_path', 167 'avb_system_algorithm', 168 'avb_system_rollback_index_location', 169 'avb_product_hashtree_enable', 170 'avb_product_add_hashtree_footer_args', 171 'avb_system_ext_hashtree_enable', 172 'avb_system_ext_add_hashtree_footer_args', 173 'system_root_image', 174 'root_dir', 175 'ab_update', 176 'default_system_dev_certificate', 177 'system_size', 178 'building_system_image', 179 'building_system_ext_image', 180 'building_product_image', 181) 182 183# DEFAULT_VENDOR_ITEM_LIST is a list of items to extract from the partial 184# vendor target files package as is, meaning these items will land in the output 185# target files package exactly as they appear in the input partial vendor target 186# files package. 187 188DEFAULT_VENDOR_ITEM_LIST = ( 189 'META/boot_filesystem_config.txt', 190 'META/otakeys.txt', 191 'META/releasetools.py', 192 'META/vendor_filesystem_config.txt', 193 'BOOT/*', 194 'DATA/*', 195 'ODM/*', 196 'OTA/android-info.txt', 197 'PREBUILT_IMAGES/*', 198 'RADIO/*', 199 'VENDOR/*', 200 'VENDOR_DLKM/*', 201 'ODM_DLKM/*', 202) 203 204# VENDOR_EXTRACT_SPECIAL_ITEM_LIST is a list of items to extract from the 205# partial vendor target files package that need some special processing, such as 206# some sort of combination with items from the partial framework target files 207# package. 208 209VENDOR_EXTRACT_SPECIAL_ITEM_LIST = ('META/*',) 210 211# The merge config lists should not attempt to extract items from both 212# builds for any of the following partitions. The partitions in 213# SINGLE_BUILD_PARTITIONS should come entirely from a single build (either 214# framework or vendor, but not both). 215 216SINGLE_BUILD_PARTITIONS = ( 217 'BOOT/', 218 'DATA/', 219 'ODM/', 220 'PRODUCT/', 221 'SYSTEM_EXT/', 222 'RADIO/', 223 'RECOVERY/', 224 'ROOT/', 225 'SYSTEM/', 226 'SYSTEM_OTHER/', 227 'VENDOR/', 228 'VENDOR_DLKM/', 229 'ODM_DLKM/', 230) 231 232 233def write_sorted_data(data, path): 234 """Writes the sorted contents of either a list or dict to file. 235 236 This function sorts the contents of the list or dict and then writes the 237 resulting sorted contents to a file specified by path. 238 239 Args: 240 data: The list or dict to sort and write. 241 path: Path to the file to write the sorted values to. The file at path will 242 be overridden if it exists. 243 """ 244 with open(path, 'w') as output: 245 for entry in sorted(data): 246 out_str = '{}={}\n'.format(entry, data[entry]) if isinstance( 247 data, dict) else '{}\n'.format(entry) 248 output.write(out_str) 249 250 251def extract_items(target_files, target_files_temp_dir, extract_item_list): 252 """Extracts items from target files to temporary directory. 253 254 This function extracts from the specified target files zip archive into the 255 specified temporary directory, the items specified in the extract item list. 256 257 Args: 258 target_files: The target files zip archive from which to extract items. 259 target_files_temp_dir: The temporary directory where the extracted items 260 will land. 261 extract_item_list: A list of items to extract. 262 """ 263 264 logger.info('extracting from %s', target_files) 265 266 # Filter the extract_item_list to remove any items that do not exist in the 267 # zip file. Otherwise, the extraction step will fail. 268 269 with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zipfile: 270 target_files_namelist = target_files_zipfile.namelist() 271 272 filtered_extract_item_list = [] 273 for pattern in extract_item_list: 274 matching_namelist = fnmatch.filter(target_files_namelist, pattern) 275 if not matching_namelist: 276 logger.warning('no match for %s', pattern) 277 else: 278 filtered_extract_item_list.append(pattern) 279 280 # Extract from target_files into target_files_temp_dir the 281 # filtered_extract_item_list. 282 283 common.UnzipToDir(target_files, target_files_temp_dir, 284 filtered_extract_item_list) 285 286 287def copy_items(from_dir, to_dir, patterns): 288 """Similar to extract_items() except uses an input dir instead of zip.""" 289 file_paths = [] 290 for dirpath, _, filenames in os.walk(from_dir): 291 file_paths.extend( 292 os.path.relpath(path=os.path.join(dirpath, filename), start=from_dir) 293 for filename in filenames) 294 295 filtered_file_paths = set() 296 for pattern in patterns: 297 filtered_file_paths.update(fnmatch.filter(file_paths, pattern)) 298 299 for file_path in filtered_file_paths: 300 original_file_path = os.path.join(from_dir, file_path) 301 copied_file_path = os.path.join(to_dir, file_path) 302 copied_file_dir = os.path.dirname(copied_file_path) 303 if not os.path.exists(copied_file_dir): 304 os.makedirs(copied_file_dir) 305 if os.path.islink(original_file_path): 306 os.symlink(os.readlink(original_file_path), copied_file_path) 307 else: 308 shutil.copyfile(original_file_path, copied_file_path) 309 310 311def validate_config_lists(framework_item_list, framework_misc_info_keys, 312 vendor_item_list): 313 """Performs validations on the merge config lists. 314 315 Args: 316 framework_item_list: The list of items to extract from the partial framework 317 target files package as is. 318 framework_misc_info_keys: A list of keys to obtain from the framework 319 instance of META/misc_info.txt. The remaining keys from the vendor 320 instance. 321 vendor_item_list: The list of items to extract from the partial vendor 322 target files package as is. 323 324 Returns: 325 False if a validation fails, otherwise true. 326 """ 327 has_error = False 328 329 default_combined_item_set = set(DEFAULT_FRAMEWORK_ITEM_LIST) 330 default_combined_item_set.update(DEFAULT_VENDOR_ITEM_LIST) 331 332 combined_item_set = set(framework_item_list) 333 combined_item_set.update(vendor_item_list) 334 335 # Check that the merge config lists are not missing any item specified 336 # by the default config lists. 337 difference = default_combined_item_set.difference(combined_item_set) 338 if difference: 339 logger.error('Missing merge config items: %s', list(difference)) 340 logger.error('Please ensure missing items are in either the ' 341 'framework-item-list or vendor-item-list files provided to ' 342 'this script.') 343 has_error = True 344 345 for partition in SINGLE_BUILD_PARTITIONS: 346 in_framework = any( 347 item.startswith(partition) for item in framework_item_list) 348 in_vendor = any(item.startswith(partition) for item in vendor_item_list) 349 if in_framework and in_vendor: 350 logger.error( 351 'Cannot extract items from %s for both the framework and vendor' 352 ' builds. Please ensure only one merge config item list' 353 ' includes %s.', partition, partition) 354 has_error = True 355 356 if ('dynamic_partition_list' in framework_misc_info_keys) or ( 357 'super_partition_groups' in framework_misc_info_keys): 358 logger.error('Dynamic partition misc info keys should come from ' 359 'the vendor instance of META/misc_info.txt.') 360 has_error = True 361 362 return not has_error 363 364 365def process_ab_partitions_txt(framework_target_files_temp_dir, 366 vendor_target_files_temp_dir, 367 output_target_files_temp_dir): 368 """Performs special processing for META/ab_partitions.txt. 369 370 This function merges the contents of the META/ab_partitions.txt files from the 371 framework directory and the vendor directory, placing the merged result in the 372 output directory. The precondition in that the files are already extracted. 373 The post condition is that the output META/ab_partitions.txt contains the 374 merged content. The format for each ab_partitions.txt a one partition name per 375 line. The output file contains the union of the parition names. 376 377 Args: 378 framework_target_files_temp_dir: The name of a directory containing the 379 special items extracted from the framework target files package. 380 vendor_target_files_temp_dir: The name of a directory containing the special 381 items extracted from the vendor target files package. 382 output_target_files_temp_dir: The name of a directory that will be used to 383 create the output target files package after all the special cases are 384 processed. 385 """ 386 387 framework_ab_partitions_txt = os.path.join(framework_target_files_temp_dir, 388 'META', 'ab_partitions.txt') 389 390 vendor_ab_partitions_txt = os.path.join(vendor_target_files_temp_dir, 'META', 391 'ab_partitions.txt') 392 393 with open(framework_ab_partitions_txt) as f: 394 framework_ab_partitions = f.read().splitlines() 395 396 with open(vendor_ab_partitions_txt) as f: 397 vendor_ab_partitions = f.read().splitlines() 398 399 output_ab_partitions = set(framework_ab_partitions + vendor_ab_partitions) 400 401 output_ab_partitions_txt = os.path.join(output_target_files_temp_dir, 'META', 402 'ab_partitions.txt') 403 404 write_sorted_data(data=output_ab_partitions, path=output_ab_partitions_txt) 405 406 407def process_misc_info_txt(framework_target_files_temp_dir, 408 vendor_target_files_temp_dir, 409 output_target_files_temp_dir, 410 framework_misc_info_keys): 411 """Performs special processing for META/misc_info.txt. 412 413 This function merges the contents of the META/misc_info.txt files from the 414 framework directory and the vendor directory, placing the merged result in the 415 output directory. The precondition in that the files are already extracted. 416 The post condition is that the output META/misc_info.txt contains the merged 417 content. 418 419 Args: 420 framework_target_files_temp_dir: The name of a directory containing the 421 special items extracted from the framework target files package. 422 vendor_target_files_temp_dir: The name of a directory containing the special 423 items extracted from the vendor target files package. 424 output_target_files_temp_dir: The name of a directory that will be used to 425 create the output target files package after all the special cases are 426 processed. 427 framework_misc_info_keys: A list of keys to obtain from the framework 428 instance of META/misc_info.txt. The remaining keys from the vendor 429 instance. 430 """ 431 432 misc_info_path = ['META', 'misc_info.txt'] 433 framework_dict = common.LoadDictionaryFromFile( 434 os.path.join(framework_target_files_temp_dir, *misc_info_path)) 435 436 # We take most of the misc info from the vendor target files. 437 438 merged_dict = common.LoadDictionaryFromFile( 439 os.path.join(vendor_target_files_temp_dir, *misc_info_path)) 440 441 # Replace certain values in merged_dict with values from 442 # framework_dict. 443 444 for key in framework_misc_info_keys: 445 merged_dict[key] = framework_dict[key] 446 447 # Merge misc info keys used for Dynamic Partitions. 448 if (merged_dict.get('use_dynamic_partitions') == 'true') and ( 449 framework_dict.get('use_dynamic_partitions') == 'true'): 450 merged_dynamic_partitions_dict = common.MergeDynamicPartitionInfoDicts( 451 framework_dict=framework_dict, vendor_dict=merged_dict) 452 merged_dict.update(merged_dynamic_partitions_dict) 453 # Ensure that add_img_to_target_files rebuilds super split images for 454 # devices that retrofit dynamic partitions. This flag may have been set to 455 # false in the partial builds to prevent duplicate building of super.img. 456 merged_dict['build_super_partition'] = 'true' 457 458 # Replace <image>_selinux_fc values with framework or vendor file_contexts.bin 459 # depending on which dictionary the key came from. 460 # Only the file basename is required because all selinux_fc properties are 461 # replaced with the full path to the file under META/ when misc_info.txt is 462 # loaded from target files for repacking. See common.py LoadInfoDict(). 463 for key in merged_dict: 464 if key.endswith('_selinux_fc'): 465 merged_dict[key] = 'vendor_file_contexts.bin' 466 for key in framework_dict: 467 if key.endswith('_selinux_fc'): 468 merged_dict[key] = 'framework_file_contexts.bin' 469 470 output_misc_info_txt = os.path.join(output_target_files_temp_dir, 'META', 471 'misc_info.txt') 472 write_sorted_data(data=merged_dict, path=output_misc_info_txt) 473 474 475def process_dynamic_partitions_info_txt(framework_target_files_dir, 476 vendor_target_files_dir, 477 output_target_files_dir): 478 """Performs special processing for META/dynamic_partitions_info.txt. 479 480 This function merges the contents of the META/dynamic_partitions_info.txt 481 files from the framework directory and the vendor directory, placing the 482 merged result in the output directory. 483 484 This function does nothing if META/dynamic_partitions_info.txt from the vendor 485 directory does not exist. 486 487 Args: 488 framework_target_files_dir: The name of a directory containing the special 489 items extracted from the framework target files package. 490 vendor_target_files_dir: The name of a directory containing the special 491 items extracted from the vendor target files package. 492 output_target_files_dir: The name of a directory that will be used to create 493 the output target files package after all the special cases are processed. 494 """ 495 496 if not os.path.exists( 497 os.path.join(vendor_target_files_dir, 'META', 498 'dynamic_partitions_info.txt')): 499 return 500 501 dynamic_partitions_info_path = ['META', 'dynamic_partitions_info.txt'] 502 503 framework_dynamic_partitions_dict = common.LoadDictionaryFromFile( 504 os.path.join(framework_target_files_dir, *dynamic_partitions_info_path)) 505 vendor_dynamic_partitions_dict = common.LoadDictionaryFromFile( 506 os.path.join(vendor_target_files_dir, *dynamic_partitions_info_path)) 507 508 merged_dynamic_partitions_dict = common.MergeDynamicPartitionInfoDicts( 509 framework_dict=framework_dynamic_partitions_dict, 510 vendor_dict=vendor_dynamic_partitions_dict) 511 512 output_dynamic_partitions_info_txt = os.path.join( 513 output_target_files_dir, 'META', 'dynamic_partitions_info.txt') 514 write_sorted_data( 515 data=merged_dynamic_partitions_dict, 516 path=output_dynamic_partitions_info_txt) 517 518 519def item_list_to_partition_set(item_list): 520 """Converts a target files item list to a partition set. 521 522 The item list contains items that might look like 'SYSTEM/*' or 'VENDOR/*' or 523 'OTA/android-info.txt'. Items that end in '/*' are assumed to match entire 524 directories where 'SYSTEM' or 'VENDOR' is a directory name that identifies the 525 contents of a partition of the same name. Other items in the list, such as the 526 'OTA' example contain metadata. This function iterates such a list, returning 527 a set that contains the partition entries. 528 529 Args: 530 item_list: A list of items in a target files package. 531 Returns: 532 A set of partitions extracted from the list of items. 533 """ 534 535 partition_set = set() 536 537 for item in item_list: 538 match = PARTITION_ITEM_PATTERN.search(item.strip()) 539 partition_tag = match.group(1).lower() if match else None 540 541 if partition_tag: 542 partition_set.add(partition_tag) 543 544 return partition_set 545 546 547def process_apex_keys_apk_certs_common(framework_target_files_dir, 548 vendor_target_files_dir, 549 output_target_files_dir, 550 framework_partition_set, 551 vendor_partition_set, file_name): 552 553 """Performs special processing for META/apexkeys.txt or META/apkcerts.txt. 554 555 This function merges the contents of the META/apexkeys.txt or 556 META/apkcerts.txt files from the framework directory and the vendor directory, 557 placing the merged result in the output directory. The precondition in that 558 the files are already extracted. The post condition is that the output 559 META/apexkeys.txt or META/apkcerts.txt contains the merged content. 560 561 Args: 562 framework_target_files_dir: The name of a directory containing the special 563 items extracted from the framework target files package. 564 vendor_target_files_dir: The name of a directory containing the special 565 items extracted from the vendor target files package. 566 output_target_files_dir: The name of a directory that will be used to create 567 the output target files package after all the special cases are processed. 568 framework_partition_set: Partitions that are considered framework 569 partitions. Used to filter apexkeys.txt and apkcerts.txt. 570 vendor_partition_set: Partitions that are considered vendor partitions. Used 571 to filter apexkeys.txt and apkcerts.txt. 572 file_name: The name of the file to merge. One of apkcerts.txt or 573 apexkeys.txt. 574 """ 575 576 def read_helper(d): 577 temp = {} 578 file_path = os.path.join(d, 'META', file_name) 579 with open(file_path) as f: 580 for line in f: 581 if line.strip(): 582 name = line.split()[0] 583 match = MODULE_KEY_PATTERN.search(name) 584 temp[match.group(1)] = line.strip() 585 return temp 586 587 framework_dict = read_helper(framework_target_files_dir) 588 vendor_dict = read_helper(vendor_target_files_dir) 589 merged_dict = {} 590 591 def filter_into_merged_dict(item_dict, partition_set): 592 for key, value in item_dict.items(): 593 match = PARTITION_TAG_PATTERN.search(value) 594 595 if match is None: 596 raise ValueError('Entry missing partition tag: %s' % value) 597 598 partition_tag = match.group(1) 599 600 if partition_tag in partition_set: 601 if key in merged_dict: 602 raise ValueError('Duplicate key %s' % key) 603 604 merged_dict[key] = value 605 606 filter_into_merged_dict(framework_dict, framework_partition_set) 607 filter_into_merged_dict(vendor_dict, vendor_partition_set) 608 609 output_file = os.path.join(output_target_files_dir, 'META', file_name) 610 611 # The following code is similar to write_sorted_data, but different enough 612 # that we couldn't use that function. We need the output to be sorted by the 613 # basename of the apex/apk (without the ".apex" or ".apk" suffix). This 614 # allows the sort to be consistent with the framework/vendor input data and 615 # eases comparison of input data with merged data. 616 with open(output_file, 'w') as output: 617 for key in sorted(merged_dict.keys()): 618 out_str = merged_dict[key] + '\n' 619 output.write(out_str) 620 621 622def copy_file_contexts(framework_target_files_dir, vendor_target_files_dir, 623 output_target_files_dir): 624 """Creates named copies of each build's file_contexts.bin in output META/.""" 625 framework_fc_path = os.path.join(framework_target_files_dir, 'META', 626 'framework_file_contexts.bin') 627 if not os.path.exists(framework_fc_path): 628 framework_fc_path = os.path.join(framework_target_files_dir, 'META', 629 'file_contexts.bin') 630 if not os.path.exists(framework_fc_path): 631 raise ValueError('Missing framework file_contexts.bin.') 632 shutil.copyfile( 633 framework_fc_path, 634 os.path.join(output_target_files_dir, 'META', 635 'framework_file_contexts.bin')) 636 637 vendor_fc_path = os.path.join(vendor_target_files_dir, 'META', 638 'vendor_file_contexts.bin') 639 if not os.path.exists(vendor_fc_path): 640 vendor_fc_path = os.path.join(vendor_target_files_dir, 'META', 641 'file_contexts.bin') 642 if not os.path.exists(vendor_fc_path): 643 raise ValueError('Missing vendor file_contexts.bin.') 644 shutil.copyfile( 645 vendor_fc_path, 646 os.path.join(output_target_files_dir, 'META', 'vendor_file_contexts.bin')) 647 648 649def process_special_cases(framework_target_files_temp_dir, 650 vendor_target_files_temp_dir, 651 output_target_files_temp_dir, 652 framework_misc_info_keys, 653 framework_partition_set, 654 vendor_partition_set): 655 """Performs special-case processing for certain target files items. 656 657 Certain files in the output target files package require special-case 658 processing. This function performs all that special-case processing. 659 660 Args: 661 framework_target_files_temp_dir: The name of a directory containing the 662 special items extracted from the framework target files package. 663 vendor_target_files_temp_dir: The name of a directory containing the special 664 items extracted from the vendor target files package. 665 output_target_files_temp_dir: The name of a directory that will be used to 666 create the output target files package after all the special cases are 667 processed. 668 framework_misc_info_keys: A list of keys to obtain from the framework 669 instance of META/misc_info.txt. The remaining keys from the vendor 670 instance. 671 framework_partition_set: Partitions that are considered framework 672 partitions. Used to filter apexkeys.txt and apkcerts.txt. 673 vendor_partition_set: Partitions that are considered vendor partitions. Used 674 to filter apexkeys.txt and apkcerts.txt. 675 """ 676 677 if 'ab_update' in framework_misc_info_keys: 678 process_ab_partitions_txt( 679 framework_target_files_temp_dir=framework_target_files_temp_dir, 680 vendor_target_files_temp_dir=vendor_target_files_temp_dir, 681 output_target_files_temp_dir=output_target_files_temp_dir) 682 683 copy_file_contexts( 684 framework_target_files_dir=framework_target_files_temp_dir, 685 vendor_target_files_dir=vendor_target_files_temp_dir, 686 output_target_files_dir=output_target_files_temp_dir) 687 688 process_misc_info_txt( 689 framework_target_files_temp_dir=framework_target_files_temp_dir, 690 vendor_target_files_temp_dir=vendor_target_files_temp_dir, 691 output_target_files_temp_dir=output_target_files_temp_dir, 692 framework_misc_info_keys=framework_misc_info_keys) 693 694 process_dynamic_partitions_info_txt( 695 framework_target_files_dir=framework_target_files_temp_dir, 696 vendor_target_files_dir=vendor_target_files_temp_dir, 697 output_target_files_dir=output_target_files_temp_dir) 698 699 process_apex_keys_apk_certs_common( 700 framework_target_files_dir=framework_target_files_temp_dir, 701 vendor_target_files_dir=vendor_target_files_temp_dir, 702 output_target_files_dir=output_target_files_temp_dir, 703 framework_partition_set=framework_partition_set, 704 vendor_partition_set=vendor_partition_set, 705 file_name='apkcerts.txt') 706 707 process_apex_keys_apk_certs_common( 708 framework_target_files_dir=framework_target_files_temp_dir, 709 vendor_target_files_dir=vendor_target_files_temp_dir, 710 output_target_files_dir=output_target_files_temp_dir, 711 framework_partition_set=framework_partition_set, 712 vendor_partition_set=vendor_partition_set, 713 file_name='apexkeys.txt') 714 715 716def files_from_path(target_path, extra_args=None): 717 """Gets files under given path. 718 719 Get (sub)files from given target path and return sorted list. 720 721 Args: 722 target_path: Target path to get subfiles. 723 extra_args: List of extra argument for find command. Optional. 724 725 Returns: 726 Sorted files and directories list. 727 """ 728 729 find_command = ['find', target_path] + (extra_args or []) 730 find_process = common.Run(find_command, stdout=subprocess.PIPE, verbose=False) 731 return common.RunAndCheckOutput(['sort'], 732 stdin=find_process.stdout, 733 verbose=False) 734 735 736def create_merged_package(temp_dir, framework_target_files, framework_item_list, 737 vendor_target_files, vendor_item_list, 738 framework_misc_info_keys, rebuild_recovery): 739 """Merges two target files packages into one target files structure. 740 741 Args: 742 temp_dir: The name of a directory we use when we extract items from the 743 input target files packages, and also a scratch directory that we use for 744 temporary files. 745 framework_target_files: The name of the zip archive containing the framework 746 partial target files package. 747 framework_item_list: The list of items to extract from the partial framework 748 target files package as is, meaning these items will land in the output 749 target files package exactly as they appear in the input partial framework 750 target files package. 751 vendor_target_files: The name of the zip archive containing the vendor 752 partial target files package. 753 vendor_item_list: The list of items to extract from the partial vendor 754 target files package as is, meaning these items will land in the output 755 target files package exactly as they appear in the input partial vendor 756 target files package. 757 framework_misc_info_keys: The list of keys to obtain from the framework 758 instance of META/misc_info.txt. The remaining keys from the vendor 759 instance. 760 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B 761 devices and write it to the system image. 762 763 Returns: 764 Path to merged package under temp directory. 765 """ 766 767 # Create directory names that we'll use when we extract files from framework, 768 # and vendor, and for zipping the final output. 769 770 framework_target_files_temp_dir = os.path.join(temp_dir, 'framework') 771 vendor_target_files_temp_dir = os.path.join(temp_dir, 'vendor') 772 output_target_files_temp_dir = os.path.join(temp_dir, 'output') 773 774 # Extract "as is" items from the input framework partial target files package. 775 # We extract them directly into the output temporary directory since the 776 # items do not need special case processing. 777 778 extract_items( 779 target_files=framework_target_files, 780 target_files_temp_dir=output_target_files_temp_dir, 781 extract_item_list=framework_item_list) 782 783 # Extract "as is" items from the input vendor partial target files package. We 784 # extract them directly into the output temporary directory since the items 785 # do not need special case processing. 786 787 extract_items( 788 target_files=vendor_target_files, 789 target_files_temp_dir=output_target_files_temp_dir, 790 extract_item_list=vendor_item_list) 791 792 # Extract "special" items from the input framework partial target files 793 # package. We extract these items to different directory since they require 794 # special processing before they will end up in the output directory. 795 796 extract_items( 797 target_files=framework_target_files, 798 target_files_temp_dir=framework_target_files_temp_dir, 799 extract_item_list=FRAMEWORK_EXTRACT_SPECIAL_ITEM_LIST) 800 801 # Extract "special" items from the input vendor partial target files package. 802 # We extract these items to different directory since they require special 803 # processing before they will end up in the output directory. 804 805 extract_items( 806 target_files=vendor_target_files, 807 target_files_temp_dir=vendor_target_files_temp_dir, 808 extract_item_list=VENDOR_EXTRACT_SPECIAL_ITEM_LIST) 809 810 # Now that the temporary directories contain all the extracted files, perform 811 # special case processing on any items that need it. After this function 812 # completes successfully, all the files we need to create the output target 813 # files package are in place. 814 815 process_special_cases( 816 framework_target_files_temp_dir=framework_target_files_temp_dir, 817 vendor_target_files_temp_dir=vendor_target_files_temp_dir, 818 output_target_files_temp_dir=output_target_files_temp_dir, 819 framework_misc_info_keys=framework_misc_info_keys, 820 framework_partition_set=item_list_to_partition_set(framework_item_list), 821 vendor_partition_set=item_list_to_partition_set(vendor_item_list)) 822 823 return output_target_files_temp_dir 824 825 826def generate_images(target_files_dir, rebuild_recovery): 827 """Generate images from target files. 828 829 This function takes merged output temporary directory and create images 830 from it. 831 832 Args: 833 target_files_dir: Path to merged temp directory. 834 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B 835 devices and write it to the system image. 836 """ 837 838 # Regenerate IMAGES in the target directory. 839 840 add_img_args = ['--verbose'] 841 add_img_args.append('--add_missing') 842 # TODO(b/132730255): Remove this if statement. 843 if rebuild_recovery: 844 add_img_args.append('--rebuild_recovery') 845 add_img_args.append(target_files_dir) 846 847 add_img_to_target_files.main(add_img_args) 848 849 850def generate_super_empty_image(target_dir, output_super_empty): 851 """Generates super_empty image from target package. 852 853 Args: 854 target_dir: Path to the target file package which contains misc_info.txt for 855 detailed information for super image. 856 output_super_empty: If provided, copies a super_empty.img file from the 857 target files package to this path. 858 """ 859 # Create super_empty.img using the merged misc_info.txt. 860 861 misc_info_txt = os.path.join(target_dir, 'META', 'misc_info.txt') 862 863 use_dynamic_partitions = common.LoadDictionaryFromFile(misc_info_txt).get( 864 'use_dynamic_partitions') 865 866 if use_dynamic_partitions != 'true' and output_super_empty: 867 raise ValueError( 868 'Building super_empty.img requires use_dynamic_partitions=true.') 869 elif use_dynamic_partitions == 'true': 870 super_empty_img = os.path.join(target_dir, 'IMAGES', 'super_empty.img') 871 build_super_image_args = [ 872 misc_info_txt, 873 super_empty_img, 874 ] 875 build_super_image.main(build_super_image_args) 876 877 # Copy super_empty.img to the user-provided output_super_empty location. 878 if output_super_empty: 879 shutil.copyfile(super_empty_img, output_super_empty) 880 881 882def create_target_files_archive(output_file, source_dir, temp_dir): 883 """Creates archive from target package. 884 885 Args: 886 output_file: The name of the zip archive target files package. 887 source_dir: The target directory contains package to be archived. 888 temp_dir: Path to temporary directory for any intermediate files. 889 """ 890 output_target_files_list = os.path.join(temp_dir, 'output.list') 891 output_zip = os.path.abspath(output_file) 892 output_target_files_meta_dir = os.path.join(source_dir, 'META') 893 894 meta_content = files_from_path(output_target_files_meta_dir) 895 other_content = files_from_path( 896 source_dir, 897 ['-path', output_target_files_meta_dir, '-prune', '-o', '-print']) 898 899 with open(output_target_files_list, 'w') as f: 900 f.write(meta_content) 901 f.write(other_content) 902 903 command = [ 904 'soong_zip', 905 '-d', 906 '-o', 907 output_zip, 908 '-C', 909 source_dir, 910 '-l', 911 output_target_files_list, 912 ] 913 914 logger.info('creating %s', output_file) 915 common.RunAndWait(command, verbose=True) 916 logger.info('finished creating %s', output_file) 917 918 return output_zip 919 920 921def merge_target_files(temp_dir, framework_target_files, framework_item_list, 922 framework_misc_info_keys, vendor_target_files, 923 vendor_item_list, output_target_files, output_dir, 924 output_item_list, output_ota, output_img, 925 output_super_empty, rebuild_recovery): 926 """Merges two target files packages together. 927 928 This function takes framework and vendor target files packages as input, 929 performs various file extractions, special case processing, and finally 930 creates a merged zip archive as output. 931 932 Args: 933 temp_dir: The name of a directory we use when we extract items from the 934 input target files packages, and also a scratch directory that we use for 935 temporary files. 936 framework_target_files: The name of the zip archive containing the framework 937 partial target files package. 938 framework_item_list: The list of items to extract from the partial framework 939 target files package as is, meaning these items will land in the output 940 target files package exactly as they appear in the input partial framework 941 target files package. 942 framework_misc_info_keys: The list of keys to obtain from the framework 943 instance of META/misc_info.txt. The remaining keys from the vendor 944 instance. 945 vendor_target_files: The name of the zip archive containing the vendor 946 partial target files package. 947 vendor_item_list: The list of items to extract from the partial vendor 948 target files package as is, meaning these items will land in the output 949 target files package exactly as they appear in the input partial vendor 950 target files package. 951 output_target_files: The name of the output zip archive target files package 952 created by merging framework and vendor. 953 output_dir: The destination directory for saving merged files. 954 output_item_list: The list of items to copy into the output_dir. 955 output_ota: The name of the output zip archive ota package. 956 output_img: The name of the output zip archive img package. 957 output_super_empty: If provided, creates a super_empty.img file from the 958 merged target files package and saves it at this path. 959 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B 960 devices and write it to the system image. 961 """ 962 963 logger.info('starting: merge framework %s and vendor %s into output %s', 964 framework_target_files, vendor_target_files, output_target_files) 965 966 output_target_files_temp_dir = create_merged_package( 967 temp_dir, framework_target_files, framework_item_list, 968 vendor_target_files, vendor_item_list, framework_misc_info_keys, 969 rebuild_recovery) 970 971 if not check_target_files_vintf.CheckVintf(output_target_files_temp_dir): 972 raise RuntimeError("Incompatible VINTF metadata") 973 974 generate_images(output_target_files_temp_dir, rebuild_recovery) 975 976 generate_super_empty_image(output_target_files_temp_dir, output_super_empty) 977 978 # Finally, create the output target files zip archive and/or copy the 979 # output items to the output target files directory. 980 981 if output_dir: 982 copy_items(output_target_files_temp_dir, output_dir, output_item_list) 983 984 if not output_target_files: 985 return 986 987 output_zip = create_target_files_archive(output_target_files, 988 output_target_files_temp_dir, 989 temp_dir) 990 991 # Create the IMG package from the merged target files package. 992 993 if output_img: 994 img_from_target_files.main([output_zip, output_img]) 995 996 # Create the OTA package from the merged target files package. 997 998 if output_ota: 999 ota_from_target_files.main([output_zip, output_ota]) 1000 1001 1002def call_func_with_temp_dir(func, keep_tmp): 1003 """Manages the creation and cleanup of the temporary directory. 1004 1005 This function calls the given function after first creating a temporary 1006 directory. It also cleans up the temporary directory. 1007 1008 Args: 1009 func: The function to call. Should accept one parameter, the path to the 1010 temporary directory. 1011 keep_tmp: Keep the temporary directory after processing is complete. 1012 """ 1013 1014 # Create a temporary directory. This will serve as the parent of directories 1015 # we use when we extract items from the input target files packages, and also 1016 # a scratch directory that we use for temporary files. 1017 1018 temp_dir = common.MakeTempDir(prefix='merge_target_files_') 1019 1020 try: 1021 func(temp_dir) 1022 finally: 1023 if keep_tmp: 1024 logger.info('keeping %s', temp_dir) 1025 else: 1026 common.Cleanup() 1027 1028 1029def main(): 1030 """The main function. 1031 1032 Process command line arguments, then call merge_target_files to 1033 perform the heavy lifting. 1034 """ 1035 1036 common.InitLogging() 1037 1038 def option_handler(o, a): 1039 if o == '--system-target-files': 1040 logger.warning( 1041 '--system-target-files has been renamed to --framework-target-files') 1042 OPTIONS.framework_target_files = a 1043 elif o == '--framework-target-files': 1044 OPTIONS.framework_target_files = a 1045 elif o == '--system-item-list': 1046 logger.warning( 1047 '--system-item-list has been renamed to --framework-item-list') 1048 OPTIONS.framework_item_list = a 1049 elif o == '--framework-item-list': 1050 OPTIONS.framework_item_list = a 1051 elif o == '--system-misc-info-keys': 1052 logger.warning('--system-misc-info-keys has been renamed to ' 1053 '--framework-misc-info-keys') 1054 OPTIONS.framework_misc_info_keys = a 1055 elif o == '--framework-misc-info-keys': 1056 OPTIONS.framework_misc_info_keys = a 1057 elif o == '--other-target-files': 1058 logger.warning( 1059 '--other-target-files has been renamed to --vendor-target-files') 1060 OPTIONS.vendor_target_files = a 1061 elif o == '--vendor-target-files': 1062 OPTIONS.vendor_target_files = a 1063 elif o == '--other-item-list': 1064 logger.warning('--other-item-list has been renamed to --vendor-item-list') 1065 OPTIONS.vendor_item_list = a 1066 elif o == '--vendor-item-list': 1067 OPTIONS.vendor_item_list = a 1068 elif o == '--output-target-files': 1069 OPTIONS.output_target_files = a 1070 elif o == '--output-dir': 1071 OPTIONS.output_dir = a 1072 elif o == '--output-item-list': 1073 OPTIONS.output_item_list = a 1074 elif o == '--output-ota': 1075 OPTIONS.output_ota = a 1076 elif o == '--output-img': 1077 OPTIONS.output_img = a 1078 elif o == '--output-super-empty': 1079 OPTIONS.output_super_empty = a 1080 elif o == '--rebuild_recovery': # TODO(b/132730255): Warn 1081 OPTIONS.rebuild_recovery = True 1082 elif o == '--keep-tmp': 1083 OPTIONS.keep_tmp = True 1084 else: 1085 return False 1086 return True 1087 1088 args = common.ParseOptions( 1089 sys.argv[1:], 1090 __doc__, 1091 extra_long_opts=[ 1092 'system-target-files=', 1093 'framework-target-files=', 1094 'system-item-list=', 1095 'framework-item-list=', 1096 'system-misc-info-keys=', 1097 'framework-misc-info-keys=', 1098 'other-target-files=', 1099 'vendor-target-files=', 1100 'other-item-list=', 1101 'vendor-item-list=', 1102 'output-target-files=', 1103 'output-dir=', 1104 'output-item-list=', 1105 'output-ota=', 1106 'output-img=', 1107 'output-super-empty=', 1108 'rebuild_recovery', 1109 'keep-tmp', 1110 ], 1111 extra_option_handler=option_handler) 1112 1113 # pylint: disable=too-many-boolean-expressions 1114 if (args or OPTIONS.framework_target_files is None or 1115 OPTIONS.vendor_target_files is None or 1116 (OPTIONS.output_target_files is None and OPTIONS.output_dir is None) or 1117 (OPTIONS.output_dir is not None and OPTIONS.output_item_list is None)): 1118 common.Usage(__doc__) 1119 sys.exit(1) 1120 1121 if OPTIONS.framework_item_list: 1122 framework_item_list = common.LoadListFromFile(OPTIONS.framework_item_list) 1123 else: 1124 framework_item_list = DEFAULT_FRAMEWORK_ITEM_LIST 1125 1126 if OPTIONS.framework_misc_info_keys: 1127 framework_misc_info_keys = common.LoadListFromFile( 1128 OPTIONS.framework_misc_info_keys) 1129 else: 1130 framework_misc_info_keys = DEFAULT_FRAMEWORK_MISC_INFO_KEYS 1131 1132 if OPTIONS.vendor_item_list: 1133 vendor_item_list = common.LoadListFromFile(OPTIONS.vendor_item_list) 1134 else: 1135 vendor_item_list = DEFAULT_VENDOR_ITEM_LIST 1136 1137 if OPTIONS.output_item_list: 1138 output_item_list = common.LoadListFromFile(OPTIONS.output_item_list) 1139 else: 1140 output_item_list = None 1141 1142 if not validate_config_lists( 1143 framework_item_list=framework_item_list, 1144 framework_misc_info_keys=framework_misc_info_keys, 1145 vendor_item_list=vendor_item_list): 1146 sys.exit(1) 1147 1148 call_func_with_temp_dir( 1149 lambda temp_dir: merge_target_files( 1150 temp_dir=temp_dir, 1151 framework_target_files=OPTIONS.framework_target_files, 1152 framework_item_list=framework_item_list, 1153 framework_misc_info_keys=framework_misc_info_keys, 1154 vendor_target_files=OPTIONS.vendor_target_files, 1155 vendor_item_list=vendor_item_list, 1156 output_target_files=OPTIONS.output_target_files, 1157 output_dir=OPTIONS.output_dir, 1158 output_item_list=output_item_list, 1159 output_ota=OPTIONS.output_ota, 1160 output_img=OPTIONS.output_img, 1161 output_super_empty=OPTIONS.output_super_empty, 1162 rebuild_recovery=OPTIONS.rebuild_recovery), OPTIONS.keep_tmp) 1163 1164 1165if __name__ == '__main__': 1166 main() 1167