[Android] Run lint using a cache in the output directory.

BUG=583661

Review URL: https://codereview.chromium.org/1815563005

Cr-Original-Commit-Position: refs/heads/master@{#382306}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: c2b563ca5489aaf7579a4b9f389533b7af336187
This commit is contained in:
jbudorick 2016-03-21 09:43:40 -07:00 коммит произвёл Commit bot
Родитель ee7f78cd2d
Коммит 75acdc80f9
9 изменённых файлов: 233 добавлений и 93 удалений

Просмотреть файл

@ -7,6 +7,42 @@ import("//third_party/ijar/ijar.gni")
sun_tools_jar_path = "$root_gen_dir/sun_tools_jar/tools.jar"
# Create or update the API versions cache if necessary by running a
# functionally empty lint task. This prevents racy creation of the
# cache while linting java targets in android_lint.
action("prepare_android_lint_cache") {
_cache_dir = "${root_out_dir}/android_lint_cache"
depfile = "${_cache_dir}/prepare_android_lint_cache.d"
_manifest_file = "//build/android/AndroidManifest.xml"
_result_file = "${_cache_dir}/result.xml"
script = "//build/android/gyp/lint.py"
inputs = [
"${android_sdk_root}/platform-tools/api/api-versions.xml",
]
outputs = [
depfile,
]
args = [
"--build-tools-version",
android_sdk_build_tools_version,
"--cache-dir",
rebase_path(_cache_dir, root_build_dir),
"--depfile",
rebase_path(depfile, root_build_dir),
"--lint-path",
"$rebased_android_sdk_root/tools/lint",
"--manifest-path",
rebase_path(_manifest_file, root_build_dir),
"--product-dir",
".",
"--result-path",
rebase_path(_result_file, root_build_dir),
"--silent",
"--enable",
]
}
action("find_sun_tools_jar") {
script = "//build/android/gyp/find_sun_tools_jar.py"
depfile = "$target_gen_dir/$target_name.d"

Просмотреть файл

@ -0,0 +1,47 @@
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
{
'targets': [
{
# This target runs a functionally empty lint to create or update the
# API versions cache if necessary. This prevents racy creation of the
# cache while linting java targets in lint_action.gypi.
'target_name': 'android_lint_cache',
'type': 'none',
'actions': [
{
'action_name': 'prepare_android_lint_cache',
'message': 'Preparing Android lint cache',
'variables': {
'android_lint_cache_stamp': '<(PRODUCT_DIR)/android_lint_cache/android_lint_cache.stamp',
'android_manifest_path': '<(DEPTH)/build/android/AndroidManifest.xml',
'cache_file': '<(PRODUCT_DIR)/android_lint_cache/.android/cache/api-versions-6-23.0.1.bin',
'result_path': '<(PRODUCT_DIR)/android_lint_cache/result.xml',
},
'inputs': [
'<(DEPTH)/build/android/gyp/lint.py',
'<(android_manifest_path)',
],
'outputs': [
'<(cache_file)',
'<(result_path)',
],
'action': [
'python', '<(DEPTH)/build/android/gyp/lint.py',
'--lint-path', '<(android_sdk_root)/tools/lint',
'--cache-dir', '<(PRODUCT_DIR)/android_lint_cache',
'--build-tools-version', '<(android_sdk_build_tools_version)',
'--manifest-path', '<(android_manifest_path)',
'--product-dir', '<(PRODUCT_DIR)',
'--result-path', '<(result_path)',
'--stamp', '<(android_lint_cache_stamp)',
'--silent',
'--enable'
],
},
],
},
],
}

Просмотреть файл

@ -7,7 +7,7 @@
"""Runs Android's lint tool."""
import optparse
import argparse
import os
import sys
import traceback
@ -22,7 +22,8 @@ _SRC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),
def _OnStaleMd5(changes, lint_path, config_path, processed_config_path,
manifest_path, result_path, product_dir, sources, jar_path,
resource_dir=None, can_fail_build=False):
cache_dir, resource_dir=None, can_fail_build=False,
silent=False):
def _RelativizePath(path):
"""Returns relative path to top-level src dir.
@ -33,6 +34,8 @@ def _OnStaleMd5(changes, lint_path, config_path, processed_config_path,
return os.path.relpath(os.path.abspath(path), _SRC_ROOT)
def _ProcessConfigFile():
if not config_path or not processed_config_path:
return
if not build_utils.IsTimeStale(processed_config_path, [config_path]):
return
@ -54,23 +57,24 @@ def _OnStaleMd5(changes, lint_path, config_path, processed_config_path,
def _ParseAndShowResultFile():
dom = minidom.parse(result_path)
issues = dom.getElementsByTagName('issue')
print >> 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 >> sys.stderr, error.encode('utf-8')
for attr in ['errorLine1', 'errorLine2']:
error_line = issue.getAttribute(attr)
if error_line:
print >> sys.stderr, error_line.encode('utf-8')
if not silent:
print >> 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 >> sys.stderr, error.encode('utf-8')
for attr in ['errorLine1', 'errorLine2']:
error_line = issue.getAttribute(attr)
if error_line:
print >> sys.stderr, error_line.encode('utf-8')
return len(issues)
# Need to include all sources when a resource_dir is set so that resources are
@ -84,10 +88,12 @@ def _OnStaleMd5(changes, lint_path, config_path, processed_config_path,
cmd = [
_RelativizePath(lint_path), '-Werror', '--exitcode', '--showall',
'--config', _RelativizePath(processed_config_path),
'--classpath', _RelativizePath(jar_path),
'--xml', _RelativizePath(result_path),
]
if jar_path:
cmd.extend(['--classpath', _RelativizePath(jar_path)])
if processed_config_path:
cmd.extend(['--config', _RelativizePath(processed_config_path)])
if resource_dir:
cmd.extend(['--resources', _RelativizePath(resource_dir)])
@ -117,13 +123,18 @@ def _OnStaleMd5(changes, lint_path, config_path, processed_config_path,
src_dir = NewSourceDir()
os.symlink(os.path.abspath(src), PathInDir(src_dir, src))
cmd.append(_RelativizePath(os.path.join(manifest_path, os.pardir)))
if manifest_path:
cmd.append(_RelativizePath(os.path.join(manifest_path, os.pardir)))
if os.path.exists(result_path):
os.remove(result_path)
env = {}
if cache_dir:
env['_JAVA_OPTIONS'] = '-Duser.home=%s' % _RelativizePath(cache_dir)
try:
build_utils.CheckOutput(cmd, cwd=_SRC_ROOT)
build_utils.CheckOutput(cmd, cwd=_SRC_ROOT, env=env or None)
except build_utils.CalledProcessError:
if can_fail_build:
traceback.print_exc()
@ -137,92 +148,129 @@ def _OnStaleMd5(changes, lint_path, config_path, processed_config_path,
try:
num_issues = _ParseAndShowResultFile()
except Exception: # pylint: disable=broad-except
print 'Lint created unparseable xml file...'
print 'File contents:'
with open(result_path) as f:
print f.read()
if not silent:
print 'Lint created unparseable xml file...'
print 'File contents:'
with open(result_path) as f:
print f.read()
raise
_ProcessResultFile()
msg = ('\nLint found %d new issues.\n'
' - For full explanation refer to %s\n'
' - Wanna suppress these issues?\n'
' 1. Read comment in %s\n'
' 2. Run "python %s %s"\n' %
' - For full explanation refer to %s\n' %
(num_issues,
_RelativizePath(result_path),
_RelativizePath(config_path),
_RelativizePath(os.path.join(_SRC_ROOT, 'build', 'android',
'lint', 'suppress.py')),
_RelativizePath(result_path)))
print >> sys.stderr, msg
if config_path:
msg += (' - Wanna suppress these issues?\n'
' 1. Read comment in %s\n'
' 2. Run "python %s %s"\n' %
(_RelativizePath(config_path),
_RelativizePath(os.path.join(_SRC_ROOT, 'build', 'android',
'lint', 'suppress.py')),
_RelativizePath(result_path)))
if not silent:
print >> sys.stderr, msg
if can_fail_build:
raise Exception('Lint failed.')
def main():
parser = optparse.OptionParser()
parser = argparse.ArgumentParser()
build_utils.AddDepfileOption(parser)
parser.add_option('--lint-path', help='Path to lint executable.')
parser.add_option('--config-path', help='Path to lint suppressions file.')
parser.add_option('--processed-config-path',
help='Path to processed lint suppressions file.')
parser.add_option('--manifest-path', help='Path to AndroidManifest.xml')
parser.add_option('--result-path', help='Path to XML lint result file.')
parser.add_option('--product-dir', help='Path to product dir.')
parser.add_option('--src-dirs', help='Directories containing java files.')
parser.add_option('--java-files', help='Paths to java files.')
parser.add_option('--jar-path', help='Jar file containing class files.')
parser.add_option('--resource-dir', help='Path to resource dir.')
parser.add_option('--can-fail-build', action='store_true',
help='If set, script will exit with nonzero exit status'
' if lint errors are present')
parser.add_option('--stamp', help='Path to touch on success.')
parser.add_option('--enable', action='store_true',
help='Run lint instead of just touching stamp.')
options, _ = parser.parse_args()
parser.add_argument('--lint-path', required=True,
help='Path to lint executable.')
parser.add_argument('--product-dir', required=True,
help='Path to product dir.')
parser.add_argument('--result-path', required=True,
help='Path to XML lint result file.')
build_utils.CheckOptions(
options, parser, required=['lint_path', 'config_path',
'processed_config_path', 'manifest_path',
'result_path', 'product_dir',
'jar_path'])
parser.add_argument('--build-tools-version',
help='Version of the build tools in the Android SDK.')
parser.add_argument('--cache-dir',
help='Path to the directory in which the android cache '
'directory tree should be stored.')
parser.add_argument('--can-fail-build', action='store_true',
help='If set, script will exit with nonzero exit status'
' if lint errors are present')
parser.add_argument('--config-path',
help='Path to lint suppressions file.')
parser.add_argument('--enable', action='store_true',
help='Run lint instead of just touching stamp.')
parser.add_argument('--jar-path',
help='Jar file containing class files.')
parser.add_argument('--java-files',
help='Paths to java files.')
parser.add_argument('--manifest-path',
help='Path to AndroidManifest.xml')
parser.add_argument('--platform-xml-path',
help='Path to api-platforms.xml')
parser.add_argument('--processed-config-path',
help='Path to processed lint suppressions file.')
parser.add_argument('--resource-dir',
help='Path to resource dir.')
parser.add_argument('--silent', action='store_true',
help='If set, script will not log anything.')
parser.add_argument('--src-dirs',
help='Directories containing java files.')
parser.add_argument('--stamp',
help='Path to touch on success.')
if options.enable:
args = parser.parse_args()
if args.enable:
sources = []
if options.src_dirs:
src_dirs = build_utils.ParseGypList(options.src_dirs)
if args.src_dirs:
src_dirs = build_utils.ParseGypList(args.src_dirs)
sources = build_utils.FindInDirectories(src_dirs, '*.java')
elif options.java_files:
sources = build_utils.ParseGypList(options.java_files)
else:
print 'One of --src-dirs or --java-files must be specified.'
return 1
elif args.java_files:
sources = build_utils.ParseGypList(args.java_files)
if args.config_path and not args.processed_config_path:
parser.error('--config-path specified without --processed-config-path')
elif args.processed_config_path and not args.config_path:
parser.error('--processed-config-path specified without --config-path')
input_paths = [
options.lint_path,
options.config_path,
options.manifest_path,
options.jar_path,
args.lint_path,
]
input_paths.extend(sources)
if options.resource_dir:
input_paths.extend(build_utils.FindInDirectory(options.resource_dir, '*'))
if args.config_path:
input_paths.append(args.config_path)
if args.jar_path:
input_paths.append(args.jar_path)
if args.manifest_path:
input_paths.append(args.manifest_path)
if args.platform_xml_path:
input_paths.append(args.platform_xml_path)
if args.resource_dir:
input_paths.extend(build_utils.FindInDirectory(args.resource_dir, '*'))
if sources:
input_paths.extend(sources)
input_strings = [ options.processed_config_path ]
output_paths = [ options.result_path ]
input_strings = []
if args.processed_config_path:
input_strings.append(args.processed_config_path)
output_paths = [ args.result_path ]
if args.cache_dir:
if not args.build_tools_version:
parser.error('--cache-dir specified without --build-tools-version')
output_paths.append(os.path.join(
args.cache_dir, '.android', 'cache',
'api-versions-6-%s.bin' % args.build_tools_version))
build_utils.CallAndWriteDepfileIfStale(
lambda changes: _OnStaleMd5(changes, options.lint_path,
options.config_path,
options.processed_config_path,
options.manifest_path, options.result_path,
options.product_dir, sources,
options.jar_path,
resource_dir=options.resource_dir,
can_fail_build=options.can_fail_build),
options,
lambda changes: _OnStaleMd5(changes, args.lint_path,
args.config_path,
args.processed_config_path,
args.manifest_path, args.result_path,
args.product_dir, sources,
args.jar_path,
args.cache_dir,
resource_dir=args.resource_dir,
can_fail_build=args.can_fail_build,
silent=args.silent),
args,
input_paths=input_paths,
input_strings=input_strings,
output_paths=output_paths,

Просмотреть файл

@ -152,7 +152,7 @@ class CalledProcessError(Exception):
# This can be used in most cases like subprocess.check_output(). The output,
# particularly when the command fails, better highlights the command's failure.
# If the command fails, raises a build_utils.CalledProcessError.
def CheckOutput(args, cwd=None,
def CheckOutput(args, cwd=None, env=None,
print_stdout=False, print_stderr=True,
stdout_filter=None,
stderr_filter=None,
@ -161,7 +161,7 @@ def CheckOutput(args, cwd=None,
cwd = os.getcwd()
child = subprocess.Popen(args,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)
stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, env=env)
stdout, stderr = child.communicate()
if stdout_filter is not None:

Просмотреть файл

@ -11,9 +11,9 @@
'variables': {
'conditions': [
['chromium_code != 0 and android_lint != 0 and never_lint == 0', {
'is_enabled': '--enable',
'additional_args': ['--enable'],
}, {
'is_enabled': '',
'additional_args': [],
}]
],
'android_manifest_path%': '<(DEPTH)/build/android/AndroidManifest.xml',
@ -24,14 +24,16 @@
'<(DEPTH)/build/android/gyp/util/build_utils.py',
'<(DEPTH)/build/android/gyp/lint.py',
'<(android_manifest_path)',
'<(suppressions_file)',
'<(lint_jar_path)',
'<(suppressions_file)',
],
'action': [
'python', '<(DEPTH)/build/android/gyp/lint.py',
'--lint-path=<(android_sdk_root)/tools/lint',
'--config-path=<(suppressions_file)',
'--processed-config-path=<(config_path)',
'--cache-dir', '<(PRODUCT_DIR)/android_lint_cache',
'--build-tools-version', '<(android_sdk_build_tools_version)',
'--manifest-path=<(android_manifest_path)',
'--result-path=<(result_path)',
'--resource-dir=<(resource_dir)',
@ -40,6 +42,6 @@
'--jar-path=<(lint_jar_path)',
'--can-fail-build',
'--stamp=<(stamp_path)',
'<(is_enabled)',
'<@(additional_args)',
],
}

Просмотреть файл

@ -1769,6 +1769,7 @@
'android_ndk_absolute_root%': '<(android_ndk_absolute_root)',
'android_sdk_root%': '<(android_sdk_root)',
'android_sdk_version%': '<(android_sdk_version)',
'android_sdk_build_tools_version%': '<(android_sdk_build_tools_version)',
'android_libcpp_root': '<(android_ndk_root)/sources/cxx-stl/llvm-libc++',
'android_libcpp_library': '<(android_libcpp_library)',
'android_must_copy_system_libraries': '<(android_must_copy_system_libraries)',
@ -1843,6 +1844,7 @@
'android_ndk_include': '<(android_ndk_sysroot)/usr/include',
'android_ndk_lib': '<(android_ndk_sysroot)/<(android_ndk_lib_dir)',
'android_sdk_build_tools_version%': '<(android_sdk_build_tools_version)',
'android_sdk_tools%': '<(android_sdk_tools)',
'android_aapt_path%': '<(android_sdk_tools)/aapt',
'android_sdk%': '<(android_sdk)',

Просмотреть файл

@ -32,6 +32,7 @@ template("android_lint") {
base_path = "$target_gen_dir/$target_name"
action(target_name) {
deps = []
forward_variables_from(invoker,
[
"deps",
@ -54,6 +55,8 @@ template("android_lint") {
result_path,
]
deps += [ "//build/android:prepare_android_lint_cache" ]
rebased_java_files = rebase_path(java_files, root_build_dir)
args = [

Просмотреть файл

@ -48,7 +48,8 @@
{
'dependencies': [
'<(DEPTH)/build/android/setup.gyp:build_output_dirs'
'<(DEPTH)/build/android/android_lint_cache.gyp:android_lint_cache',
'<(DEPTH)/build/android/setup.gyp:build_output_dirs',
],
'variables': {
'add_to_dependents_classpaths%': 1,

Просмотреть файл

@ -856,6 +856,7 @@
}],
],
'dependencies': [
'<(DEPTH)/build/android/android_lint_cache.gyp:android_lint_cache',
'<(DEPTH)/tools/android/md5sum/md5sum.gyp:md5sum',
],
'actions': [