| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python | 
|  | 2 | # | 
|  | 3 | # Copyright (C) 2018 The Android Open Source Project | 
|  | 4 | # | 
|  | 5 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 6 | # you may not use this file except in compliance with the License. | 
|  | 7 | # You may obtain a copy of 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, | 
|  | 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 14 | # See the License for the specific language governing permissions and | 
|  | 15 | # limitations under the License. | 
|  | 16 | # | 
|  | 17 | """A tool for checking that a manifest agrees with the build system.""" | 
|  | 18 |  | 
|  | 19 | from __future__ import print_function | 
|  | 20 |  | 
|  | 21 | import argparse | 
| Ulya Trafimovich | 3c902e7 | 2021-03-04 18:06:27 +0000 | [diff] [blame] | 22 | import json | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 23 | import re | 
|  | 24 | import subprocess | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 25 | import sys | 
|  | 26 | from xml.dom import minidom | 
|  | 27 |  | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 28 | from manifest import android_ns | 
|  | 29 | from manifest import get_children_with_tag | 
|  | 30 | from manifest import parse_manifest | 
|  | 31 | from manifest import write_xml | 
|  | 32 |  | 
|  | 33 |  | 
|  | 34 | class ManifestMismatchError(Exception): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 35 | pass | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 36 |  | 
|  | 37 |  | 
|  | 38 | def parse_args(): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 39 | """Parse commandline arguments.""" | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 40 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 41 | parser = argparse.ArgumentParser() | 
|  | 42 | parser.add_argument( | 
|  | 43 | '--uses-library', | 
|  | 44 | dest='uses_libraries', | 
|  | 45 | action='append', | 
|  | 46 | help='specify uses-library entries known to the build system') | 
|  | 47 | parser.add_argument( | 
|  | 48 | '--optional-uses-library', | 
|  | 49 | dest='optional_uses_libraries', | 
|  | 50 | action='append', | 
|  | 51 | help='specify uses-library entries known to the build system with ' | 
|  | 52 | 'required:false' | 
|  | 53 | ) | 
|  | 54 | parser.add_argument( | 
| Jiakai Zhang | f98da19 | 2024-04-15 11:15:41 +0000 | [diff] [blame] | 55 | '--missing-optional-uses-library', | 
|  | 56 | dest='missing_optional_uses_libraries', | 
|  | 57 | action='append', | 
|  | 58 | help='specify uses-library entries missing from the build system with ' | 
|  | 59 | 'required:false', | 
|  | 60 | default=[] | 
|  | 61 | ) | 
|  | 62 | parser.add_argument( | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 63 | '--enforce-uses-libraries', | 
|  | 64 | dest='enforce_uses_libraries', | 
|  | 65 | action='store_true', | 
|  | 66 | help='check the uses-library entries known to the build system against ' | 
|  | 67 | 'the manifest' | 
|  | 68 | ) | 
|  | 69 | parser.add_argument( | 
|  | 70 | '--enforce-uses-libraries-relax', | 
|  | 71 | dest='enforce_uses_libraries_relax', | 
|  | 72 | action='store_true', | 
|  | 73 | help='do not fail immediately, just save the error message to file') | 
|  | 74 | parser.add_argument( | 
|  | 75 | '--enforce-uses-libraries-status', | 
|  | 76 | dest='enforce_uses_libraries_status', | 
|  | 77 | help='output file to store check status (error message)') | 
|  | 78 | parser.add_argument( | 
|  | 79 | '--extract-target-sdk-version', | 
|  | 80 | dest='extract_target_sdk_version', | 
|  | 81 | action='store_true', | 
|  | 82 | help='print the targetSdkVersion from the manifest') | 
|  | 83 | parser.add_argument( | 
|  | 84 | '--dexpreopt-config', | 
| Ulya Trofimovich | c68b289 | 2022-06-13 09:04:49 +0000 | [diff] [blame] | 85 | dest='dexpreopt_configs', | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 86 | action='append', | 
| Ulya Trofimovich | c68b289 | 2022-06-13 09:04:49 +0000 | [diff] [blame] | 87 | help='a paths to a dexpreopt.config of some library') | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 88 | parser.add_argument('--aapt', dest='aapt', help='path to aapt executable') | 
|  | 89 | parser.add_argument( | 
|  | 90 | '--output', '-o', dest='output', help='output AndroidManifest.xml file') | 
|  | 91 | parser.add_argument('input', help='input AndroidManifest.xml file') | 
|  | 92 | return parser.parse_args() | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 93 |  | 
|  | 94 |  | 
| Ulya Trafimovich | b4c19f8 | 2021-11-01 12:57:59 +0000 | [diff] [blame] | 95 | C_RED = "\033[1;31m" | 
|  | 96 | C_GREEN = "\033[1;32m" | 
|  | 97 | C_BLUE = "\033[1;34m" | 
|  | 98 | C_OFF = "\033[0m" | 
|  | 99 | C_BOLD = "\033[1m" | 
|  | 100 |  | 
|  | 101 |  | 
| Jiakai Zhang | f98da19 | 2024-04-15 11:15:41 +0000 | [diff] [blame] | 102 | def enforce_uses_libraries(manifest, required, optional, missing_optional, relax, is_apk, path): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 103 | """Verify that the <uses-library> tags in the manifest match those provided | 
|  | 104 |  | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 105 | by the build system. | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 106 |  | 
|  | 107 | Args: | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 108 | manifest: manifest (either parsed XML or aapt dump of APK) | 
|  | 109 | required: required libs known to the build system | 
|  | 110 | optional: optional libs known to the build system | 
|  | 111 | relax:    if true, suppress error on mismatch and just write it to file | 
|  | 112 | is_apk:   if the manifest comes from an APK or an XML file | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 113 | """ | 
|  | 114 | if is_apk: | 
|  | 115 | manifest_required, manifest_optional, tags = extract_uses_libs_apk( | 
|  | 116 | manifest) | 
|  | 117 | else: | 
|  | 118 | manifest_required, manifest_optional, tags = extract_uses_libs_xml( | 
|  | 119 | manifest) | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 120 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 121 | # Trim namespace component. Normally Soong does that automatically when it | 
|  | 122 | # handles module names specified in Android.bp properties. However not all | 
|  | 123 | # <uses-library> entries in the manifest correspond to real modules: some of | 
|  | 124 | # the optional libraries may be missing at build time. Therefor this script | 
|  | 125 | # accepts raw module names as spelled in Android.bp/Amdroid.mk and trims the | 
|  | 126 | # optional namespace part manually. | 
|  | 127 | required = trim_namespace_parts(required) | 
|  | 128 | optional = trim_namespace_parts(optional) | 
| Ulya Trafimovich | 1b51345 | 2021-07-20 14:27:32 +0100 | [diff] [blame] | 129 |  | 
| Jiakai Zhang | f98da19 | 2024-04-15 11:15:41 +0000 | [diff] [blame] | 130 | existing_manifest_optional = [ | 
|  | 131 | lib for lib in manifest_optional if lib not in missing_optional] | 
|  | 132 |  | 
|  | 133 | # The order of the existing libraries matter, while the order of the missing | 
|  | 134 | # ones doesn't. | 
|  | 135 | if manifest_required == required and existing_manifest_optional == optional: | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 136 | return None | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 137 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 138 | #pylint: disable=line-too-long | 
|  | 139 | errmsg = ''.join([ | 
|  | 140 | 'mismatch in the <uses-library> tags between the build system and the ' | 
|  | 141 | 'manifest:\n', | 
| Ulya Trafimovich | b4c19f8 | 2021-11-01 12:57:59 +0000 | [diff] [blame] | 142 | '\t- required libraries in build system: %s[%s]%s\n' % (C_RED, ', '.join(required), C_OFF), | 
|  | 143 | '\t                 vs. in the manifest: %s[%s]%s\n' % (C_RED, ', '.join(manifest_required), C_OFF), | 
|  | 144 | '\t- optional libraries in build system: %s[%s]%s\n' % (C_RED, ', '.join(optional), C_OFF), | 
| Jiakai Zhang | f98da19 | 2024-04-15 11:15:41 +0000 | [diff] [blame] | 145 | '\t    and missing ones in build system: %s[%s]%s\n' % (C_RED, ', '.join(missing_optional), C_OFF), | 
| Ulya Trafimovich | b4c19f8 | 2021-11-01 12:57:59 +0000 | [diff] [blame] | 146 | '\t                 vs. in the manifest: %s[%s]%s\n' % (C_RED, ', '.join(manifest_optional), C_OFF), | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 147 | '\t- tags in the manifest (%s):\n' % path, | 
|  | 148 | '\t\t%s\n' % '\t\t'.join(tags), | 
| Ulya Trafimovich | b4c19f8 | 2021-11-01 12:57:59 +0000 | [diff] [blame] | 149 | '%snote:%s the following options are available:\n' % (C_BLUE, C_OFF), | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 150 | '\t- to temporarily disable the check on command line, rebuild with ', | 
| Ulya Trafimovich | b4c19f8 | 2021-11-01 12:57:59 +0000 | [diff] [blame] | 151 | '%sRELAX_USES_LIBRARY_CHECK=true%s' % (C_BOLD, C_OFF), | 
|  | 152 | ' (this will set compiler filter "verify" and disable AOT-compilation in dexpreopt)\n', | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 153 | '\t- to temporarily disable the check for the whole product, set ', | 
| Ulya Trafimovich | b4c19f8 | 2021-11-01 12:57:59 +0000 | [diff] [blame] | 154 | '%sPRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true%s in the product makefiles\n' % (C_BOLD, C_OFF), | 
|  | 155 | '\t- to fix the check, make build system properties coherent with the manifest\n', | 
|  | 156 | '\t- for details, see %sbuild/make/Changes.md%s' % (C_GREEN, C_OFF), | 
|  | 157 | ' and %shttps://source.android.com/devices/tech/dalvik/art-class-loader-context%s\n' % (C_GREEN, C_OFF) | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 158 | ]) | 
|  | 159 | #pylint: enable=line-too-long | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 160 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 161 | if not relax: | 
|  | 162 | raise ManifestMismatchError(errmsg) | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 163 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 164 | return errmsg | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 165 |  | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 166 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 167 | MODULE_NAMESPACE = re.compile('^//[^:]+:') | 
|  | 168 |  | 
| Ulya Trafimovich | 1b51345 | 2021-07-20 14:27:32 +0100 | [diff] [blame] | 169 |  | 
|  | 170 | def trim_namespace_parts(modules): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 171 | """Trim the namespace part of each module, if present. | 
| Ulya Trafimovich | 1b51345 | 2021-07-20 14:27:32 +0100 | [diff] [blame] | 172 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 173 | Leave only the name. | 
|  | 174 | """ | 
|  | 175 |  | 
|  | 176 | trimmed = [] | 
|  | 177 | for module in modules: | 
|  | 178 | trimmed.append(MODULE_NAMESPACE.sub('', module)) | 
|  | 179 | return trimmed | 
| Ulya Trafimovich | 1b51345 | 2021-07-20 14:27:32 +0100 | [diff] [blame] | 180 |  | 
|  | 181 |  | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 182 | def extract_uses_libs_apk(badging): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 183 | """Extract <uses-library> tags from the manifest of an APK.""" | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 184 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 185 | pattern = re.compile("^uses-library(-not-required)?:'(.*)'$", re.MULTILINE) | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 186 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 187 | required = [] | 
|  | 188 | optional = [] | 
|  | 189 | lines = [] | 
|  | 190 | for match in re.finditer(pattern, badging): | 
|  | 191 | lines.append(match.group(0)) | 
|  | 192 | libname = match.group(2) | 
|  | 193 | if match.group(1) is None: | 
|  | 194 | required.append(libname) | 
|  | 195 | else: | 
|  | 196 | optional.append(libname) | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 197 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 198 | required = first_unique_elements(required) | 
|  | 199 | optional = first_unique_elements(optional) | 
|  | 200 | tags = first_unique_elements(lines) | 
|  | 201 | return required, optional, tags | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 202 |  | 
|  | 203 |  | 
| Colin Cross | c00fa15 | 2023-10-06 13:10:52 -0700 | [diff] [blame] | 204 | def extract_uses_libs_xml(xml): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 205 | """Extract <uses-library> tags from the manifest.""" | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 206 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 207 | manifest = parse_manifest(xml) | 
|  | 208 | elems = get_children_with_tag(manifest, 'application') | 
| Colin Cross | c00fa15 | 2023-10-06 13:10:52 -0700 | [diff] [blame] | 209 | if len(elems) > 1: | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 210 | raise RuntimeError('found multiple <application> tags') | 
| Colin Cross | c00fa15 | 2023-10-06 13:10:52 -0700 | [diff] [blame] | 211 | if not elems: | 
|  | 212 | return [], [], [] | 
|  | 213 |  | 
|  | 214 | application = elems[0] | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 215 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 216 | libs = get_children_with_tag(application, 'uses-library') | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 217 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 218 | required = [uses_library_name(x) for x in libs if uses_library_required(x)] | 
|  | 219 | optional = [ | 
|  | 220 | uses_library_name(x) for x in libs if not uses_library_required(x) | 
|  | 221 | ] | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 222 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 223 | # render <uses-library> tags as XML for a pretty error message | 
|  | 224 | tags = [] | 
|  | 225 | for lib in libs: | 
|  | 226 | tags.append(lib.toprettyxml()) | 
| Ulya Trafimovich | bb7513d | 2021-03-30 17:15:16 +0100 | [diff] [blame] | 227 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 228 | required = first_unique_elements(required) | 
|  | 229 | optional = first_unique_elements(optional) | 
|  | 230 | tags = first_unique_elements(tags) | 
|  | 231 | return required, optional, tags | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 232 |  | 
|  | 233 |  | 
|  | 234 | def first_unique_elements(l): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 235 | result = [] | 
|  | 236 | for x in l: | 
|  | 237 | if x not in result: | 
|  | 238 | result.append(x) | 
|  | 239 | return result | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 240 |  | 
|  | 241 |  | 
|  | 242 | def uses_library_name(lib): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 243 | """Extract the name attribute of a uses-library tag. | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 244 |  | 
|  | 245 | Args: | 
|  | 246 | lib: a <uses-library> tag. | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 247 | """ | 
|  | 248 | name = lib.getAttributeNodeNS(android_ns, 'name') | 
|  | 249 | return name.value if name is not None else '' | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 250 |  | 
|  | 251 |  | 
|  | 252 | def uses_library_required(lib): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 253 | """Extract the required attribute of a uses-library tag. | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 254 |  | 
|  | 255 | Args: | 
|  | 256 | lib: a <uses-library> tag. | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 257 | """ | 
|  | 258 | required = lib.getAttributeNodeNS(android_ns, 'required') | 
|  | 259 | return (required.value == 'true') if required is not None else True | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 260 |  | 
|  | 261 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 262 | def extract_target_sdk_version(manifest, is_apk=False): | 
|  | 263 | """Returns the targetSdkVersion from the manifest. | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 264 |  | 
|  | 265 | Args: | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 266 | manifest: manifest (either parsed XML or aapt dump of APK) | 
|  | 267 | is_apk:   if the manifest comes from an APK or an XML file | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 268 | """ | 
|  | 269 | if is_apk: #pylint: disable=no-else-return | 
|  | 270 | return extract_target_sdk_version_apk(manifest) | 
|  | 271 | else: | 
|  | 272 | return extract_target_sdk_version_xml(manifest) | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 273 |  | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 274 |  | 
|  | 275 | def extract_target_sdk_version_apk(badging): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 276 | """Extract targetSdkVersion tags from the manifest of an APK.""" | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 277 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 278 | pattern = re.compile("^targetSdkVersion?:'(.*)'$", re.MULTILINE) | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 279 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 280 | for match in re.finditer(pattern, badging): | 
|  | 281 | return match.group(1) | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 282 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 283 | raise RuntimeError('cannot find targetSdkVersion in the manifest') | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 284 |  | 
|  | 285 |  | 
|  | 286 | def extract_target_sdk_version_xml(xml): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 287 | """Extract targetSdkVersion tags from the manifest.""" | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 288 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 289 | manifest = parse_manifest(xml) | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 290 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 291 | # Get or insert the uses-sdk element | 
|  | 292 | uses_sdk = get_children_with_tag(manifest, 'uses-sdk') | 
|  | 293 | if len(uses_sdk) > 1: #pylint: disable=no-else-raise | 
|  | 294 | raise RuntimeError('found multiple uses-sdk elements') | 
|  | 295 | elif len(uses_sdk) == 0: | 
|  | 296 | raise RuntimeError('missing uses-sdk element') | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 297 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 298 | uses_sdk = uses_sdk[0] | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 299 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 300 | min_attr = uses_sdk.getAttributeNodeNS(android_ns, 'minSdkVersion') | 
|  | 301 | if min_attr is None: | 
|  | 302 | raise RuntimeError('minSdkVersion is not specified') | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 303 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 304 | target_attr = uses_sdk.getAttributeNodeNS(android_ns, 'targetSdkVersion') | 
|  | 305 | if target_attr is None: | 
|  | 306 | target_attr = min_attr | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 307 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 308 | return target_attr.value | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 309 |  | 
|  | 310 |  | 
| Ulya Trofimovich | c68b289 | 2022-06-13 09:04:49 +0000 | [diff] [blame] | 311 | def load_dexpreopt_configs(configs): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 312 | """Load dexpreopt.config files and map module names to library names.""" | 
|  | 313 | module_to_libname = {} | 
| Ulya Trafimovich | 3c902e7 | 2021-03-04 18:06:27 +0000 | [diff] [blame] | 314 |  | 
| Ulya Trofimovich | c68b289 | 2022-06-13 09:04:49 +0000 | [diff] [blame] | 315 | if configs is None: | 
|  | 316 | configs = [] | 
| Ulya Trafimovich | 3c902e7 | 2021-03-04 18:06:27 +0000 | [diff] [blame] | 317 |  | 
| Ulya Trofimovich | c68b289 | 2022-06-13 09:04:49 +0000 | [diff] [blame] | 318 | for config in configs: | 
|  | 319 | with open(config, 'r') as f: | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 320 | contents = json.load(f) | 
| Ulya Trofimovich | c68b289 | 2022-06-13 09:04:49 +0000 | [diff] [blame] | 321 | module_to_libname[contents['Name']] = contents['ProvidesUsesLibrary'] | 
| Ulya Trafimovich | 3c902e7 | 2021-03-04 18:06:27 +0000 | [diff] [blame] | 322 |  | 
| Ulya Trofimovich | c68b289 | 2022-06-13 09:04:49 +0000 | [diff] [blame] | 323 | return module_to_libname | 
| Ulya Trafimovich | 3c902e7 | 2021-03-04 18:06:27 +0000 | [diff] [blame] | 324 |  | 
|  | 325 |  | 
|  | 326 | def translate_libnames(modules, module_to_libname): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 327 | """Translate module names into library names using the mapping.""" | 
| Ulya Trofimovich | c68b289 | 2022-06-13 09:04:49 +0000 | [diff] [blame] | 328 | if modules is None: | 
|  | 329 | modules = [] | 
| Ulya Trafimovich | 3c902e7 | 2021-03-04 18:06:27 +0000 | [diff] [blame] | 330 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 331 | libnames = [] | 
|  | 332 | for name in modules: | 
|  | 333 | if name in module_to_libname: | 
|  | 334 | name = module_to_libname[name] | 
|  | 335 | libnames.append(name) | 
| Ulya Trafimovich | 3c902e7 | 2021-03-04 18:06:27 +0000 | [diff] [blame] | 336 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 337 | return libnames | 
| Ulya Trafimovich | 3c902e7 | 2021-03-04 18:06:27 +0000 | [diff] [blame] | 338 |  | 
|  | 339 |  | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 340 | def main(): | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 341 | """Program entry point.""" | 
|  | 342 | try: | 
|  | 343 | args = parse_args() | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 344 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 345 | # The input can be either an XML manifest or an APK, they are parsed and | 
|  | 346 | # processed in different ways. | 
|  | 347 | is_apk = args.input.endswith('.apk') | 
|  | 348 | if is_apk: | 
|  | 349 | aapt = args.aapt if args.aapt is not None else 'aapt' | 
|  | 350 | manifest = subprocess.check_output( | 
| Cole Faust | c41dd72 | 2021-11-09 15:08:26 -0800 | [diff] [blame] | 351 | [aapt, 'dump', 'badging', args.input]).decode('utf-8') | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 352 | else: | 
|  | 353 | manifest = minidom.parse(args.input) | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 354 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 355 | if args.enforce_uses_libraries: | 
|  | 356 | # Load dexpreopt.config files and build a mapping from module | 
| Jiakai Zhang | f98da19 | 2024-04-15 11:15:41 +0000 | [diff] [blame] | 357 | # names to library names. This is for Make only and it's necessary | 
|  | 358 | # because Make passes module names from `LOCAL_USES_LIBRARIES`, | 
|  | 359 | # `LOCAL_OPTIONAL_LIBRARY_NAMES`, while the manifest addresses | 
|  | 360 | # libraries by their name. Soong doesn't use it and doesn't need it | 
|  | 361 | # because it converts the module names to the library names and | 
|  | 362 | # passes the library names. There is no need to translate missing | 
|  | 363 | # optional libs because they are missing and therefore there is no | 
|  | 364 | # mapping for them. | 
| Ulya Trofimovich | c68b289 | 2022-06-13 09:04:49 +0000 | [diff] [blame] | 365 | mod_to_lib = load_dexpreopt_configs(args.dexpreopt_configs) | 
|  | 366 | required = translate_libnames(args.uses_libraries, mod_to_lib) | 
|  | 367 | optional = translate_libnames(args.optional_uses_libraries, | 
|  | 368 | mod_to_lib) | 
| Ulya Trafimovich | 3c902e7 | 2021-03-04 18:06:27 +0000 | [diff] [blame] | 369 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 370 | # Check if the <uses-library> lists in the build system agree with | 
|  | 371 | # those in the manifest. Raise an exception on mismatch, unless the | 
|  | 372 | # script was passed a special parameter to suppress exceptions. | 
|  | 373 | errmsg = enforce_uses_libraries(manifest, required, optional, | 
| Jiakai Zhang | f98da19 | 2024-04-15 11:15:41 +0000 | [diff] [blame] | 374 | args.missing_optional_uses_libraries, | 
|  | 375 | args.enforce_uses_libraries_relax, is_apk, args.input) | 
| Ulya Trafimovich | 8c35fcf | 2021-02-17 16:23:28 +0000 | [diff] [blame] | 376 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 377 | # Create a status file that is empty on success, or contains an | 
|  | 378 | # error message on failure. When exceptions are suppressed, | 
|  | 379 | # dexpreopt command command will check file size to determine if | 
|  | 380 | # the check has failed. | 
|  | 381 | if args.enforce_uses_libraries_status: | 
|  | 382 | with open(args.enforce_uses_libraries_status, 'w') as f: | 
| Spandan Das | 3d5cd4d | 2021-09-20 18:24:56 +0000 | [diff] [blame] | 383 | if errmsg is not None: | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 384 | f.write('%s\n' % errmsg) | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 385 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 386 | if args.extract_target_sdk_version: | 
|  | 387 | try: | 
|  | 388 | print(extract_target_sdk_version(manifest, is_apk)) | 
|  | 389 | except: #pylint: disable=bare-except | 
|  | 390 | # Failed; don't crash, return "any" SDK version. This will | 
|  | 391 | # result in dexpreopt not adding any compatibility libraries. | 
|  | 392 | print(10000) | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 393 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 394 | if args.output: | 
|  | 395 | # XML output is supposed to be written only when this script is | 
|  | 396 | # invoked with XML input manifest, not with an APK. | 
|  | 397 | if is_apk: | 
|  | 398 | raise RuntimeError('cannot save APK manifest as XML') | 
| Ulya Trafimovich | 0aba252 | 2021-03-03 16:38:37 +0000 | [diff] [blame] | 399 |  | 
| Cole Faust | c41dd72 | 2021-11-09 15:08:26 -0800 | [diff] [blame] | 400 | with open(args.output, 'w') as f: | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 401 | write_xml(f, manifest) | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 402 |  | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 403 | # pylint: disable=broad-except | 
|  | 404 | except Exception as err: | 
| Ulya Trafimovich | b4c19f8 | 2021-11-01 12:57:59 +0000 | [diff] [blame] | 405 | print('%serror:%s ' % (C_RED, C_OFF) + str(err), file=sys.stderr) | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 406 | sys.exit(-1) | 
|  | 407 |  | 
| Colin Cross | 7211910 | 2019-05-20 13:14:18 -0700 | [diff] [blame] | 408 |  | 
|  | 409 | if __name__ == '__main__': | 
| Spandan Das | f880742 | 2021-08-25 20:01:17 +0000 | [diff] [blame] | 410 | main() |