blob: c0c94bfbff9c141bbcb52165798db0e8d00376b6 [file] [log] [blame]
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001#!/usr/bin/env python
2#
Daniel Norman03747412022-02-25 10:38:37 -08003# Copyright (C) 2022 The Android Open Source Project
Bill Peckhame9eb5f92019-02-01 15:52:10 -08004#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
Daniel Norman4cc9df62019-07-18 10:11:07 -070016#
17"""This script merges two partial target files packages.
Bill Peckhame9eb5f92019-02-01 15:52:10 -080018
Daniel Normandbbf5a32020-10-22 16:03:32 -070019One input package contains framework files, and the other contains vendor files.
Bill Peckhame9eb5f92019-02-01 15:52:10 -080020
Daniel Normandbbf5a32020-10-22 16:03:32 -070021This 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]
Bill Peckhame9eb5f92019-02-01 15:52:10 -080028
Daniel Normand5d70ea2019-06-05 15:13:43 -070029 --framework-target-files framework-target-files-zip-archive
30 The input target files package containing framework bits. This is a zip
Bill Peckhame9eb5f92019-02-01 15:52:10 -080031 archive.
32
Daniel Normand5d70ea2019-06-05 15:13:43 -070033 --framework-item-list framework-item-list-file
Daniel Norman2c99c5b2019-03-07 13:01:48 -080034 The optional path to a newline-separated config file that replaces the
Daniel Normand5d70ea2019-06-05 15:13:43 -070035 contents of DEFAULT_FRAMEWORK_ITEM_LIST if provided.
Daniel Norman2c99c5b2019-03-07 13:01:48 -080036
Daniel Normand5d70ea2019-06-05 15:13:43 -070037 --framework-misc-info-keys framework-misc-info-keys-file
Daniel Norman2c99c5b2019-03-07 13:01:48 -080038 The optional path to a newline-separated config file that replaces the
Daniel Normand5d70ea2019-06-05 15:13:43 -070039 contents of DEFAULT_FRAMEWORK_MISC_INFO_KEYS if provided.
Daniel Norman2c99c5b2019-03-07 13:01:48 -080040
Daniel Normand5d70ea2019-06-05 15:13:43 -070041 --vendor-target-files vendor-target-files-zip-archive
42 The input target files package containing vendor bits. This is a zip
Bill Peckhame9eb5f92019-02-01 15:52:10 -080043 archive.
44
Daniel Normand5d70ea2019-06-05 15:13:43 -070045 --vendor-item-list vendor-item-list-file
Daniel Norman2c99c5b2019-03-07 13:01:48 -080046 The optional path to a newline-separated config file that replaces the
Daniel Normand5d70ea2019-06-05 15:13:43 -070047 contents of DEFAULT_VENDOR_ITEM_LIST if provided.
Daniel Norman2c99c5b2019-03-07 13:01:48 -080048
Bill Peckhame9eb5f92019-02-01 15:52:10 -080049 --output-target-files output-target-files-package
Daniel Normanfdb38812019-04-15 09:47:24 -070050 If provided, the output merged target files package. Also a zip archive.
51
52 --output-dir output-directory
53 If provided, the destination directory for saving merged files. Requires
54 the --output-item-list flag.
55 Can be provided alongside --output-target-files, or by itself.
56
57 --output-item-list output-item-list-file.
58 The optional path to a newline-separated config file that specifies the
59 file patterns to copy into the --output-dir. Required if providing
60 the --output-dir flag.
Daniel Normana4911da2019-03-15 14:36:21 -070061
Daniel Norman3b64ce12019-04-16 16:11:35 -070062 --output-ota output-ota-package
63 The output ota package. This is a zip archive. Use of this flag may
64 require passing the --path common flag; see common.py.
65
Daniel Norman1bd2a1d2019-04-18 12:32:18 -070066 --output-img output-img-package
67 The output img package, suitable for use with 'fastboot update'. Use of
68 this flag may require passing the --path common flag; see common.py.
69
Daniel Normanf0318252019-04-15 11:34:56 -070070 --output-super-empty output-super-empty-image
71 If provided, creates a super_empty.img file from the merged target
72 files package and saves it at this path.
73
Daniel Normana4911da2019-03-15 14:36:21 -070074 --rebuild_recovery
Po Hua6c59122022-02-16 08:41:29 +000075 Copy the recovery image used by non-A/B devices, used when
76 regenerating vendor images with --rebuild-sepolicy.
Bill Peckham364c1cc2019-03-29 18:27:23 -070077
Daniel Normanb0c75912020-09-24 14:30:21 -070078 --allow-duplicate-apkapex-keys
79 If provided, duplicate APK/APEX keys are ignored and the value from the
80 framework is used.
81
Daniel Norman571e1822021-06-25 17:18:25 -070082 --rebuild-sepolicy
83 If provided, rebuilds odm.img or vendor.img to include merged sepolicy
84 files. If odm is present then odm is preferred.
85
86 --vendor-otatools otatools.zip
87 If provided, use this otatools.zip when recompiling the odm or vendor
88 image to include sepolicy.
89
Bill Peckham364c1cc2019-03-29 18:27:23 -070090 --keep-tmp
91 Keep tempoary files for debugging purposes.
Jose Galmes9c8f6eb2021-07-21 09:34:08 -070092
93 The following only apply when using the VSDK to perform dexopt on vendor apps:
94
95 --framework-dexpreopt-config
96 If provided, the location of framwework's dexpreopt_config.zip.
97
98 --framework-dexpreopt-tools
99 if provided, the location of framework's dexpreopt_tools.zip.
100
101 --vendor-dexpreopt-config
102 If provided, the location of vendor's dexpreopt_config.zip.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800103"""
104
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800105import fnmatch
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700106import glob
Daniel Normand3351562020-10-29 12:33:11 -0700107import json
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800108import logging
109import os
Bill Peckham19c3feb2020-03-20 18:31:43 -0700110import re
Daniel Normanfdb38812019-04-15 09:47:24 -0700111import shutil
Bill Peckham540d91a2019-04-25 14:18:16 -0700112import subprocess
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800113import sys
114import zipfile
Daniel Norman48603ff2021-02-22 15:15:24 -0800115from xml.etree import ElementTree
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800116
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800117import add_img_to_target_files
Daniel Normane9af70a2021-04-15 16:39:22 -0700118import apex_utils
Daniel Normandb8cacc2021-04-09 15:34:43 -0700119import build_image
Daniel Normanf0318252019-04-15 11:34:56 -0700120import build_super_image
Yifan Hongade0d3f2019-08-21 16:37:11 -0700121import check_target_files_vintf
Daniel Normanf0318252019-04-15 11:34:56 -0700122import common
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700123import img_from_target_files
Daniel Normanb8d52a22020-10-26 17:55:00 -0700124import find_shareduid_violation
Daniel Norman3b64ce12019-04-16 16:11:35 -0700125import ota_from_target_files
Daniel Normandb8cacc2021-04-09 15:34:43 -0700126import sparse_img
127import verity_utils
128
Daniel Normana84d13b2022-02-17 14:16:40 -0800129from common import ExternalError
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800130
131logger = logging.getLogger(__name__)
Tao Bao2ad4b822019-06-27 16:52:12 -0700132
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800133OPTIONS = common.OPTIONS
Bill Peckhamcb848172020-04-03 12:50:47 -0700134# Always turn on verbose logging.
135OPTIONS.verbose = True
Daniel Normand5d70ea2019-06-05 15:13:43 -0700136OPTIONS.framework_target_files = None
137OPTIONS.framework_item_list = None
138OPTIONS.framework_misc_info_keys = None
139OPTIONS.vendor_target_files = None
140OPTIONS.vendor_item_list = None
Bill Peckhamf753e152019-02-19 18:02:46 -0800141OPTIONS.output_target_files = None
Daniel Normanfdb38812019-04-15 09:47:24 -0700142OPTIONS.output_dir = None
143OPTIONS.output_item_list = None
Daniel Norman3b64ce12019-04-16 16:11:35 -0700144OPTIONS.output_ota = None
Daniel Norman1bd2a1d2019-04-18 12:32:18 -0700145OPTIONS.output_img = None
Daniel Normanf0318252019-04-15 11:34:56 -0700146OPTIONS.output_super_empty = None
Daniel Normana4911da2019-03-15 14:36:21 -0700147OPTIONS.rebuild_recovery = False
Daniel Normanb0c75912020-09-24 14:30:21 -0700148# TODO(b/150582573): Remove this option.
149OPTIONS.allow_duplicate_apkapex_keys = False
Daniel Norman571e1822021-06-25 17:18:25 -0700150OPTIONS.vendor_otatools = None
151OPTIONS.rebuild_sepolicy = False
Bill Peckhamf753e152019-02-19 18:02:46 -0800152OPTIONS.keep_tmp = False
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700153OPTIONS.framework_dexpreopt_config = None
154OPTIONS.framework_dexpreopt_tools = None
155OPTIONS.vendor_dexpreopt_config = None
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800156
Bill Peckham19c3feb2020-03-20 18:31:43 -0700157# In an item list (framework or vendor), we may see entries that select whole
158# partitions. Such an entry might look like this 'SYSTEM/*' (e.g., for the
159# system partition). The following regex matches this and extracts the
160# partition name.
161
162PARTITION_ITEM_PATTERN = re.compile(r'^([A-Z_]+)/\*$')
163
Bill Peckham5c7b0342020-04-03 15:36:23 -0700164# In apexkeys.txt or apkcerts.txt, we will find partition tags on each entry in
165# the file. We use these partition tags to filter the entries in those files
166# from the two different target files packages to produce a merged apexkeys.txt
167# or apkcerts.txt file. A partition tag (e.g., for the product partition) looks
168# like this: 'partition="product"'. We use the group syntax grab the value of
169# the tag. We use non-greedy matching in case there are other fields on the
170# same line.
Bill Peckham19c3feb2020-03-20 18:31:43 -0700171
Bill Peckham5c7b0342020-04-03 15:36:23 -0700172PARTITION_TAG_PATTERN = re.compile(r'partition="(.*?)"')
Bill Peckham19c3feb2020-03-20 18:31:43 -0700173
174# The sorting algorithm for apexkeys.txt and apkcerts.txt does not include the
175# ".apex" or ".apk" suffix, so we use the following pattern to extract a key.
176
177MODULE_KEY_PATTERN = re.compile(r'name="(.+)\.(apex|apk)"')
178
Daniel Normand5d70ea2019-06-05 15:13:43 -0700179# DEFAULT_FRAMEWORK_ITEM_LIST is a list of items to extract from the partial
180# framework target files package as is, meaning these items will land in the
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800181# output target files package exactly as they appear in the input partial
Daniel Normand5d70ea2019-06-05 15:13:43 -0700182# framework target files package.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800183
Daniel Normand5d70ea2019-06-05 15:13:43 -0700184DEFAULT_FRAMEWORK_ITEM_LIST = (
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800185 'META/apkcerts.txt',
186 'META/filesystem_config.txt',
187 'META/root_filesystem_config.txt',
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800188 'META/update_engine_config.txt',
189 'PRODUCT/*',
190 'ROOT/*',
191 'SYSTEM/*',
Daniel Normanedf12472019-05-22 10:47:08 -0700192)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800193
Daniel Normand5d70ea2019-06-05 15:13:43 -0700194# DEFAULT_FRAMEWORK_MISC_INFO_KEYS is a list of keys to obtain from the
Daniel Normandbbf5a32020-10-22 16:03:32 -0700195# framework instance of META/misc_info.txt. The remaining keys should come
196# from the vendor instance.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800197
Daniel Normand5d70ea2019-06-05 15:13:43 -0700198DEFAULT_FRAMEWORK_MISC_INFO_KEYS = (
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800199 'avb_system_hashtree_enable',
200 'avb_system_add_hashtree_footer_args',
201 'avb_system_key_path',
202 'avb_system_algorithm',
203 'avb_system_rollback_index_location',
204 'avb_product_hashtree_enable',
205 'avb_product_add_hashtree_footer_args',
Justin Yun6151e3f2019-06-25 15:58:13 +0900206 'avb_system_ext_hashtree_enable',
207 'avb_system_ext_add_hashtree_footer_args',
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800208 'system_root_image',
209 'root_dir',
210 'ab_update',
211 'default_system_dev_certificate',
212 'system_size',
Chris Gross203191b2020-05-30 02:39:12 +0000213 'building_system_image',
214 'building_system_ext_image',
215 'building_product_image',
Daniel Normanedf12472019-05-22 10:47:08 -0700216)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800217
Daniel Normand5d70ea2019-06-05 15:13:43 -0700218# DEFAULT_VENDOR_ITEM_LIST is a list of items to extract from the partial
219# vendor target files package as is, meaning these items will land in the output
220# target files package exactly as they appear in the input partial vendor target
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800221# files package.
222
Daniel Normand5d70ea2019-06-05 15:13:43 -0700223DEFAULT_VENDOR_ITEM_LIST = (
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800224 'META/boot_filesystem_config.txt',
225 'META/otakeys.txt',
226 'META/releasetools.py',
227 'META/vendor_filesystem_config.txt',
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800228 'BOOT/*',
229 'DATA/*',
230 'ODM/*',
231 'OTA/android-info.txt',
232 'PREBUILT_IMAGES/*',
233 'RADIO/*',
234 'VENDOR/*',
Daniel Normanedf12472019-05-22 10:47:08 -0700235)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800236
Daniel Normanedf12472019-05-22 10:47:08 -0700237# The merge config lists should not attempt to extract items from both
238# builds for any of the following partitions. The partitions in
239# SINGLE_BUILD_PARTITIONS should come entirely from a single build (either
Daniel Normand5d70ea2019-06-05 15:13:43 -0700240# framework or vendor, but not both).
Daniel Normanedf12472019-05-22 10:47:08 -0700241
242SINGLE_BUILD_PARTITIONS = (
243 'BOOT/',
244 'DATA/',
245 'ODM/',
246 'PRODUCT/',
Justin Yun6151e3f2019-06-25 15:58:13 +0900247 'SYSTEM_EXT/',
Daniel Normanedf12472019-05-22 10:47:08 -0700248 'RADIO/',
249 'RECOVERY/',
250 'ROOT/',
251 'SYSTEM/',
252 'SYSTEM_OTHER/',
253 'VENDOR/',
Yifan Hongcfb917a2020-05-07 14:58:20 -0700254 'VENDOR_DLKM/',
Yifan Hongf496f1b2020-07-15 16:52:59 -0700255 'ODM_DLKM/',
Ramji Jiyani13a41372022-01-27 07:05:08 +0000256 'SYSTEM_DLKM/',
Daniel Normanedf12472019-05-22 10:47:08 -0700257)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800258
259
Chris Grossfabf50a2019-05-02 12:42:09 -0700260def write_sorted_data(data, path):
Tao Bao2ad4b822019-06-27 16:52:12 -0700261 """Writes the sorted contents of either a list or dict to file.
Chris Grossfabf50a2019-05-02 12:42:09 -0700262
Tao Bao2ad4b822019-06-27 16:52:12 -0700263 This function sorts the contents of the list or dict and then writes the
264 resulting sorted contents to a file specified by path.
Chris Grossfabf50a2019-05-02 12:42:09 -0700265
266 Args:
267 data: The list or dict to sort and write.
268 path: Path to the file to write the sorted values to. The file at path will
269 be overridden if it exists.
270 """
271 with open(path, 'w') as output:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700272 for entry in sorted(data):
Chris Grossfabf50a2019-05-02 12:42:09 -0700273 out_str = '{}={}\n'.format(entry, data[entry]) if isinstance(
274 data, dict) else '{}\n'.format(entry)
275 output.write(out_str)
276
277
Daniel Norman03747412022-02-25 10:38:37 -0800278def extract_items(input_zip, output_dir, extract_item_list):
279 """Extracts items in extra_item_list from a zip to a dir."""
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800280
Daniel Norman03747412022-02-25 10:38:37 -0800281 logger.info('extracting from %s', input_zip)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800282
283 # Filter the extract_item_list to remove any items that do not exist in the
284 # zip file. Otherwise, the extraction step will fail.
285
Daniel Norman03747412022-02-25 10:38:37 -0800286 with zipfile.ZipFile(input_zip, allowZip64=True) as input_zipfile:
287 input_namelist = input_zipfile.namelist()
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800288
289 filtered_extract_item_list = []
290 for pattern in extract_item_list:
Daniel Norman03747412022-02-25 10:38:37 -0800291 matching_namelist = fnmatch.filter(input_namelist, pattern)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800292 if not matching_namelist:
293 logger.warning('no match for %s', pattern)
294 else:
295 filtered_extract_item_list.append(pattern)
296
Daniel Norman03747412022-02-25 10:38:37 -0800297 common.UnzipToDir(input_zip, output_dir, filtered_extract_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800298
299
Daniel Normanfdb38812019-04-15 09:47:24 -0700300def copy_items(from_dir, to_dir, patterns):
301 """Similar to extract_items() except uses an input dir instead of zip."""
302 file_paths = []
303 for dirpath, _, filenames in os.walk(from_dir):
Daniel Normane5b134a2019-04-17 14:54:06 -0700304 file_paths.extend(
305 os.path.relpath(path=os.path.join(dirpath, filename), start=from_dir)
306 for filename in filenames)
Daniel Normanfdb38812019-04-15 09:47:24 -0700307
308 filtered_file_paths = set()
309 for pattern in patterns:
310 filtered_file_paths.update(fnmatch.filter(file_paths, pattern))
311
312 for file_path in filtered_file_paths:
313 original_file_path = os.path.join(from_dir, file_path)
314 copied_file_path = os.path.join(to_dir, file_path)
315 copied_file_dir = os.path.dirname(copied_file_path)
316 if not os.path.exists(copied_file_dir):
317 os.makedirs(copied_file_dir)
318 if os.path.islink(original_file_path):
319 os.symlink(os.readlink(original_file_path), copied_file_path)
320 else:
321 shutil.copyfile(original_file_path, copied_file_path)
322
323
Daniel Norman03747412022-02-25 10:38:37 -0800324def validate_config_lists():
Daniel Normane5964522019-03-19 10:32:03 -0700325 """Performs validations on the merge config lists.
326
Daniel Normane5964522019-03-19 10:32:03 -0700327 Returns:
328 False if a validation fails, otherwise true.
329 """
Daniel Normanedf12472019-05-22 10:47:08 -0700330 has_error = False
331
Daniel Normand5d70ea2019-06-05 15:13:43 -0700332 default_combined_item_set = set(DEFAULT_FRAMEWORK_ITEM_LIST)
333 default_combined_item_set.update(DEFAULT_VENDOR_ITEM_LIST)
Daniel Normane5964522019-03-19 10:32:03 -0700334
Daniel Norman03747412022-02-25 10:38:37 -0800335 combined_item_set = set(OPTIONS.framework_item_list)
336 combined_item_set.update(OPTIONS.vendor_item_list)
Daniel Normane5964522019-03-19 10:32:03 -0700337
338 # Check that the merge config lists are not missing any item specified
339 # by the default config lists.
340 difference = default_combined_item_set.difference(combined_item_set)
341 if difference:
Daniel Normane5b134a2019-04-17 14:54:06 -0700342 logger.error('Missing merge config items: %s', list(difference))
Daniel Normane5964522019-03-19 10:32:03 -0700343 logger.error('Please ensure missing items are in either the '
Daniel Normand5d70ea2019-06-05 15:13:43 -0700344 'framework-item-list or vendor-item-list files provided to '
Daniel Normane5964522019-03-19 10:32:03 -0700345 'this script.')
Daniel Normanedf12472019-05-22 10:47:08 -0700346 has_error = True
347
Daniel Normandbbf5a32020-10-22 16:03:32 -0700348 # Check that partitions only come from one input.
Daniel Normanedf12472019-05-22 10:47:08 -0700349 for partition in SINGLE_BUILD_PARTITIONS:
Daniel Normandbbf5a32020-10-22 16:03:32 -0700350 image_path = 'IMAGES/{}.img'.format(partition.lower().replace('/', ''))
351 in_framework = (
Daniel Norman03747412022-02-25 10:38:37 -0800352 any(item.startswith(partition) for item in OPTIONS.framework_item_list)
353 or image_path in OPTIONS.framework_item_list)
Daniel Normandbbf5a32020-10-22 16:03:32 -0700354 in_vendor = (
Daniel Norman03747412022-02-25 10:38:37 -0800355 any(item.startswith(partition) for item in OPTIONS.vendor_item_list) or
356 image_path in OPTIONS.vendor_item_list)
Daniel Normand5d70ea2019-06-05 15:13:43 -0700357 if in_framework and in_vendor:
Daniel Normanedf12472019-05-22 10:47:08 -0700358 logger.error(
Tao Bao2ad4b822019-06-27 16:52:12 -0700359 'Cannot extract items from %s for both the framework and vendor'
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900360 ' builds. Please ensure only one merge config item list'
Tao Bao2ad4b822019-06-27 16:52:12 -0700361 ' includes %s.', partition, partition)
Daniel Normanedf12472019-05-22 10:47:08 -0700362 has_error = True
Daniel Normane5964522019-03-19 10:32:03 -0700363
Daniel Norman03747412022-02-25 10:38:37 -0800364 if ('dynamic_partition_list' in OPTIONS.framework_misc_info_keys) or (
365 'super_partition_groups' in OPTIONS.framework_misc_info_keys):
Daniel Norman19b9fe92019-03-19 14:48:02 -0700366 logger.error('Dynamic partition misc info keys should come from '
Daniel Normand5d70ea2019-06-05 15:13:43 -0700367 'the vendor instance of META/misc_info.txt.')
Daniel Normanedf12472019-05-22 10:47:08 -0700368 has_error = True
Daniel Norman19b9fe92019-03-19 14:48:02 -0700369
Daniel Normanedf12472019-05-22 10:47:08 -0700370 return not has_error
Daniel Normane5964522019-03-19 10:32:03 -0700371
372
Daniel Norman03747412022-02-25 10:38:37 -0800373def merge_ab_partitions_txt(framework_meta_dir, vendor_meta_dir,
374 merged_meta_dir):
375 """Merges META/ab_partitions.txt.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800376
Daniel Norman03747412022-02-25 10:38:37 -0800377 The output contains the union of the partition names.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800378 """
Daniel Norman03747412022-02-25 10:38:37 -0800379 with open(os.path.join(framework_meta_dir, 'ab_partitions.txt')) as f:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700380 framework_ab_partitions = f.read().splitlines()
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800381
Daniel Norman03747412022-02-25 10:38:37 -0800382 with open(os.path.join(vendor_meta_dir, 'ab_partitions.txt')) as f:
Daniel Normand5d70ea2019-06-05 15:13:43 -0700383 vendor_ab_partitions = f.read().splitlines()
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800384
Daniel Norman03747412022-02-25 10:38:37 -0800385 write_sorted_data(
386 data=set(framework_ab_partitions + vendor_ab_partitions),
387 path=os.path.join(merged_meta_dir, 'ab_partitions.txt'))
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800388
389
Daniel Norman03747412022-02-25 10:38:37 -0800390def merge_misc_info_txt(framework_meta_dir, vendor_meta_dir, merged_meta_dir):
391 """Merges META/misc_info.txt.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800392
Daniel Norman03747412022-02-25 10:38:37 -0800393 The output contains a combination of key=value pairs from both inputs.
394 Most pairs are taken from the vendor input, while some are taken from
395 the framework input.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800396 """
397
Daniel Norman03747412022-02-25 10:38:37 -0800398 OPTIONS.framework_misc_info = common.LoadDictionaryFromFile(
399 os.path.join(framework_meta_dir, 'misc_info.txt'))
400 OPTIONS.vendor_misc_info = common.LoadDictionaryFromFile(
401 os.path.join(vendor_meta_dir, 'misc_info.txt'))
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800402
Daniel Norman03747412022-02-25 10:38:37 -0800403 # Merged misc info is a combination of vendor misc info plus certain values
404 # from the framework misc info.
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800405
Daniel Norman03747412022-02-25 10:38:37 -0800406 merged_dict = OPTIONS.vendor_misc_info
407 for key in OPTIONS.framework_misc_info_keys:
408 merged_dict[key] = OPTIONS.framework_misc_info[key]
Daniel Norman19b9fe92019-03-19 14:48:02 -0700409
Daniel Norman38888d32020-11-19 14:51:15 -0800410 # If AVB is enabled then ensure that we build vbmeta.img.
411 # Partial builds with AVB enabled may set PRODUCT_BUILD_VBMETA_IMAGE=false to
412 # skip building an incomplete vbmeta.img.
413 if merged_dict.get('avb_enable') == 'true':
414 merged_dict['avb_building_vbmeta_image'] = 'true'
415
Daniel Norman03747412022-02-25 10:38:37 -0800416 return merged_dict
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800417
418
Daniel Norman03747412022-02-25 10:38:37 -0800419def merge_dynamic_partitions_info_txt(framework_meta_dir, vendor_meta_dir,
420 merged_meta_dir):
421 """Merge META/dynamic_partitions_info.txt."""
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900422 framework_dynamic_partitions_dict = common.LoadDictionaryFromFile(
Daniel Norman03747412022-02-25 10:38:37 -0800423 os.path.join(framework_meta_dir, 'dynamic_partitions_info.txt'))
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +0900424 vendor_dynamic_partitions_dict = common.LoadDictionaryFromFile(
Daniel Norman03747412022-02-25 10:38:37 -0800425 os.path.join(vendor_meta_dir, 'dynamic_partitions_info.txt'))
Daniel Normana61cde02019-05-03 14:19:13 -0700426
Daniel Normanbfc51ef2019-07-24 14:34:54 -0700427 merged_dynamic_partitions_dict = common.MergeDynamicPartitionInfoDicts(
Daniel Normand5d70ea2019-06-05 15:13:43 -0700428 framework_dict=framework_dynamic_partitions_dict,
Daniel Norman55417142019-11-25 16:04:36 -0800429 vendor_dict=vendor_dynamic_partitions_dict)
Daniel Normana61cde02019-05-03 14:19:13 -0700430
Chris Grossfabf50a2019-05-02 12:42:09 -0700431 write_sorted_data(
432 data=merged_dynamic_partitions_dict,
Daniel Norman03747412022-02-25 10:38:37 -0800433 path=os.path.join(merged_meta_dir, 'dynamic_partitions_info.txt'))
434
435 # Merge misc info keys used for Dynamic Partitions.
436 OPTIONS.merged_misc_info.update(merged_dynamic_partitions_dict)
437 # Ensure that add_img_to_target_files rebuilds super split images for
438 # devices that retrofit dynamic partitions. This flag may have been set to
439 # false in the partial builds to prevent duplicate building of super.img.
440 OPTIONS.merged_misc_info['build_super_partition'] = 'true'
Chris Grossfabf50a2019-05-02 12:42:09 -0700441
442
Bill Peckham19c3feb2020-03-20 18:31:43 -0700443def item_list_to_partition_set(item_list):
444 """Converts a target files item list to a partition set.
445
446 The item list contains items that might look like 'SYSTEM/*' or 'VENDOR/*' or
447 'OTA/android-info.txt'. Items that end in '/*' are assumed to match entire
448 directories where 'SYSTEM' or 'VENDOR' is a directory name that identifies the
449 contents of a partition of the same name. Other items in the list, such as the
450 'OTA' example contain metadata. This function iterates such a list, returning
451 a set that contains the partition entries.
452
453 Args:
454 item_list: A list of items in a target files package.
Daniel Normanb0c75912020-09-24 14:30:21 -0700455
Bill Peckham19c3feb2020-03-20 18:31:43 -0700456 Returns:
457 A set of partitions extracted from the list of items.
458 """
459
460 partition_set = set()
461
462 for item in item_list:
463 match = PARTITION_ITEM_PATTERN.search(item.strip())
464 partition_tag = match.group(1).lower() if match else None
465
466 if partition_tag:
467 partition_set.add(partition_tag)
468
469 return partition_set
470
471
Daniel Norman03747412022-02-25 10:38:37 -0800472def merge_package_keys_txt(framework_meta_dir, vendor_meta_dir, merged_meta_dir,
473 file_name):
474 """Merges APK/APEX key list files."""
Chris Grossfabf50a2019-05-02 12:42:09 -0700475
Daniel Norman03747412022-02-25 10:38:37 -0800476 if file_name not in ('apkcerts.txt', 'apexkeys.txt'):
477 raise ExternalError(
478 'Unexpected file_name provided to merge_package_keys_txt: %s',
479 file_name)
Chris Grossfabf50a2019-05-02 12:42:09 -0700480
481 def read_helper(d):
482 temp = {}
Daniel Norman03747412022-02-25 10:38:37 -0800483 with open(os.path.join(d, file_name)) as f:
484 for line in f.read().splitlines():
485 line = line.strip()
486 if line:
487 name_search = MODULE_KEY_PATTERN.search(line.split()[0])
488 temp[name_search.group(1)] = line
Chris Grossfabf50a2019-05-02 12:42:09 -0700489 return temp
490
Daniel Norman03747412022-02-25 10:38:37 -0800491 framework_dict = read_helper(framework_meta_dir)
492 vendor_dict = read_helper(vendor_meta_dir)
Bill Peckham19c3feb2020-03-20 18:31:43 -0700493 merged_dict = {}
Chris Grossfabf50a2019-05-02 12:42:09 -0700494
Bill Peckham19c3feb2020-03-20 18:31:43 -0700495 def filter_into_merged_dict(item_dict, partition_set):
496 for key, value in item_dict.items():
Daniel Norman03747412022-02-25 10:38:37 -0800497 tag_search = PARTITION_TAG_PATTERN.search(value)
Bill Peckham19c3feb2020-03-20 18:31:43 -0700498
Daniel Norman03747412022-02-25 10:38:37 -0800499 if tag_search is None:
Bill Peckham19c3feb2020-03-20 18:31:43 -0700500 raise ValueError('Entry missing partition tag: %s' % value)
501
Daniel Norman03747412022-02-25 10:38:37 -0800502 partition_tag = tag_search.group(1)
Bill Peckham19c3feb2020-03-20 18:31:43 -0700503
504 if partition_tag in partition_set:
505 if key in merged_dict:
Daniel Normanb0c75912020-09-24 14:30:21 -0700506 if OPTIONS.allow_duplicate_apkapex_keys:
507 # TODO(b/150582573) Always raise on duplicates.
508 logger.warning('Duplicate key %s' % key)
509 continue
510 else:
511 raise ValueError('Duplicate key %s' % key)
Bill Peckham19c3feb2020-03-20 18:31:43 -0700512
513 merged_dict[key] = value
514
Daniel Norman03747412022-02-25 10:38:37 -0800515 # Prioritize framework keys first.
516 # Duplicate keys from vendor are an error, or ignored.
517 filter_into_merged_dict(framework_dict, OPTIONS.framework_partition_set)
518 filter_into_merged_dict(vendor_dict, OPTIONS.vendor_partition_set)
Chris Grossfabf50a2019-05-02 12:42:09 -0700519
Bill Peckham19c3feb2020-03-20 18:31:43 -0700520 # The following code is similar to write_sorted_data, but different enough
521 # that we couldn't use that function. We need the output to be sorted by the
522 # basename of the apex/apk (without the ".apex" or ".apk" suffix). This
523 # allows the sort to be consistent with the framework/vendor input data and
524 # eases comparison of input data with merged data.
Daniel Norman03747412022-02-25 10:38:37 -0800525 with open(os.path.join(merged_meta_dir, file_name), 'w') as output:
526 for key, value in sorted(merged_dict.items()):
527 output.write(value + '\n')
Daniel Normana61cde02019-05-03 14:19:13 -0700528
529
Daniel Norman03747412022-02-25 10:38:37 -0800530def create_file_contexts_copies(framework_meta_dir, vendor_meta_dir,
531 merged_meta_dir):
532 """Creates named copies of each partial build's file_contexts.bin.
Daniel Normand5d70ea2019-06-05 15:13:43 -0700533
Daniel Norman03747412022-02-25 10:38:37 -0800534 Used when regenerating images from the partial build.
535 """
536
537 def copy_fc_file(source_dir, file_name):
538 for name in (file_name, 'file_contexts.bin'):
539 fc_path = os.path.join(source_dir, name)
540 if os.path.exists(fc_path):
541 shutil.copyfile(fc_path, os.path.join(merged_meta_dir, file_name))
542 return
543 raise ValueError('Missing file_contexts file from %s: %s', source_dir,
544 file_name)
545
546 copy_fc_file(framework_meta_dir, 'framework_file_contexts.bin')
547 copy_fc_file(vendor_meta_dir, 'vendor_file_contexts.bin')
548
549 # Replace <image>_selinux_fc values with framework or vendor file_contexts.bin
550 # depending on which dictionary the key came from.
551 # Only the file basename is required because all selinux_fc properties are
552 # replaced with the full path to the file under META/ when misc_info.txt is
553 # loaded from target files for repacking. See common.py LoadInfoDict().
554 for key in OPTIONS.vendor_misc_info:
555 if key.endswith('_selinux_fc'):
556 OPTIONS.merged_misc_info[key] = 'vendor_file_contexts.bin'
557 for key in OPTIONS.framework_misc_info:
558 if key.endswith('_selinux_fc'):
559 OPTIONS.merged_misc_info[key] = 'framework_file_contexts.bin'
Daniel Norman72c626f2019-05-13 15:58:14 -0700560
561
Daniel Norman03747412022-02-25 10:38:37 -0800562def compile_split_sepolicy(target_files_dir, partition_map):
Daniel Norman48603ff2021-02-22 15:15:24 -0800563 """Uses secilc to compile a split sepolicy file.
564
565 Depends on various */etc/selinux/* and */etc/vintf/* files within partitions.
566
567 Args:
Daniel Norman03747412022-02-25 10:38:37 -0800568 target_files_dir: Extracted directory of target_files, containing partition
569 directories.
570 partition_map: A map of partition name -> relative path within
571 target_files_dir.
Daniel Norman48603ff2021-02-22 15:15:24 -0800572
573 Returns:
574 A command list that can be executed to create the compiled sepolicy.
575 """
576
577 def get_file(partition, path):
578 if partition not in partition_map:
579 logger.warning('Cannot load SEPolicy files for missing partition %s',
580 partition)
581 return None
Daniel Norman03747412022-02-25 10:38:37 -0800582 return os.path.join(target_files_dir, partition_map[partition], path)
Daniel Norman48603ff2021-02-22 15:15:24 -0800583
584 # Load the kernel sepolicy version from the FCM. This is normally provided
585 # directly to selinux.cpp as a build flag, but is also available in this file.
586 fcm_file = get_file('system', 'etc/vintf/compatibility_matrix.device.xml')
587 if not fcm_file or not os.path.exists(fcm_file):
588 raise ExternalError('Missing required file for loading sepolicy: %s', fcm)
589 kernel_sepolicy_version = ElementTree.parse(fcm_file).getroot().find(
590 'sepolicy/kernel-sepolicy-version').text
591
592 # Load the vendor's plat sepolicy version. This is the version used for
593 # locating sepolicy mapping files.
594 vendor_plat_version_file = get_file('vendor',
595 'etc/selinux/plat_sepolicy_vers.txt')
596 if not vendor_plat_version_file or not os.path.exists(
Daniel Norman2d7989a2021-04-05 17:40:47 +0000597 vendor_plat_version_file):
Daniel Norman48603ff2021-02-22 15:15:24 -0800598 raise ExternalError('Missing required sepolicy file %s',
599 vendor_plat_version_file)
600 with open(vendor_plat_version_file) as f:
601 vendor_plat_version = f.read().strip()
602
603 # Use the same flags and arguments as selinux.cpp OpenSplitPolicy().
604 cmd = ['secilc', '-m', '-M', 'true', '-G', '-N']
605 cmd.extend(['-c', kernel_sepolicy_version])
Daniel Norman03747412022-02-25 10:38:37 -0800606 cmd.extend(['-o', os.path.join(target_files_dir, 'META/combined_sepolicy')])
Daniel Norman48603ff2021-02-22 15:15:24 -0800607 cmd.extend(['-f', '/dev/null'])
608
609 required_policy_files = (
610 ('system', 'etc/selinux/plat_sepolicy.cil'),
611 ('system', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
612 ('vendor', 'etc/selinux/vendor_sepolicy.cil'),
613 ('vendor', 'etc/selinux/plat_pub_versioned.cil'),
614 )
615 for policy in (map(lambda partition_and_path: get_file(*partition_and_path),
616 required_policy_files)):
617 if not policy or not os.path.exists(policy):
618 raise ExternalError('Missing required sepolicy file %s', policy)
619 cmd.append(policy)
620
621 optional_policy_files = (
622 ('system', 'etc/selinux/mapping/%s.compat.cil' % vendor_plat_version),
623 ('system_ext', 'etc/selinux/system_ext_sepolicy.cil'),
624 ('system_ext', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
625 ('product', 'etc/selinux/product_sepolicy.cil'),
626 ('product', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
627 ('odm', 'etc/selinux/odm_sepolicy.cil'),
628 )
629 for policy in (map(lambda partition_and_path: get_file(*partition_and_path),
630 optional_policy_files)):
631 if policy and os.path.exists(policy):
632 cmd.append(policy)
633
634 return cmd
635
636
Daniel Norman03747412022-02-25 10:38:37 -0800637def validate_merged_apex_info(target_files_dir, partitions):
Daniel Normane9af70a2021-04-15 16:39:22 -0700638 """Validates the APEX files in the merged target files directory.
639
640 Checks the APEX files in all possible preinstalled APEX directories.
641 Depends on the <partition>/apex/* APEX files within partitions.
642
643 Args:
Daniel Norman03747412022-02-25 10:38:37 -0800644 target_files_dir: Extracted directory of target_files, containing partition
Daniel Norman571e1822021-06-25 17:18:25 -0700645 directories.
Daniel Normane9af70a2021-04-15 16:39:22 -0700646 partitions: A list of all the partitions in the output directory.
647
648 Raises:
649 RuntimeError: if apex_utils fails to parse any APEX file.
650 ExternalError: if the same APEX package is provided by multiple partitions.
651 """
652 apex_packages = set()
653
Daniel Norman03747412022-02-25 10:38:37 -0800654 apex_partitions = ('system', 'system_ext', 'product', 'vendor', 'odm')
Daniel Normane9af70a2021-04-15 16:39:22 -0700655 for partition in filter(lambda p: p in apex_partitions, partitions):
656 apex_info = apex_utils.GetApexInfoFromTargetFiles(
Daniel Norman03747412022-02-25 10:38:37 -0800657 target_files_dir, partition, compressed_only=False)
Daniel Normane9af70a2021-04-15 16:39:22 -0700658 partition_apex_packages = set([info.package_name for info in apex_info])
659 duplicates = apex_packages.intersection(partition_apex_packages)
660 if duplicates:
661 raise ExternalError(
662 'Duplicate APEX packages found in multiple partitions: %s' %
663 ' '.join(duplicates))
664 apex_packages.update(partition_apex_packages)
665
666
Daniel Norman03747412022-02-25 10:38:37 -0800667def generate_care_map(partitions, target_files_dir):
668 """Generates a merged META/care_map.pb file in the target files dir.
Daniel Normandb8cacc2021-04-09 15:34:43 -0700669
670 Depends on the info dict from META/misc_info.txt, as well as built images
671 within IMAGES/.
672
673 Args:
674 partitions: A list of partitions to potentially include in the care map.
Daniel Norman03747412022-02-25 10:38:37 -0800675 target_files_dir: Extracted directory of target_files, containing partition
676 directories.
Daniel Normandb8cacc2021-04-09 15:34:43 -0700677 """
Daniel Norman03747412022-02-25 10:38:37 -0800678 OPTIONS.info_dict = common.LoadInfoDict(target_files_dir)
Daniel Normandb8cacc2021-04-09 15:34:43 -0700679 partition_image_map = {}
680 for partition in partitions:
Daniel Norman03747412022-02-25 10:38:37 -0800681 image_path = os.path.join(target_files_dir, 'IMAGES',
Daniel Normandb8cacc2021-04-09 15:34:43 -0700682 '{}.img'.format(partition))
683 if os.path.exists(image_path):
684 partition_image_map[partition] = image_path
685 # Regenerated images should have their image_size property already set.
686 image_size_prop = '{}_image_size'.format(partition)
687 if image_size_prop not in OPTIONS.info_dict:
688 # Images copied directly from input target files packages will need
689 # their image sizes calculated.
690 partition_size = sparse_img.GetImagePartitionSize(image_path)
691 image_props = build_image.ImagePropFromGlobalDict(
692 OPTIONS.info_dict, partition)
693 verity_image_builder = verity_utils.CreateVerityImageBuilder(
694 image_props)
695 image_size = verity_image_builder.CalculateMaxImageSize(partition_size)
696 OPTIONS.info_dict[image_size_prop] = image_size
697
Daniel Normandb8cacc2021-04-09 15:34:43 -0700698
Daniel Norman03747412022-02-25 10:38:37 -0800699def merge_meta_files(temp_dir, merged_dir):
700 """Merges various files in META/*."""
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800701
Daniel Norman03747412022-02-25 10:38:37 -0800702 framework_meta_dir = os.path.join(temp_dir, 'framework_meta', 'META')
703 extract_items(
704 input_zip=OPTIONS.framework_target_files,
705 output_dir=os.path.dirname(framework_meta_dir),
706 extract_item_list=('META/*',))
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800707
Daniel Norman03747412022-02-25 10:38:37 -0800708 vendor_meta_dir = os.path.join(temp_dir, 'vendor_meta', 'META')
709 extract_items(
710 input_zip=OPTIONS.vendor_target_files,
711 output_dir=os.path.dirname(vendor_meta_dir),
712 extract_item_list=('META/*',))
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800713
Daniel Norman03747412022-02-25 10:38:37 -0800714 merged_meta_dir = os.path.join(merged_dir, 'META')
Bill Peckham364c1cc2019-03-29 18:27:23 -0700715
Daniel Norman03747412022-02-25 10:38:37 -0800716 # Merge META/misc_info.txt into OPTIONS.merged_misc_info,
717 # but do not write it yet. The following functions may further
718 # modify this dict.
719 OPTIONS.merged_misc_info = merge_misc_info_txt(
720 framework_meta_dir=framework_meta_dir,
721 vendor_meta_dir=vendor_meta_dir,
722 merged_meta_dir=merged_meta_dir)
Daniel Norman72c626f2019-05-13 15:58:14 -0700723
Daniel Norman03747412022-02-25 10:38:37 -0800724 create_file_contexts_copies(
725 framework_meta_dir=framework_meta_dir,
726 vendor_meta_dir=vendor_meta_dir,
727 merged_meta_dir=merged_meta_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -0800728
Daniel Norman03747412022-02-25 10:38:37 -0800729 if OPTIONS.merged_misc_info['use_dynamic_partitions'] == 'true':
730 merge_dynamic_partitions_info_txt(
731 framework_meta_dir=framework_meta_dir,
732 vendor_meta_dir=vendor_meta_dir,
733 merged_meta_dir=merged_meta_dir)
Daniel Normana61cde02019-05-03 14:19:13 -0700734
Daniel Norman03747412022-02-25 10:38:37 -0800735 if OPTIONS.merged_misc_info['ab_update'] == 'true':
736 merge_ab_partitions_txt(
737 framework_meta_dir=framework_meta_dir,
738 vendor_meta_dir=vendor_meta_dir,
739 merged_meta_dir=merged_meta_dir)
Chris Grossfabf50a2019-05-02 12:42:09 -0700740
Daniel Norman03747412022-02-25 10:38:37 -0800741 for file_name in ('apkcerts.txt', 'apexkeys.txt'):
742 merge_package_keys_txt(
743 framework_meta_dir=framework_meta_dir,
744 vendor_meta_dir=vendor_meta_dir,
745 merged_meta_dir=merged_meta_dir,
746 file_name=file_name)
Chris Grossfabf50a2019-05-02 12:42:09 -0700747
Daniel Norman03747412022-02-25 10:38:37 -0800748 # Write the now-finalized OPTIONS.merged_misc_info.
749 write_sorted_data(
750 data=OPTIONS.merged_misc_info,
751 path=os.path.join(merged_meta_dir, 'misc_info.txt'))
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700752
753
Daniel Norman03747412022-02-25 10:38:37 -0800754def process_dexopt(temp_dir, output_target_files_dir):
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700755 """If needed, generates dexopt files for vendor apps.
756
757 Args:
758 temp_dir: Location containing an 'output' directory where target files have
Daniel Normane292f5f2022-02-17 14:16:10 -0800759 been extracted, e.g. <temp_dir>/output/SYSTEM, <temp_dir>/output/IMAGES,
760 etc.
Daniel Norman03747412022-02-25 10:38:37 -0800761 output_target_files_dir: The name of a directory that will be used to create
762 the output target files package after all the special cases are processed.
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700763 """
764 # Load vendor and framework META/misc_info.txt.
Daniel Norman03747412022-02-25 10:38:37 -0800765 if (OPTIONS.vendor_misc_info.get('building_with_vsdk') != 'true' or
766 OPTIONS.framework_dexpreopt_tools is None or
767 OPTIONS.framework_dexpreopt_config is None or
768 OPTIONS.vendor_dexpreopt_config is None):
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700769 return
770
771 logger.info('applying dexpreopt')
772
773 # The directory structure to apply dexpreopt is:
774 #
775 # <temp_dir>/
776 # framework_meta/
777 # META/
778 # vendor_meta/
779 # META/
780 # output/
781 # SYSTEM/
782 # VENDOR/
783 # IMAGES/
784 # <other items extracted from system and vendor target files>
785 # tools/
786 # <contents of dexpreopt_tools.zip>
787 # system_config/
788 # <contents of system dexpreopt_config.zip>
789 # vendor_config/
790 # <contents of vendor dexpreopt_config.zip>
791 # system -> output/SYSTEM
792 # vendor -> output/VENDOR
793 # apex -> output/SYSTEM/apex (only for flattened APEX builds)
794 # apex/ (extracted updatable APEX)
795 # <apex 1>/
796 # ...
797 # <apex 2>/
798 # ...
799 # ...
800 # out/dex2oat_result/vendor/
801 # <app>
802 # oat/arm64/
803 # package.vdex
804 # package.odex
805 # <priv-app>
806 # oat/arm64/
807 # package.vdex
808 # package.odex
809 dexpreopt_tools_files_temp_dir = os.path.join(temp_dir, 'tools')
Daniel Normane292f5f2022-02-17 14:16:10 -0800810 dexpreopt_framework_config_files_temp_dir = os.path.join(
811 temp_dir, 'system_config')
812 dexpreopt_vendor_config_files_temp_dir = os.path.join(temp_dir,
813 'vendor_config')
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700814
815 extract_items(
Daniel Norman03747412022-02-25 10:38:37 -0800816 input_zip=OPTIONS.framework_dexpreopt_tools,
817 output_dir=dexpreopt_tools_files_temp_dir,
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700818 extract_item_list=('*',))
819 extract_items(
Daniel Norman03747412022-02-25 10:38:37 -0800820 input_zip=OPTIONS.framework_dexpreopt_config,
821 output_dir=dexpreopt_framework_config_files_temp_dir,
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700822 extract_item_list=('*',))
823 extract_items(
Daniel Norman03747412022-02-25 10:38:37 -0800824 input_zip=OPTIONS.vendor_dexpreopt_config,
825 output_dir=dexpreopt_vendor_config_files_temp_dir,
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700826 extract_item_list=('*',))
827
Daniel Normane292f5f2022-02-17 14:16:10 -0800828 os.symlink(
Daniel Norman03747412022-02-25 10:38:37 -0800829 os.path.join(output_target_files_dir, 'SYSTEM'),
Daniel Normane292f5f2022-02-17 14:16:10 -0800830 os.path.join(temp_dir, 'system'))
831 os.symlink(
Daniel Norman03747412022-02-25 10:38:37 -0800832 os.path.join(output_target_files_dir, 'VENDOR'),
Daniel Normane292f5f2022-02-17 14:16:10 -0800833 os.path.join(temp_dir, 'vendor'))
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700834
835 # The directory structure for flatteded APEXes is:
836 #
837 # SYSTEM
838 # apex
839 # <APEX name, e.g., com.android.wifi>
840 # apex_manifest.pb
841 # apex_pubkey
842 # etc/
843 # javalib/
844 # lib/
845 # lib64/
846 # priv-app/
847 #
848 # The directory structure for updatable APEXes is:
849 #
850 # SYSTEM
851 # apex
852 # com.android.adbd.apex
853 # com.android.appsearch.apex
854 # com.android.art.apex
855 # ...
Daniel Norman03747412022-02-25 10:38:37 -0800856 apex_root = os.path.join(output_target_files_dir, 'SYSTEM', 'apex')
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700857
858 # Check for flattended versus updatable APEX.
Daniel Norman03747412022-02-25 10:38:37 -0800859 if OPTIONS.framework_misc_info.get('target_flatten_apex') == 'false':
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700860 # Extract APEX.
861 logging.info('extracting APEX')
862
863 apex_extract_root_dir = os.path.join(temp_dir, 'apex')
864 os.makedirs(apex_extract_root_dir)
865
866 for apex in (glob.glob(os.path.join(apex_root, '*.apex')) +
867 glob.glob(os.path.join(apex_root, '*.capex'))):
868 logging.info(' apex: %s', apex)
869 # deapexer is in the same directory as the merge_target_files binary extracted
870 # from otatools.zip.
871 apex_json_info = subprocess.check_output(['deapexer', 'info', apex])
872 logging.info(' info: %s', apex_json_info)
873 apex_info = json.loads(apex_json_info)
874 apex_name = apex_info['name']
875 logging.info(' name: %s', apex_name)
876
877 apex_extract_dir = os.path.join(apex_extract_root_dir, apex_name)
878 os.makedirs(apex_extract_dir)
879
880 # deapexer uses debugfs_static, which is part of otatools.zip.
881 command = [
882 'deapexer',
883 '--debugfs_path',
884 'debugfs_static',
885 'extract',
886 apex,
887 apex_extract_dir,
888 ]
889 logging.info(' running %s', command)
890 subprocess.check_call(command)
891 else:
892 # Flattened APEXes don't need to be extracted since they have the necessary
893 # directory structure.
894 os.symlink(os.path.join(apex_root), os.path.join(temp_dir, 'apex'))
895
896 # Modify system config to point to the tools that have been extracted.
897 # Absolute or .. paths are not allowed by the dexpreopt_gen tool in
898 # dexpreopt_soong.config.
899 dexpreopt_framework_soon_config = os.path.join(
900 dexpreopt_framework_config_files_temp_dir, 'dexpreopt_soong.config')
901 with open(dexpreopt_framework_soon_config, 'w') as f:
902 dexpreopt_soong_config = {
903 'Profman': 'tools/profman',
904 'Dex2oat': 'tools/dex2oatd',
905 'Aapt': 'tools/aapt2',
906 'SoongZip': 'tools/soong_zip',
907 'Zip2zip': 'tools/zip2zip',
908 'ManifestCheck': 'tools/manifest_check',
909 'ConstructContext': 'tools/construct_context',
910 }
911 json.dump(dexpreopt_soong_config, f)
912
913 # TODO(b/188179859): Make *dex location configurable to vendor or system_other.
914 use_system_other_odex = False
915
916 if use_system_other_odex:
917 dex_img = 'SYSTEM_OTHER'
918 else:
919 dex_img = 'VENDOR'
920 # Open vendor_filesystem_config to append the items generated by dexopt.
921 vendor_file_system_config = open(
Daniel Normane292f5f2022-02-17 14:16:10 -0800922 os.path.join(temp_dir, 'output', 'META',
923 'vendor_filesystem_config.txt'), 'a')
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700924
925 # Dexpreopt vendor apps.
926 dexpreopt_config_suffix = '_dexpreopt.config'
Daniel Normane292f5f2022-02-17 14:16:10 -0800927 for config in glob.glob(
928 os.path.join(dexpreopt_vendor_config_files_temp_dir,
929 '*' + dexpreopt_config_suffix)):
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700930 app = os.path.basename(config)[:-len(dexpreopt_config_suffix)]
931 logging.info('dexpreopt config: %s %s', config, app)
932
933 apk_dir = 'app'
934 apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')
935 if not os.path.exists(apk_path):
936 apk_dir = 'priv-app'
937 apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')
938 if not os.path.exists(apk_path):
Daniel Normane292f5f2022-02-17 14:16:10 -0800939 logging.warning(
940 'skipping dexpreopt for %s, no apk found in vendor/app '
941 'or vendor/priv-app', app)
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700942 continue
943
944 # Generate dexpreopting script. Note 'out_dir' is not the output directory
945 # where the script is generated, but the OUT_DIR at build time referenced
946 # in the dexpreot config files, e.g., "out/.../core-oj.jar", so the tool knows
947 # how to adjust the path.
948 command = [
949 os.path.join(dexpreopt_tools_files_temp_dir, 'dexpreopt_gen'),
950 '-global',
Daniel Normane292f5f2022-02-17 14:16:10 -0800951 os.path.join(dexpreopt_framework_config_files_temp_dir,
952 'dexpreopt.config'),
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700953 '-global_soong',
Daniel Normane292f5f2022-02-17 14:16:10 -0800954 os.path.join(dexpreopt_framework_config_files_temp_dir,
955 'dexpreopt_soong.config'),
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700956 '-module',
957 config,
958 '-dexpreopt_script',
959 'dexpreopt_app.sh',
960 '-out_dir',
961 'out',
962 '-base_path',
963 '.',
964 '--uses_target_files',
965 ]
966
967 # Run the command from temp_dir so all tool paths are its descendants.
Daniel Normane292f5f2022-02-17 14:16:10 -0800968 logging.info('running %s', command)
969 subprocess.check_call(command, cwd=temp_dir)
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700970
971 # Call the generated script.
972 command = ['sh', 'dexpreopt_app.sh', apk_path]
Daniel Normane292f5f2022-02-17 14:16:10 -0800973 logging.info('running %s', command)
974 subprocess.check_call(command, cwd=temp_dir)
Jose Galmes9c8f6eb2021-07-21 09:34:08 -0700975
976 # Output files are in:
977 #
978 # <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.vdex
979 # <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.odex
980 # <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.vdex
981 # <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.odex
982 #
983 # Copy the files to their destination. The structure of system_other is:
984 #
985 # system_other/
986 # system-other-odex-marker
987 # system/
988 # app/
989 # <app>/oat/arm64/
990 # <app>.odex
991 # <app>.vdex
992 # ...
993 # priv-app/
994 # <app>/oat/arm64/
995 # <app>.odex
996 # <app>.vdex
997 # ...
998
999 # TODO(b/188179859): Support for other architectures.
1000 arch = 'arm64'
1001
Daniel Normane292f5f2022-02-17 14:16:10 -08001002 dex_destination = os.path.join(temp_dir, 'output', dex_img, apk_dir, app,
1003 'oat', arch)
Jose Galmes9c8f6eb2021-07-21 09:34:08 -07001004 os.makedirs(dex_destination)
Daniel Normane292f5f2022-02-17 14:16:10 -08001005 dex2oat_path = os.path.join(temp_dir, 'out', 'dex2oat_result', 'vendor',
1006 apk_dir, app, 'oat', arch)
1007 shutil.copy(
1008 os.path.join(dex2oat_path, 'package.vdex'),
1009 os.path.join(dex_destination, app + '.vdex'))
1010 shutil.copy(
1011 os.path.join(dex2oat_path, 'package.odex'),
1012 os.path.join(dex_destination, app + '.odex'))
Jose Galmes9c8f6eb2021-07-21 09:34:08 -07001013
1014 # Append entries to vendor_file_system_config.txt, such as:
1015 #
1016 # vendor/app/<app>/oat 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
1017 # vendor/app/<app>/oat/arm64 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
1018 # vendor/app/<app>/oat/arm64/<app>.odex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
1019 # vendor/app/<app>/oat/arm64/<app>.vdex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
1020 if not use_system_other_odex:
1021 vendor_app_prefix = 'vendor/' + apk_dir + '/' + app + '/oat'
1022 selabel = 'selabel=u:object_r:vendor_app_file:s0 capabilities=0x0'
1023 vendor_file_system_config.writelines([
1024 vendor_app_prefix + ' 0 2000 755 ' + selabel + '\n',
1025 vendor_app_prefix + '/' + arch + ' 0 2000 755 ' + selabel + '\n',
Daniel Normane292f5f2022-02-17 14:16:10 -08001026 vendor_app_prefix + '/' + arch + '/' + app + '.odex 0 0 644 ' +
1027 selabel + '\n',
1028 vendor_app_prefix + '/' + arch + '/' + app + '.vdex 0 0 644 ' +
1029 selabel + '\n',
Jose Galmes9c8f6eb2021-07-21 09:34:08 -07001030 ])
1031
1032 if not use_system_other_odex:
1033 vendor_file_system_config.close()
1034 # Delete vendor.img so that it will be regenerated.
1035 # TODO(b/188179859): Rebuilding a vendor image in GRF mode (e.g., T(framework)
1036 # and S(vendor) may require logic similar to that in
1037 # rebuild_image_with_sepolicy.
Daniel Norman03747412022-02-25 10:38:37 -08001038 vendor_img = os.path.join(output_target_files_dir, 'IMAGES', 'vendor.img')
Jose Galmes9c8f6eb2021-07-21 09:34:08 -07001039 if os.path.exists(vendor_img):
1040 logging.info('Deleting %s', vendor_img)
1041 os.remove(vendor_img)
1042
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001043
Daniel Norman03747412022-02-25 10:38:37 -08001044def create_merged_package(temp_dir):
Tao Bao2ad4b822019-06-27 16:52:12 -07001045 """Merges two target files packages into one target files structure.
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001046
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001047 Returns:
1048 Path to merged package under temp directory.
1049 """
Daniel Normandbbf5a32020-10-22 16:03:32 -07001050 # Extract "as is" items from the input framework and vendor partial target
1051 # files packages directly into the output temporary directory, since these items
1052 # do not need special case processing.
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001053
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001054 output_target_files_temp_dir = os.path.join(temp_dir, 'output')
Bill Peckham889b0c62019-02-21 18:53:37 -08001055 extract_items(
Daniel Norman03747412022-02-25 10:38:37 -08001056 input_zip=OPTIONS.framework_target_files,
1057 output_dir=output_target_files_temp_dir,
1058 extract_item_list=OPTIONS.framework_item_list)
Bill Peckham889b0c62019-02-21 18:53:37 -08001059 extract_items(
Daniel Norman03747412022-02-25 10:38:37 -08001060 input_zip=OPTIONS.vendor_target_files,
1061 output_dir=output_target_files_temp_dir,
1062 extract_item_list=OPTIONS.vendor_item_list)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001063
Daniel Normandbbf5a32020-10-22 16:03:32 -07001064 # Perform special case processing on META/* items.
1065 # After this function completes successfully, all the files we need to create
1066 # the output target files package are in place.
Daniel Norman03747412022-02-25 10:38:37 -08001067 merge_meta_files(temp_dir=temp_dir, merged_dir=output_target_files_temp_dir)
1068
1069 process_dexopt(
1070 temp_dir=temp_dir, output_target_files_dir=output_target_files_temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001071
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001072 return output_target_files_temp_dir
1073
1074
Daniel Norman03747412022-02-25 10:38:37 -08001075def generate_missing_images(target_files_dir):
1076 """Generate any missing images from target files."""
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001077
1078 # Regenerate IMAGES in the target directory.
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001079
Daniel Normandbbf5a32020-10-22 16:03:32 -07001080 add_img_args = [
1081 '--verbose',
1082 '--add_missing',
1083 ]
Daniel Norman03747412022-02-25 10:38:37 -08001084 if OPTIONS.rebuild_recovery:
Daniel Normana4911da2019-03-15 14:36:21 -07001085 add_img_args.append('--rebuild_recovery')
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001086 add_img_args.append(target_files_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001087
1088 add_img_to_target_files.main(add_img_args)
1089
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001090
Daniel Norman03747412022-02-25 10:38:37 -08001091def rebuild_image_with_sepolicy(target_files_dir):
Daniel Norman571e1822021-06-25 17:18:25 -07001092 """Rebuilds odm.img or vendor.img to include merged sepolicy files.
1093
1094 If odm is present then odm is preferred -- otherwise vendor is used.
Daniel Norman571e1822021-06-25 17:18:25 -07001095 """
1096 partition = 'vendor'
1097 if os.path.exists(os.path.join(target_files_dir, 'ODM')) or os.path.exists(
1098 os.path.join(target_files_dir, 'IMAGES/odm.img')):
1099 partition = 'odm'
1100 partition_img = '{}.img'.format(partition)
Po Hua6c59122022-02-16 08:41:29 +00001101 partition_map = '{}.map'.format(partition)
Daniel Norman571e1822021-06-25 17:18:25 -07001102
1103 logger.info('Recompiling %s using the merged sepolicy files.', partition_img)
1104
1105 # Copy the combined SEPolicy file and framework hashes to the image that is
1106 # being rebuilt.
1107 def copy_selinux_file(input_path, output_filename):
Po Hu0e4403e2021-07-06 17:05:56 +08001108 input_filename = os.path.join(target_files_dir, input_path)
1109 if not os.path.exists(input_filename):
1110 input_filename = input_filename.replace('SYSTEM_EXT/', 'SYSTEM/system_ext/') \
1111 .replace('PRODUCT/', 'SYSTEM/product/')
1112 if not os.path.exists(input_filename):
1113 logger.info('Skipping copy_selinux_file for %s', input_filename)
1114 return
Daniel Norman571e1822021-06-25 17:18:25 -07001115 shutil.copy(
Po Hu0e4403e2021-07-06 17:05:56 +08001116 input_filename,
Daniel Norman571e1822021-06-25 17:18:25 -07001117 os.path.join(target_files_dir, partition.upper(), 'etc/selinux',
1118 output_filename))
1119
1120 copy_selinux_file('META/combined_sepolicy', 'precompiled_sepolicy')
1121 copy_selinux_file('SYSTEM/etc/selinux/plat_sepolicy_and_mapping.sha256',
1122 'precompiled_sepolicy.plat_sepolicy_and_mapping.sha256')
1123 copy_selinux_file(
1124 'SYSTEM_EXT/etc/selinux/system_ext_sepolicy_and_mapping.sha256',
1125 'precompiled_sepolicy.system_ext_sepolicy_and_mapping.sha256')
1126 copy_selinux_file('PRODUCT/etc/selinux/product_sepolicy_and_mapping.sha256',
1127 'precompiled_sepolicy.product_sepolicy_and_mapping.sha256')
1128
Daniel Norman03747412022-02-25 10:38:37 -08001129 if not OPTIONS.vendor_otatools:
Daniel Norman571e1822021-06-25 17:18:25 -07001130 # Remove the partition from the merged target-files archive. It will be
Daniel Norman03747412022-02-25 10:38:37 -08001131 # rebuilt later automatically by generate_missing_images().
Daniel Norman571e1822021-06-25 17:18:25 -07001132 os.remove(os.path.join(target_files_dir, 'IMAGES', partition_img))
Daniel Norman03747412022-02-25 10:38:37 -08001133 return
Daniel Norman571e1822021-06-25 17:18:25 -07001134
Daniel Norman03747412022-02-25 10:38:37 -08001135 # TODO(b/192253131): Remove the need for vendor_otatools by fixing
1136 # backwards-compatibility issues when compiling images across releases.
1137 if not OPTIONS.vendor_target_files:
1138 raise ValueError(
1139 'Expected vendor_target_files if vendor_otatools is not None.')
1140 logger.info(
1141 '%s recompilation will be performed using the vendor otatools.zip',
1142 partition_img)
Daniel Norman571e1822021-06-25 17:18:25 -07001143
Daniel Norman03747412022-02-25 10:38:37 -08001144 # Unzip the vendor build's otatools.zip and target-files archive.
1145 vendor_otatools_dir = common.MakeTempDir(
1146 prefix='merge_target_files_vendor_otatools_')
1147 vendor_target_files_dir = common.MakeTempDir(
1148 prefix='merge_target_files_vendor_target_files_')
1149 common.UnzipToDir(OPTIONS.vendor_otatools, vendor_otatools_dir)
1150 common.UnzipToDir(OPTIONS.vendor_target_files, vendor_target_files_dir)
Daniel Norman571e1822021-06-25 17:18:25 -07001151
Daniel Norman03747412022-02-25 10:38:37 -08001152 # Copy the partition contents from the merged target-files archive to the
1153 # vendor target-files archive.
1154 shutil.rmtree(os.path.join(vendor_target_files_dir, partition.upper()))
1155 shutil.copytree(
1156 os.path.join(target_files_dir, partition.upper()),
1157 os.path.join(vendor_target_files_dir, partition.upper()),
1158 symlinks=True)
Daniel Norman571e1822021-06-25 17:18:25 -07001159
Daniel Norman03747412022-02-25 10:38:37 -08001160 # Delete then rebuild the partition.
1161 os.remove(os.path.join(vendor_target_files_dir, 'IMAGES', partition_img))
1162 rebuild_partition_command = [
1163 os.path.join(vendor_otatools_dir, 'bin', 'add_img_to_target_files'),
1164 '--verbose',
1165 '--add_missing',
1166 ]
1167 if OPTIONS.rebuild_recovery:
1168 rebuild_partition_command.append('--rebuild_recovery')
1169 rebuild_partition_command.append(vendor_target_files_dir)
1170 logger.info('Recompiling %s: %s', partition_img,
1171 ' '.join(rebuild_partition_command))
1172 common.RunAndCheckOutput(rebuild_partition_command, verbose=True)
Po Hua6c59122022-02-16 08:41:29 +00001173
Daniel Norman03747412022-02-25 10:38:37 -08001174 # Move the newly-created image to the merged target files dir.
1175 if not os.path.exists(os.path.join(target_files_dir, 'IMAGES')):
1176 os.makedirs(os.path.join(target_files_dir, 'IMAGES'))
1177 shutil.move(
1178 os.path.join(vendor_target_files_dir, 'IMAGES', partition_img),
1179 os.path.join(target_files_dir, 'IMAGES', partition_img))
1180 shutil.move(
1181 os.path.join(vendor_target_files_dir, 'IMAGES', partition_map),
1182 os.path.join(target_files_dir, 'IMAGES', partition_map))
Po Hua6c59122022-02-16 08:41:29 +00001183
Daniel Norman03747412022-02-25 10:38:37 -08001184 def copy_recovery_file(filename):
1185 for subdir in ('VENDOR', 'SYSTEM/vendor'):
1186 source = os.path.join(vendor_target_files_dir, subdir, filename)
1187 if os.path.exists(source):
1188 dest = os.path.join(target_files_dir, subdir, filename)
1189 shutil.copy(source, dest)
1190 return
1191 logger.info('Skipping copy_recovery_file for %s, file not found', filename)
1192
1193 if OPTIONS.rebuild_recovery:
1194 copy_recovery_file('etc/recovery.img')
1195 copy_recovery_file('bin/install-recovery.sh')
1196 copy_recovery_file('recovery-from-boot.p')
Daniel Norman571e1822021-06-25 17:18:25 -07001197
1198
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001199def generate_super_empty_image(target_dir, output_super_empty):
Tao Bao2ad4b822019-06-27 16:52:12 -07001200 """Generates super_empty image from target package.
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001201
1202 Args:
1203 target_dir: Path to the target file package which contains misc_info.txt for
1204 detailed information for super image.
1205 output_super_empty: If provided, copies a super_empty.img file from the
1206 target files package to this path.
1207 """
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001208 # Create super_empty.img using the merged misc_info.txt.
1209
Daniel Norman4cc9df62019-07-18 10:11:07 -07001210 misc_info_txt = os.path.join(target_dir, 'META', 'misc_info.txt')
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001211
Kiyoung Kimebe7c9c2019-06-25 17:09:55 +09001212 use_dynamic_partitions = common.LoadDictionaryFromFile(misc_info_txt).get(
1213 'use_dynamic_partitions')
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001214
1215 if use_dynamic_partitions != 'true' and output_super_empty:
1216 raise ValueError(
1217 'Building super_empty.img requires use_dynamic_partitions=true.')
1218 elif use_dynamic_partitions == 'true':
Daniel Norman4cc9df62019-07-18 10:11:07 -07001219 super_empty_img = os.path.join(target_dir, 'IMAGES', 'super_empty.img')
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001220 build_super_image_args = [
1221 misc_info_txt,
1222 super_empty_img,
1223 ]
1224 build_super_image.main(build_super_image_args)
1225
1226 # Copy super_empty.img to the user-provided output_super_empty location.
1227 if output_super_empty:
1228 shutil.copyfile(super_empty_img, output_super_empty)
1229
Daniel Normanb8a2f9d2019-04-24 12:55:51 -07001230
Daniel Norman03747412022-02-25 10:38:37 -08001231def create_target_files_archive(output_zip, source_dir, temp_dir):
1232 """Creates a target_files zip archive from the input source dir.
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001233
1234 Args:
Daniel Norman03747412022-02-25 10:38:37 -08001235 output_zip: The name of the zip archive target files package.
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001236 source_dir: The target directory contains package to be archived.
1237 temp_dir: Path to temporary directory for any intermediate files.
1238 """
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001239 output_target_files_list = os.path.join(temp_dir, 'output.list')
Daniel Norman4cc9df62019-07-18 10:11:07 -07001240 output_target_files_meta_dir = os.path.join(source_dir, 'META')
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001241
Daniel Normandbbf5a32020-10-22 16:03:32 -07001242 def files_from_path(target_path, extra_args=None):
1243 """Gets files under the given path and return a sorted list."""
1244 find_command = ['find', target_path] + (extra_args or [])
1245 find_process = common.Run(
1246 find_command, stdout=subprocess.PIPE, verbose=False)
1247 return common.RunAndCheckOutput(['sort'],
1248 stdin=find_process.stdout,
1249 verbose=False)
1250
Daniel Norman03747412022-02-25 10:38:37 -08001251 # META content appears first in the zip. This is done by the
1252 # standard build system for optimized extraction of those files,
1253 # so we do the same step for merged target_files.zips here too.
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001254 meta_content = files_from_path(output_target_files_meta_dir)
Daniel Norman4cc9df62019-07-18 10:11:07 -07001255 other_content = files_from_path(
1256 source_dir,
1257 ['-path', output_target_files_meta_dir, '-prune', '-o', '-print'])
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001258
Tao Bao2ad4b822019-06-27 16:52:12 -07001259 with open(output_target_files_list, 'w') as f:
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001260 f.write(meta_content)
1261 f.write(other_content)
1262
1263 command = [
Bill Peckhamf753e152019-02-19 18:02:46 -08001264 'soong_zip',
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001265 '-d',
Daniel Normane5b134a2019-04-17 14:54:06 -07001266 '-o',
Daniel Norman03747412022-02-25 10:38:37 -08001267 os.path.abspath(output_zip),
Daniel Normane5b134a2019-04-17 14:54:06 -07001268 '-C',
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001269 source_dir,
Daniel Normaneaf5c1d2021-02-09 11:01:42 -08001270 '-r',
Daniel Normane5b134a2019-04-17 14:54:06 -07001271 output_target_files_list,
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001272 ]
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001273
Daniel Norman03747412022-02-25 10:38:37 -08001274 logger.info('creating %s', output_zip)
Daniel Normaneaf5c1d2021-02-09 11:01:42 -08001275 common.RunAndCheckOutput(command, verbose=True)
Daniel Norman03747412022-02-25 10:38:37 -08001276 logger.info('finished creating %s', output_zip)
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001277
1278
Daniel Norman03747412022-02-25 10:38:37 -08001279def merge_target_files(temp_dir):
Tao Bao2ad4b822019-06-27 16:52:12 -07001280 """Merges two target files packages together.
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001281
Daniel Norman03747412022-02-25 10:38:37 -08001282 This function uses framework and vendor target files packages as input,
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001283 performs various file extractions, special case processing, and finally
1284 creates a merged zip archive as output.
1285
1286 Args:
1287 temp_dir: The name of a directory we use when we extract items from the
1288 input target files packages, and also a scratch directory that we use for
1289 temporary files.
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001290 """
1291
1292 logger.info('starting: merge framework %s and vendor %s into output %s',
Daniel Norman03747412022-02-25 10:38:37 -08001293 OPTIONS.framework_target_files, OPTIONS.vendor_target_files,
1294 OPTIONS.output_target_files)
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001295
Daniel Norman03747412022-02-25 10:38:37 -08001296 output_target_files_temp_dir = create_merged_package(temp_dir)
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001297
Yifan Hongade0d3f2019-08-21 16:37:11 -07001298 if not check_target_files_vintf.CheckVintf(output_target_files_temp_dir):
Daniel Normanb0c75912020-09-24 14:30:21 -07001299 raise RuntimeError('Incompatible VINTF metadata')
Yifan Hongade0d3f2019-08-21 16:37:11 -07001300
Daniel Norman21c34f72020-11-11 17:25:50 -08001301 partition_map = common.PartitionMapFromTargetFiles(
1302 output_target_files_temp_dir)
1303
Daniel Normand3351562020-10-29 12:33:11 -07001304 # Generate and check for cross-partition violations of sharedUserId
1305 # values in APKs. This requires the input target-files packages to contain
1306 # *.apk files.
Daniel Normanb8d52a22020-10-26 17:55:00 -07001307 shareduid_violation_modules = os.path.join(
1308 output_target_files_temp_dir, 'META', 'shareduid_violation_modules.json')
1309 with open(shareduid_violation_modules, 'w') as f:
Daniel Normanb8d52a22020-10-26 17:55:00 -07001310 violation = find_shareduid_violation.FindShareduidViolation(
1311 output_target_files_temp_dir, partition_map)
Daniel Normand3351562020-10-29 12:33:11 -07001312
1313 # Write the output to a file to enable debugging.
Daniel Normanb8d52a22020-10-26 17:55:00 -07001314 f.write(violation)
Daniel Normand3351562020-10-29 12:33:11 -07001315
1316 # Check for violations across the input builds' partition groups.
1317 shareduid_errors = common.SharedUidPartitionViolations(
Daniel Norman03747412022-02-25 10:38:37 -08001318 json.loads(violation),
1319 [OPTIONS.framework_partition_set, OPTIONS.vendor_partition_set])
Daniel Normand3351562020-10-29 12:33:11 -07001320 if shareduid_errors:
1321 for error in shareduid_errors:
1322 logger.error(error)
1323 raise ValueError('sharedUserId APK error. See %s' %
1324 shareduid_violation_modules)
Daniel Normanb8d52a22020-10-26 17:55:00 -07001325
Daniel Norman48603ff2021-02-22 15:15:24 -08001326 # host_init_verifier and secilc check only the following partitions:
Daniel Norman21c34f72020-11-11 17:25:50 -08001327 filtered_partitions = {
1328 partition: path
1329 for partition, path in partition_map.items()
Daniel Norman21c34f72020-11-11 17:25:50 -08001330 if partition in ['system', 'system_ext', 'product', 'vendor', 'odm']
1331 }
Daniel Norman48603ff2021-02-22 15:15:24 -08001332
1333 # Run host_init_verifier on the combined init rc files.
Daniel Norman21c34f72020-11-11 17:25:50 -08001334 common.RunHostInitVerifier(
1335 product_out=output_target_files_temp_dir,
1336 partition_map=filtered_partitions)
1337
Daniel Norman48603ff2021-02-22 15:15:24 -08001338 # Check that the split sepolicy from the multiple builds can compile.
Daniel Norman571e1822021-06-25 17:18:25 -07001339 split_sepolicy_cmd = compile_split_sepolicy(output_target_files_temp_dir,
1340 filtered_partitions)
Daniel Norman48603ff2021-02-22 15:15:24 -08001341 logger.info('Compiling split sepolicy: %s', ' '.join(split_sepolicy_cmd))
1342 common.RunAndCheckOutput(split_sepolicy_cmd)
Daniel Norman571e1822021-06-25 17:18:25 -07001343 # Include the compiled policy in an image if requested.
Daniel Norman03747412022-02-25 10:38:37 -08001344 if OPTIONS.rebuild_sepolicy:
1345 rebuild_image_with_sepolicy(output_target_files_temp_dir)
Daniel Norman48603ff2021-02-22 15:15:24 -08001346
Daniel Normane9af70a2021-04-15 16:39:22 -07001347 # Run validation checks on the pre-installed APEX files.
1348 validate_merged_apex_info(output_target_files_temp_dir, partition_map.keys())
1349
Daniel Norman03747412022-02-25 10:38:37 -08001350 generate_missing_images(output_target_files_temp_dir)
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001351
Daniel Norman03747412022-02-25 10:38:37 -08001352 generate_super_empty_image(output_target_files_temp_dir,
1353 OPTIONS.output_super_empty)
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001354
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001355 # Finally, create the output target files zip archive and/or copy the
1356 # output items to the output target files directory.
1357
Daniel Norman03747412022-02-25 10:38:37 -08001358 if OPTIONS.output_dir:
1359 copy_items(output_target_files_temp_dir, OPTIONS.output_dir,
1360 OPTIONS.output_item_list)
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001361
Daniel Norman03747412022-02-25 10:38:37 -08001362 if not OPTIONS.output_target_files:
Kiyoung Kim7cbeda72019-06-28 13:26:04 +09001363 return
1364
Daniel Norman03747412022-02-25 10:38:37 -08001365 # Create the merged META/care_map.pb if the device uses A/B updates.
1366 if OPTIONS.merged_misc_info['ab_update'] == 'true':
Iavor-Valentin Iftimeb837b712022-01-27 16:29:37 +00001367 generate_care_map(partition_map.keys(), output_target_files_temp_dir)
Daniel Normandb8cacc2021-04-09 15:34:43 -07001368
Daniel Norman03747412022-02-25 10:38:37 -08001369 create_target_files_archive(OPTIONS.output_target_files,
1370 output_target_files_temp_dir, temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001371
Daniel Norman74eb74b2019-09-18 14:01:48 -07001372 # Create the IMG package from the merged target files package.
Daniel Norman03747412022-02-25 10:38:37 -08001373 if OPTIONS.output_img:
1374 img_from_target_files.main(
1375 [OPTIONS.output_target_files, OPTIONS.output_img])
Daniel Norman74eb74b2019-09-18 14:01:48 -07001376
Daniel Norman3b64ce12019-04-16 16:11:35 -07001377 # Create the OTA package from the merged target files package.
1378
Daniel Norman03747412022-02-25 10:38:37 -08001379 if OPTIONS.output_ota:
1380 ota_from_target_files.main(
1381 [OPTIONS.output_target_files, OPTIONS.output_ota])
Daniel Norman3b64ce12019-04-16 16:11:35 -07001382
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001383
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001384def call_func_with_temp_dir(func, keep_tmp):
Tao Bao2ad4b822019-06-27 16:52:12 -07001385 """Manages the creation and cleanup of the temporary directory.
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001386
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001387 This function calls the given function after first creating a temporary
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001388 directory. It also cleans up the temporary directory.
1389
1390 Args:
Daniel Normane5b134a2019-04-17 14:54:06 -07001391 func: The function to call. Should accept one parameter, the path to the
1392 temporary directory.
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001393 keep_tmp: Keep the temporary directory after processing is complete.
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001394 """
1395
1396 # Create a temporary directory. This will serve as the parent of directories
1397 # we use when we extract items from the input target files packages, and also
1398 # a scratch directory that we use for temporary files.
1399
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001400 temp_dir = common.MakeTempDir(prefix='merge_target_files_')
1401
1402 try:
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001403 func(temp_dir)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001404 finally:
1405 if keep_tmp:
1406 logger.info('keeping %s', temp_dir)
1407 else:
1408 common.Cleanup()
1409
1410
1411def main():
1412 """The main function.
1413
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001414 Process command line arguments, then call merge_target_files to
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001415 perform the heavy lifting.
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001416 """
1417
1418 common.InitLogging()
1419
Bill Peckhamf753e152019-02-19 18:02:46 -08001420 def option_handler(o, a):
1421 if o == '--system-target-files':
Daniel Normand5d70ea2019-06-05 15:13:43 -07001422 logger.warning(
1423 '--system-target-files has been renamed to --framework-target-files')
1424 OPTIONS.framework_target_files = a
1425 elif o == '--framework-target-files':
1426 OPTIONS.framework_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001427 elif o == '--system-item-list':
Daniel Normand5d70ea2019-06-05 15:13:43 -07001428 logger.warning(
1429 '--system-item-list has been renamed to --framework-item-list')
1430 OPTIONS.framework_item_list = a
1431 elif o == '--framework-item-list':
1432 OPTIONS.framework_item_list = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001433 elif o == '--system-misc-info-keys':
Daniel Norman4cc9df62019-07-18 10:11:07 -07001434 logger.warning('--system-misc-info-keys has been renamed to '
1435 '--framework-misc-info-keys')
Daniel Normand5d70ea2019-06-05 15:13:43 -07001436 OPTIONS.framework_misc_info_keys = a
1437 elif o == '--framework-misc-info-keys':
1438 OPTIONS.framework_misc_info_keys = a
Bill Peckhamf753e152019-02-19 18:02:46 -08001439 elif o == '--other-target-files':
Daniel Normand5d70ea2019-06-05 15:13:43 -07001440 logger.warning(
1441 '--other-target-files has been renamed to --vendor-target-files')
1442 OPTIONS.vendor_target_files = a
1443 elif o == '--vendor-target-files':
1444 OPTIONS.vendor_target_files = a
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001445 elif o == '--other-item-list':
Daniel Norman2d7989a2021-04-05 17:40:47 +00001446 logger.warning('--other-item-list has been renamed to --vendor-item-list')
Daniel Normand5d70ea2019-06-05 15:13:43 -07001447 OPTIONS.vendor_item_list = a
1448 elif o == '--vendor-item-list':
1449 OPTIONS.vendor_item_list = a
Bill Peckhamf753e152019-02-19 18:02:46 -08001450 elif o == '--output-target-files':
1451 OPTIONS.output_target_files = a
Daniel Normanfdb38812019-04-15 09:47:24 -07001452 elif o == '--output-dir':
1453 OPTIONS.output_dir = a
1454 elif o == '--output-item-list':
1455 OPTIONS.output_item_list = a
Daniel Norman3b64ce12019-04-16 16:11:35 -07001456 elif o == '--output-ota':
1457 OPTIONS.output_ota = a
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001458 elif o == '--output-img':
1459 OPTIONS.output_img = a
Daniel Normanf0318252019-04-15 11:34:56 -07001460 elif o == '--output-super-empty':
1461 OPTIONS.output_super_empty = a
Po Hua6c59122022-02-16 08:41:29 +00001462 elif o == '--rebuild_recovery':
Daniel Normana4911da2019-03-15 14:36:21 -07001463 OPTIONS.rebuild_recovery = True
Daniel Normanb0c75912020-09-24 14:30:21 -07001464 elif o == '--allow-duplicate-apkapex-keys':
1465 OPTIONS.allow_duplicate_apkapex_keys = True
Daniel Norman571e1822021-06-25 17:18:25 -07001466 elif o == '--vendor-otatools':
1467 OPTIONS.vendor_otatools = a
1468 elif o == '--rebuild-sepolicy':
1469 OPTIONS.rebuild_sepolicy = True
Bill Peckham364c1cc2019-03-29 18:27:23 -07001470 elif o == '--keep-tmp':
Bill Peckhamf753e152019-02-19 18:02:46 -08001471 OPTIONS.keep_tmp = True
Jose Galmes9c8f6eb2021-07-21 09:34:08 -07001472 elif o == '--framework-dexpreopt-config':
1473 OPTIONS.framework_dexpreopt_config = a
1474 elif o == '--framework-dexpreopt-tools':
1475 OPTIONS.framework_dexpreopt_tools = a
1476 elif o == '--vendor-dexpreopt-config':
1477 OPTIONS.vendor_dexpreopt_config = a
Bill Peckhamf753e152019-02-19 18:02:46 -08001478 else:
1479 return False
1480 return True
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001481
Bill Peckhamf753e152019-02-19 18:02:46 -08001482 args = common.ParseOptions(
Daniel Normane5b134a2019-04-17 14:54:06 -07001483 sys.argv[1:],
1484 __doc__,
Bill Peckhamf753e152019-02-19 18:02:46 -08001485 extra_long_opts=[
1486 'system-target-files=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001487 'framework-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001488 'system-item-list=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001489 'framework-item-list=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001490 'system-misc-info-keys=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001491 'framework-misc-info-keys=',
Bill Peckhamf753e152019-02-19 18:02:46 -08001492 'other-target-files=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001493 'vendor-target-files=',
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001494 'other-item-list=',
Daniel Normand5d70ea2019-06-05 15:13:43 -07001495 'vendor-item-list=',
Bill Peckhamf753e152019-02-19 18:02:46 -08001496 'output-target-files=',
Daniel Normanfdb38812019-04-15 09:47:24 -07001497 'output-dir=',
1498 'output-item-list=',
Daniel Norman3b64ce12019-04-16 16:11:35 -07001499 'output-ota=',
Daniel Norman1bd2a1d2019-04-18 12:32:18 -07001500 'output-img=',
Daniel Normanf0318252019-04-15 11:34:56 -07001501 'output-super-empty=',
Jose Galmes9c8f6eb2021-07-21 09:34:08 -07001502 'framework-dexpreopt-config=',
1503 'framework-dexpreopt-tools=',
1504 'vendor-dexpreopt-config=',
Daniel Normana4911da2019-03-15 14:36:21 -07001505 'rebuild_recovery',
Daniel Normanb0c75912020-09-24 14:30:21 -07001506 'allow-duplicate-apkapex-keys',
Daniel Norman571e1822021-06-25 17:18:25 -07001507 'vendor-otatools=',
1508 'rebuild-sepolicy',
Bill Peckham364c1cc2019-03-29 18:27:23 -07001509 'keep-tmp',
Bill Peckhamf753e152019-02-19 18:02:46 -08001510 ],
1511 extra_option_handler=option_handler)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001512
Tao Bao2ad4b822019-06-27 16:52:12 -07001513 # pylint: disable=too-many-boolean-expressions
Daniel Normand5d70ea2019-06-05 15:13:43 -07001514 if (args or OPTIONS.framework_target_files is None or
1515 OPTIONS.vendor_target_files is None or
Daniel Normane5b134a2019-04-17 14:54:06 -07001516 (OPTIONS.output_target_files is None and OPTIONS.output_dir is None) or
Po Hua6c59122022-02-16 08:41:29 +00001517 (OPTIONS.output_dir is not None and OPTIONS.output_item_list is None) or
1518 (OPTIONS.rebuild_recovery and not OPTIONS.rebuild_sepolicy)):
Bill Peckhamf753e152019-02-19 18:02:46 -08001519 common.Usage(__doc__)
Bill Peckham889b0c62019-02-21 18:53:37 -08001520 sys.exit(1)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001521
Daniel Normand5d70ea2019-06-05 15:13:43 -07001522 if OPTIONS.framework_item_list:
Daniel Norman03747412022-02-25 10:38:37 -08001523 OPTIONS.framework_item_list = common.LoadListFromFile(
1524 OPTIONS.framework_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001525 else:
Daniel Norman03747412022-02-25 10:38:37 -08001526 OPTIONS.framework_item_list = DEFAULT_FRAMEWORK_ITEM_LIST
1527 OPTIONS.framework_partition_set = item_list_to_partition_set(
1528 OPTIONS.framework_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001529
Daniel Normand5d70ea2019-06-05 15:13:43 -07001530 if OPTIONS.framework_misc_info_keys:
Daniel Norman03747412022-02-25 10:38:37 -08001531 OPTIONS.framework_misc_info_keys = common.LoadListFromFile(
Daniel Normand5d70ea2019-06-05 15:13:43 -07001532 OPTIONS.framework_misc_info_keys)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001533 else:
Daniel Norman03747412022-02-25 10:38:37 -08001534 OPTIONS.framework_misc_info_keys = DEFAULT_FRAMEWORK_MISC_INFO_KEYS
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001535
Daniel Normand5d70ea2019-06-05 15:13:43 -07001536 if OPTIONS.vendor_item_list:
Daniel Norman03747412022-02-25 10:38:37 -08001537 OPTIONS.vendor_item_list = common.LoadListFromFile(OPTIONS.vendor_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001538 else:
Daniel Norman03747412022-02-25 10:38:37 -08001539 OPTIONS.vendor_item_list = DEFAULT_VENDOR_ITEM_LIST
1540 OPTIONS.vendor_partition_set = item_list_to_partition_set(
1541 OPTIONS.vendor_item_list)
Daniel Norman2c99c5b2019-03-07 13:01:48 -08001542
Daniel Normanfdb38812019-04-15 09:47:24 -07001543 if OPTIONS.output_item_list:
Daniel Norman03747412022-02-25 10:38:37 -08001544 OPTIONS.output_item_list = common.LoadListFromFile(OPTIONS.output_item_list)
Daniel Normanfdb38812019-04-15 09:47:24 -07001545 else:
Daniel Norman03747412022-02-25 10:38:37 -08001546 OPTIONS.output_item_list = None
Daniel Normanfdb38812019-04-15 09:47:24 -07001547
Daniel Norman03747412022-02-25 10:38:37 -08001548 if not validate_config_lists():
Daniel Normane5964522019-03-19 10:32:03 -07001549 sys.exit(1)
1550
Daniel Norman03747412022-02-25 10:38:37 -08001551 call_func_with_temp_dir(lambda temp_dir: merge_target_files(temp_dir),
1552 OPTIONS.keep_tmp)
Bill Peckhame9eb5f92019-02-01 15:52:10 -08001553
1554
1555if __name__ == '__main__':
Bill Peckham889b0c62019-02-21 18:53:37 -08001556 main()