Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(167)

Side by Side Diff: build/android/gyp/lint.py

Issue 1815563005: [Android] Run lint using a cache in the output directory. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: prereview 2 Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 3 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """Runs Android's lint tool.""" 7 """Runs Android's lint tool."""
8 8
9 9
10 import optparse 10 import argparse
11 import os 11 import os
12 import sys 12 import sys
13 import traceback 13 import traceback
14 from xml.dom import minidom 14 from xml.dom import minidom
15 15
16 from util import build_utils 16 from util import build_utils
17 17
18 18
19 _SRC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), 19 _SRC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),
20 '..', '..', '..')) 20 '..', '..', '..'))
21 21
22 22
23 def _OnStaleMd5(changes, lint_path, config_path, processed_config_path, 23 def _OnStaleMd5(changes, lint_path, config_path, processed_config_path,
24 manifest_path, result_path, product_dir, sources, jar_path, 24 manifest_path, result_path, product_dir, sources, jar_path,
25 resource_dir=None, can_fail_build=False): 25 cache_dir, resource_dir=None, can_fail_build=False,
26 silent=False):
26 27
27 def _RelativizePath(path): 28 def _RelativizePath(path):
28 """Returns relative path to top-level src dir. 29 """Returns relative path to top-level src dir.
29 30
30 Args: 31 Args:
31 path: A path relative to cwd. 32 path: A path relative to cwd.
32 """ 33 """
33 return os.path.relpath(os.path.abspath(path), _SRC_ROOT) 34 return os.path.relpath(os.path.abspath(path), _SRC_ROOT)
34 35
35 def _ProcessConfigFile(): 36 def _ProcessConfigFile():
37 if not config_path or not processed_config_path:
38 return
36 if not build_utils.IsTimeStale(processed_config_path, [config_path]): 39 if not build_utils.IsTimeStale(processed_config_path, [config_path]):
37 return 40 return
38 41
39 with open(config_path, 'rb') as f: 42 with open(config_path, 'rb') as f:
40 content = f.read().replace( 43 content = f.read().replace(
41 'PRODUCT_DIR', _RelativizePath(product_dir)) 44 'PRODUCT_DIR', _RelativizePath(product_dir))
42 45
43 with open(processed_config_path, 'wb') as f: 46 with open(processed_config_path, 'wb') as f:
44 f.write(content) 47 f.write(content)
45 48
46 def _ProcessResultFile(): 49 def _ProcessResultFile():
47 with open(result_path, 'rb') as f: 50 with open(result_path, 'rb') as f:
48 content = f.read().replace( 51 content = f.read().replace(
49 _RelativizePath(product_dir), 'PRODUCT_DIR') 52 _RelativizePath(product_dir), 'PRODUCT_DIR')
50 53
51 with open(result_path, 'wb') as f: 54 with open(result_path, 'wb') as f:
52 f.write(content) 55 f.write(content)
53 56
54 def _ParseAndShowResultFile(): 57 def _ParseAndShowResultFile():
55 dom = minidom.parse(result_path) 58 dom = minidom.parse(result_path)
56 issues = dom.getElementsByTagName('issue') 59 issues = dom.getElementsByTagName('issue')
57 print >> sys.stderr 60 if not silent:
58 for issue in issues: 61 print >> sys.stderr
59 issue_id = issue.attributes['id'].value 62 for issue in issues:
60 message = issue.attributes['message'].value 63 issue_id = issue.attributes['id'].value
61 location_elem = issue.getElementsByTagName('location')[0] 64 message = issue.attributes['message'].value
62 path = location_elem.attributes['file'].value 65 location_elem = issue.getElementsByTagName('location')[0]
63 line = location_elem.getAttribute('line') 66 path = location_elem.attributes['file'].value
64 if line: 67 line = location_elem.getAttribute('line')
65 error = '%s:%s %s: %s [warning]' % (path, line, message, issue_id) 68 if line:
66 else: 69 error = '%s:%s %s: %s [warning]' % (path, line, message, issue_id)
67 # Issues in class files don't have a line number. 70 else:
68 error = '%s %s: %s [warning]' % (path, message, issue_id) 71 # Issues in class files don't have a line number.
69 print >> sys.stderr, error.encode('utf-8') 72 error = '%s %s: %s [warning]' % (path, message, issue_id)
70 for attr in ['errorLine1', 'errorLine2']: 73 print >> sys.stderr, error.encode('utf-8')
71 error_line = issue.getAttribute(attr) 74 for attr in ['errorLine1', 'errorLine2']:
72 if error_line: 75 error_line = issue.getAttribute(attr)
73 print >> sys.stderr, error_line.encode('utf-8') 76 if error_line:
77 print >> sys.stderr, error_line.encode('utf-8')
74 return len(issues) 78 return len(issues)
75 79
76 # Need to include all sources when a resource_dir is set so that resources are 80 # Need to include all sources when a resource_dir is set so that resources are
77 # not marked as unused. 81 # not marked as unused.
78 if not resource_dir and changes.AddedOrModifiedOnly(): 82 if not resource_dir and changes.AddedOrModifiedOnly():
79 changed_paths = set(changes.IterChangedPaths()) 83 changed_paths = set(changes.IterChangedPaths())
80 sources = [s for s in sources if s in changed_paths] 84 sources = [s for s in sources if s in changed_paths]
81 85
82 with build_utils.TempDir() as temp_dir: 86 with build_utils.TempDir() as temp_dir:
83 _ProcessConfigFile() 87 _ProcessConfigFile()
84 88
85 cmd = [ 89 cmd = [
86 _RelativizePath(lint_path), '-Werror', '--exitcode', '--showall', 90 _RelativizePath(lint_path), '-Werror', '--exitcode', '--showall',
87 '--config', _RelativizePath(processed_config_path),
88 '--classpath', _RelativizePath(jar_path),
89 '--xml', _RelativizePath(result_path), 91 '--xml', _RelativizePath(result_path),
90 ] 92 ]
93 if jar_path:
94 cmd.extend(['--classpath', _RelativizePath(jar_path)])
95 if processed_config_path:
96 cmd.extend(['--config', _RelativizePath(processed_config_path)])
91 if resource_dir: 97 if resource_dir:
92 cmd.extend(['--resources', _RelativizePath(resource_dir)]) 98 cmd.extend(['--resources', _RelativizePath(resource_dir)])
93 99
94 # There may be multiple source files with the same basename (but in 100 # There may be multiple source files with the same basename (but in
95 # different directories). It is difficult to determine what part of the path 101 # different directories). It is difficult to determine what part of the path
96 # corresponds to the java package, and so instead just link the source files 102 # corresponds to the java package, and so instead just link the source files
97 # into temporary directories (creating a new one whenever there is a name 103 # into temporary directories (creating a new one whenever there is a name
98 # conflict). 104 # conflict).
99 src_dirs = [] 105 src_dirs = []
100 def NewSourceDir(): 106 def NewSourceDir():
101 new_dir = os.path.join(temp_dir, str(len(src_dirs))) 107 new_dir = os.path.join(temp_dir, str(len(src_dirs)))
102 os.mkdir(new_dir) 108 os.mkdir(new_dir)
103 src_dirs.append(new_dir) 109 src_dirs.append(new_dir)
104 cmd.extend(['--sources', _RelativizePath(new_dir)]) 110 cmd.extend(['--sources', _RelativizePath(new_dir)])
105 return new_dir 111 return new_dir
106 112
107 def PathInDir(d, src): 113 def PathInDir(d, src):
108 return os.path.join(d, os.path.basename(src)) 114 return os.path.join(d, os.path.basename(src))
109 115
110 for src in sources: 116 for src in sources:
111 src_dir = None 117 src_dir = None
112 for d in src_dirs: 118 for d in src_dirs:
113 if not os.path.exists(PathInDir(d, src)): 119 if not os.path.exists(PathInDir(d, src)):
114 src_dir = d 120 src_dir = d
115 break 121 break
116 if not src_dir: 122 if not src_dir:
117 src_dir = NewSourceDir() 123 src_dir = NewSourceDir()
118 os.symlink(os.path.abspath(src), PathInDir(src_dir, src)) 124 os.symlink(os.path.abspath(src), PathInDir(src_dir, src))
119 125
120 cmd.append(_RelativizePath(os.path.join(manifest_path, os.pardir))) 126 if manifest_path:
127 cmd.append(_RelativizePath(os.path.join(manifest_path, os.pardir)))
121 128
122 if os.path.exists(result_path): 129 if os.path.exists(result_path):
123 os.remove(result_path) 130 os.remove(result_path)
124 131
132 env = {}
133 if cache_dir:
134 env['_JAVA_OPTIONS'] = '-Duser.home=%s' % _RelativizePath(cache_dir)
135
125 try: 136 try:
126 build_utils.CheckOutput(cmd, cwd=_SRC_ROOT) 137 build_utils.CheckOutput(cmd, cwd=_SRC_ROOT, env=env or None)
agrieve 2016/03/19 00:12:53 I'm seeing a message "Picked up _JAVA_OPTIONS: ...
jbudorick 2016/03/19 00:57:42 Interesting, I don't see that in either gyp or gn.
agrieve 2016/03/19 01:02:44 Weird - seems to be a common issue: http://stackov
127 except build_utils.CalledProcessError: 138 except build_utils.CalledProcessError:
128 if can_fail_build: 139 if can_fail_build:
129 traceback.print_exc() 140 traceback.print_exc()
130 141
131 # There is a problem with lint usage 142 # There is a problem with lint usage
132 if not os.path.exists(result_path): 143 if not os.path.exists(result_path):
133 raise 144 raise
134 145
135 # There are actual lint issues 146 # There are actual lint issues
136 else: 147 else:
137 try: 148 try:
138 num_issues = _ParseAndShowResultFile() 149 num_issues = _ParseAndShowResultFile()
139 except Exception: # pylint: disable=broad-except 150 except Exception: # pylint: disable=broad-except
140 print 'Lint created unparseable xml file...' 151 if not silent:
141 print 'File contents:' 152 print 'Lint created unparseable xml file...'
142 with open(result_path) as f: 153 print 'File contents:'
143 print f.read() 154 with open(result_path) as f:
155 print f.read()
144 raise 156 raise
145 157
146 _ProcessResultFile() 158 _ProcessResultFile()
147 msg = ('\nLint found %d new issues.\n' 159 msg = ('\nLint found %d new issues.\n'
148 ' - For full explanation refer to %s\n' 160 ' - For full explanation refer to %s\n' %
149 ' - Wanna suppress these issues?\n'
150 ' 1. Read comment in %s\n'
151 ' 2. Run "python %s %s"\n' %
152 (num_issues, 161 (num_issues,
153 _RelativizePath(result_path),
154 _RelativizePath(config_path),
155 _RelativizePath(os.path.join(_SRC_ROOT, 'build', 'android',
156 'lint', 'suppress.py')),
157 _RelativizePath(result_path))) 162 _RelativizePath(result_path)))
158 print >> sys.stderr, msg 163 if config_path:
164 msg += (' - Wanna suppress these issues?\n'
165 ' 1. Read comment in %s\n'
166 ' 2. Run "python %s %s"\n' %
167 (_RelativizePath(config_path),
168 _RelativizePath(os.path.join(_SRC_ROOT, 'build', 'android',
169 'lint', 'suppress.py')),
170 _RelativizePath(result_path)))
171 if not silent:
172 print >> sys.stderr, msg
159 if can_fail_build: 173 if can_fail_build:
160 raise Exception('Lint failed.') 174 raise Exception('Lint failed.')
161 175
162 176
163 def main(): 177 def main():
164 parser = optparse.OptionParser() 178 parser = argparse.ArgumentParser()
165 build_utils.AddDepfileOption(parser) 179 build_utils.AddDepfileOption(parser)
166 parser.add_option('--lint-path', help='Path to lint executable.')
167 parser.add_option('--config-path', help='Path to lint suppressions file.')
168 parser.add_option('--processed-config-path',
169 help='Path to processed lint suppressions file.')
170 parser.add_option('--manifest-path', help='Path to AndroidManifest.xml')
171 parser.add_option('--result-path', help='Path to XML lint result file.')
172 parser.add_option('--product-dir', help='Path to product dir.')
173 parser.add_option('--src-dirs', help='Directories containing java files.')
174 parser.add_option('--java-files', help='Paths to java files.')
175 parser.add_option('--jar-path', help='Jar file containing class files.')
176 parser.add_option('--resource-dir', help='Path to resource dir.')
177 parser.add_option('--can-fail-build', action='store_true',
178 help='If set, script will exit with nonzero exit status'
179 ' if lint errors are present')
180 parser.add_option('--stamp', help='Path to touch on success.')
181 parser.add_option('--enable', action='store_true',
182 help='Run lint instead of just touching stamp.')
183 180
184 options, _ = parser.parse_args() 181 parser.add_argument('--lint-path', required=True,
182 help='Path to lint executable.')
183 parser.add_argument('--product-dir', required=True,
184 help='Path to product dir.')
185 parser.add_argument('--result-path', required=True,
186 help='Path to XML lint result file.')
185 187
186 build_utils.CheckOptions( 188 parser.add_argument('--build-tools-version',
187 options, parser, required=['lint_path', 'config_path', 189 help='Version of the build tools in the Android SDK.')
188 'processed_config_path', 'manifest_path', 190 parser.add_argument('--cache-dir',
189 'result_path', 'product_dir', 191 help='Path to the directory in which the android cache '
190 'jar_path']) 192 'directory tree should be stored.')
193 parser.add_argument('--can-fail-build', action='store_true',
194 help='If set, script will exit with nonzero exit status'
195 ' if lint errors are present')
196 parser.add_argument('--config-path',
197 help='Path to lint suppressions file.')
198 parser.add_argument('--enable', action='store_true',
199 help='Run lint instead of just touching stamp.')
200 parser.add_argument('--jar-path',
201 help='Jar file containing class files.')
202 parser.add_argument('--java-files',
203 help='Paths to java files.')
204 parser.add_argument('--manifest-path',
205 help='Path to AndroidManifest.xml')
206 parser.add_argument('--platform-xml-path',
207 help='Path to api-platforms.xml')
208 parser.add_argument('--processed-config-path',
209 help='Path to processed lint suppressions file.')
210 parser.add_argument('--resource-dir',
211 help='Path to resource dir.')
212 parser.add_argument('--silent', action='store_true',
213 help='If set, script will not log anything.')
214 parser.add_argument('--src-dirs',
215 help='Directories containing java files.')
216 parser.add_argument('--stamp',
217 help='Path to touch on success.')
191 218
192 if options.enable: 219 args = parser.parse_args()
220
221 if args.enable:
193 sources = [] 222 sources = []
194 if options.src_dirs: 223 if args.src_dirs:
195 src_dirs = build_utils.ParseGypList(options.src_dirs) 224 src_dirs = build_utils.ParseGypList(args.src_dirs)
196 sources = build_utils.FindInDirectories(src_dirs, '*.java') 225 sources = build_utils.FindInDirectories(src_dirs, '*.java')
197 elif options.java_files: 226 elif args.java_files:
198 sources = build_utils.ParseGypList(options.java_files) 227 sources = build_utils.ParseGypList(args.java_files)
199 else: 228
200 print 'One of --src-dirs or --java-files must be specified.' 229 if args.config_path and not args.processed_config_path:
201 return 1 230 parser.error('--config-path specified without --processed-config-path')
231 elif args.processed_config_path and not args.config_path:
232 parser.error('--processed-config-path specified without --config-path')
202 233
203 input_paths = [ 234 input_paths = [
204 options.lint_path, 235 args.lint_path,
205 options.config_path,
206 options.manifest_path,
207 options.jar_path,
208 ] 236 ]
209 input_paths.extend(sources) 237 if args.config_path:
210 if options.resource_dir: 238 input_paths.append(args.config_path)
211 input_paths.extend(build_utils.FindInDirectory(options.resource_dir, '*')) 239 if args.jar_path:
240 input_paths.append(args.jar_path)
241 if args.manifest_path:
242 input_paths.append(args.manifest_path)
243 if args.platform_xml_path:
244 input_paths.append(args.platform_xml_path)
245 if args.resource_dir:
246 input_paths.extend(build_utils.FindInDirectory(args.resource_dir, '*'))
247 if sources:
248 input_paths.extend(sources)
212 249
213 input_strings = [ options.processed_config_path ] 250 input_strings = []
214 output_paths = [ options.result_path ] 251 if args.processed_config_path:
252 input_strings.append(args.processed_config_path)
253
254 output_paths = [ args.result_path ]
255 if args.cache_dir:
256 if not args.build_tools_version:
257 parser.error('--cache-dir specified without --build-tools-version')
258 output_paths.append(os.path.join(
259 args.cache_dir, '.android', 'cache',
260 'api-versions-6-%s.bin' % args.build_tools_version))
215 261
216 build_utils.CallAndWriteDepfileIfStale( 262 build_utils.CallAndWriteDepfileIfStale(
217 lambda changes: _OnStaleMd5(changes, options.lint_path, 263 lambda changes: _OnStaleMd5(changes, args.lint_path,
218 options.config_path, 264 args.config_path,
219 options.processed_config_path, 265 args.processed_config_path,
220 options.manifest_path, options.result_path, 266 args.manifest_path, args.result_path,
221 options.product_dir, sources, 267 args.product_dir, sources,
222 options.jar_path, 268 args.jar_path,
223 resource_dir=options.resource_dir, 269 args.cache_dir,
224 can_fail_build=options.can_fail_build), 270 resource_dir=args.resource_dir,
225 options, 271 can_fail_build=args.can_fail_build,
272 silent=args.silent),
273 args,
226 input_paths=input_paths, 274 input_paths=input_paths,
227 input_strings=input_strings, 275 input_strings=input_strings,
228 output_paths=output_paths, 276 output_paths=output_paths,
229 pass_changes=True) 277 pass_changes=True)
230 278
231 279
232 if __name__ == '__main__': 280 if __name__ == '__main__':
233 sys.exit(main()) 281 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698