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