blob: 1c0c7433e575b3c5a77d4f121193da31dbbca78f [file] [log] [blame]
Daniel Norman2465fc82022-03-02 12:01:20 -08001#!/usr/bin/env python
2#
3# Copyright (C) 2022 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
16#
17"""Generates dexopt files for vendor apps, from a merged target_files.
18
19Expects items in OPTIONS prepared by merge_target_files.py.
20"""
21
22import glob
23import json
24import logging
25import os
26import shutil
27import subprocess
28
29import common
Rob Seymourc4e27542022-03-15 17:43:51 +000030import merge_utils
Daniel Norman2465fc82022-03-02 12:01:20 -080031
32logger = logging.getLogger(__name__)
33OPTIONS = common.OPTIONS
34
35
36def MergeDexopt(temp_dir, output_target_files_dir):
37 """If needed, generates dexopt files for vendor apps.
38
39 Args:
40 temp_dir: Location containing an 'output' directory where target files have
41 been extracted, e.g. <temp_dir>/output/SYSTEM, <temp_dir>/output/IMAGES,
42 etc.
43 output_target_files_dir: The name of a directory that will be used to create
44 the output target files package after all the special cases are processed.
45 """
46 # Load vendor and framework META/misc_info.txt.
47 if (OPTIONS.vendor_misc_info.get('building_with_vsdk') != 'true' or
48 OPTIONS.framework_dexpreopt_tools is None or
49 OPTIONS.framework_dexpreopt_config is None or
50 OPTIONS.vendor_dexpreopt_config is None):
51 return
52
53 logger.info('applying dexpreopt')
54
55 # The directory structure to apply dexpreopt is:
56 #
57 # <temp_dir>/
58 # framework_meta/
59 # META/
60 # vendor_meta/
61 # META/
62 # output/
63 # SYSTEM/
64 # VENDOR/
65 # IMAGES/
66 # <other items extracted from system and vendor target files>
67 # tools/
68 # <contents of dexpreopt_tools.zip>
69 # system_config/
70 # <contents of system dexpreopt_config.zip>
71 # vendor_config/
72 # <contents of vendor dexpreopt_config.zip>
73 # system -> output/SYSTEM
74 # vendor -> output/VENDOR
Daniel Norman2465fc82022-03-02 12:01:20 -080075 # apex/ (extracted updatable APEX)
76 # <apex 1>/
77 # ...
78 # <apex 2>/
79 # ...
80 # ...
81 # out/dex2oat_result/vendor/
82 # <app>
83 # oat/arm64/
84 # package.vdex
85 # package.odex
86 # <priv-app>
87 # oat/arm64/
88 # package.vdex
89 # package.odex
90 dexpreopt_tools_files_temp_dir = os.path.join(temp_dir, 'tools')
91 dexpreopt_framework_config_files_temp_dir = os.path.join(
92 temp_dir, 'system_config')
93 dexpreopt_vendor_config_files_temp_dir = os.path.join(temp_dir,
94 'vendor_config')
95
Rob Seymourc4e27542022-03-15 17:43:51 +000096 merge_utils.ExtractItems(
Daniel Norman2465fc82022-03-02 12:01:20 -080097 input_zip=OPTIONS.framework_dexpreopt_tools,
98 output_dir=dexpreopt_tools_files_temp_dir,
99 extract_item_list=('*',))
Rob Seymourc4e27542022-03-15 17:43:51 +0000100 merge_utils.ExtractItems(
Daniel Norman2465fc82022-03-02 12:01:20 -0800101 input_zip=OPTIONS.framework_dexpreopt_config,
102 output_dir=dexpreopt_framework_config_files_temp_dir,
103 extract_item_list=('*',))
Rob Seymourc4e27542022-03-15 17:43:51 +0000104 merge_utils.ExtractItems(
Daniel Norman2465fc82022-03-02 12:01:20 -0800105 input_zip=OPTIONS.vendor_dexpreopt_config,
106 output_dir=dexpreopt_vendor_config_files_temp_dir,
107 extract_item_list=('*',))
108
109 os.symlink(
110 os.path.join(output_target_files_dir, 'SYSTEM'),
111 os.path.join(temp_dir, 'system'))
112 os.symlink(
113 os.path.join(output_target_files_dir, 'VENDOR'),
114 os.path.join(temp_dir, 'vendor'))
115
Jooyung Han2ac1f2f2023-06-27 13:01:56 +0900116 # Extract APEX.
117 logging.info('extracting APEX')
118 apex_extract_root_dir = os.path.join(temp_dir, 'apex')
119 os.makedirs(apex_extract_root_dir)
120
Jooyung Hanc9542ab2023-06-27 16:12:32 +0900121 command = [
122 'apexd_host',
123 '--system_path',
124 os.path.join(temp_dir, 'system'),
125 '--apex_path',
126 apex_extract_root_dir,
127 ]
128 logging.info(' running %s', command)
129 subprocess.check_call(command)
Daniel Norman2465fc82022-03-02 12:01:20 -0800130
131 # Modify system config to point to the tools that have been extracted.
132 # Absolute or .. paths are not allowed by the dexpreopt_gen tool in
133 # dexpreopt_soong.config.
134 dexpreopt_framework_soon_config = os.path.join(
135 dexpreopt_framework_config_files_temp_dir, 'dexpreopt_soong.config')
136 with open(dexpreopt_framework_soon_config, 'w') as f:
137 dexpreopt_soong_config = {
138 'Profman': 'tools/profman',
139 'Dex2oat': 'tools/dex2oatd',
140 'Aapt': 'tools/aapt2',
141 'SoongZip': 'tools/soong_zip',
142 'Zip2zip': 'tools/zip2zip',
143 'ManifestCheck': 'tools/manifest_check',
144 'ConstructContext': 'tools/construct_context',
145 }
146 json.dump(dexpreopt_soong_config, f)
147
148 # TODO(b/188179859): Make *dex location configurable to vendor or system_other.
149 use_system_other_odex = False
150
151 if use_system_other_odex:
152 dex_img = 'SYSTEM_OTHER'
153 else:
154 dex_img = 'VENDOR'
155 # Open vendor_filesystem_config to append the items generated by dexopt.
156 vendor_file_system_config = open(
157 os.path.join(temp_dir, 'output', 'META',
158 'vendor_filesystem_config.txt'), 'a')
159
160 # Dexpreopt vendor apps.
161 dexpreopt_config_suffix = '_dexpreopt.config'
162 for config in glob.glob(
163 os.path.join(dexpreopt_vendor_config_files_temp_dir,
164 '*' + dexpreopt_config_suffix)):
165 app = os.path.basename(config)[:-len(dexpreopt_config_suffix)]
166 logging.info('dexpreopt config: %s %s', config, app)
167
168 apk_dir = 'app'
169 apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')
170 if not os.path.exists(apk_path):
171 apk_dir = 'priv-app'
172 apk_path = os.path.join(temp_dir, 'vendor', apk_dir, app, app + '.apk')
173 if not os.path.exists(apk_path):
174 logging.warning(
175 'skipping dexpreopt for %s, no apk found in vendor/app '
176 'or vendor/priv-app', app)
177 continue
178
179 # Generate dexpreopting script. Note 'out_dir' is not the output directory
180 # where the script is generated, but the OUT_DIR at build time referenced
181 # in the dexpreot config files, e.g., "out/.../core-oj.jar", so the tool knows
182 # how to adjust the path.
183 command = [
184 os.path.join(dexpreopt_tools_files_temp_dir, 'dexpreopt_gen'),
185 '-global',
186 os.path.join(dexpreopt_framework_config_files_temp_dir,
187 'dexpreopt.config'),
188 '-global_soong',
189 os.path.join(dexpreopt_framework_config_files_temp_dir,
190 'dexpreopt_soong.config'),
191 '-module',
192 config,
193 '-dexpreopt_script',
194 'dexpreopt_app.sh',
195 '-out_dir',
196 'out',
197 '-base_path',
198 '.',
199 '--uses_target_files',
200 ]
201
202 # Run the command from temp_dir so all tool paths are its descendants.
203 logging.info('running %s', command)
204 subprocess.check_call(command, cwd=temp_dir)
205
206 # Call the generated script.
207 command = ['sh', 'dexpreopt_app.sh', apk_path]
208 logging.info('running %s', command)
209 subprocess.check_call(command, cwd=temp_dir)
210
211 # Output files are in:
212 #
213 # <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.vdex
214 # <temp_dir>/out/dex2oat_result/vendor/priv-app/<app>/oat/arm64/package.odex
215 # <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.vdex
216 # <temp_dir>/out/dex2oat_result/vendor/app/<app>/oat/arm64/package.odex
217 #
218 # Copy the files to their destination. The structure of system_other is:
219 #
220 # system_other/
221 # system-other-odex-marker
222 # system/
223 # app/
224 # <app>/oat/arm64/
225 # <app>.odex
226 # <app>.vdex
227 # ...
228 # priv-app/
229 # <app>/oat/arm64/
230 # <app>.odex
231 # <app>.vdex
232 # ...
233
234 # TODO(b/188179859): Support for other architectures.
235 arch = 'arm64'
236
237 dex_destination = os.path.join(temp_dir, 'output', dex_img, apk_dir, app,
238 'oat', arch)
239 os.makedirs(dex_destination)
240 dex2oat_path = os.path.join(temp_dir, 'out', 'dex2oat_result', 'vendor',
241 apk_dir, app, 'oat', arch)
242 shutil.copy(
243 os.path.join(dex2oat_path, 'package.vdex'),
244 os.path.join(dex_destination, app + '.vdex'))
245 shutil.copy(
246 os.path.join(dex2oat_path, 'package.odex'),
247 os.path.join(dex_destination, app + '.odex'))
248
249 # Append entries to vendor_file_system_config.txt, such as:
250 #
251 # vendor/app/<app>/oat 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
252 # vendor/app/<app>/oat/arm64 0 2000 755 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
253 # vendor/app/<app>/oat/arm64/<app>.odex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
254 # vendor/app/<app>/oat/arm64/<app>.vdex 0 0 644 selabel=u:object_r:vendor_app_file:s0 capabilities=0x0
255 if not use_system_other_odex:
256 vendor_app_prefix = 'vendor/' + apk_dir + '/' + app + '/oat'
257 selabel = 'selabel=u:object_r:vendor_app_file:s0 capabilities=0x0'
258 vendor_file_system_config.writelines([
259 vendor_app_prefix + ' 0 2000 755 ' + selabel + '\n',
260 vendor_app_prefix + '/' + arch + ' 0 2000 755 ' + selabel + '\n',
261 vendor_app_prefix + '/' + arch + '/' + app + '.odex 0 0 644 ' +
262 selabel + '\n',
263 vendor_app_prefix + '/' + arch + '/' + app + '.vdex 0 0 644 ' +
264 selabel + '\n',
265 ])
266
267 if not use_system_other_odex:
268 vendor_file_system_config.close()
269 # Delete vendor.img so that it will be regenerated.
270 # TODO(b/188179859): Rebuilding a vendor image in GRF mode (e.g., T(framework)
271 # and S(vendor) may require logic similar to that in
272 # rebuild_image_with_sepolicy.
273 vendor_img = os.path.join(output_target_files_dir, 'IMAGES', 'vendor.img')
274 if os.path.exists(vendor_img):
275 logging.info('Deleting %s', vendor_img)
276 os.remove(vendor_img)