def _FixManifest(options, temp_dir):
    """Fix the APK's AndroidManifest.xml.

  This adds any missing namespaces for 'android' and 'tools', and
  sets certains elements like 'platformBuildVersionCode' or
  'android:debuggable' depending on the content of |options|.

  Args:
    options: The command-line arguments tuple.
    temp_dir: A temporary directory where the fixed manifest will be written to.
  Returns:
    Tuple of:
     * Manifest path within |temp_dir|.
     * Original package_name.
  """
    def maybe_extract_version(j):
        try:
            return resource_utils.ExtractBinaryManifestValues(
                options.aapt2_path, j)
        except build_utils.CalledProcessError:
            return None

    android_sdk_jars = [
        j for j in options.include_resources
        if os.path.basename(j) in ('android.jar', 'android_system.jar')
    ]
    extract_all = [maybe_extract_version(j) for j in android_sdk_jars]
    successful_extractions = [x for x in extract_all if x]
    if len(successful_extractions) == 0:
        raise Exception('Unable to find android SDK jar among candidates: %s' %
                        ', '.join(android_sdk_jars))
    elif len(successful_extractions) > 1:
        raise Exception(
            'Found multiple android SDK jars among candidates: %s' %
            ', '.join(android_sdk_jars))
    version_code, version_name = successful_extractions.pop()[:2]

    debug_manifest_path = os.path.join(temp_dir, 'AndroidManifest.xml')
    doc, manifest_node, app_node = manifest_utils.ParseManifest(
        options.android_manifest)

    manifest_utils.AssertUsesSdk(manifest_node, options.min_sdk_version,
                                 options.target_sdk_version,
                                 options.max_sdk_version)
    manifest_utils.AssertPackage(manifest_node, options.manifest_package)

    manifest_node.set('platformBuildVersionCode', version_code)
    manifest_node.set('platformBuildVersionName', version_name)

    orig_package = manifest_node.get('package')
    if options.arsc_package_name:
        manifest_node.set('package', options.arsc_package_name)

    if options.debuggable:
        app_node.set(
            '{%s}%s' % (manifest_utils.ANDROID_NAMESPACE, 'debuggable'),
            'true')

    manifest_utils.SaveManifest(doc, debug_manifest_path)
    return debug_manifest_path, orig_package
示例#2
0
def _ProcessManifest(manifest_path, allow_uses_sdk):
    """Patches an Android manifest to always include the 'tools' namespace
  declaration, as it is not propagated by the manifest merger from the SDK.

  See https://issuetracker.google.com/issues/63411481
  """
    doc, manifest, _ = manifest_utils.ParseManifest(manifest_path)
    package = manifest_utils.GetPackage(manifest)
    if not allow_uses_sdk:
        manifest_utils.AssertNoUsesSdk(manifest)
    tmp_prefix = os.path.basename(manifest_path)
    with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest:
        manifest_utils.SaveManifest(doc, patched_manifest.name)
        yield patched_manifest.name, package
示例#3
0
def _ProcessManifest(manifest_path, min_sdk_version, target_sdk_version,
                     max_sdk_version, manifest_package):
    """Patches an Android manifest's package and performs assertions to ensure
  correctness for the manifest.
  """
    doc, manifest, _ = manifest_utils.ParseManifest(manifest_path)
    manifest_utils.AssertUsesSdk(manifest, min_sdk_version, target_sdk_version,
                                 max_sdk_version)
    assert manifest_utils.GetPackage(manifest) or manifest_package, \
              'Must set manifest package in GN or in AndroidManifest.xml'
    manifest_utils.AssertPackage(manifest, manifest_package)
    if manifest_package:
        manifest.set('package', manifest_package)
    tmp_prefix = os.path.basename(manifest_path)
    with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest:
        manifest_utils.SaveManifest(doc, patched_manifest.name)
        yield patched_manifest.name, manifest_utils.GetPackage(manifest)
示例#4
0
def _ProcessManifest(manifest_path, min_sdk_version, target_sdk_version,
                     manifest_package):
  """Patches an Android manifest to always include the 'tools' namespace
  declaration, as it is not propagated by the manifest merger from the SDK.

  See https://issuetracker.google.com/issues/63411481
  """
  doc, manifest, _ = manifest_utils.ParseManifest(manifest_path)
  manifest_utils.AssertUsesSdk(manifest, min_sdk_version, target_sdk_version)
  assert manifest_utils.GetPackage(manifest) or manifest_package, \
            'Must set manifest package in GN or in AndroidManifest.xml'
  manifest_utils.AssertPackage(manifest, manifest_package)
  if manifest_package:
    manifest.set('package', manifest_package)
  tmp_prefix = os.path.basename(manifest_path)
  with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest:
    manifest_utils.SaveManifest(doc, patched_manifest.name)
    yield patched_manifest.name, manifest_utils.GetPackage(manifest)
示例#5
0
def _FixManifest(options, temp_dir, extra_manifest=None):
    """Fix the APK's AndroidManifest.xml.

  This adds any missing namespaces for 'android' and 'tools', and
  sets certains elements like 'platformBuildVersionCode' or
  'android:debuggable' depending on the content of |options|.

  Args:
    options: The command-line arguments tuple.
    temp_dir: A temporary directory where the fixed manifest will be written to.
    extra_manifest: Path to an AndroidManifest.xml file which will get merged
        into the application node of the base manifest.
  Returns:
    Tuple of:
     * Manifest path within |temp_dir|.
     * Original package_name.
  """
    def maybe_extract_version(j):
        try:
            return resource_utils.ExtractBinaryManifestValues(
                options.aapt2_path, j)
        except build_utils.CalledProcessError:
            return None

    android_sdk_jars = [
        j for j in options.include_resources
        if os.path.basename(j) in ('android.jar', 'android_system.jar')
    ]
    extract_all = [maybe_extract_version(j) for j in android_sdk_jars]
    successful_extractions = [x for x in extract_all if x]
    if len(successful_extractions) == 0:
        raise Exception('Unable to find android SDK jar among candidates: %s' %
                        ', '.join(android_sdk_jars))
    elif len(successful_extractions) > 1:
        raise Exception(
            'Found multiple android SDK jars among candidates: %s' %
            ', '.join(android_sdk_jars))
    version_code, version_name = successful_extractions.pop()[:2]

    debug_manifest_path = os.path.join(temp_dir, 'AndroidManifest.xml')
    doc, manifest_node, app_node = manifest_utils.ParseManifest(
        options.android_manifest)

    if extra_manifest:
        _, extra_manifest_node, extra_app_node = manifest_utils.ParseManifest(
            extra_manifest)
        for node in extra_app_node:
            app_node.append(node)
        for node in extra_manifest_node:
            # DFM manifests have a bunch of tags we don't care about inside
            # <manifest>, so only take <queries>.
            if node.tag == 'queries':
                manifest_node.append(node)

    manifest_utils.AssertUsesSdk(manifest_node, options.min_sdk_version,
                                 options.target_sdk_version)
    # We explicitly check that maxSdkVersion is set in the manifest since we don't
    # add it later like minSdkVersion and targetSdkVersion.
    manifest_utils.AssertUsesSdk(manifest_node,
                                 max_sdk_version=options.max_sdk_version,
                                 fail_if_not_exist=True)
    manifest_utils.AssertPackage(manifest_node, options.manifest_package)

    manifest_node.set('platformBuildVersionCode', version_code)
    manifest_node.set('platformBuildVersionName', version_name)

    orig_package = manifest_node.get('package')
    if options.arsc_package_name:
        manifest_node.set('package', options.arsc_package_name)

    if options.debuggable:
        app_node.set(
            '{%s}%s' % (manifest_utils.ANDROID_NAMESPACE, 'debuggable'),
            'true')

    if options.uses_split:
        uses_split = ElementTree.SubElement(manifest_node, 'uses-split')
        uses_split.set('{%s}name' % manifest_utils.ANDROID_NAMESPACE,
                       options.uses_split)

    # Make sure the min-sdk condition is not less than the min-sdk of the bundle.
    for min_sdk_node in manifest_node.iter('{%s}min-sdk' %
                                           manifest_utils.DIST_NAMESPACE):
        dist_value = '{%s}value' % manifest_utils.DIST_NAMESPACE
        if int(min_sdk_node.get(dist_value)) < int(options.min_sdk_version):
            min_sdk_node.set(dist_value, options.min_sdk_version)

    manifest_utils.SaveManifest(doc, debug_manifest_path)
    return debug_manifest_path, orig_package
示例#6
0
def _OnStaleMd5(lint_path,
                config_path,
                processed_config_path,
                manifest_path,
                result_path,
                product_dir,
                sources,
                jar_path,
                cache_dir,
                android_sdk_version,
                srcjars,
                min_sdk_version,
                manifest_package,
                resource_sources,
                disable=None,
                classpath=None,
                can_fail_build=False,
                include_unexpected=False,
                silent=False):

  def _RebasePath(path):
    """Returns relative path to top-level src dir.

    Args:
      path: A path relative to cwd.
    """
    ret = os.path.relpath(os.path.abspath(path), build_utils.DIR_SOURCE_ROOT)
    # If it's outside of src/, just use abspath.
    if ret.startswith('..'):
      ret = os.path.abspath(path)
    return ret

  def _ProcessConfigFile():
    if not config_path or not processed_config_path:
      return
    if not build_utils.IsTimeStale(processed_config_path, [config_path]):
      return

    with open(config_path, 'rb') as f:
      content = f.read().replace(
          'PRODUCT_DIR', _RebasePath(product_dir))

    with open(processed_config_path, 'wb') as f:
      f.write(content)

  def _ProcessResultFile():
    with open(result_path, 'rb') as f:
      content = f.read().replace(
          _RebasePath(product_dir), 'PRODUCT_DIR')

    with open(result_path, 'wb') as f:
      f.write(content)

  def _ParseAndShowResultFile():
    dom = minidom.parse(result_path)
    issues = dom.getElementsByTagName('issue')
    if not silent:
      print(file=sys.stderr)
      for issue in issues:
        issue_id = issue.attributes['id'].value
        message = issue.attributes['message'].value
        location_elem = issue.getElementsByTagName('location')[0]
        path = location_elem.attributes['file'].value
        line = location_elem.getAttribute('line')
        if line:
          error = '%s:%s %s: %s [warning]' % (path, line, message, issue_id)
        else:
          # Issues in class files don't have a line number.
          error = '%s %s: %s [warning]' % (path, message, issue_id)
        print(error.encode('utf-8'), file=sys.stderr)
        for attr in ['errorLine1', 'errorLine2']:
          error_line = issue.getAttribute(attr)
          if error_line:
            print(error_line.encode('utf-8'), file=sys.stderr)
    return len(issues)

  with build_utils.TempDir() as temp_dir:
    _ProcessConfigFile()

    cmd = [
        _RebasePath(lint_path), '-Werror', '--exitcode', '--showall',
        '--xml', _RebasePath(result_path),
    ]
    if jar_path:
      # --classpath is just for .class files for this one target.
      cmd.extend(['--classpath', _RebasePath(jar_path)])
    if processed_config_path:
      cmd.extend(['--config', _RebasePath(processed_config_path)])

    tmp_dir_counter = [0]
    def _NewTempSubdir(prefix, append_digit=True):
      # Helper function to create a new sub directory based on the number of
      # subdirs created earlier.
      if append_digit:
        tmp_dir_counter[0] += 1
        prefix += str(tmp_dir_counter[0])
      new_dir = os.path.join(temp_dir, prefix)
      os.makedirs(new_dir)
      return new_dir

    resource_dirs = []
    for resource_source in resource_sources:
      if os.path.isdir(resource_source):
        resource_dirs.append(resource_source)
      else:
        # This is a zip file with generated resources (e. g. strings from GRD).
        # Extract it to temporary folder.
        resource_dir = _NewTempSubdir(resource_source, append_digit=False)
        resource_dirs.append(resource_dir)
        build_utils.ExtractAll(resource_source, path=resource_dir)

    for resource_dir in resource_dirs:
      cmd.extend(['--resources', _RebasePath(resource_dir)])

    if classpath:
      # --libraries is the classpath (excluding active target).
      cp = ':'.join(_RebasePath(p) for p in classpath)
      cmd.extend(['--libraries', cp])

    # There may be multiple source files with the same basename (but in
    # different directories). It is difficult to determine what part of the path
    # corresponds to the java package, and so instead just link the source files
    # into temporary directories (creating a new one whenever there is a name
    # conflict).
    def PathInDir(d, src):
      subpath = os.path.join(d, _RebasePath(src))
      subdir = os.path.dirname(subpath)
      if not os.path.exists(subdir):
        os.makedirs(subdir)
      return subpath

    src_dirs = []
    for src in sources:
      src_dir = None
      for d in src_dirs:
        if not os.path.exists(PathInDir(d, src)):
          src_dir = d
          break
      if not src_dir:
        src_dir = _NewTempSubdir('SRC_ROOT')
        src_dirs.append(src_dir)
        cmd.extend(['--sources', _RebasePath(src_dir)])
      # In cases where the build dir is outside of the src dir, this can
      # result in trying to symlink a file to itself for this file:
      # gen/components/version_info/android/java/org/chromium/
      #   components/version_info/VersionConstants.java
      src = os.path.abspath(src)
      dst = PathInDir(src_dir, src)
      if src == dst:
        continue
      os.symlink(src, dst)

    if srcjars:
      srcjar_paths = build_utils.ParseGnList(srcjars)
      if srcjar_paths:
        srcjar_dir = _NewTempSubdir('SRC_ROOT')
        cmd.extend(['--sources', _RebasePath(srcjar_dir)])
        for srcjar in srcjar_paths:
          build_utils.ExtractAll(srcjar, path=srcjar_dir)

    if disable:
      cmd.extend(['--disable', ','.join(disable)])

    project_dir = _NewTempSubdir('SRC_ROOT')
    if android_sdk_version:
      # Create dummy project.properies file in a temporary "project" directory.
      # It is the only way to add Android SDK to the Lint's classpath. Proper
      # classpath is necessary for most source-level checks.
      with open(os.path.join(project_dir, 'project.properties'), 'w') \
          as propfile:
        print('target=android-{}'.format(android_sdk_version), file=propfile)

    # Put the manifest in a temporary directory in order to avoid lint detecting
    # sibling res/ and src/ directories (which should be pass explicitly if they
    # are to be included).
    if not manifest_path:
      manifest_path = os.path.join(
          build_utils.DIR_SOURCE_ROOT, 'build', 'android',
          'AndroidManifest.xml')
    lint_manifest_path = os.path.join(project_dir, 'AndroidManifest.xml')
    shutil.copyfile(os.path.abspath(manifest_path), lint_manifest_path)

    # Check that minSdkVersion and package is correct and add it to the manifest
    # in case it does not exist.
    doc, manifest, _ = manifest_utils.ParseManifest(lint_manifest_path)
    manifest_utils.AssertUsesSdk(manifest, min_sdk_version)
    manifest_utils.AssertPackage(manifest, manifest_package)
    uses_sdk = manifest.find('./uses-sdk')
    if uses_sdk is None:
      uses_sdk = ElementTree.Element('uses-sdk')
      manifest.insert(0, uses_sdk)
    uses_sdk.set('{%s}minSdkVersion' % manifest_utils.ANDROID_NAMESPACE,
                 min_sdk_version)
    if manifest_package:
      manifest.set('package', manifest_package)
    manifest_utils.SaveManifest(doc, lint_manifest_path)

    cmd.append(project_dir)

    if os.path.exists(result_path):
      os.remove(result_path)

    env = os.environ.copy()
    stderr_filter = None
    if cache_dir:
      env['_JAVA_OPTIONS'] = '-Duser.home=%s' % _RebasePath(cache_dir)
      # When _JAVA_OPTIONS is set, java prints to stderr:
      # Picked up _JAVA_OPTIONS: ...
      #
      # We drop all lines that contain _JAVA_OPTIONS from the output
      stderr_filter = lambda l: re.sub(r'.*_JAVA_OPTIONS.*\n?', '', l)

    def fail_func(returncode, stderr):
      if returncode != 0:
        return True
      if (include_unexpected and
          'Unexpected failure during lint analysis' in stderr):
        return True
      return False

    try:
      build_utils.CheckOutput(cmd, cwd=build_utils.DIR_SOURCE_ROOT,
                              env=env or None, stderr_filter=stderr_filter,
                              fail_func=fail_func)
    except build_utils.CalledProcessError:
      # There is a problem with lint usage
      if not os.path.exists(result_path):
        raise

      # Sometimes produces empty (almost) files:
      if os.path.getsize(result_path) < 10:
        if can_fail_build:
          raise
        elif not silent:
          traceback.print_exc()
        return

      # There are actual lint issues
      try:
        num_issues = _ParseAndShowResultFile()
      except Exception: # pylint: disable=broad-except
        if not silent:
          print('Lint created unparseable xml file...')
          print('File contents:')
          with open(result_path) as f:
            print(f.read())
          if can_fail_build:
            traceback.print_exc()
        if can_fail_build:
          raise
        else:
          return

      _ProcessResultFile()
      if num_issues == 0 and include_unexpected:
        msg = 'Please refer to output above for unexpected lint failures.\n'
      else:
        msg = ('\nLint found %d new issues.\n'
               ' - For full explanation, please refer to %s\n'
               ' - For more information about lint and how to fix lint issues,'
               ' please refer to %s\n' %
               (num_issues, _RebasePath(result_path), _LINT_MD_URL))
      if not silent:
        print(msg, file=sys.stderr)
      if can_fail_build:
        raise Exception('Lint failed.')
示例#7
0
def main(argv):
    argv = build_utils.ExpandFileArgs(argv)
    parser = argparse.ArgumentParser(description=__doc__)
    build_utils.AddDepfileOption(parser)
    parser.add_argument('--build-vars',
                        help='Path to GN build vars file',
                        required=True)
    parser.add_argument('--root-manifest',
                        help='Root manifest which to merge into',
                        required=True)
    parser.add_argument('--output', help='Output manifest path', required=True)
    parser.add_argument('--extras',
                        help='GN list of additional manifest to merge')
    parser.add_argument('--min-sdk-version',
                        required=True,
                        help='android:minSdkVersion for merging.')
    parser.add_argument('--target-sdk-version',
                        required=True,
                        help='android:targetSdkVersion for merging.')
    parser.add_argument(
        '--allow-uses-sdk',
        action='store_true',
        help='Use only for third party code. '
        'Don\'t fail if input manifest contains a <uses-sdk> element.')
    args = parser.parse_args(argv)

    classpath = _BuildManifestMergerClasspath(
        build_utils.ReadBuildVars(args.build_vars))

    with build_utils.AtomicOutput(args.output) as output:
        cmd = [
            'java',
            '-cp',
            classpath,
            _MANIFEST_MERGER_MAIN_CLASS,
            '--out',
            output.name,
        ]

        extras = build_utils.ParseGnList(args.extras)
        if extras:
            cmd += ['--libs', ':'.join(extras)]

        with _ProcessManifest(args.root_manifest, args.allow_uses_sdk) as tup:
            root_manifest, package = tup
            cmd += [
                '--main',
                root_manifest,
                '--property',
                'PACKAGE=' + package,
                '--property',
                'MIN_SDK_VERSION=' + args.min_sdk_version,
                '--property',
                'TARGET_SDK_VERSION=' + args.target_sdk_version,
            ]
            build_utils.CheckOutput(
                cmd,
                # https://issuetracker.google.com/issues/63514300:
                # The merger doesn't set a nonzero exit code for failures.
                fail_func=lambda returncode, stderr: returncode != 0 or
                build_utils.IsTimeStale(output.name, [root_manifest] + extras))

        # Subsequent build system steps expect uses-sdk tag does not exist.
        # Therefore, check it has the expected attribute values and remove it.
        doc, manifest, _ = manifest_utils.ParseManifest(output.name)
        uses_sdk = manifest.find('./uses-sdk')
        assert uses_sdk.get(
            '{%s}minSdkVersion' %
            manifest_utils.ANDROID_NAMESPACE) == args.min_sdk_version
        assert uses_sdk.get(
            '{%s}targetSdkVersion' %
            manifest_utils.ANDROID_NAMESPACE) == args.target_sdk_version
        manifest.remove(uses_sdk)
        manifest_utils.SaveManifest(doc, output.name)

    if args.depfile:
        inputs = extras + classpath.split(':')
        build_utils.WriteDepfile(args.depfile,
                                 args.output,
                                 inputs=inputs,
                                 add_pydeps=False)
示例#8
0
def _RunLint(lint_path,
             config_path,
             manifest_path,
             result_path,
             product_dir,
             sources,
             cache_dir,
             android_sdk_version,
             srcjars,
             min_sdk_version,
             manifest_package,
             resource_sources,
             resource_zips,
             android_sdk_root,
             testonly_target=False,
             can_fail_build=False,
             include_unexpected=False,
             silent=False):
    logging.info('Lint starting')

    def _RebasePath(path):
        """Returns relative path to top-level src dir.

    Args:
      path: A path relative to cwd.
    """
        ret = os.path.relpath(os.path.abspath(path),
                              build_utils.DIR_SOURCE_ROOT)
        # If it's outside of src/, just use abspath.
        if ret.startswith('..'):
            ret = os.path.abspath(path)
        return ret

    def _ProcessResultFile():
        with open(result_path, 'rb') as f:
            content = f.read().replace(_RebasePath(product_dir), 'PRODUCT_DIR')

        with open(result_path, 'wb') as f:
            f.write(content)

    def _ParseAndShowResultFile():
        dom = minidom.parse(result_path)
        issues = dom.getElementsByTagName('issue')
        if not silent:
            print(file=sys.stderr)
            for issue in issues:
                issue_id = issue.attributes['id'].value
                message = issue.attributes['message'].value
                location_elem = issue.getElementsByTagName('location')[0]
                path = location_elem.attributes['file'].value
                line = location_elem.getAttribute('line')
                error = '%s:%s %s: %s [warning]' % (path, line, message,
                                                    issue_id)
                print(error.encode('utf-8'), file=sys.stderr)
                for attr in ['errorLine1', 'errorLine2']:
                    error_line = issue.getAttribute(attr)
                    if error_line:
                        print(error_line.encode('utf-8'), file=sys.stderr)
        return len(issues)

    with build_utils.TempDir() as temp_dir:
        cmd = [
            _RebasePath(lint_path),
            '-Werror',
            '--exitcode',
            '--showall',
            '--xml',
            _RebasePath(result_path),
            # An explicit sdk root needs to be specified since we have an extra
            # intermediate 'lastest' directory under cmdline-tools which prevents
            # lint from automatically deducing the location of the sdk. The sdk is
            # required for many checks (e.g. NewApi). Lint also requires absolute
            # paths.
            '--sdk-home',
            os.path.abspath(android_sdk_root),
        ]
        if config_path:
            cmd.extend(['--config', _RebasePath(config_path)])
        if testonly_target:
            cmd.extend(['--disable', ','.join(_DISABLED_FOR_TESTS)])

        tmp_dir_counter = [0]

        def _NewTempSubdir(prefix, append_digit=True):
            # Helper function to create a new sub directory based on the number of
            # subdirs created earlier.
            if append_digit:
                tmp_dir_counter[0] += 1
                prefix += str(tmp_dir_counter[0])
            new_dir = os.path.join(temp_dir, prefix)
            os.makedirs(new_dir)
            return new_dir

        resource_dirs = resource_utils.DeduceResourceDirsFromFileList(
            resource_sources)
        # These are zip files with generated resources (e. g. strings from GRD).
        for resource_zip in resource_zips:
            resource_dir = _NewTempSubdir(resource_zip, append_digit=False)
            resource_dirs.append(resource_dir)
            build_utils.ExtractAll(resource_zip, path=resource_dir)

        for resource_dir in resource_dirs:
            cmd.extend(['--resources', _RebasePath(resource_dir)])

        # There may be multiple source files with the same basename (but in
        # different directories). It is difficult to determine what part of the path
        # corresponds to the java package, and so instead just link the source files
        # into temporary directories (creating a new one whenever there is a name
        # conflict).
        def PathInDir(d, src):
            subpath = os.path.join(d, _RebasePath(src))
            subdir = os.path.dirname(subpath)
            if not os.path.exists(subdir):
                os.makedirs(subdir)
            return subpath

        src_dirs = []
        for src in sources:
            src_dir = None
            for d in src_dirs:
                if not os.path.exists(PathInDir(d, src)):
                    src_dir = d
                    break
            if not src_dir:
                src_dir = _NewTempSubdir('SRC_ROOT')
                src_dirs.append(src_dir)
                cmd.extend(['--sources', _RebasePath(src_dir)])
            # In cases where the build dir is outside of the src dir, this can
            # result in trying to symlink a file to itself for this file:
            # gen/components/version_info/android/java/org/chromium/
            #   components/version_info/VersionConstants.java
            src = os.path.abspath(src)
            dst = PathInDir(src_dir, src)
            if src == dst:
                continue
            os.symlink(src, dst)

        if srcjars:
            srcjar_dir = _NewTempSubdir('GENERATED_SRC_ROOT',
                                        append_digit=False)
            cmd.extend(['--sources', _RebasePath(srcjar_dir)])
            for srcjar in srcjars:
                # We choose to allow srcjars that contain java files which have the
                # same package and name to clobber each other. This happens for
                # generated files like BuildConfig.java. It is generated for
                # targets like base_build_config_gen as well as targets like
                # chrome_modern_public_base_bundle_module__build_config_srcjar.
                # Although we could extract each srcjar to a separate folder, that
                # slows down some invocations of lint by 20 seconds or more.
                # TODO(wnwen): Switch lint.py to generate a project.xml file which
                #              supports srcjar inputs by default.
                build_utils.ExtractAll(srcjar,
                                       path=srcjar_dir,
                                       no_clobber=False)

        project_dir = _NewTempSubdir('PROJECT_ROOT', append_digit=False)
        if android_sdk_version:
            # Create dummy project.properies file in a temporary "project" directory.
            # It is the only way to add Android SDK to the Lint's classpath. Proper
            # classpath is necessary for most source-level checks.
            with open(os.path.join(project_dir, 'project.properties'), 'w') \
                as propfile:
                print('target=android-{}'.format(android_sdk_version),
                      file=propfile)

        # Put the manifest in a temporary directory in order to avoid lint detecting
        # sibling res/ and src/ directories (which should be pass explicitly if they
        # are to be included).
        if not manifest_path:
            manifest_path = os.path.join(build_utils.DIR_SOURCE_ROOT, 'build',
                                         'android', 'AndroidManifest.xml')
        lint_manifest_path = os.path.join(project_dir, 'AndroidManifest.xml')
        shutil.copyfile(os.path.abspath(manifest_path), lint_manifest_path)

        # Check that minSdkVersion and package is correct and add it to the manifest
        # in case it does not exist.
        doc, manifest, _ = manifest_utils.ParseManifest(lint_manifest_path)
        manifest_utils.AssertUsesSdk(manifest, min_sdk_version)
        manifest_utils.AssertPackage(manifest, manifest_package)
        uses_sdk = manifest.find('./uses-sdk')
        if uses_sdk is None:
            uses_sdk = ElementTree.Element('uses-sdk')
            manifest.insert(0, uses_sdk)
        uses_sdk.set('{%s}minSdkVersion' % manifest_utils.ANDROID_NAMESPACE,
                     min_sdk_version)
        if manifest_package:
            manifest.set('package', manifest_package)
        manifest_utils.SaveManifest(doc, lint_manifest_path)

        cmd.append(project_dir)

        if os.path.exists(result_path):
            os.remove(result_path)

        env = os.environ.copy()
        stderr_filter = build_utils.FilterReflectiveAccessJavaWarnings
        if cache_dir:
            env['_JAVA_OPTIONS'] = '-Duser.home=%s' % _RebasePath(cache_dir)
            # When _JAVA_OPTIONS is set, java prints to stderr:
            # Picked up _JAVA_OPTIONS: ...
            #
            # We drop all lines that contain _JAVA_OPTIONS from the output
            stderr_filter = lambda l: re.sub(
                r'.*_JAVA_OPTIONS.*\n?', '',
                build_utils.FilterReflectiveAccessJavaWarnings(l))

        def fail_func(returncode, stderr):
            if returncode != 0:
                return True
            if (include_unexpected
                    and 'Unexpected failure during lint analysis' in stderr):
                return True
            return False

        try:
            env['JAVA_HOME'] = os.path.relpath(build_utils.JAVA_HOME,
                                               build_utils.DIR_SOURCE_ROOT)
            logging.debug('Lint command %s', cmd)
            start = time.time()
            build_utils.CheckOutput(cmd,
                                    cwd=build_utils.DIR_SOURCE_ROOT,
                                    env=env or None,
                                    stderr_filter=stderr_filter,
                                    fail_func=fail_func)
            end = time.time() - start
            logging.info('Lint command took %ss', end)
        except build_utils.CalledProcessError:
            # There is a problem with lint usage
            if not os.path.exists(result_path):
                raise

            # Sometimes produces empty (almost) files:
            if os.path.getsize(result_path) < 10:
                if can_fail_build:
                    raise
                elif not silent:
                    traceback.print_exc()
                return

            # There are actual lint issues
            try:
                num_issues = _ParseAndShowResultFile()
            except Exception:  # pylint: disable=broad-except
                if not silent:
                    print('Lint created unparseable xml file...')
                    print('File contents:')
                    with open(result_path) as f:
                        print(f.read())
                    if can_fail_build:
                        traceback.print_exc()
                if can_fail_build:
                    raise
                else:
                    return

            _ProcessResultFile()
            if num_issues == 0 and include_unexpected:
                msg = 'Please refer to output above for unexpected lint failures.\n'
            else:
                msg = (
                    '\nLint found %d new issues.\n'
                    ' - For full explanation, please refer to %s\n'
                    ' - For more information about lint and how to fix lint issues,'
                    ' please refer to %s\n' %
                    (num_issues, _RebasePath(result_path), _LINT_MD_URL))
            if not silent:
                print(msg, file=sys.stderr)
            if can_fail_build:
                raise Exception('Lint failed.')

    logging.info('Lint completed')