blob: 97b53d92a75555c01c3d32287a2fcb7c26560abc [file] [log] [blame]
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001#!/usr/bin/env python
2#
3# Copyright (C) 2019 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
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
Daniel Norman2c99c5b2019-03-07 13:01:48 -080028 --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
Bill Peckhame9eb5f92019-02-01 15:52:10 -080036 --other-target-files other-target-files-zip-archive
37 The input target files package containing other bits. This is a zip
38 archive.
39
Daniel Norman2c99c5b2019-03-07 13:01:48 -080040 --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
Bill Peckhame9eb5f92019-02-01 15:52:10 -080044 --output-target-files output-target-files-package
Daniel Normanfdb38812019-04-15 09:47:24 -070045 If provided, the output merged target files package. Also a zip archive.
46
47 --output-dir output-directory
48 If provided, the destination directory for saving merged files. Requires
49 the --output-item-list flag.
50 Can be provided alongside --output-target-files, or by itself.
51
52 --output-item-list output-item-list-file.
53 The optional path to a newline-separated config file that specifies the
54 file patterns to copy into the --output-dir. Required if providing
55 the --output-dir flag.
Daniel Normana4911da2019-03-15 14:36:21 -070056
Daniel Normanf0318252019-04-15 11:34:56 -070057 --output-super-empty output-super-empty-image
58 If provided, creates a super_empty.img file from the merged target
59 files package and saves it at this path.
60
Daniel Normana4911da2019-03-15 14:36:21 -070061 --rebuild_recovery
62 Rebuild the recovery patch used by non-A/B devices and write it to the
63 system image.
Bill Peckham364c1cc2019-03-29 18:27:23 -070064
65 --keep-tmp
66 Keep tempoary files for debugging purposes.
Bill Peckhame9eb5f92019-02-01 15:52:10 -080067"""
68
69from __future__ import print_function
70
Bill Peckhame9eb5f92019-02-01 15:52:10 -080071import fnmatch
72import logging
73import os
Daniel Normanfdb38812019-04-15 09:47:24 -070074import shutil
Bill Peckhame9eb5f92019-02-01 15:52:10 -080075import sys
76import zipfile
77
Bill Peckhame9eb5f92019-02-01 15:52:10 -080078import add_img_to_target_files
Daniel Normanf0318252019-04-15 11:34:56 -070079import build_super_image
80import common
Bill Peckhame9eb5f92019-02-01 15:52:10 -080081
82logger = logging.getLogger(__name__)
83OPTIONS = common.OPTIONS
84OPTIONS.verbose = True
Bill Peckhamf753e152019-02-19 18:02:46 -080085OPTIONS.system_target_files = None
Daniel Norman2c99c5b2019-03-07 13:01:48 -080086OPTIONS.system_item_list = None
87OPTIONS.system_misc_info_keys = None
Bill Peckhamf753e152019-02-19 18:02:46 -080088OPTIONS.other_target_files = None
Daniel Norman2c99c5b2019-03-07 13:01:48 -080089OPTIONS.other_item_list = None
Bill Peckhamf753e152019-02-19 18:02:46 -080090OPTIONS.output_target_files = None
Daniel Normanfdb38812019-04-15 09:47:24 -070091OPTIONS.output_dir = None
92OPTIONS.output_item_list = None
Daniel Normanf0318252019-04-15 11:34:56 -070093OPTIONS.output_super_empty = None
Daniel Normana4911da2019-03-15 14:36:21 -070094OPTIONS.rebuild_recovery = False
Bill Peckhamf753e152019-02-19 18:02:46 -080095OPTIONS.keep_tmp = False
Bill Peckhame9eb5f92019-02-01 15:52:10 -080096
Daniel Norman2c99c5b2019-03-07 13:01:48 -080097# default_system_item_list is a list of items to extract from the partial
Bill Peckhame9eb5f92019-02-01 15:52:10 -080098# system target files package as is, meaning these items will land in the
99# output target files package exactly as they appear in the input partial
100# system target files package.
101
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800102default_system_item_list = [
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800103 'META/apkcerts.txt',
104 'META/filesystem_config.txt',
105 'META/root_filesystem_config.txt',
106 'META/system_manifest.xml',
107 'META/system_matrix.xml',
108 'META/update_engine_config.txt',
109 'PRODUCT/*',
110 'ROOT/*',
111 'SYSTEM/*',
112]
113
114# system_extract_special_item_list is a list of items to extract from the
115# partial system target files package that need some special processing, such
116# as some sort of combination with items from the partial other target files
117# package.
118
119system_extract_special_item_list = [
120 'META/*',
121]
122
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800123# default_system_misc_info_keys is a list of keys to obtain from the system instance of
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800124# META/misc_info.txt. The remaining keys from the other instance.
125
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800126default_system_misc_info_keys = [
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800127 'avb_system_hashtree_enable',
128 'avb_system_add_hashtree_footer_args',
129 'avb_system_key_path',
130 'avb_system_algorithm',
131 'avb_system_rollback_index_location',
132 'avb_product_hashtree_enable',
133 'avb_product_add_hashtree_footer_args',
134 'avb_product_services_hashtree_enable',
135 'avb_product_services_add_hashtree_footer_args',
136 'system_root_image',
137 'root_dir',
138 'ab_update',
139 'default_system_dev_certificate',
140 'system_size',
141]
142
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800143# default_other_item_list is a list of items to extract from the partial
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800144# other target files package as is, meaning these items will land in the output
145# target files package exactly as they appear in the input partial other target
146# files package.
147
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800148default_other_item_list = [
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800149 'META/boot_filesystem_config.txt',
150 'META/otakeys.txt',
151 'META/releasetools.py',
152 'META/vendor_filesystem_config.txt',
153 'META/vendor_manifest.xml',
154 'META/vendor_matrix.xml',
155 'BOOT/*',
156 'DATA/*',
157 'ODM/*',
158 'OTA/android-info.txt',
159 'PREBUILT_IMAGES/*',
160 'RADIO/*',
161 'VENDOR/*',
162]
163
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800164# other_extract_special_item_list is a list of items to extract from the
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800165# partial other target files package that need some special processing, such as
166# some sort of combination with items from the partial system target files
167# package.
168
169other_extract_special_item_list = [
170 'META/*',
171]
172
173
174def extract_items(target_files, target_files_temp_dir, extract_item_list):
175 """Extract items from target files to temporary directory.
176
177 This function extracts from the specified target files zip archive into the
178 specified temporary directory, the items specified in the extract item list.
179
180 Args:
181 target_files: The target files zip archive from which to extract items.
182
183 target_files_temp_dir: The temporary directory where the extracted items
184 will land.
185
186 extract_item_list: A list of items to extract.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800187 """
188
189 logger.info('extracting from %s', target_files)
190
191 # Filter the extract_item_list to remove any items that do not exist in the
192 # zip file. Otherwise, the extraction step will fail.
193
194 with zipfile.ZipFile(
195 target_files,
196 'r',
197 allowZip64=True) as target_files_zipfile:
198 target_files_namelist = target_files_zipfile.namelist()
199
200 filtered_extract_item_list = []
201 for pattern in extract_item_list:
202 matching_namelist = fnmatch.filter(target_files_namelist, pattern)
203 if not matching_namelist:
204 logger.warning('no match for %s', pattern)
205 else:
206 filtered_extract_item_list.append(pattern)
207
Bill Peckham8ff3fbd2019-02-22 10:57:43 -0800208 # Extract from target_files into target_files_temp_dir the
209 # filtered_extract_item_list.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800210
Bill Peckham8ff3fbd2019-02-22 10:57:43 -0800211 common.UnzipToDir(
212 target_files,
213 target_files_temp_dir,
214 filtered_extract_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800215
216
Daniel Normanfdb38812019-04-15 09:47:24 -0700217def copy_items(from_dir, to_dir, patterns):
218 """Similar to extract_items() except uses an input dir instead of zip."""
219 file_paths = []
220 for dirpath, _, filenames in os.walk(from_dir):
221 file_paths.extend(os.path.relpath(path=os.path.join(dirpath, filename),
222 start=from_dir) for filename in filenames)
223
224 filtered_file_paths = set()
225 for pattern in patterns:
226 filtered_file_paths.update(fnmatch.filter(file_paths, pattern))
227
228 for file_path in filtered_file_paths:
229 original_file_path = os.path.join(from_dir, file_path)
230 copied_file_path = os.path.join(to_dir, file_path)
231 copied_file_dir = os.path.dirname(copied_file_path)
232 if not os.path.exists(copied_file_dir):
233 os.makedirs(copied_file_dir)
234 if os.path.islink(original_file_path):
235 os.symlink(os.readlink(original_file_path), copied_file_path)
236 else:
237 shutil.copyfile(original_file_path, copied_file_path)
238
239
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800240def read_config_list(config_file_path):
241 """Reads a config file into a list of strings.
242
243 Expects the file to be newline-separated.
244
245 Args:
246 config_file_path: The path to the config file to open and read.
247 """
248 with open(config_file_path) as config_file:
249 return config_file.read().splitlines()
250
251
Daniel Norman19b9fe92019-03-19 14:48:02 -0700252def validate_config_lists(
253 system_item_list,
254 system_misc_info_keys,
255 other_item_list):
Daniel Normane5964522019-03-19 10:32:03 -0700256 """Performs validations on the merge config lists.
257
258 Args:
259 system_item_list: The list of items to extract from the partial
260 system target files package as is.
261
Daniel Norman19b9fe92019-03-19 14:48:02 -0700262 system_misc_info_keys: A list of keys to obtain from the system instance
263 of META/misc_info.txt. The remaining keys from the other instance.
264
Daniel Normane5964522019-03-19 10:32:03 -0700265 other_item_list: The list of items to extract from the partial
266 other target files package as is.
267
268 Returns:
269 False if a validation fails, otherwise true.
270 """
271 default_combined_item_set = set(default_system_item_list)
272 default_combined_item_set.update(default_other_item_list)
273
274 combined_item_set = set(system_item_list)
275 combined_item_set.update(other_item_list)
276
277 # Check that the merge config lists are not missing any item specified
278 # by the default config lists.
279 difference = default_combined_item_set.difference(combined_item_set)
280 if difference:
281 logger.error('Missing merge config items: %s' % list(difference))
282 logger.error('Please ensure missing items are in either the '
283 'system-item-list or other-item-list files provided to '
284 'this script.')
285 return False
286
Daniel Norman19b9fe92019-03-19 14:48:02 -0700287 if ('dynamic_partition_list' in system_misc_info_keys) or (
288 'super_partition_groups' in system_misc_info_keys):
289 logger.error('Dynamic partition misc info keys should come from '
290 'the other instance of META/misc_info.txt.')
291 return False
292
Daniel Normane5964522019-03-19 10:32:03 -0700293 return True
294
295
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800296def process_ab_partitions_txt(
297 system_target_files_temp_dir,
298 other_target_files_temp_dir,
299 output_target_files_temp_dir):
300 """Perform special processing for META/ab_partitions.txt
301
302 This function merges the contents of the META/ab_partitions.txt files from
303 the system directory and the other directory, placing the merged result in
304 the output directory. The precondition in that the files are already
305 extracted. The post condition is that the output META/ab_partitions.txt
306 contains the merged content. The format for each ab_partitions.txt a one
307 partition name per line. The output file contains the union of the parition
308 names.
309
310 Args:
311 system_target_files_temp_dir: The name of a directory containing the
312 special items extracted from the system target files package.
313
314 other_target_files_temp_dir: The name of a directory containing the
315 special items extracted from the other target files package.
316
317 output_target_files_temp_dir: The name of a directory that will be used
318 to create the output target files package after all the special cases
319 are processed.
320 """
321
322 system_ab_partitions_txt = os.path.join(
323 system_target_files_temp_dir, 'META', 'ab_partitions.txt')
324
325 other_ab_partitions_txt = os.path.join(
326 other_target_files_temp_dir, 'META', 'ab_partitions.txt')
327
328 with open(system_ab_partitions_txt) as f:
329 system_ab_partitions = f.read().splitlines()
330
331 with open(other_ab_partitions_txt) as f:
332 other_ab_partitions = f.read().splitlines()
333
334 output_ab_partitions = set(system_ab_partitions + other_ab_partitions)
335
336 output_ab_partitions_txt = os.path.join(
337 output_target_files_temp_dir, 'META', 'ab_partitions.txt')
338
339 with open(output_ab_partitions_txt, 'w') as output:
340 for partition in sorted(output_ab_partitions):
341 output.write('%s\n' % partition)
342
343
Bill Peckham364c1cc2019-03-29 18:27:23 -0700344def append_recovery_to_filesystem_config(output_target_files_temp_dir):
345 """Perform special processing for META/filesystem_config.txt
346
347 This function appends recovery information to META/filesystem_config.txt
348 so that recovery patch regeneration will succeed.
349
350 Args:
351 output_target_files_temp_dir: The name of a directory that will be used
352 to create the output target files package after all the special cases
353 are processed. We find filesystem_config.txt here.
354 """
355
356 filesystem_config_txt = os.path.join(
357 output_target_files_temp_dir,
358 'META',
359 'filesystem_config.txt')
360
361 with open(filesystem_config_txt, 'a') as f:
362 # TODO(bpeckham) this data is hard coded. It should be generated
363 # programmatically.
364 f.write(
365 'system/bin/install-recovery.sh 0 0 750 '
366 'selabel=u:object_r:install_recovery_exec:s0 capabilities=0x0\n')
367 f.write(
368 'system/recovery-from-boot.p 0 0 644 '
369 'selabel=u:object_r:system_file:s0 capabilities=0x0\n')
370 f.write(
371 'system/etc/recovery.img 0 0 440 '
372 'selabel=u:object_r:install_recovery_exec:s0 capabilities=0x0\n')
373
374
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800375def process_misc_info_txt(
376 system_target_files_temp_dir,
377 other_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800378 output_target_files_temp_dir,
379 system_misc_info_keys):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800380 """Perform special processing for META/misc_info.txt
381
382 This function merges the contents of the META/misc_info.txt files from the
383 system directory and the other directory, placing the merged result in the
384 output directory. The precondition in that the files are already extracted.
385 The post condition is that the output META/misc_info.txt contains the merged
386 content.
387
388 Args:
389 system_target_files_temp_dir: The name of a directory containing the
390 special items extracted from the system target files package.
391
392 other_target_files_temp_dir: The name of a directory containing the
393 special items extracted from the other target files package.
394
395 output_target_files_temp_dir: The name of a directory that will be used
396 to create the output target files package after all the special cases
397 are processed.
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800398
399 system_misc_info_keys: A list of keys to obtain from the system instance
400 of META/misc_info.txt. The remaining keys from the other instance.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800401 """
402
403 def read_helper(d):
404 misc_info_txt = os.path.join(d, 'META', 'misc_info.txt')
405 with open(misc_info_txt) as f:
406 return list(f.read().splitlines())
407
408 system_info_dict = common.LoadDictionaryFromLines(
409 read_helper(system_target_files_temp_dir))
410
411 # We take most of the misc info from the other target files.
412
413 merged_info_dict = common.LoadDictionaryFromLines(
414 read_helper(other_target_files_temp_dir))
415
416 # Replace certain values in merged_info_dict with values from
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800417 # system_info_dict.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800418
419 for key in system_misc_info_keys:
420 merged_info_dict[key] = system_info_dict[key]
421
Daniel Norman19b9fe92019-03-19 14:48:02 -0700422 # Merge misc info keys used for Dynamic Partitions.
423 if (merged_info_dict.get('use_dynamic_partitions') == 'true') and (
424 system_info_dict.get('use_dynamic_partitions') == 'true'):
425 merged_info_dict['dynamic_partition_list'] = '%s %s' % (
426 system_info_dict.get('dynamic_partition_list', ''),
427 merged_info_dict.get('dynamic_partition_list', ''))
428 # Partition groups and group sizes are defined by the other (non-system)
429 # misc info file because these values may vary for each board that uses
430 # a shared system image.
431 for partition_group in merged_info_dict['super_partition_groups'].split(' '):
432 if ('super_%s_group_size' % partition_group) not in merged_info_dict:
Daniel Normanf0318252019-04-15 11:34:56 -0700433 raise ValueError(
Daniel Norman19b9fe92019-03-19 14:48:02 -0700434 'Other META/misc_info.txt does not contain required key '
435 'super_%s_group_size.' % partition_group)
436 key = 'super_%s_partition_list' % partition_group
437 merged_info_dict[key] = '%s %s' % (
438 system_info_dict.get(key, ''),
439 merged_info_dict.get(key, ''))
440
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800441 output_misc_info_txt = os.path.join(
442 output_target_files_temp_dir,
443 'META', 'misc_info.txt')
444
445 sorted_keys = sorted(merged_info_dict.keys())
446
447 with open(output_misc_info_txt, 'w') as output:
448 for key in sorted_keys:
449 output.write('{}={}\n'.format(key, merged_info_dict[key]))
450
451
452def process_file_contexts_bin(temp_dir, output_target_files_temp_dir):
453 """Perform special processing for META/file_contexts.bin.
454
455 This function combines plat_file_contexts and vendor_file_contexts, which are
456 expected to already be extracted in temp_dir, to produce a merged
457 file_contexts.bin that will land in temp_dir at META/file_contexts.bin.
458
459 Args:
460 temp_dir: The name of a scratch directory that this function can use for
461 intermediate files generated during processing.
462
463 output_target_files_temp_dir: The name of the working directory that must
464 already contain plat_file_contexts and vendor_file_contexts (in the
465 appropriate sub directories), and to which META/file_contexts.bin will be
466 written.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800467 """
468
469 # To create a merged file_contexts.bin file, we use the system and vendor
470 # file contexts files as input, the m4 tool to combine them, the sorting tool
471 # to sort, and finally the sefcontext_compile tool to generate the final
472 # output. We currently omit a checkfc step since the files had been checked
473 # as part of the build.
474
475 # The m4 step concatenates the two input files contexts files. Since m4
476 # writes to stdout, we receive that into an array of bytes, and then write it
477 # to a file.
478
479 # Collect the file contexts that we're going to combine from SYSTEM, VENDOR,
480 # PRODUCT, and ODM. We require SYSTEM and VENDOR, but others are optional.
481
482 file_contexts_list = []
483
484 for partition in ['SYSTEM', 'VENDOR', 'PRODUCT', 'ODM']:
485 prefix = 'plat' if partition == 'SYSTEM' else partition.lower()
486
487 file_contexts = os.path.join(
488 output_target_files_temp_dir,
489 partition, 'etc', 'selinux', prefix + '_file_contexts')
490
491 mandatory = partition in ['SYSTEM', 'VENDOR']
492
493 if mandatory or os.path.isfile(file_contexts):
494 file_contexts_list.append(file_contexts)
495 else:
496 logger.warning('file not found: %s', file_contexts)
497
498 command = ['m4', '--fatal-warnings', '-s'] + file_contexts_list
499
500 merged_content = common.RunAndCheckOutput(command, verbose=False)
501
502 merged_file_contexts_txt = os.path.join(temp_dir, 'merged_file_contexts.txt')
503
504 with open(merged_file_contexts_txt, 'wb') as f:
505 f.write(merged_content)
506
507 # The sort step sorts the concatenated file.
508
509 sorted_file_contexts_txt = os.path.join(temp_dir, 'sorted_file_contexts.txt')
510 command = ['fc_sort', merged_file_contexts_txt, sorted_file_contexts_txt]
Bill Peckham889b0c62019-02-21 18:53:37 -0800511 common.RunAndWait(command, verbose=True)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800512
513 # Finally, the compile step creates the final META/file_contexts.bin.
514
515 file_contexts_bin = os.path.join(
516 output_target_files_temp_dir,
517 'META', 'file_contexts.bin')
518
519 command = [
520 'sefcontext_compile',
521 '-o', file_contexts_bin,
522 sorted_file_contexts_txt,
523 ]
524
Bill Peckham889b0c62019-02-21 18:53:37 -0800525 common.RunAndWait(command, verbose=True)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800526
527
528def process_special_cases(
529 temp_dir,
530 system_target_files_temp_dir,
531 other_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800532 output_target_files_temp_dir,
Bill Peckham364c1cc2019-03-29 18:27:23 -0700533 system_misc_info_keys,
534 rebuild_recovery
535):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800536 """Perform special-case processing for certain target files items.
537
538 Certain files in the output target files package require special-case
539 processing. This function performs all that special-case processing.
540
541 Args:
542 temp_dir: The name of a scratch directory that this function can use for
543 intermediate files generated during processing.
544
545 system_target_files_temp_dir: The name of a directory containing the
546 special items extracted from the system target files package.
547
548 other_target_files_temp_dir: The name of a directory containing the
549 special items extracted from the other target files package.
550
551 output_target_files_temp_dir: The name of a directory that will be used
552 to create the output target files package after all the special cases
553 are processed.
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800554
555 system_misc_info_keys: A list of keys to obtain from the system instance
556 of META/misc_info.txt. The remaining keys from the other instance.
Bill Peckham364c1cc2019-03-29 18:27:23 -0700557
558 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
559 devices and write it to the system image.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800560 """
561
Bill Peckham364c1cc2019-03-29 18:27:23 -0700562 if 'ab_update' in system_misc_info_keys:
563 process_ab_partitions_txt(
564 system_target_files_temp_dir=system_target_files_temp_dir,
565 other_target_files_temp_dir=other_target_files_temp_dir,
566 output_target_files_temp_dir=output_target_files_temp_dir)
567
568 if rebuild_recovery:
569 append_recovery_to_filesystem_config(
570 output_target_files_temp_dir=output_target_files_temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800571
572 process_misc_info_txt(
573 system_target_files_temp_dir=system_target_files_temp_dir,
574 other_target_files_temp_dir=other_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800575 output_target_files_temp_dir=output_target_files_temp_dir,
576 system_misc_info_keys=system_misc_info_keys)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800577
Bill Peckham889b0c62019-02-21 18:53:37 -0800578 process_file_contexts_bin(
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800579 temp_dir=temp_dir,
580 output_target_files_temp_dir=output_target_files_temp_dir)
581
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800582
583def merge_target_files(
584 temp_dir,
585 system_target_files,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800586 system_item_list,
587 system_misc_info_keys,
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800588 other_target_files,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800589 other_item_list,
Daniel Normana4911da2019-03-15 14:36:21 -0700590 output_target_files,
Daniel Normanfdb38812019-04-15 09:47:24 -0700591 output_dir,
592 output_item_list,
Daniel Normanf0318252019-04-15 11:34:56 -0700593 output_super_empty,
Daniel Normana4911da2019-03-15 14:36:21 -0700594 rebuild_recovery):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800595 """Merge two target files packages together.
596
597 This function takes system and other target files packages as input, performs
598 various file extractions, special case processing, and finally creates a
599 merged zip archive as output.
600
601 Args:
602 temp_dir: The name of a directory we use when we extract items from the
603 input target files packages, and also a scratch directory that we use for
604 temporary files.
605
606 system_target_files: The name of the zip archive containing the system
607 partial target files package.
608
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800609 system_item_list: The list of items to extract from the partial system
610 target files package as is, meaning these items will land in the output
611 target files package exactly as they appear in the input partial system
612 target files package.
613
614 system_misc_info_keys: The list of keys to obtain from the system instance
615 of META/misc_info.txt. The remaining keys from the other instance.
616
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800617 other_target_files: The name of the zip archive containing the other
618 partial target files package.
619
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800620 other_item_list: The list of items to extract from the partial other
621 target files package as is, meaning these items will land in the output
622 target files package exactly as they appear in the input partial other
623 target files package.
624
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800625 output_target_files: The name of the output zip archive target files
626 package created by merging system and other.
Daniel Normana4911da2019-03-15 14:36:21 -0700627
Daniel Normanf0318252019-04-15 11:34:56 -0700628 output_super_empty: If provided, creates a super_empty.img file from the
629 merged target files package and saves it at this path.
630
Daniel Normana4911da2019-03-15 14:36:21 -0700631 rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
632 devices and write it to the system image.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800633 """
634
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800635 logger.info(
636 'starting: merge system %s and other %s into output %s',
637 system_target_files,
638 other_target_files,
639 output_target_files)
640
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800641 # Create directory names that we'll use when we extract files from system,
642 # and other, and for zipping the final output.
643
644 system_target_files_temp_dir = os.path.join(temp_dir, 'system')
645 other_target_files_temp_dir = os.path.join(temp_dir, 'other')
646 output_target_files_temp_dir = os.path.join(temp_dir, 'output')
647
648 # Extract "as is" items from the input system partial target files package.
649 # We extract them directly into the output temporary directory since the
650 # items do not need special case processing.
651
Bill Peckham889b0c62019-02-21 18:53:37 -0800652 extract_items(
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800653 target_files=system_target_files,
654 target_files_temp_dir=output_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800655 extract_item_list=system_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800656
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800657 # Extract "as is" items from the input other partial target files package. We
658 # extract them directly into the output temporary directory since the items
659 # do not need special case processing.
660
Bill Peckham889b0c62019-02-21 18:53:37 -0800661 extract_items(
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800662 target_files=other_target_files,
663 target_files_temp_dir=output_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800664 extract_item_list=other_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800665
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800666 # Extract "special" items from the input system partial target files package.
667 # We extract these items to different directory since they require special
668 # processing before they will end up in the output directory.
669
Bill Peckham889b0c62019-02-21 18:53:37 -0800670 extract_items(
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800671 target_files=system_target_files,
672 target_files_temp_dir=system_target_files_temp_dir,
673 extract_item_list=system_extract_special_item_list)
674
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800675 # Extract "special" items from the input other partial target files package.
676 # We extract these items to different directory since they require special
677 # processing before they will end up in the output directory.
678
Bill Peckham889b0c62019-02-21 18:53:37 -0800679 extract_items(
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800680 target_files=other_target_files,
681 target_files_temp_dir=other_target_files_temp_dir,
682 extract_item_list=other_extract_special_item_list)
683
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800684 # Now that the temporary directories contain all the extracted files, perform
685 # special case processing on any items that need it. After this function
686 # completes successfully, all the files we need to create the output target
687 # files package are in place.
688
Bill Peckham889b0c62019-02-21 18:53:37 -0800689 process_special_cases(
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800690 temp_dir=temp_dir,
691 system_target_files_temp_dir=system_target_files_temp_dir,
692 other_target_files_temp_dir=other_target_files_temp_dir,
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800693 output_target_files_temp_dir=output_target_files_temp_dir,
Bill Peckham364c1cc2019-03-29 18:27:23 -0700694 system_misc_info_keys=system_misc_info_keys,
695 rebuild_recovery=rebuild_recovery)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800696
Daniel Normanf0318252019-04-15 11:34:56 -0700697 # Create super_empty.img using the merged misc_info.txt.
698
699 if output_super_empty:
700 misc_info_txt = os.path.join(output_target_files_temp_dir,
701 'META', 'misc_info.txt')
702 def read_helper():
703 with open(misc_info_txt) as f:
704 return list(f.read().splitlines())
705
706 misc_info_dict = common.LoadDictionaryFromLines(read_helper())
707 if misc_info_dict.get('use_dynamic_partitions') != 'true':
708 raise ValueError(
709 'Building super_empty.img requires use_dynamic_partitions=true.')
710
711 build_super_image_args = [
712 '--verbose',
713 misc_info_txt,
714 output_super_empty,
715 ]
716 build_super_image.main(build_super_image_args)
717
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800718 # Regenerate IMAGES in the temporary directory.
719
Daniel Normana4911da2019-03-15 14:36:21 -0700720 add_img_args = ['--verbose']
721 if rebuild_recovery:
722 add_img_args.append('--rebuild_recovery')
723 add_img_args.append(output_target_files_temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800724
725 add_img_to_target_files.main(add_img_args)
726
Daniel Normanfdb38812019-04-15 09:47:24 -0700727 # Finally, create the output target files zip archive and/or copy the
728 # output items to the output target files directory.
729
730 if output_dir:
731 copy_items(output_target_files_temp_dir, output_dir, output_item_list)
732
733 if not output_target_files:
734 return
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800735
736 output_zip = os.path.abspath(output_target_files)
737 output_target_files_list = os.path.join(temp_dir, 'output.list')
738 output_target_files_meta_dir = os.path.join(
739 output_target_files_temp_dir, 'META')
740
741 command = [
742 'find',
743 output_target_files_meta_dir,
744 ]
745 # TODO(bpeckham): sort this to be more like build.
746 meta_content = common.RunAndCheckOutput(command, verbose=False)
747 command = [
748 'find',
749 output_target_files_temp_dir,
750 '-path',
751 output_target_files_meta_dir,
752 '-prune',
753 '-o',
754 '-print'
755 ]
756 # TODO(bpeckham): sort this to be more like build.
757 other_content = common.RunAndCheckOutput(command, verbose=False)
758
759 with open(output_target_files_list, 'wb') as f:
760 f.write(meta_content)
761 f.write(other_content)
762
763 command = [
Bill Peckhamf753e152019-02-19 18:02:46 -0800764 'soong_zip',
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800765 '-d',
766 '-o', output_zip,
767 '-C', output_target_files_temp_dir,
768 '-l', output_target_files_list,
769 ]
770 logger.info('creating %s', output_target_files)
Bill Peckham889b0c62019-02-21 18:53:37 -0800771 common.RunAndWait(command, verbose=True)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800772
773
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800774def call_func_with_temp_dir(func, keep_tmp):
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800775 """Manage the creation and cleanup of the temporary directory.
776
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800777 This function calls the given function after first creating a temporary
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800778 directory. It also cleans up the temporary directory.
779
780 Args:
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800781 func: The function to call. Should accept one parameter, the path to
782 the temporary directory.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800783
784 keep_tmp: Keep the temporary directory after processing is complete.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800785 """
786
787 # Create a temporary directory. This will serve as the parent of directories
788 # we use when we extract items from the input target files packages, and also
789 # a scratch directory that we use for temporary files.
790
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800791 temp_dir = common.MakeTempDir(prefix='merge_target_files_')
792
793 try:
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800794 func(temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800795 except:
796 raise
797 finally:
798 if keep_tmp:
799 logger.info('keeping %s', temp_dir)
800 else:
801 common.Cleanup()
802
803
804def main():
805 """The main function.
806
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800807 Process command line arguments, then call merge_target_files to
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800808 perform the heavy lifting.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800809 """
810
811 common.InitLogging()
812
Bill Peckhamf753e152019-02-19 18:02:46 -0800813 def option_handler(o, a):
814 if o == '--system-target-files':
815 OPTIONS.system_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800816 elif o == '--system-item-list':
817 OPTIONS.system_item_list = a
818 elif o == '--system-misc-info-keys':
819 OPTIONS.system_misc_info_keys = a
Bill Peckhamf753e152019-02-19 18:02:46 -0800820 elif o == '--other-target-files':
821 OPTIONS.other_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800822 elif o == '--other-item-list':
823 OPTIONS.other_item_list = a
Bill Peckhamf753e152019-02-19 18:02:46 -0800824 elif o == '--output-target-files':
825 OPTIONS.output_target_files = a
Daniel Normanfdb38812019-04-15 09:47:24 -0700826 elif o == '--output-dir':
827 OPTIONS.output_dir = a
828 elif o == '--output-item-list':
829 OPTIONS.output_item_list = a
Daniel Normanf0318252019-04-15 11:34:56 -0700830 elif o == '--output-super-empty':
831 OPTIONS.output_super_empty = a
Daniel Normana4911da2019-03-15 14:36:21 -0700832 elif o == '--rebuild_recovery':
833 OPTIONS.rebuild_recovery = True
Bill Peckham364c1cc2019-03-29 18:27:23 -0700834 elif o == '--keep-tmp':
Bill Peckhamf753e152019-02-19 18:02:46 -0800835 OPTIONS.keep_tmp = True
836 else:
837 return False
838 return True
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800839
Bill Peckhamf753e152019-02-19 18:02:46 -0800840 args = common.ParseOptions(
841 sys.argv[1:], __doc__,
842 extra_long_opts=[
843 'system-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800844 'system-item-list=',
845 'system-misc-info-keys=',
Bill Peckhamf753e152019-02-19 18:02:46 -0800846 'other-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800847 'other-item-list=',
Bill Peckhamf753e152019-02-19 18:02:46 -0800848 'output-target-files=',
Daniel Normanfdb38812019-04-15 09:47:24 -0700849 'output-dir=',
850 'output-item-list=',
Daniel Normanf0318252019-04-15 11:34:56 -0700851 'output-super-empty=',
Daniel Normana4911da2019-03-15 14:36:21 -0700852 'rebuild_recovery',
Bill Peckham364c1cc2019-03-29 18:27:23 -0700853 'keep-tmp',
Bill Peckhamf753e152019-02-19 18:02:46 -0800854 ],
855 extra_option_handler=option_handler)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800856
Bill Peckham889b0c62019-02-21 18:53:37 -0800857 if (len(args) != 0 or
Bill Peckhamf753e152019-02-19 18:02:46 -0800858 OPTIONS.system_target_files is None or
Daniel Normanfdb38812019-04-15 09:47:24 -0700859 OPTIONS.other_target_files is None or (
860 OPTIONS.output_target_files is None and
861 OPTIONS.output_dir is None) or (
862 OPTIONS.output_dir is not None and
863 OPTIONS.output_item_list is None)):
Bill Peckhamf753e152019-02-19 18:02:46 -0800864 common.Usage(__doc__)
Bill Peckham889b0c62019-02-21 18:53:37 -0800865 sys.exit(1)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800866
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800867 if OPTIONS.system_item_list:
868 system_item_list = read_config_list(OPTIONS.system_item_list)
869 else:
870 system_item_list = default_system_item_list
871
872 if OPTIONS.system_misc_info_keys:
873 system_misc_info_keys = read_config_list(OPTIONS.system_misc_info_keys)
874 else:
875 system_misc_info_keys = default_system_misc_info_keys
876
877 if OPTIONS.other_item_list:
878 other_item_list = read_config_list(OPTIONS.other_item_list)
879 else:
880 other_item_list = default_other_item_list
881
Daniel Normanfdb38812019-04-15 09:47:24 -0700882 if OPTIONS.output_item_list:
883 output_item_list = read_config_list(OPTIONS.output_item_list)
884 else:
885 output_item_list = None
886
Daniel Normane5964522019-03-19 10:32:03 -0700887 if not validate_config_lists(
888 system_item_list=system_item_list,
Daniel Norman19b9fe92019-03-19 14:48:02 -0700889 system_misc_info_keys=system_misc_info_keys,
Daniel Normane5964522019-03-19 10:32:03 -0700890 other_item_list=other_item_list):
891 sys.exit(1)
892
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800893 call_func_with_temp_dir(
894 lambda temp_dir: merge_target_files(
895 temp_dir=temp_dir,
896 system_target_files=OPTIONS.system_target_files,
897 system_item_list=system_item_list,
898 system_misc_info_keys=system_misc_info_keys,
899 other_target_files=OPTIONS.other_target_files,
900 other_item_list=other_item_list,
Daniel Normana4911da2019-03-15 14:36:21 -0700901 output_target_files=OPTIONS.output_target_files,
Daniel Normanfdb38812019-04-15 09:47:24 -0700902 output_dir=OPTIONS.output_dir,
903 output_item_list=output_item_list,
Daniel Normanf0318252019-04-15 11:34:56 -0700904 output_super_empty=OPTIONS.output_super_empty,
Daniel Normana4911da2019-03-15 14:36:21 -0700905 rebuild_recovery=OPTIONS.rebuild_recovery),
Daniel Norman2c99c5b2019-03-07 13:01:48 -0800906 OPTIONS.keep_tmp)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800907
908
909if __name__ == '__main__':
Bill Peckham889b0c62019-02-21 18:53:37 -0800910 main()