324 lines
12 KiB
Python
324 lines
12 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# Copyright (C) 2022 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
# use this file except in compliance with the License. You may obtain a copy of
|
|
# the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations under
|
|
# the License.
|
|
#
|
|
"""Generates dexopt files for vendor apps, from a merged target_files.
|
|
|
|
Expects items in OPTIONS prepared by merge_target_files.py.
|
|
"""
|
|
|
|
import glob
|
|
import json
|
|
import logging
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
|
|
import common
|
|
import merge_utils
|
|
|
|
logger = logging.getLogger(__name__)
|
|
OPTIONS = common.OPTIONS
|
|
|
|
|
|
def MergeDexopt(temp_dir, output_target_files_dir):
|
|
"""If needed, generates dexopt files for vendor apps.
|
|
|
|
Args:
|
|
temp_dir: Location containing an 'output' directory where target files have
|
|
been extracted, e.g. <temp_dir>/output/SYSTEM, <temp_dir>/output/IMAGES,
|
|
etc.
|
|
output_target_files_dir: The name of a directory that will be used to create
|
|
the output target files package after all the special cases are processed.
|
|
"""
|
|
# Load vendor and framework META/misc_info.txt.
|
|
if (OPTIONS.vendor_misc_info.get('building_with_vsdk') != 'true' or
|
|
OPTIONS.framework_dexpreopt_tools is None or
|
|
OPTIONS.framework_dexpreopt_config is None or
|
|
OPTIONS.vendor_dexpreopt_config is None):
|
|
return
|
|
|
|
logger.info('applying dexpreopt')
|
|
|
|
# The directory structure to apply dexpreopt is:
|
|
#
|
|
# <temp_dir>/
|
|
# framework_meta/
|
|
# META/
|
|
# vendor_meta/
|
|
# META/
|
|
# output/
|
|
# SYSTEM/
|
|
# VENDOR/
|
|
# IMAGES/
|
|
# <other items extracted from system and vendor target files>
|
|
# tools/
|
|
# <contents of dexpreopt_tools.zip>
|
|
# system_config/
|
|
# <contents of system dexpreopt_config.zip>
|
|
# vendor_config/
|
|
# <contents of vendor dexpreopt_config.zip>
|
|
# system -> output/SYSTEM
|
|
# vendor -> output/VENDOR
|
|
# apex -> output/SYSTEM/apex (only for flattened APEX builds)
|
|
# apex/ (extracted updatable APEX)
|
|
# <apex 1>/
|
|
# ...
|
|
# <apex 2>/
|
|
# ...
|
|
# ...
|
|
# out/dex2oat_result/vendor/
|
|
# <app>
|
|
# oat/arm64/
|
|
# package.vdex
|
|
# package.odex
|
|
# <priv-app>
|
|
# oat/arm64/
|
|
# package.vdex
|
|
# package.odex
|
|
dexpreopt_tools_files_temp_dir = os.path.join(temp_dir, 'tools')
|
|
dexpreopt_framework_config_files_temp_dir = os.path.join(
|
|
temp_dir, 'system_config')
|
|
dexpreopt_vendor_config_files_temp_dir = os.path.join(temp_dir,
|
|
'vendor_config')
|
|
|
|
merge_utils.ExtractItems(
|
|
input_zip=OPTIONS.framework_dexpreopt_tools,
|
|
output_dir=dexpreopt_tools_files_temp_dir,
|
|
extract_item_list=('*',))
|
|
merge_utils.ExtractItems(
|
|
input_zip=OPTIONS.framework_dexpreopt_config,
|
|
output_dir=dexpreopt_framework_config_files_temp_dir,
|
|
extract_item_list=('*',))
|
|
merge_utils.ExtractItems(
|
|
input_zip=OPTIONS.vendor_dexpreopt_config,
|
|
output_dir=dexpreopt_vendor_config_files_temp_dir,
|
|
extract_item_list=('*',))
|
|
|
|
os.symlink(
|
|
os.path.join(output_target_files_dir, 'SYSTEM'),
|
|
os.path.join(temp_dir, 'system'))
|
|
os.symlink(
|
|
os.path.join(output_target_files_dir, 'VENDOR'),
|
|
os.path.join(temp_dir, 'vendor'))
|
|
|
|
# The directory structure for flatteded APEXes is:
|
|
#
|
|
# SYSTEM
|
|
# apex
|
|
# <APEX name, e.g., com.android.wifi>
|
|
# apex_manifest.pb
|
|
# apex_pubkey
|
|
# etc/
|
|
# javalib/
|
|
# lib/
|
|
# lib64/
|
|
# priv-app/
|
|
#
|
|
# The directory structure for updatable APEXes is:
|
|
#
|
|
# SYSTEM
|
|
# apex
|
|
# com.android.adbd.apex
|
|
# com.android.appsearch.apex
|
|
# com.android.art.apex
|
|
# ...
|
|
apex_root = os.path.join(output_target_files_dir, 'SYSTEM', 'apex')
|
|
|
|
# Check for flattended versus updatable APEX.
|
|
if OPTIONS.framework_misc_info.get('target_flatten_apex') == 'false':
|
|
# Extract APEX.
|
|
logging.info('extracting APEX')
|
|
|
|
apex_extract_root_dir = os.path.join(temp_dir, 'apex')
|
|
os.makedirs(apex_extract_root_dir)
|
|
|
|
for apex in (glob.glob(os.path.join(apex_root, '*.apex')) +
|
|
glob.glob(os.path.join(apex_root, '*.capex'))):
|
|
logging.info(' apex: %s', apex)
|
|
# deapexer is in the same directory as the merge_target_files binary extracted
|
|
# from otatools.zip.
|
|
apex_json_info = subprocess.check_output(['deapexer', 'info', apex])
|
|
logging.info(' info: %s', apex_json_info)
|
|
apex_info = json.loads(apex_json_info)
|
|
apex_name = apex_info['name']
|
|
logging.info(' name: %s', apex_name)
|
|
|
|
apex_extract_dir = os.path.join(apex_extract_root_dir, apex_name)
|
|
os.makedirs(apex_extract_dir)
|
|
|
|
# deapexer uses debugfs_static, which is part of otatools.zip.
|
|
command = [
|
|
'deapexer',
|
|
'--debugfs_path',
|
|
'debugfs_static',
|
|
'extract',
|
|
apex,
|
|
apex_extract_dir,
|
|
]
|
|
logging.info(' running %s', command)
|
|
subprocess.check_call(command)
|
|
else:
|
|
# Flattened APEXes don't need to be extracted since they have the necessary
|
|
# directory structure.
|
|
os.symlink(os.path.join(apex_root), os.path.join(temp_dir, 'apex'))
|
|
|
|
# Modify system config to point to the tools that have been extracted.
|
|
# Absolute or .. paths are not allowed by the dexpreopt_gen tool in
|
|
# dexpreopt_soong.config.
|
|
dexpreopt_framework_soon_config = os.path.join(
|
|
dexpreopt_framework_config_files_temp_dir, 'dexpreopt_soong.config')
|
|
with open(dexpreopt_framework_soon_config, 'w') as f:
|
|
dexpreopt_soong_config = {
|
|
'Profman': 'tools/profman',
|
|
'Dex2oat': 'tools/dex2oatd',
|
|
'Aapt': 'tools/aapt2',
|
|
'SoongZip': 'tools/soong_zip',
|
|
'Zip2zip': 'tools/zip2zip',
|
|
'ManifestCheck': 'tools/manifest_check',
|
|
'ConstructContext': 'tools/construct_context',
|
|
}
|
|
json.dump(dexpreopt_soong_config, f)
|
|
|
|
# TODO(b/188179859): Make *dex location configurable to vendor or system_other.
|
|
use_system_other_odex = False
|
|
|
|
if use_system_other_odex:
|
|
dex_img = 'SYSTEM_OTHER'
|
|
else:
|
|
dex_img = 'VENDOR'
|
|
# Open vendor_filesystem_config to append the items generated by dexopt.
|
|
vendor_file_system_config = open(
|
|
os.path.join(temp_dir, 'output', 'META',
|
|
'vendor_filesystem_config.txt'), 'a')
|
|
|
|
# Dexpreopt vendor apps.
|
|
dexpreopt_config_suffix = '_dexpreopt.config'
|
|
for config in glob.glob(
|
|
os.path.join(dexpreopt_vendor_config_files_temp_dir,
|
|
'*' + dexpreopt_config_suffix)):
|
|
app = os.path.basename(config)[:-len(dexpreopt_config_suffix)]
|
|
logging.info('dexpreopt config: %s %s', config, app)
|
|
|
|
apk_dir = 'app'
|
|
apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')
|
|
if not os.path.exists(apk_path):
|
|
apk_dir = 'priv-app'
|
|
apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')
|
|
if not os.path.exists(apk_path):
|
|
logging.warning(
|
|
'skipping dexpreopt for %s, no apk found in vendor/app '
|
|
'or vendor/priv-app', app)
|
|
continue
|
|
|
|
# Generate dexpreopting script. Note 'out_dir' is not the output directory
|
|
# where the script is generated, but the OUT_DIR at build time referenced
|
|
# in the dexpreot config files, e.g., "out/.../core-oj.jar", so the tool knows
|
|
# how to adjust the path.
|
|
command = [
|
|
os.path.join(dexpreopt_tools_files_temp_dir, 'dexpreopt_gen'),
|
|
'-global',
|
|
os.path.join(dexpreopt_framework_config_files_temp_dir,
|
|
'dexpreopt.config'),
|
|
'-global_soong',
|
|
os.path.join(dexpreopt_framework_config_files_temp_dir,
|
|
'dexpreopt_soong.config'),
|
|
'-module',
|
|
config,
|
|
'-dexpreopt_script',
|
|
'dexpreopt_app.sh',
|
|
'-out_dir',
|
|
'out',
|
|
'-base_path',
|
|
'.',
|
|
'--uses_target_files',
|
|
]
|
|
|
|
# Run the command from temp_dir so all tool paths are its descendants.
|
|
logging.info('running %s', command)
|
|
subprocess.check_call(command, cwd=temp_dir)
|
|
|
|
# Call the generated script.
|
|
command = ['sh', 'dexpreopt_app.sh', apk_path]
|
|
logging.info('running %s', command)
|
|
subprocess.check_call(command, cwd=temp_dir)
|
|
|
|
# Output files are in:
|
|
#
|
|
# <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.vdex
|
|
# <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.odex
|
|
# <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.vdex
|
|
# <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.odex
|
|
#
|
|
# Copy the files to their destination. The structure of system_other is:
|
|
#
|
|
# system_other/
|
|
# system-other-odex-marker
|
|
# system/
|
|
# app/
|
|
# <app>/oat/arm64/
|
|
# <app>.odex
|
|
# <app>.vdex
|
|
# ...
|
|
# priv-app/
|
|
# <app>/oat/arm64/
|
|
# <app>.odex
|
|
# <app>.vdex
|
|
# ...
|
|
|
|
# TODO(b/188179859): Support for other architectures.
|
|
arch = 'arm64'
|
|
|
|
dex_destination = os.path.join(temp_dir, 'output', dex_img, apk_dir, app,
|
|
'oat', arch)
|
|
os.makedirs(dex_destination)
|
|
dex2oat_path = os.path.join(temp_dir, 'out', 'dex2oat_result', 'vendor',
|
|
apk_dir, app, 'oat', arch)
|
|
shutil.copy(
|
|
os.path.join(dex2oat_path, 'package.vdex'),
|
|
os.path.join(dex_destination, app + '.vdex'))
|
|
shutil.copy(
|
|
os.path.join(dex2oat_path, 'package.odex'),
|
|
os.path.join(dex_destination, app + '.odex'))
|
|
|
|
# Append entries to vendor_file_system_config.txt, such as:
|
|
#
|
|
# vendor/app/<app>/oat 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
|
|
# vendor/app/<app>/oat/arm64 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
|
|
# vendor/app/<app>/oat/arm64/<app>.odex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
|
|
# vendor/app/<app>/oat/arm64/<app>.vdex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
|
|
if not use_system_other_odex:
|
|
vendor_app_prefix = 'vendor/' + apk_dir + '/' + app + '/oat'
|
|
selabel = 'selabel=u:object_r:vendor_app_file:s0 capabilities=0x0'
|
|
vendor_file_system_config.writelines([
|
|
vendor_app_prefix + ' 0 2000 755 ' + selabel + '\n',
|
|
vendor_app_prefix + '/' + arch + ' 0 2000 755 ' + selabel + '\n',
|
|
vendor_app_prefix + '/' + arch + '/' + app + '.odex 0 0 644 ' +
|
|
selabel + '\n',
|
|
vendor_app_prefix + '/' + arch + '/' + app + '.vdex 0 0 644 ' +
|
|
selabel + '\n',
|
|
])
|
|
|
|
if not use_system_other_odex:
|
|
vendor_file_system_config.close()
|
|
# Delete vendor.img so that it will be regenerated.
|
|
# TODO(b/188179859): Rebuilding a vendor image in GRF mode (e.g., T(framework)
|
|
# and S(vendor) may require logic similar to that in
|
|
# rebuild_image_with_sepolicy.
|
|
vendor_img = os.path.join(output_target_files_dir, 'IMAGES', 'vendor.img')
|
|
if os.path.exists(vendor_img):
|
|
logging.info('Deleting %s', vendor_img)
|
|
os.remove(vendor_img)
|