• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""
18This script merges two partial target files packages (one of which contains
19system files, and the other contains non-system files) together, producing a
20complete target files package that can be used to generate an OTA package.
21
22Usage: merge_target_files.py [args]
23
24  --system-target-files system-target-files-zip-archive
25      The input target files package containing system bits. This is a zip
26      archive.
27
28  --system-item-list system-item-list-file
29      The optional path to a newline-separated config file that replaces the
30      contents of default_system_item_list if provided.
31
32  --system-misc-info-keys system-misc-info-keys-file
33      The optional path to a newline-separated config file that replaces the
34      contents of default_system_misc_info_keys if provided.
35
36  --other-target-files other-target-files-zip-archive
37      The input target files package containing other bits. This is a zip
38      archive.
39
40  --other-item-list other-item-list-file
41      The optional path to a newline-separated config file that replaces the
42      contents of default_other_item_list if provided.
43
44  --output-target-files output-target-files-package
45      The output merged target files package. Also a zip archive.
46
47  --rebuild_recovery
48      Rebuild the recovery patch used by non-A/B devices and write it to the
49      system image.
50
51  --keep-tmp
52      Keep tempoary files for debugging purposes.
53"""
54
55from __future__ import print_function
56
57import fnmatch
58import logging
59import os
60import sys
61import zipfile
62
63import common
64import add_img_to_target_files
65
66logger = logging.getLogger(__name__)
67OPTIONS = common.OPTIONS
68OPTIONS.verbose = True
69OPTIONS.system_target_files = None
70OPTIONS.system_item_list = None
71OPTIONS.system_misc_info_keys = None
72OPTIONS.other_target_files = None
73OPTIONS.other_item_list = None
74OPTIONS.output_target_files = None
75OPTIONS.rebuild_recovery = False
76OPTIONS.keep_tmp = False
77
78# default_system_item_list is a list of items to extract from the partial
79# system target files package as is, meaning these items will land in the
80# output target files package exactly as they appear in the input partial
81# system target files package.
82
83default_system_item_list = [
84    'META/apkcerts.txt',
85    'META/filesystem_config.txt',
86    'META/root_filesystem_config.txt',
87    'META/system_manifest.xml',
88    'META/system_matrix.xml',
89    'META/update_engine_config.txt',
90    'PRODUCT/*',
91    'ROOT/*',
92    'SYSTEM/*',
93]
94
95# system_extract_special_item_list is a list of items to extract from the
96# partial system target files package that need some special processing, such
97# as some sort of combination with items from the partial other target files
98# package.
99
100system_extract_special_item_list = [
101    'META/*',
102]
103
104# default_system_misc_info_keys is a list of keys to obtain from the system instance of
105# META/misc_info.txt. The remaining keys from the other instance.
106
107default_system_misc_info_keys = [
108    'avb_system_hashtree_enable',
109    'avb_system_add_hashtree_footer_args',
110    'avb_system_key_path',
111    'avb_system_algorithm',
112    'avb_system_rollback_index_location',
113    'avb_product_hashtree_enable',
114    'avb_product_add_hashtree_footer_args',
115    'avb_product_services_hashtree_enable',
116    'avb_product_services_add_hashtree_footer_args',
117    'system_root_image',
118    'root_dir',
119    'ab_update',
120    'default_system_dev_certificate',
121    'system_size',
122]
123
124# default_other_item_list is a list of items to extract from the partial
125# other target files package as is, meaning these items will land in the output
126# target files package exactly as they appear in the input partial other target
127# files package.
128
129default_other_item_list = [
130    'META/boot_filesystem_config.txt',
131    'META/otakeys.txt',
132    'META/releasetools.py',
133    'META/vendor_filesystem_config.txt',
134    'META/vendor_manifest.xml',
135    'META/vendor_matrix.xml',
136    'BOOT/*',
137    'DATA/*',
138    'ODM/*',
139    'OTA/android-info.txt',
140    'PREBUILT_IMAGES/*',
141    'RADIO/*',
142    'VENDOR/*',
143]
144
145# other_extract_special_item_list is a list of items to extract from the
146# partial other target files package that need some special processing, such as
147# some sort of combination with items from the partial system target files
148# package.
149
150other_extract_special_item_list = [
151    'META/*',
152]
153
154
155def extract_items(target_files, target_files_temp_dir, extract_item_list):
156  """Extract items from target files to temporary directory.
157
158  This function extracts from the specified target files zip archive into the
159  specified temporary directory, the items specified in the extract item list.
160
161  Args:
162    target_files: The target files zip archive from which to extract items.
163
164    target_files_temp_dir: The temporary directory where the extracted items
165    will land.
166
167    extract_item_list: A list of items to extract.
168  """
169
170  logger.info('extracting from %s', target_files)
171
172  # Filter the extract_item_list to remove any items that do not exist in the
173  # zip file. Otherwise, the extraction step will fail.
174
175  with zipfile.ZipFile(
176      target_files,
177      'r',
178      allowZip64=True) as target_files_zipfile:
179    target_files_namelist = target_files_zipfile.namelist()
180
181  filtered_extract_item_list = []
182  for pattern in extract_item_list:
183    matching_namelist = fnmatch.filter(target_files_namelist, pattern)
184    if not matching_namelist:
185      logger.warning('no match for %s', pattern)
186    else:
187      filtered_extract_item_list.append(pattern)
188
189  # Extract from target_files into target_files_temp_dir the
190  # filtered_extract_item_list.
191
192  common.UnzipToDir(
193      target_files,
194      target_files_temp_dir,
195      filtered_extract_item_list)
196
197
198def read_config_list(config_file_path):
199  """Reads a config file into a list of strings.
200
201  Expects the file to be newline-separated.
202
203  Args:
204    config_file_path: The path to the config file to open and read.
205  """
206  with open(config_file_path) as config_file:
207    return config_file.read().splitlines()
208
209
210def validate_config_lists(
211    system_item_list,
212    system_misc_info_keys,
213    other_item_list):
214  """Performs validations on the merge config lists.
215
216  Args:
217    system_item_list: The list of items to extract from the partial
218    system target files package as is.
219
220    system_misc_info_keys: A list of keys to obtain from the system instance
221    of META/misc_info.txt. The remaining keys from the other instance.
222
223    other_item_list: The list of items to extract from the partial
224    other target files package as is.
225
226  Returns:
227    False if a validation fails, otherwise true.
228  """
229  default_combined_item_set = set(default_system_item_list)
230  default_combined_item_set.update(default_other_item_list)
231
232  combined_item_set = set(system_item_list)
233  combined_item_set.update(other_item_list)
234
235  # Check that the merge config lists are not missing any item specified
236  # by the default config lists.
237  difference = default_combined_item_set.difference(combined_item_set)
238  if difference:
239    logger.error('Missing merge config items: %s' % list(difference))
240    logger.error('Please ensure missing items are in either the '
241                 'system-item-list or other-item-list files provided to '
242                 'this script.')
243    return False
244
245  if ('dynamic_partition_list' in system_misc_info_keys) or (
246      'super_partition_groups' in system_misc_info_keys):
247    logger.error('Dynamic partition misc info keys should come from '
248                 'the other instance of META/misc_info.txt.')
249    return False
250
251  return True
252
253
254def process_ab_partitions_txt(
255    system_target_files_temp_dir,
256    other_target_files_temp_dir,
257    output_target_files_temp_dir):
258  """Perform special processing for META/ab_partitions.txt
259
260  This function merges the contents of the META/ab_partitions.txt files from
261  the system directory and the other directory, placing the merged result in
262  the output directory. The precondition in that the files are already
263  extracted. The post condition is that the output META/ab_partitions.txt
264  contains the merged content. The format for each ab_partitions.txt a one
265  partition name per line. The output file contains the union of the parition
266  names.
267
268  Args:
269    system_target_files_temp_dir: The name of a directory containing the
270    special items extracted from the system target files package.
271
272    other_target_files_temp_dir: The name of a directory containing the
273    special items extracted from the other target files package.
274
275    output_target_files_temp_dir: The name of a directory that will be used
276    to create the output target files package after all the special cases
277    are processed.
278  """
279
280  system_ab_partitions_txt = os.path.join(
281      system_target_files_temp_dir, 'META', 'ab_partitions.txt')
282
283  other_ab_partitions_txt = os.path.join(
284      other_target_files_temp_dir, 'META', 'ab_partitions.txt')
285
286  with open(system_ab_partitions_txt) as f:
287    system_ab_partitions = f.read().splitlines()
288
289  with open(other_ab_partitions_txt) as f:
290    other_ab_partitions = f.read().splitlines()
291
292  output_ab_partitions = set(system_ab_partitions + other_ab_partitions)
293
294  output_ab_partitions_txt = os.path.join(
295      output_target_files_temp_dir, 'META', 'ab_partitions.txt')
296
297  with open(output_ab_partitions_txt, 'w') as output:
298    for partition in sorted(output_ab_partitions):
299      output.write('%s\n' % partition)
300
301
302def append_recovery_to_filesystem_config(output_target_files_temp_dir):
303  """Perform special processing for META/filesystem_config.txt
304
305  This function appends recovery information to META/filesystem_config.txt
306  so that recovery patch regeneration will succeed.
307
308  Args:
309    output_target_files_temp_dir: The name of a directory that will be used
310    to create the output target files package after all the special cases
311    are processed. We find filesystem_config.txt here.
312  """
313
314  filesystem_config_txt = os.path.join(
315      output_target_files_temp_dir,
316      'META',
317      'filesystem_config.txt')
318
319  with open(filesystem_config_txt, 'a') as f:
320    # TODO(bpeckham) this data is hard coded. It should be generated
321    # programmatically.
322    f.write(
323        'system/bin/install-recovery.sh 0 0 750 '
324        'selabel=u:object_r:install_recovery_exec:s0 capabilities=0x0\n')
325    f.write(
326        'system/recovery-from-boot.p 0 0 644 '
327        'selabel=u:object_r:system_file:s0 capabilities=0x0\n')
328    f.write(
329        'system/etc/recovery.img 0 0 440 '
330        'selabel=u:object_r:install_recovery_exec:s0 capabilities=0x0\n')
331
332
333def process_misc_info_txt(
334    system_target_files_temp_dir,
335    other_target_files_temp_dir,
336    output_target_files_temp_dir,
337    system_misc_info_keys):
338  """Perform special processing for META/misc_info.txt
339
340  This function merges the contents of the META/misc_info.txt files from the
341  system directory and the other directory, placing the merged result in the
342  output directory. The precondition in that the files are already extracted.
343  The post condition is that the output META/misc_info.txt contains the merged
344  content.
345
346  Args:
347    system_target_files_temp_dir: The name of a directory containing the
348    special items extracted from the system target files package.
349
350    other_target_files_temp_dir: The name of a directory containing the
351    special items extracted from the other target files package.
352
353    output_target_files_temp_dir: The name of a directory that will be used
354    to create the output target files package after all the special cases
355    are processed.
356
357    system_misc_info_keys: A list of keys to obtain from the system instance
358    of META/misc_info.txt. The remaining keys from the other instance.
359  """
360
361  def read_helper(d):
362    misc_info_txt = os.path.join(d, 'META', 'misc_info.txt')
363    with open(misc_info_txt) as f:
364      return list(f.read().splitlines())
365
366  system_info_dict = common.LoadDictionaryFromLines(
367      read_helper(system_target_files_temp_dir))
368
369  # We take most of the misc info from the other target files.
370
371  merged_info_dict = common.LoadDictionaryFromLines(
372      read_helper(other_target_files_temp_dir))
373
374  # Replace certain values in merged_info_dict with values from
375  # system_info_dict.
376
377  for key in system_misc_info_keys:
378    merged_info_dict[key] = system_info_dict[key]
379
380  # Merge misc info keys used for Dynamic Partitions.
381  if (merged_info_dict.get('use_dynamic_partitions') == 'true') and (
382      system_info_dict.get('use_dynamic_partitions') == 'true'):
383    merged_info_dict['dynamic_partition_list'] = '%s %s' % (
384        system_info_dict.get('dynamic_partition_list', ''),
385        merged_info_dict.get('dynamic_partition_list', ''))
386    # Partition groups and group sizes are defined by the other (non-system)
387    # misc info file because these values may vary for each board that uses
388    # a shared system image.
389    for partition_group in merged_info_dict['super_partition_groups'].split(' '):
390      if ('super_%s_group_size' % partition_group) not in merged_info_dict:
391        raise common.ExternalError(
392            'Other META/misc_info.txt does not contain required key '
393            'super_%s_group_size.' % partition_group)
394      key = 'super_%s_partition_list' % partition_group
395      merged_info_dict[key] = '%s %s' % (
396        system_info_dict.get(key, ''),
397        merged_info_dict.get(key, ''))
398
399  output_misc_info_txt = os.path.join(
400      output_target_files_temp_dir,
401      'META', 'misc_info.txt')
402
403  sorted_keys = sorted(merged_info_dict.keys())
404
405  with open(output_misc_info_txt, 'w') as output:
406    for key in sorted_keys:
407      output.write('{}={}\n'.format(key, merged_info_dict[key]))
408
409
410def process_file_contexts_bin(temp_dir, output_target_files_temp_dir):
411  """Perform special processing for META/file_contexts.bin.
412
413  This function combines plat_file_contexts and vendor_file_contexts, which are
414  expected to already be extracted in temp_dir, to produce a merged
415  file_contexts.bin that will land in temp_dir at META/file_contexts.bin.
416
417  Args:
418    temp_dir: The name of a scratch directory that this function can use for
419    intermediate files generated during processing.
420
421    output_target_files_temp_dir: The name of the working directory that must
422    already contain plat_file_contexts and vendor_file_contexts (in the
423    appropriate sub directories), and to which META/file_contexts.bin will be
424    written.
425  """
426
427  # To create a merged file_contexts.bin file, we use the system and vendor
428  # file contexts files as input, the m4 tool to combine them, the sorting tool
429  # to sort, and finally the sefcontext_compile tool to generate the final
430  # output. We currently omit a checkfc step since the files had been checked
431  # as part of the build.
432
433  # The m4 step concatenates the two input files contexts files. Since m4
434  # writes to stdout, we receive that into an array of bytes, and then write it
435  # to a file.
436
437  # Collect the file contexts that we're going to combine from SYSTEM, VENDOR,
438  # PRODUCT, and ODM. We require SYSTEM and VENDOR, but others are optional.
439
440  file_contexts_list = []
441
442  for partition in ['SYSTEM', 'VENDOR', 'PRODUCT', 'ODM']:
443    prefix = 'plat' if partition == 'SYSTEM' else partition.lower()
444
445    file_contexts = os.path.join(
446        output_target_files_temp_dir,
447        partition, 'etc', 'selinux', prefix + '_file_contexts')
448
449    mandatory = partition in ['SYSTEM', 'VENDOR']
450
451    if mandatory or os.path.isfile(file_contexts):
452      file_contexts_list.append(file_contexts)
453    else:
454      logger.warning('file not found: %s', file_contexts)
455
456  command = ['m4', '--fatal-warnings', '-s'] + file_contexts_list
457
458  merged_content = common.RunAndCheckOutput(command, verbose=False)
459
460  merged_file_contexts_txt = os.path.join(temp_dir, 'merged_file_contexts.txt')
461
462  with open(merged_file_contexts_txt, 'wb') as f:
463    f.write(merged_content)
464
465  # The sort step sorts the concatenated file.
466
467  sorted_file_contexts_txt = os.path.join(temp_dir, 'sorted_file_contexts.txt')
468  command = ['fc_sort', merged_file_contexts_txt, sorted_file_contexts_txt]
469  common.RunAndWait(command, verbose=True)
470
471  # Finally, the compile step creates the final META/file_contexts.bin.
472
473  file_contexts_bin = os.path.join(
474      output_target_files_temp_dir,
475      'META', 'file_contexts.bin')
476
477  command = [
478      'sefcontext_compile',
479      '-o', file_contexts_bin,
480      sorted_file_contexts_txt,
481  ]
482
483  common.RunAndWait(command, verbose=True)
484
485
486def process_special_cases(
487    temp_dir,
488    system_target_files_temp_dir,
489    other_target_files_temp_dir,
490    output_target_files_temp_dir,
491    system_misc_info_keys,
492    rebuild_recovery
493):
494  """Perform special-case processing for certain target files items.
495
496  Certain files in the output target files package require special-case
497  processing. This function performs all that special-case processing.
498
499  Args:
500    temp_dir: The name of a scratch directory that this function can use for
501    intermediate files generated during processing.
502
503    system_target_files_temp_dir: The name of a directory containing the
504    special items extracted from the system target files package.
505
506    other_target_files_temp_dir: The name of a directory containing the
507    special items extracted from the other target files package.
508
509    output_target_files_temp_dir: The name of a directory that will be used
510    to create the output target files package after all the special cases
511    are processed.
512
513    system_misc_info_keys: A list of keys to obtain from the system instance
514    of META/misc_info.txt. The remaining keys from the other instance.
515
516    rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
517    devices and write it to the system image.
518  """
519
520  if 'ab_update' in system_misc_info_keys:
521    process_ab_partitions_txt(
522        system_target_files_temp_dir=system_target_files_temp_dir,
523        other_target_files_temp_dir=other_target_files_temp_dir,
524        output_target_files_temp_dir=output_target_files_temp_dir)
525
526  if rebuild_recovery:
527    append_recovery_to_filesystem_config(
528        output_target_files_temp_dir=output_target_files_temp_dir)
529
530  process_misc_info_txt(
531      system_target_files_temp_dir=system_target_files_temp_dir,
532      other_target_files_temp_dir=other_target_files_temp_dir,
533      output_target_files_temp_dir=output_target_files_temp_dir,
534      system_misc_info_keys=system_misc_info_keys)
535
536  process_file_contexts_bin(
537      temp_dir=temp_dir,
538      output_target_files_temp_dir=output_target_files_temp_dir)
539
540
541def merge_target_files(
542    temp_dir,
543    system_target_files,
544    system_item_list,
545    system_misc_info_keys,
546    other_target_files,
547    other_item_list,
548    output_target_files,
549    rebuild_recovery):
550  """Merge two target files packages together.
551
552  This function takes system and other target files packages as input, performs
553  various file extractions, special case processing, and finally creates a
554  merged zip archive as output.
555
556  Args:
557    temp_dir: The name of a directory we use when we extract items from the
558    input target files packages, and also a scratch directory that we use for
559    temporary files.
560
561    system_target_files: The name of the zip archive containing the system
562    partial target files package.
563
564    system_item_list: The list of items to extract from the partial system
565    target files package as is, meaning these items will land in the output
566    target files package exactly as they appear in the input partial system
567    target files package.
568
569    system_misc_info_keys: The list of keys to obtain from the system instance
570    of META/misc_info.txt. The remaining keys from the other instance.
571
572    other_target_files: The name of the zip archive containing the other
573    partial target files package.
574
575    other_item_list: The list of items to extract from the partial other
576    target files package as is, meaning these items will land in the output
577    target files package exactly as they appear in the input partial other
578    target files package.
579
580    output_target_files: The name of the output zip archive target files
581    package created by merging system and other.
582
583    rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
584    devices and write it to the system image.
585  """
586
587  logger.info(
588      'starting: merge system %s and other %s into output %s',
589      system_target_files,
590      other_target_files,
591      output_target_files)
592
593  # Create directory names that we'll use when we extract files from system,
594  # and other, and for zipping the final output.
595
596  system_target_files_temp_dir = os.path.join(temp_dir, 'system')
597  other_target_files_temp_dir = os.path.join(temp_dir, 'other')
598  output_target_files_temp_dir = os.path.join(temp_dir, 'output')
599
600  # Extract "as is" items from the input system partial target files package.
601  # We extract them directly into the output temporary directory since the
602  # items do not need special case processing.
603
604  extract_items(
605      target_files=system_target_files,
606      target_files_temp_dir=output_target_files_temp_dir,
607      extract_item_list=system_item_list)
608
609  # Extract "as is" items from the input other partial target files package. We
610  # extract them directly into the output temporary directory since the items
611  # do not need special case processing.
612
613  extract_items(
614      target_files=other_target_files,
615      target_files_temp_dir=output_target_files_temp_dir,
616      extract_item_list=other_item_list)
617
618  # Extract "special" items from the input system partial target files package.
619  # We extract these items to different directory since they require special
620  # processing before they will end up in the output directory.
621
622  extract_items(
623      target_files=system_target_files,
624      target_files_temp_dir=system_target_files_temp_dir,
625      extract_item_list=system_extract_special_item_list)
626
627  # Extract "special" items from the input other partial target files package.
628  # We extract these items to different directory since they require special
629  # processing before they will end up in the output directory.
630
631  extract_items(
632      target_files=other_target_files,
633      target_files_temp_dir=other_target_files_temp_dir,
634      extract_item_list=other_extract_special_item_list)
635
636  # Now that the temporary directories contain all the extracted files, perform
637  # special case processing on any items that need it. After this function
638  # completes successfully, all the files we need to create the output target
639  # files package are in place.
640
641  process_special_cases(
642      temp_dir=temp_dir,
643      system_target_files_temp_dir=system_target_files_temp_dir,
644      other_target_files_temp_dir=other_target_files_temp_dir,
645      output_target_files_temp_dir=output_target_files_temp_dir,
646      system_misc_info_keys=system_misc_info_keys,
647      rebuild_recovery=rebuild_recovery)
648
649  # Regenerate IMAGES in the temporary directory.
650
651  add_img_args = ['--verbose']
652  if rebuild_recovery:
653    add_img_args.append('--rebuild_recovery')
654  add_img_args.append(output_target_files_temp_dir)
655
656  add_img_to_target_files.main(add_img_args)
657
658  # Finally, create the output target files zip archive.
659
660  output_zip = os.path.abspath(output_target_files)
661  output_target_files_list = os.path.join(temp_dir, 'output.list')
662  output_target_files_meta_dir = os.path.join(
663      output_target_files_temp_dir, 'META')
664
665  command = [
666      'find',
667      output_target_files_meta_dir,
668  ]
669  # TODO(bpeckham): sort this to be more like build.
670  meta_content = common.RunAndCheckOutput(command, verbose=False)
671  command = [
672      'find',
673      output_target_files_temp_dir,
674      '-path',
675      output_target_files_meta_dir,
676      '-prune',
677      '-o',
678      '-print'
679  ]
680  # TODO(bpeckham): sort this to be more like build.
681  other_content = common.RunAndCheckOutput(command, verbose=False)
682
683  with open(output_target_files_list, 'wb') as f:
684    f.write(meta_content)
685    f.write(other_content)
686
687  command = [
688      'soong_zip',
689      '-d',
690      '-o', output_zip,
691      '-C', output_target_files_temp_dir,
692      '-l', output_target_files_list,
693  ]
694  logger.info('creating %s', output_target_files)
695  common.RunAndWait(command, verbose=True)
696
697
698def call_func_with_temp_dir(func, keep_tmp):
699  """Manage the creation and cleanup of the temporary directory.
700
701  This function calls the given function after first creating a temporary
702  directory. It also cleans up the temporary directory.
703
704  Args:
705    func: The function to call. Should accept one parameter, the path to
706    the temporary directory.
707
708    keep_tmp: Keep the temporary directory after processing is complete.
709  """
710
711  # Create a temporary directory. This will serve as the parent of directories
712  # we use when we extract items from the input target files packages, and also
713  # a scratch directory that we use for temporary files.
714
715  temp_dir = common.MakeTempDir(prefix='merge_target_files_')
716
717  try:
718    func(temp_dir)
719  except:
720    raise
721  finally:
722    if keep_tmp:
723      logger.info('keeping %s', temp_dir)
724    else:
725      common.Cleanup()
726
727
728def main():
729  """The main function.
730
731  Process command line arguments, then call merge_target_files to
732  perform the heavy lifting.
733  """
734
735  common.InitLogging()
736
737  def option_handler(o, a):
738    if o == '--system-target-files':
739      OPTIONS.system_target_files = a
740    elif o == '--system-item-list':
741      OPTIONS.system_item_list = a
742    elif o == '--system-misc-info-keys':
743      OPTIONS.system_misc_info_keys = a
744    elif o == '--other-target-files':
745      OPTIONS.other_target_files = a
746    elif o == '--other-item-list':
747      OPTIONS.other_item_list = a
748    elif o == '--output-target-files':
749      OPTIONS.output_target_files = a
750    elif o == '--rebuild_recovery':
751      OPTIONS.rebuild_recovery = True
752    elif o == '--keep-tmp':
753      OPTIONS.keep_tmp = True
754    else:
755      return False
756    return True
757
758  args = common.ParseOptions(
759      sys.argv[1:], __doc__,
760      extra_long_opts=[
761          'system-target-files=',
762          'system-item-list=',
763          'system-misc-info-keys=',
764          'other-target-files=',
765          'other-item-list=',
766          'output-target-files=',
767          'rebuild_recovery',
768          'keep-tmp',
769      ],
770      extra_option_handler=option_handler)
771
772  if (len(args) != 0 or
773      OPTIONS.system_target_files is None or
774      OPTIONS.other_target_files is None or
775      OPTIONS.output_target_files is None):
776    common.Usage(__doc__)
777    sys.exit(1)
778
779  if OPTIONS.system_item_list:
780    system_item_list = read_config_list(OPTIONS.system_item_list)
781  else:
782    system_item_list = default_system_item_list
783
784  if OPTIONS.system_misc_info_keys:
785    system_misc_info_keys = read_config_list(OPTIONS.system_misc_info_keys)
786  else:
787    system_misc_info_keys = default_system_misc_info_keys
788
789  if OPTIONS.other_item_list:
790    other_item_list = read_config_list(OPTIONS.other_item_list)
791  else:
792    other_item_list = default_other_item_list
793
794  if not validate_config_lists(
795      system_item_list=system_item_list,
796      system_misc_info_keys=system_misc_info_keys,
797      other_item_list=other_item_list):
798    sys.exit(1)
799
800  call_func_with_temp_dir(
801      lambda temp_dir: merge_target_files(
802          temp_dir=temp_dir,
803          system_target_files=OPTIONS.system_target_files,
804          system_item_list=system_item_list,
805          system_misc_info_keys=system_misc_info_keys,
806          other_target_files=OPTIONS.other_target_files,
807          other_item_list=other_item_list,
808          output_target_files=OPTIONS.output_target_files,
809          rebuild_recovery=OPTIONS.rebuild_recovery),
810      OPTIONS.keep_tmp)
811
812
813if __name__ == '__main__':
814  main()
815