1#!/usr/bin/env python
2#
3# Copyright (C) 2022 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 input package contains framework files, and the other contains vendor files.
20
21This script produces a complete, merged target files package:
22  - This package can be used to generate a flashable IMG package.
23    See --output-img.
24  - This package can be used to generate an OTA package. See --output-ota.
25  - The merged package is checked for compatibility between the two inputs.
26
27Usage: merge_target_files [args]
28
29  --framework-target-files framework-target-files-package
30      The input target files package containing framework bits. This is a zip
31      archive or a directory.
32
33  --framework-item-list framework-item-list-file
34      The optional path to a newline-separated config file of items that
35      are extracted as-is from the framework target files package.
36
37  --framework-misc-info-keys framework-misc-info-keys-file
38      The optional path to a newline-separated config file of keys to
39      extract from the framework META/misc_info.txt file.
40
41  --vendor-target-files vendor-target-files-package
42      The input target files package containing vendor bits. This is a zip
43      archive or a directory.
44
45  --vendor-item-list vendor-item-list-file
46      The optional path to a newline-separated config file of items that
47      are extracted as-is from the vendor target files package.
48
49  --boot-image-dir-path
50      The input boot image directory path. This path contains IMAGES/boot.img
51      file.
52
53  --output-target-files output-target-files-package
54      If provided, the output merged target files package. Also a zip archive.
55
56  --output-dir output-directory
57      If provided, the destination directory for saving merged files. Requires
58      the --output-item-list flag.
59      Can be provided alongside --output-target-files, or by itself.
60
61  --output-item-list output-item-list-file.
62      The optional path to a newline-separated config file that specifies the
63      file patterns to copy into the --output-dir. Required if providing
64      the --output-dir flag.
65
66  --output-ota output-ota-package
67      The output ota package. This is a zip archive. Use of this flag may
68      require passing the --path common flag; see common.py.
69
70  --output-img output-img-package
71      The output img package, suitable for use with 'fastboot update'. Use of
72      this flag may require passing the --path common flag; see common.py.
73
74  --output-super-empty output-super-empty-image
75      If provided, creates a super_empty.img file from the merged target
76      files package and saves it at this path.
77
78  --rebuild_recovery
79      Copy the recovery image used by non-A/B devices, used when
80      regenerating vendor images with --rebuild-sepolicy.
81
82  --allow-duplicate-apkapex-keys
83      If provided, duplicate APK/APEX keys are ignored and the value from the
84      framework is used.
85
86  --rebuild-sepolicy
87      If provided, rebuilds odm.img or vendor.img to include merged sepolicy
88      files. If odm is present then odm is preferred.
89
90  --vendor-otatools otatools.zip
91      If provided, use this otatools.zip when recompiling the odm or vendor
92      image to include sepolicy.
93
94  --keep-tmp
95      Keep tempoary files for debugging purposes.
96
97  --avb-resolve-rollback-index-location-conflict
98      If provided, resolve the conflict AVB rollback index location when
99      necessary.
100
101  --allow-partial-ab
102      If provided, allow merging non-AB framework target files with AB vendor
103      target files, which means that only the vendor has AB partitions.
104
105  The following only apply when using the VSDK to perform dexopt on vendor apps:
106
107  --framework-dexpreopt-config
108      If provided, the location of framwework's dexpreopt_config.zip.
109
110  --framework-dexpreopt-tools
111      if provided, the location of framework's dexpreopt_tools.zip.
112
113  --vendor-dexpreopt-config
114      If provided, the location of vendor's dexpreopt_config.zip.
115"""
116
117import logging
118import os
119import shutil
120import subprocess
121import sys
122import zipfile
123
124import add_img_to_target_files
125import build_image
126import build_super_image
127import common
128import img_from_target_files
129import merge_compatibility_checks
130import merge_dexopt
131import merge_meta
132import merge_utils
133import ota_from_target_files
134
135from common import ExternalError
136
137logger = logging.getLogger(__name__)
138
139OPTIONS = common.OPTIONS
140# Always turn on verbose logging.
141OPTIONS.verbose = True
142OPTIONS.framework_target_files = None
143OPTIONS.framework_item_list = []
144OPTIONS.framework_misc_info_keys = []
145OPTIONS.vendor_target_files = None
146OPTIONS.vendor_item_list = []
147OPTIONS.boot_image_dir_path = None
148OPTIONS.output_target_files = None
149OPTIONS.output_dir = None
150OPTIONS.output_item_list = []
151OPTIONS.output_ota = None
152OPTIONS.output_img = None
153OPTIONS.output_super_empty = None
154OPTIONS.rebuild_recovery = False
155# TODO(b/150582573): Remove this option.
156OPTIONS.allow_duplicate_apkapex_keys = False
157OPTIONS.vendor_otatools = None
158OPTIONS.rebuild_sepolicy = False
159OPTIONS.keep_tmp = False
160OPTIONS.avb_resolve_rollback_index_location_conflict = False
161OPTIONS.allow_partial_ab = False
162OPTIONS.framework_dexpreopt_config = None
163OPTIONS.framework_dexpreopt_tools = None
164OPTIONS.vendor_dexpreopt_config = None
165
166
167def move_only_exists(source, destination):
168  """Judge whether the file exists and then move the file."""
169
170  if os.path.exists(source):
171    shutil.move(source, destination)
172
173
174def remove_file_if_exists(file_name):
175  """Remove the file if it exists and skip otherwise."""
176
177  try:
178    os.remove(file_name)
179  except FileNotFoundError:
180    pass
181
182
183def include_extra_in_list(item_list):
184  """
185  1. Include all `META/*` files in the item list.
186
187  To ensure that `AddImagesToTargetFiles` can still be used with vendor item
188  list that do not specify all of the required META/ files, those files should
189  be included by default. This preserves the backward compatibility of
190  `rebuild_image_with_sepolicy`.
191
192  2. Include `SYSTEM/build.prop` file in the item list.
193
194  To ensure that `AddImagesToTargetFiles` for GRF vendor images, can still
195  access SYSTEM/build.prop to pass GetPartitionFingerprint check in BuildInfo
196  constructor.
197  """
198  if not item_list:
199    return None
200  return list(item_list) + ['META/*'] + ['SYSTEM/build.prop']
201
202
203def create_merged_package(temp_dir):
204  """Merges two target files packages into one target files structure.
205
206  Returns:
207    Path to merged package under temp directory.
208  """
209  # Extract "as is" items from the input framework and vendor partial target
210  # files packages directly into the output temporary directory, since these
211  # items do not need special case processing.
212
213  output_target_files_temp_dir = os.path.join(temp_dir, 'output')
214  merge_utils.CollectTargetFiles(
215      input_zipfile_or_dir=OPTIONS.framework_target_files,
216      output_dir=output_target_files_temp_dir,
217      item_list=OPTIONS.framework_item_list)
218  merge_utils.CollectTargetFiles(
219      input_zipfile_or_dir=OPTIONS.vendor_target_files,
220      output_dir=output_target_files_temp_dir,
221      item_list=OPTIONS.vendor_item_list)
222
223  if OPTIONS.boot_image_dir_path:
224    merge_utils.CollectTargetFiles(
225        input_zipfile_or_dir=OPTIONS.boot_image_dir_path,
226        output_dir=output_target_files_temp_dir,
227        item_list=['IMAGES/boot.img'])
228
229  # Perform special case processing on META/* items.
230  # After this function completes successfully, all the files we need to create
231  # the output target files package are in place.
232  merge_meta.MergeMetaFiles(
233      temp_dir=temp_dir,
234      merged_dir=output_target_files_temp_dir,
235      framework_partitions=OPTIONS.framework_partition_set)
236
237  merge_dexopt.MergeDexopt(
238      temp_dir=temp_dir, output_target_files_dir=output_target_files_temp_dir)
239
240  return output_target_files_temp_dir
241
242
243def generate_missing_images(target_files_dir):
244  """Generate any missing images from target files."""
245
246  # Regenerate IMAGES in the target directory.
247
248  add_img_args = [
249      '--verbose',
250      '--add_missing',
251  ]
252  if OPTIONS.rebuild_recovery:
253    add_img_args.append('--rebuild_recovery')
254  if OPTIONS.avb_resolve_rollback_index_location_conflict:
255    add_img_args.append('--avb_resolve_rollback_index_location_conflict')
256  add_img_args.append(target_files_dir)
257
258  add_img_to_target_files.main(add_img_args)
259
260
261def rebuild_image_with_sepolicy(target_files_dir):
262  """Rebuilds odm.img or vendor.img to include merged sepolicy files.
263
264  If odm is present then odm is preferred -- otherwise vendor is used.
265  """
266  partition = 'vendor'
267  if os.path.exists(os.path.join(target_files_dir, 'ODM')):
268    partition = 'odm'
269  partition_img = '{}.img'.format(partition)
270  partition_map = '{}.map'.format(partition)
271
272  logger.info('Recompiling %s using the merged sepolicy files.', partition_img)
273
274  # Copy the combined SEPolicy file and framework hashes to the image that is
275  # being rebuilt.
276  def copy_selinux_file(input_path, output_filename):
277    input_filename = os.path.join(target_files_dir, input_path)
278    if not os.path.exists(input_filename):
279      input_filename = input_filename.replace('SYSTEM_EXT/',
280                                              'SYSTEM/system_ext/') \
281          .replace('PRODUCT/', 'SYSTEM/product/')
282      if not os.path.exists(input_filename):
283        logger.info('Skipping copy_selinux_file for %s', input_filename)
284        return
285    shutil.copy(
286        input_filename,
287        os.path.join(target_files_dir, partition.upper(), 'etc/selinux',
288                     output_filename))
289
290  copy_selinux_file('META/combined_sepolicy', 'precompiled_sepolicy')
291  copy_selinux_file('SYSTEM/etc/selinux/plat_sepolicy_and_mapping.sha256',
292                    'precompiled_sepolicy.plat_sepolicy_and_mapping.sha256')
293  copy_selinux_file(
294      'SYSTEM_EXT/etc/selinux/system_ext_sepolicy_and_mapping.sha256',
295      'precompiled_sepolicy.system_ext_sepolicy_and_mapping.sha256')
296  copy_selinux_file('PRODUCT/etc/selinux/product_sepolicy_and_mapping.sha256',
297                    'precompiled_sepolicy.product_sepolicy_and_mapping.sha256')
298
299  if not OPTIONS.vendor_otatools:
300    # Remove the partition from the merged target-files archive. It will be
301    # rebuilt later automatically by generate_missing_images().
302    remove_file_if_exists(
303        os.path.join(target_files_dir, 'IMAGES', partition_img))
304    return
305
306  # TODO(b/192253131): Remove the need for vendor_otatools by fixing
307  # backwards-compatibility issues when compiling images across releases.
308  if not OPTIONS.vendor_target_files:
309    raise ValueError(
310        'Expected vendor_target_files if vendor_otatools is not None.')
311  logger.info(
312      '%s recompilation will be performed using the vendor otatools.zip',
313      partition_img)
314
315  # Unzip the vendor build's otatools.zip and target-files archive.
316  vendor_otatools_dir = common.MakeTempDir(
317      prefix='merge_target_files_vendor_otatools_')
318  vendor_target_files_dir = common.MakeTempDir(
319      prefix='merge_target_files_vendor_target_files_')
320  common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
321  merge_utils.CollectTargetFiles(
322      input_zipfile_or_dir=OPTIONS.vendor_target_files,
323      output_dir=vendor_target_files_dir,
324      item_list=include_extra_in_list(OPTIONS.vendor_item_list))
325
326  # Copy the partition contents from the merged target-files archive to the
327  # vendor target-files archive.
328  shutil.rmtree(os.path.join(vendor_target_files_dir, partition.upper()))
329  shutil.copytree(
330      os.path.join(target_files_dir, partition.upper()),
331      os.path.join(vendor_target_files_dir, partition.upper()),
332      symlinks=True)
333
334  # Delete then rebuild the partition.
335  remove_file_if_exists(
336      os.path.join(vendor_target_files_dir, 'IMAGES', partition_img))
337  rebuild_partition_command = [
338      os.path.join(vendor_otatools_dir, 'bin', 'add_img_to_target_files'),
339      '--verbose',
340      '--add_missing',
341  ]
342  if OPTIONS.rebuild_recovery:
343    rebuild_partition_command.append('--rebuild_recovery')
344  rebuild_partition_command.append(vendor_target_files_dir)
345  logger.info('Recompiling %s: %s', partition_img,
346              ' '.join(rebuild_partition_command))
347  common.RunAndCheckOutput(rebuild_partition_command, verbose=True)
348
349  # Move the newly-created image to the merged target files dir.
350  if not os.path.exists(os.path.join(target_files_dir, 'IMAGES')):
351    os.makedirs(os.path.join(target_files_dir, 'IMAGES'))
352  shutil.move(
353      os.path.join(vendor_target_files_dir, 'IMAGES', partition_img),
354      os.path.join(target_files_dir, 'IMAGES', partition_img))
355  move_only_exists(
356      os.path.join(vendor_target_files_dir, 'IMAGES', partition_map),
357      os.path.join(target_files_dir, 'IMAGES', partition_map))
358
359  def copy_recovery_file(filename):
360    for subdir in ('VENDOR', 'SYSTEM/vendor'):
361      source = os.path.join(vendor_target_files_dir, subdir, filename)
362      if os.path.exists(source):
363        dest = os.path.join(target_files_dir, subdir, filename)
364        shutil.copy(source, dest)
365        return
366    logger.info('Skipping copy_recovery_file for %s, file not found', filename)
367
368  if OPTIONS.rebuild_recovery:
369    copy_recovery_file('etc/recovery.img')
370    copy_recovery_file('bin/install-recovery.sh')
371    copy_recovery_file('recovery-from-boot.p')
372
373
374def generate_super_empty_image(target_dir, output_super_empty):
375  """Generates super_empty image from target package.
376
377  Args:
378    target_dir: Path to the target file package which contains misc_info.txt for
379      detailed information for super image.
380    output_super_empty: If provided, copies a super_empty.img file from the
381      target files package to this path.
382  """
383  # Create super_empty.img using the merged misc_info.txt.
384
385  misc_info_txt = os.path.join(target_dir, 'META', 'misc_info.txt')
386
387  use_dynamic_partitions = common.LoadDictionaryFromFile(misc_info_txt).get(
388      'use_dynamic_partitions')
389
390  if use_dynamic_partitions != 'true' and output_super_empty:
391    raise ValueError(
392        'Building super_empty.img requires use_dynamic_partitions=true.')
393  elif use_dynamic_partitions == 'true':
394    super_empty_img = os.path.join(target_dir, 'IMAGES', 'super_empty.img')
395    build_super_image_args = [
396        misc_info_txt,
397        super_empty_img,
398    ]
399    build_super_image.main(build_super_image_args)
400
401    # Copy super_empty.img to the user-provided output_super_empty location.
402    if output_super_empty:
403      shutil.copyfile(super_empty_img, output_super_empty)
404
405
406def create_target_files_archive(output_zip, source_dir, temp_dir):
407  """Creates a target_files zip archive from the input source dir.
408
409  Args:
410    output_zip: The name of the zip archive target files package.
411    source_dir: The target directory contains package to be archived.
412    temp_dir: Path to temporary directory for any intermediate files.
413  """
414  output_target_files_list = os.path.join(temp_dir, 'output.list')
415  output_target_files_meta_dir = os.path.join(source_dir, 'META')
416
417  def files_from_path(target_path, extra_args=None):
418    """Gets files under the given path and return a sorted list."""
419    find_command = ['find', target_path] + (extra_args or [])
420    find_process = common.Run(
421        find_command, stdout=subprocess.PIPE, verbose=False)
422    return common.RunAndCheckOutput(['sort'],
423                                    stdin=find_process.stdout,
424                                    verbose=False)
425
426  # META content appears first in the zip. This is done by the
427  # standard build system for optimized extraction of those files,
428  # so we do the same step for merged target_files.zips here too.
429  meta_content = files_from_path(output_target_files_meta_dir)
430  other_content = files_from_path(
431      source_dir,
432      ['-path', output_target_files_meta_dir, '-prune', '-o', '-print'])
433
434  with open(output_target_files_list, 'w') as f:
435    f.write(meta_content)
436    f.write(other_content)
437
438  command = [
439      'soong_zip',
440      '-d',
441      '-o',
442      os.path.abspath(output_zip),
443      '-C',
444      source_dir,
445      '-r',
446      output_target_files_list,
447  ]
448
449  logger.info('creating %s', output_zip)
450  common.RunAndCheckOutput(command, verbose=True)
451  logger.info('finished creating %s', output_zip)
452
453
454def merge_target_files(temp_dir):
455  """Merges two target files packages together.
456
457  This function uses framework and vendor target files packages as input,
458  performs various file extractions, special case processing, and finally
459  creates a merged zip archive as output.
460
461  Args:
462    temp_dir: The name of a directory we use when we extract items from the
463      input target files packages, and also a scratch directory that we use for
464      temporary files.
465  """
466
467  logger.info('starting: merge framework %s and vendor %s into output %s',
468              OPTIONS.framework_target_files, OPTIONS.vendor_target_files,
469              OPTIONS.output_target_files)
470
471  output_target_files_temp_dir = create_merged_package(temp_dir)
472
473  partition_map = common.PartitionMapFromTargetFiles(
474      output_target_files_temp_dir)
475
476  compatibility_errors = merge_compatibility_checks.CheckCompatibility(
477      target_files_dir=output_target_files_temp_dir,
478      partition_map=partition_map)
479  if compatibility_errors:
480    for error in compatibility_errors:
481      logger.error(error)
482    raise ExternalError(
483        'Found incompatibilities in the merged target files package.')
484
485  # Include the compiled policy in an image if requested.
486  if OPTIONS.rebuild_sepolicy:
487    rebuild_image_with_sepolicy(output_target_files_temp_dir)
488
489  generate_missing_images(output_target_files_temp_dir)
490
491  generate_super_empty_image(output_target_files_temp_dir,
492                             OPTIONS.output_super_empty)
493
494  # Finally, create the output target files zip archive and/or copy the
495  # output items to the output target files directory.
496
497  if OPTIONS.output_dir:
498    merge_utils.CopyItems(output_target_files_temp_dir, OPTIONS.output_dir,
499                          OPTIONS.output_item_list)
500
501  if not OPTIONS.output_target_files:
502    return
503
504  create_target_files_archive(OPTIONS.output_target_files,
505                              output_target_files_temp_dir, temp_dir)
506
507  # Create the IMG package from the merged target files package.
508  if OPTIONS.output_img:
509    img_from_target_files.main(
510        [OPTIONS.output_target_files, OPTIONS.output_img])
511
512  # Create the OTA package from the merged target files package.
513
514  if OPTIONS.output_ota:
515    ota_from_target_files.main(
516        [OPTIONS.output_target_files, OPTIONS.output_ota])
517
518
519def main():
520  """The main function.
521
522  Process command line arguments, then call merge_target_files to
523  perform the heavy lifting.
524  """
525
526  common.InitLogging()
527
528  def option_handler(o, a):
529    if o == '--system-target-files':
530      logger.warning(
531          '--system-target-files has been renamed to --framework-target-files')
532      OPTIONS.framework_target_files = a
533    elif o == '--framework-target-files':
534      OPTIONS.framework_target_files = a
535    elif o == '--system-item-list':
536      logger.warning(
537          '--system-item-list has been renamed to --framework-item-list')
538      OPTIONS.framework_item_list = a
539    elif o == '--framework-item-list':
540      OPTIONS.framework_item_list = a
541    elif o == '--system-misc-info-keys':
542      logger.warning('--system-misc-info-keys has been renamed to '
543                     '--framework-misc-info-keys')
544      OPTIONS.framework_misc_info_keys = a
545    elif o == '--framework-misc-info-keys':
546      OPTIONS.framework_misc_info_keys = a
547    elif o == '--other-target-files':
548      logger.warning(
549          '--other-target-files has been renamed to --vendor-target-files')
550      OPTIONS.vendor_target_files = a
551    elif o == '--vendor-target-files':
552      OPTIONS.vendor_target_files = a
553    elif o == '--other-item-list':
554      logger.warning('--other-item-list has been renamed to --vendor-item-list')
555      OPTIONS.vendor_item_list = a
556    elif o == '--vendor-item-list':
557      OPTIONS.vendor_item_list = a
558    elif o == '--boot-image-dir-path':
559      OPTIONS.boot_image_dir_path = a
560    elif o == '--output-target-files':
561      OPTIONS.output_target_files = a
562    elif o == '--output-dir':
563      OPTIONS.output_dir = a
564    elif o == '--output-item-list':
565      OPTIONS.output_item_list = a
566    elif o == '--output-ota':
567      OPTIONS.output_ota = a
568    elif o == '--output-img':
569      OPTIONS.output_img = a
570    elif o == '--output-super-empty':
571      OPTIONS.output_super_empty = a
572    elif o == '--rebuild_recovery' or o == '--rebuild-recovery':
573      OPTIONS.rebuild_recovery = True
574    elif o == '--allow-duplicate-apkapex-keys':
575      OPTIONS.allow_duplicate_apkapex_keys = True
576    elif o == '--vendor-otatools':
577      OPTIONS.vendor_otatools = a
578    elif o == '--rebuild-sepolicy':
579      OPTIONS.rebuild_sepolicy = True
580    elif o == '--keep-tmp':
581      OPTIONS.keep_tmp = True
582    elif o == '--avb-resolve-rollback-index-location-conflict':
583      OPTIONS.avb_resolve_rollback_index_location_conflict = True
584    elif o == '--allow-partial-ab':
585      OPTIONS.allow_partial_ab = True
586    elif o == '--framework-dexpreopt-config':
587      OPTIONS.framework_dexpreopt_config = a
588    elif o == '--framework-dexpreopt-tools':
589      OPTIONS.framework_dexpreopt_tools = a
590    elif o == '--vendor-dexpreopt-config':
591      OPTIONS.vendor_dexpreopt_config = a
592    else:
593      return False
594    return True
595
596  args = common.ParseOptions(
597      sys.argv[1:],
598      __doc__,
599      extra_long_opts=[
600          'system-target-files=',
601          'framework-target-files=',
602          'system-item-list=',
603          'framework-item-list=',
604          'system-misc-info-keys=',
605          'framework-misc-info-keys=',
606          'other-target-files=',
607          'vendor-target-files=',
608          'other-item-list=',
609          'vendor-item-list=',
610          'boot-image-dir-path=',
611          'output-target-files=',
612          'output-dir=',
613          'output-item-list=',
614          'output-ota=',
615          'output-img=',
616          'output-super-empty=',
617          'framework-dexpreopt-config=',
618          'framework-dexpreopt-tools=',
619          'vendor-dexpreopt-config=',
620          'rebuild_recovery',
621          'rebuild-recovery',
622          'allow-duplicate-apkapex-keys',
623          'vendor-otatools=',
624          'rebuild-sepolicy',
625          'keep-tmp',
626          'avb-resolve-rollback-index-location-conflict',
627          'allow-partial-ab',
628      ],
629      extra_option_handler=option_handler)
630
631  # pylint: disable=too-many-boolean-expressions
632  if (args or OPTIONS.framework_target_files is None or
633      OPTIONS.vendor_target_files is None or
634      (OPTIONS.output_target_files is None and OPTIONS.output_dir is None) or
635      (OPTIONS.output_dir is not None and not OPTIONS.output_item_list) or
636      (OPTIONS.rebuild_recovery and not OPTIONS.rebuild_sepolicy)):
637    common.Usage(__doc__)
638    sys.exit(1)
639
640  framework_namelist = merge_utils.GetTargetFilesItems(
641      OPTIONS.framework_target_files)
642  vendor_namelist = merge_utils.GetTargetFilesItems(
643      OPTIONS.vendor_target_files)
644
645  if OPTIONS.framework_item_list:
646    OPTIONS.framework_item_list = common.LoadListFromFile(
647        OPTIONS.framework_item_list)
648  else:
649    OPTIONS.framework_item_list = merge_utils.InferItemList(
650        input_namelist=framework_namelist, framework=True)
651  OPTIONS.framework_partition_set = merge_utils.ItemListToPartitionSet(
652      OPTIONS.framework_item_list)
653
654  if OPTIONS.framework_misc_info_keys:
655    OPTIONS.framework_misc_info_keys = common.LoadListFromFile(
656        OPTIONS.framework_misc_info_keys)
657  else:
658    OPTIONS.framework_misc_info_keys = merge_utils.InferFrameworkMiscInfoKeys(
659        input_namelist=framework_namelist)
660
661  if OPTIONS.vendor_item_list:
662    OPTIONS.vendor_item_list = common.LoadListFromFile(OPTIONS.vendor_item_list)
663  else:
664    OPTIONS.vendor_item_list = merge_utils.InferItemList(
665        input_namelist=vendor_namelist, framework=False)
666  OPTIONS.vendor_partition_set = merge_utils.ItemListToPartitionSet(
667      OPTIONS.vendor_item_list)
668
669  if OPTIONS.output_item_list:
670    OPTIONS.output_item_list = common.LoadListFromFile(OPTIONS.output_item_list)
671
672  if not merge_utils.ValidateConfigLists():
673    sys.exit(1)
674
675  temp_dir = common.MakeTempDir(prefix='merge_target_files_')
676  try:
677    merge_target_files(temp_dir)
678  finally:
679    if OPTIONS.keep_tmp:
680      logger.info('Keeping temp_dir %s', temp_dir)
681    else:
682      common.Cleanup()
683
684
685if __name__ == '__main__':
686  main()
687