Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit da513b1e authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Apply pylint to scripts/manifest_check*.py"

parents 96d54244 f8807425
Loading
Loading
Loading
Loading
+280 −249
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import subprocess
import sys
from xml.dom import minidom


from manifest import android_ns
from manifest import get_children_with_tag
from manifest import parse_manifest
@@ -40,42 +39,54 @@ def parse_args():
    """Parse commandline arguments."""

    parser = argparse.ArgumentParser()
  parser.add_argument('--uses-library', dest='uses_libraries',
    parser.add_argument(
        '--uses-library',
        dest='uses_libraries',
        action='append',
        help='specify uses-library entries known to the build system')
  parser.add_argument('--optional-uses-library',
    parser.add_argument(
        '--optional-uses-library',
        dest='optional_uses_libraries',
        action='append',
                      help='specify uses-library entries known to the build system with required:false')
  parser.add_argument('--enforce-uses-libraries',
        help='specify uses-library entries known to the build system with '
        'required:false'
    )
    parser.add_argument(
        '--enforce-uses-libraries',
        dest='enforce_uses_libraries',
        action='store_true',
                      help='check the uses-library entries known to the build system against the manifest')
  parser.add_argument('--enforce-uses-libraries-relax',
        help='check the uses-library entries known to the build system against '
        'the manifest'
    )
    parser.add_argument(
        '--enforce-uses-libraries-relax',
        dest='enforce_uses_libraries_relax',
        action='store_true',
        help='do not fail immediately, just save the error message to file')
  parser.add_argument('--enforce-uses-libraries-status',
    parser.add_argument(
        '--enforce-uses-libraries-status',
        dest='enforce_uses_libraries_status',
        help='output file to store check status (error message)')
  parser.add_argument('--extract-target-sdk-version',
    parser.add_argument(
        '--extract-target-sdk-version',
        dest='extract_target_sdk_version',
        action='store_true',
        help='print the targetSdkVersion from the manifest')
  parser.add_argument('--dexpreopt-config',
    parser.add_argument(
        '--dexpreopt-config',
        dest='dexpreopt_configs',
        action='append',
        help='a paths to a dexpreopt.config of some library')
  parser.add_argument('--aapt',
                      dest='aapt',
                      help='path to aapt executable')
  parser.add_argument('--output', '-o', dest='output', help='output AndroidManifest.xml file')
    parser.add_argument('--aapt', dest='aapt', help='path to aapt executable')
    parser.add_argument(
        '--output', '-o', dest='output', help='output AndroidManifest.xml file')
    parser.add_argument('input', help='input AndroidManifest.xml file')
    return parser.parse_args()


def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
    """Verify that the <uses-library> tags in the manifest match those provided

  by the build system.

  Args:
@@ -86,9 +97,11 @@ def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
    is_apk:   if the manifest comes from an APK or an XML file
    """
    if is_apk:
    manifest_required, manifest_optional, tags = extract_uses_libs_apk(manifest)
        manifest_required, manifest_optional, tags = extract_uses_libs_apk(
            manifest)
    else:
    manifest_required, manifest_optional, tags = extract_uses_libs_xml(manifest)
        manifest_required, manifest_optional, tags = extract_uses_libs_xml(
            manifest)

    # Trim namespace component. Normally Soong does that automatically when it
    # handles module names specified in Android.bp properties. However not all
@@ -102,13 +115,16 @@ def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
    if manifest_required == required and manifest_optional == optional:
        return None

    #pylint: disable=line-too-long
    errmsg = ''.join([
        'mismatch in the <uses-library> tags between the build system and the '
        'manifest:\n',
        '\t- required libraries in build system: [%s]\n' % ', '.join(required),
    '\t                 vs. in the manifest: [%s]\n' % ', '.join(manifest_required),
        '\t                 vs. in the manifest: [%s]\n' %
        ', '.join(manifest_required),
        '\t- optional libraries in build system: [%s]\n' % ', '.join(optional),
    '\t                 vs. in the manifest: [%s]\n' % ', '.join(manifest_optional),
        '\t                 vs. in the manifest: [%s]\n' %
        ', '.join(manifest_optional),
        '\t- tags in the manifest (%s):\n' % path,
        '\t\t%s\n' % '\t\t'.join(tags),
        'note: the following options are available:\n',
@@ -118,8 +134,9 @@ def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
        '\t- to temporarily disable the check for the whole product, set ',
        'PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles\n',
        '\t- to fix the check, make build system properties coherent with the '
      'manifest\n',
    '\t- see build/make/Changes.md for details\n'])
        'manifest\n', '\t- see build/make/Changes.md for details\n'
    ])
    #pylint: enable=line-too-long

    if not relax:
        raise ManifestMismatchError(errmsg)
@@ -127,10 +144,14 @@ def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
    return errmsg


MODULE_NAMESPACE = re.compile("^//[^:]+:")
MODULE_NAMESPACE = re.compile('^//[^:]+:')


def trim_namespace_parts(modules):
  """Trim the namespace part of each module, if present. Leave only the name."""
    """Trim the namespace part of each module, if present.

    Leave only the name.
    """

    trimmed = []
    for module in modules:
@@ -149,7 +170,7 @@ def extract_uses_libs_apk(badging):
    for match in re.finditer(pattern, badging):
        lines.append(match.group(0))
        libname = match.group(2)
    if match.group(1) == None:
        if match.group(1) is None:
            required.append(libname)
        else:
            optional.append(libname)
@@ -160,23 +181,25 @@ def extract_uses_libs_apk(badging):
    return required, optional, tags


def extract_uses_libs_xml(xml):
def extract_uses_libs_xml(xml): #pylint: disable=inconsistent-return-statements
    """Extract <uses-library> tags from the manifest."""

    manifest = parse_manifest(xml)
    elems = get_children_with_tag(manifest, 'application')
    application = elems[0] if len(elems) == 1 else None
  if len(elems) > 1:
    if len(elems) > 1: #pylint: disable=no-else-raise
        raise RuntimeError('found multiple <application> tags')
    elif not elems:
    if uses_libraries or optional_uses_libraries:
        if uses_libraries or optional_uses_libraries: #pylint: disable=undefined-variable
            raise ManifestMismatchError('no <application> tag found')
        return

    libs = get_children_with_tag(application, 'uses-library')

    required = [uses_library_name(x) for x in libs if uses_library_required(x)]
  optional = [uses_library_name(x) for x in libs if not uses_library_required(x)]
    optional = [
        uses_library_name(x) for x in libs if not uses_library_required(x)
    ]

    # render <uses-library> tags as XML for a pretty error message
    tags = []
@@ -191,7 +214,9 @@ def extract_uses_libs_xml(xml):

def first_unique_elements(l):
    result = []
  [result.append(x) for x in l if x not in result]
    for x in l:
        if x not in result:
            result.append(x)
    return result


@@ -202,7 +227,7 @@ def uses_library_name(lib):
    lib: a <uses-library> tag.
    """
    name = lib.getAttributeNodeNS(android_ns, 'name')
  return name.value if name is not None else ""
    return name.value if name is not None else ''


def uses_library_required(lib):
@@ -222,7 +247,7 @@ def extract_target_sdk_version(manifest, is_apk = False):
    manifest: manifest (either parsed XML or aapt dump of APK)
    is_apk:   if the manifest comes from an APK or an XML file
    """
  if is_apk:
    if is_apk: #pylint: disable=no-else-return
        return extract_target_sdk_version_apk(manifest)
    else:
        return extract_target_sdk_version_xml(manifest)
@@ -246,7 +271,7 @@ def extract_target_sdk_version_xml(xml):

    # Get or insert the uses-sdk element
    uses_sdk = get_children_with_tag(manifest, 'uses-sdk')
  if len(uses_sdk) > 1:
    if len(uses_sdk) > 1: #pylint: disable=no-else-raise
        raise RuntimeError('found multiple uses-sdk elements')
    elif len(uses_sdk) == 0:
        raise RuntimeError('missing uses-sdk element')
@@ -302,46 +327,51 @@ def main():
        # processed in different ways.
        is_apk = args.input.endswith('.apk')
        if is_apk:
      aapt = args.aapt if args.aapt != None else "aapt"
      manifest = subprocess.check_output([aapt, "dump", "badging", args.input])
            aapt = args.aapt if args.aapt is not None else 'aapt'
            manifest = subprocess.check_output(
                [aapt, 'dump', 'badging', args.input])
        else:
            manifest = minidom.parse(args.input)

        if args.enforce_uses_libraries:
      # Load dexpreopt.config files and build a mapping from module names to
      # library names. This is necessary because build system addresses
      # libraries by their module name (`uses_libs`, `optional_uses_libs`,
      # `LOCAL_USES_LIBRARIES`, `LOCAL_OPTIONAL_LIBRARY_NAMES` all contain
      # module names), while the manifest addresses libraries by their name.
            # Load dexpreopt.config files and build a mapping from module
            # names to library names. This is necessary because build system
            # addresses libraries by their module name (`uses_libs`,
            # `optional_uses_libs`, `LOCAL_USES_LIBRARIES`,
            # `LOCAL_OPTIONAL_LIBRARY_NAMES` all contain module names), while
            # the manifest addresses libraries by their name.
            mod_to_lib = load_dexpreopt_configs(args.dexpreopt_configs)
            required = translate_libnames(args.uses_libraries, mod_to_lib)
      optional = translate_libnames(args.optional_uses_libraries, mod_to_lib)
            optional = translate_libnames(args.optional_uses_libraries,
                                          mod_to_lib)

      # Check if the <uses-library> lists in the build system agree with those
      # in the manifest. Raise an exception on mismatch, unless the script was
      # passed a special parameter to suppress exceptions.
            # Check if the <uses-library> lists in the build system agree with
            # those in the manifest. Raise an exception on mismatch, unless the
            # script was passed a special parameter to suppress exceptions.
            errmsg = enforce_uses_libraries(manifest, required, optional,
        args.enforce_uses_libraries_relax, is_apk, args.input)
                                            args.enforce_uses_libraries_relax,
                                            is_apk, args.input)

      # Create a status file that is empty on success, or contains an error
      # message on failure. When exceptions are suppressed, dexpreopt command
      # command will check file size to determine if the check has failed.
            # Create a status file that is empty on success, or contains an
            # error message on failure. When exceptions are suppressed,
            # dexpreopt command command will check file size to determine if
            # the check has failed.
            if args.enforce_uses_libraries_status:
                with open(args.enforce_uses_libraries_status, 'w') as f:
          if not errmsg == None:
            f.write("%s\n" % errmsg)
                    if not errmsg is not None:
                        f.write('%s\n' % errmsg)

        if args.extract_target_sdk_version:
            try:
                print(extract_target_sdk_version(manifest, is_apk))
      except:
        # Failed; don't crash, return "any" SDK version. This will result in
        # dexpreopt not adding any compatibility libraries.
            except: #pylint: disable=bare-except
                # Failed; don't crash, return "any" SDK version. This will
                # result in dexpreopt not adding any compatibility libraries.
                print(10000)

        if args.output:
      # XML output is supposed to be written only when this script is invoked
      # with XML input manifest, not with an APK.
            # XML output is supposed to be written only when this script is
            # invoked with XML input manifest, not with an APK.
            if is_apk:
                raise RuntimeError('cannot save APK manifest as XML')

@@ -353,5 +383,6 @@ def main():
        print('error: ' + str(err), file=sys.stderr)
        sys.exit(-1)


if __name__ == '__main__':
    main()
+215 −182
Original line number Diff line number Diff line
@@ -44,32 +44,32 @@ def required_apk(value):
class EnforceUsesLibrariesTest(unittest.TestCase):
    """Unit tests for add_extract_native_libs function."""

  def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[]):
    def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[]): #pylint: disable=dangerous-default-value
        doc = minidom.parseString(xml)
        try:
            relax = False
      manifest_check.enforce_uses_libraries(doc, uses_libraries,
        optional_uses_libraries, relax, False, 'path/to/X/AndroidManifest.xml')
            manifest_check.enforce_uses_libraries(
                doc, uses_libraries, optional_uses_libraries, relax, False,
                'path/to/X/AndroidManifest.xml')
            manifest_check.enforce_uses_libraries(apk, uses_libraries,
        optional_uses_libraries, relax, True, 'path/to/X/X.apk')
                                                  optional_uses_libraries,
                                                  relax, True,
                                                  'path/to/X/X.apk')
            return True
        except manifest_check.ManifestMismatchError:
            return False

    xml_tmpl = (
      '<?xml version="1.0" encoding="utf-8"?>\n'
      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
      '    <application>\n'
      '    %s\n'
      '    </application>\n'
      '</manifest>\n')
        '<?xml version="1.0" encoding="utf-8"?>\n<manifest '
        'xmlns:android="http://schemas.android.com/apk/res/android">\n    '
        '<application>\n    %s\n    </application>\n</manifest>\n')

    apk_tmpl = (
        "package: name='com.google.android.something' versionCode='100'\n"
        "sdkVersion:'29'\n"
        "targetSdkVersion:'29'\n"
        "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n"
      "%s\n"
        '%s\n'
        "densities: '160' '240' '320' '480' '640' '65534")

    def test_uses_library(self):
@@ -127,73 +127,105 @@ class EnforceUsesLibrariesTest(unittest.TestCase):
        self.assertFalse(matches)

    def test_multiple_uses_library(self):
    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
        xml = self.xml_tmpl % ('\n'.join(
            [uses_library_xml('foo'),
             uses_library_xml('bar')]))
    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
        apk = self.apk_tmpl % ('\n'.join(
            [uses_library_apk('foo'),
             uses_library_apk('bar')]))
        matches = self.run_test(xml, apk, uses_libraries=['foo', 'bar'])
        self.assertTrue(matches)

    def test_multiple_optional_uses_library(self):
    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
                                      uses_library_xml('bar', required_xml(False))]))
    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
                                      uses_library_apk('bar', required_apk(False))]))
    matches = self.run_test(xml, apk, optional_uses_libraries=['foo', 'bar'])
        xml = self.xml_tmpl % ('\n'.join([
            uses_library_xml('foo', required_xml(False)),
            uses_library_xml('bar', required_xml(False))
        ]))
        apk = self.apk_tmpl % ('\n'.join([
            uses_library_apk('foo', required_apk(False)),
            uses_library_apk('bar', required_apk(False))
        ]))
        matches = self.run_test(
            xml, apk, optional_uses_libraries=['foo', 'bar'])
        self.assertTrue(matches)

    def test_order_uses_library(self):
    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
        xml = self.xml_tmpl % ('\n'.join(
            [uses_library_xml('foo'),
             uses_library_xml('bar')]))
    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
        apk = self.apk_tmpl % ('\n'.join(
            [uses_library_apk('foo'),
             uses_library_apk('bar')]))
        matches = self.run_test(xml, apk, uses_libraries=['bar', 'foo'])
        self.assertFalse(matches)

    def test_order_optional_uses_library(self):
    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
                                      uses_library_xml('bar', required_xml(False))]))
    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
                                      uses_library_apk('bar', required_apk(False))]))
    matches = self.run_test(xml, apk, optional_uses_libraries=['bar', 'foo'])
        xml = self.xml_tmpl % ('\n'.join([
            uses_library_xml('foo', required_xml(False)),
            uses_library_xml('bar', required_xml(False))
        ]))
        apk = self.apk_tmpl % ('\n'.join([
            uses_library_apk('foo', required_apk(False)),
            uses_library_apk('bar', required_apk(False))
        ]))
        matches = self.run_test(
            xml, apk, optional_uses_libraries=['bar', 'foo'])
        self.assertFalse(matches)

    def test_duplicate_uses_library(self):
    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
        xml = self.xml_tmpl % ('\n'.join(
            [uses_library_xml('foo'),
             uses_library_xml('foo')]))
    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
        apk = self.apk_tmpl % ('\n'.join(
            [uses_library_apk('foo'),
             uses_library_apk('foo')]))
        matches = self.run_test(xml, apk, uses_libraries=['foo'])
        self.assertTrue(matches)

    def test_duplicate_optional_uses_library(self):
    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
                                      uses_library_xml('foo', required_xml(False))]))
    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
                                      uses_library_apk('foo', required_apk(False))]))
        xml = self.xml_tmpl % ('\n'.join([
            uses_library_xml('foo', required_xml(False)),
            uses_library_xml('foo', required_xml(False))
        ]))
        apk = self.apk_tmpl % ('\n'.join([
            uses_library_apk('foo', required_apk(False)),
            uses_library_apk('foo', required_apk(False))
        ]))
        matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
        self.assertTrue(matches)

    def test_mixed(self):
    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
                                      uses_library_xml('bar', required_xml(False))]))
    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
                                      uses_library_apk('bar', required_apk(False))]))
    matches = self.run_test(xml, apk, uses_libraries=['foo'],
                            optional_uses_libraries=['bar'])
        xml = self.xml_tmpl % ('\n'.join([
            uses_library_xml('foo'),
            uses_library_xml('bar', required_xml(False))
        ]))
        apk = self.apk_tmpl % ('\n'.join([
            uses_library_apk('foo'),
            uses_library_apk('bar', required_apk(False))
        ]))
        matches = self.run_test(
            xml, apk, uses_libraries=['foo'], optional_uses_libraries=['bar'])
        self.assertTrue(matches)

    def test_mixed_with_namespace(self):
    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
                                      uses_library_xml('bar', required_xml(False))]))
    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
                                      uses_library_apk('bar', required_apk(False))]))
    matches = self.run_test(xml, apk, uses_libraries=['//x/y/z:foo'],
        xml = self.xml_tmpl % ('\n'.join([
            uses_library_xml('foo'),
            uses_library_xml('bar', required_xml(False))
        ]))
        apk = self.apk_tmpl % ('\n'.join([
            uses_library_apk('foo'),
            uses_library_apk('bar', required_apk(False))
        ]))
        matches = self.run_test(
            xml,
            apk,
            uses_libraries=['//x/y/z:foo'],
            optional_uses_libraries=['//x/y/z:bar'])
        self.assertTrue(matches)


class ExtractTargetSdkVersionTest(unittest.TestCase):

    def run_test(self, xml, apk, version):
        doc = minidom.parseString(xml)
        v = manifest_check.extract_target_sdk_version(doc, is_apk=False)
@@ -202,10 +234,10 @@ class ExtractTargetSdkVersionTest(unittest.TestCase):
        self.assertEqual(v, version)

    xml_tmpl = (
      '<?xml version="1.0" encoding="utf-8"?>\n'
      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
      '    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="%s" />\n'
      '</manifest>\n')
        '<?xml version="1.0" encoding="utf-8"?>\n<manifest '
        'xmlns:android="http://schemas.android.com/apk/res/android">\n    '
        '<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="%s" '
        '/>\n</manifest>\n')

    apk_tmpl = (
        "package: name='com.google.android.something' versionCode='100'\n"
@@ -214,14 +246,15 @@ class ExtractTargetSdkVersionTest(unittest.TestCase):
        "uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n")

    def test_targert_sdk_version_28(self):
    xml = self.xml_tmpl % "28"
    apk = self.apk_tmpl % "28"
    self.run_test(xml, apk, "28")
        xml = self.xml_tmpl % '28'
        apk = self.apk_tmpl % '28'
        self.run_test(xml, apk, '28')

    def test_targert_sdk_version_29(self):
    xml = self.xml_tmpl % "29"
    apk = self.apk_tmpl % "29"
    self.run_test(xml, apk, "29")
        xml = self.xml_tmpl % '29'
        apk = self.apk_tmpl % '29'
        self.run_test(xml, apk, '29')


if __name__ == '__main__':
    unittest.main(verbosity=2)