Exemplo n.º 1
0
def ProcessJavacOutput(output, target_name):
    # These warnings cannot be suppressed even for third party code. Deprecation
    # warnings especially do not help since we must support older android version.
    deprecated_re = re.compile(
        r'(Note: .* uses? or overrides? a deprecated API.)$')
    unchecked_re = re.compile(
        r'(Note: .* uses? unchecked or unsafe operations.)$')
    recompile_re = re.compile(
        r'(Note: Recompile with -Xlint:.* for details.)$')

    activity_re = re.compile(r'^(?P<prefix>\s*location: )class Activity$')

    def ApplyFilters(line):
        return not (deprecated_re.match(line) or unchecked_re.match(line)
                    or recompile_re.match(line))

    def Elaborate(line):
        if activity_re.match(line):
            prefix = ' ' * activity_re.match(line).end('prefix')
            return '{}\n{}Expecting a FragmentActivity? See {}'.format(
                line, prefix, 'docs/ui/android/bytecode_rewriting.md')
        return line

    output = build_utils.FilterReflectiveAccessJavaWarnings(output)

    lines = (l for l in output.split('\n') if ApplyFilters(l))
    lines = (Elaborate(l) for l in lines)

    output_processor = javac_output_processor.JavacOutputProcessor(target_name)
    lines = output_processor.Process(lines)

    return '\n'.join(lines)
Exemplo n.º 2
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 = 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)
      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.')
Exemplo n.º 3
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')