OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Runs Closure compiler on a JavaScript file to check for errors.""" | 6 """Runs Closure compiler on a JavaScript file to check for errors.""" |
7 | 7 |
8 import argparse | 8 import argparse |
9 import os | 9 import os |
10 import re | 10 import re |
(...skipping 26 matching lines...) Expand all Loading... | |
37 "--jscomp_error=missingReturn", | 37 "--jscomp_error=missingReturn", |
38 "--jscomp_error=nonStandardJsDocs", | 38 "--jscomp_error=nonStandardJsDocs", |
39 "--jscomp_error=suspiciousCode", | 39 "--jscomp_error=suspiciousCode", |
40 "--jscomp_error=undefinedNames", | 40 "--jscomp_error=undefinedNames", |
41 "--jscomp_error=undefinedVars", | 41 "--jscomp_error=undefinedVars", |
42 "--jscomp_error=unknownDefines", | 42 "--jscomp_error=unknownDefines", |
43 "--jscomp_error=uselessCode", | 43 "--jscomp_error=uselessCode", |
44 "--jscomp_error=visibility", | 44 "--jscomp_error=visibility", |
45 "--language_in=ECMASCRIPT5_STRICT", | 45 "--language_in=ECMASCRIPT5_STRICT", |
46 "--summary_detail_level=3", | 46 "--summary_detail_level=3", |
47 '--compilation_level=WHITESPACE_ONLY' | |
Dan Beam
2015/03/06 02:57:29
' -> "
Dan Beam
2015/03/06 02:57:29
nit: trailing ,
Theresa
2015/03/06 18:09:19
Done.
Theresa
2015/03/06 18:09:19
Done.
| |
47 ] | 48 ] |
48 | 49 |
49 # These are the extra flags used when compiling in 'strict' mode. | 50 # These are the extra flags used when compiling in 'strict' mode. |
50 # Flags that are normally disabled are turned on for strict mode. | 51 # Flags that are normally disabled are turned on for strict mode. |
51 _STRICT_CLOSURE_ARGS = [ | 52 _STRICT_CLOSURE_ARGS = [ |
52 "--jscomp_error=reportUnknownTypes", | 53 "--jscomp_error=reportUnknownTypes", |
53 "--jscomp_error=duplicate", | 54 "--jscomp_error=duplicate", |
54 "--jscomp_error=misplacedTypeAnnotation", | 55 "--jscomp_error=misplacedTypeAnnotation", |
55 ] | 56 ] |
56 | 57 |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
170 errors = filter(None, errors) | 171 errors = filter(None, errors) |
171 contents = "\n## ".join("\n\n".join(errors).splitlines()) | 172 contents = "\n## ".join("\n\n".join(errors).splitlines()) |
172 return "## %s" % contents if contents else "" | 173 return "## %s" % contents if contents else "" |
173 | 174 |
174 def _create_temp_file(self, contents): | 175 def _create_temp_file(self, contents): |
175 with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file: | 176 with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file: |
176 self._temp_files.append(tmp_file.name) | 177 self._temp_files.append(tmp_file.name) |
177 tmp_file.write(contents) | 178 tmp_file.write(contents) |
178 return tmp_file.name | 179 return tmp_file.name |
179 | 180 |
180 def run_js_check(self, sources, externs=None): | 181 def run_js_check(self, sources, externs=None, out_file=None, |
Dan Beam
2015/03/06 02:57:29
i think if the code will be modified, there should
Theresa
2015/03/06 03:07:19
Right now, the out_file is never empty. I'll look
Dan Beam
2015/03/06 03:41:38
why not just write the output for all files?
| |
182 generate_output=False): | |
181 if not self._check_java_path(): | 183 if not self._check_java_path(): |
182 return 1, "" | 184 return 1, "" |
183 | 185 |
184 args = ["--js=%s" % s for s in sources] | 186 args = ["--js=%s" % s for s in sources] |
187 | |
188 if generate_output: | |
189 args += ["--js_output_file=%s" % out_file] | |
190 args += ["--source_map_format=V3"] | |
191 args += ["--create_source_map=%s.map" % out_file] | |
192 | |
185 if externs: | 193 if externs: |
186 args += ["--externs=%s" % e for e in externs] | 194 args += ["--externs=%s" % e for e in externs] |
187 args_file_content = " %s" % " ".join(self._common_args() + args) | 195 args_file_content = " %s" % " ".join(self._common_args() + args) |
188 self._debug("Args: %s" % args_file_content.strip()) | 196 self._debug("Args: %s" % args_file_content.strip()) |
189 | 197 |
190 args_file = self._create_temp_file(args_file_content) | 198 args_file = self._create_temp_file(args_file_content) |
191 self._debug("Args file: %s" % args_file) | 199 self._debug("Args file: %s" % args_file) |
192 | 200 |
193 runner_args = ["--compiler-args-file=%s" % args_file] | 201 runner_args = ["--compiler-args-file=%s" % args_file] |
194 runner_cmd = self._run_jar(self._runner_jar, args=runner_args) | 202 runner_cmd = self._run_jar(self._runner_jar, args=runner_args) |
195 _, stderr = runner_cmd.communicate() | 203 _, stderr = runner_cmd.communicate() |
196 | 204 |
197 errors = stderr.strip().split("\n\n") | 205 errors = stderr.strip().split("\n\n") |
198 self._debug("Summary: %s" % errors.pop()) | 206 self._debug("Summary: %s" % errors.pop()) |
199 | 207 |
200 self._clean_up() | 208 self._clean_up() |
201 | 209 |
202 return errors, stderr | 210 return errors, stderr |
203 | 211 |
204 def check(self, source_file, depends=None, externs=None): | 212 def check(self, source_file, depends=None, externs=None, |
213 out_file=None, generate_output=False): | |
205 """Closure compile a file and check for errors. | 214 """Closure compile a file and check for errors. |
206 | 215 |
207 Args: | 216 Args: |
208 source_file: A file to check. | 217 source_file: A file to check. |
209 depends: Other files that would be included with a <script> earlier in | 218 depends: Other files that would be included with a <script> earlier in |
210 the page. | 219 the page. |
211 externs: @extern files that inform the compiler about custom globals. | 220 externs: @extern files that inform the compiler about custom globals. |
212 | 221 |
213 Returns: | 222 Returns: |
214 (has_errors, output) A boolean indicating if there were errors and the | 223 (has_errors, output) A boolean indicating if there were errors and the |
(...skipping 18 matching lines...) Expand all Loading... | |
233 | 242 |
234 includes = [rel_path(f) for f in depends + [source_file]] | 243 includes = [rel_path(f) for f in depends + [source_file]] |
235 contents = ['<include src="%s">' % i for i in includes] | 244 contents = ['<include src="%s">' % i for i in includes] |
236 meta_file = self._create_temp_file("\n".join(contents)) | 245 meta_file = self._create_temp_file("\n".join(contents)) |
237 self._debug("Meta file: %s" % meta_file) | 246 self._debug("Meta file: %s" % meta_file) |
238 | 247 |
239 self._processor = processor.Processor(meta_file) | 248 self._processor = processor.Processor(meta_file) |
240 self._expanded_file = self._create_temp_file(self._processor.contents) | 249 self._expanded_file = self._create_temp_file(self._processor.contents) |
241 self._debug("Expanded file: %s" % self._expanded_file) | 250 self._debug("Expanded file: %s" % self._expanded_file) |
242 | 251 |
243 errors, stderr = self.run_js_check([self._expanded_file], externs) | 252 errors, stderr = self.run_js_check([self._expanded_file], externs, |
253 out_file, generate_output) | |
244 | 254 |
245 # Filter out false-positive promise chain errors. | 255 # Filter out false-positive promise chain errors. |
246 # See https://github.com/google/closure-compiler/issues/715 for details. | 256 # See https://github.com/google/closure-compiler/issues/715 for details. |
247 errors = self._error_filter.filter(errors); | 257 errors = self._error_filter.filter(errors); |
248 | 258 |
249 output = self._format_errors(map(self._fix_up_error, errors)) | 259 output = self._format_errors(map(self._fix_up_error, errors)) |
250 if errors: | 260 if errors: |
251 prefix = "\n" if output else "" | 261 prefix = "\n" if output else "" |
252 self._error("Error in: %s%s%s" % (source_file, prefix, output)) | 262 self._error("Error in: %s%s%s" % (source_file, prefix, output)) |
253 elif output: | 263 elif output: |
(...skipping 23 matching lines...) Expand all Loading... | |
277 single_file_group = parser.add_mutually_exclusive_group() | 287 single_file_group = parser.add_mutually_exclusive_group() |
278 single_file_group.add_argument("--single-file", dest="single_file", | 288 single_file_group.add_argument("--single-file", dest="single_file", |
279 action="store_true", | 289 action="store_true", |
280 help="Process each source file individually") | 290 help="Process each source file individually") |
281 single_file_group.add_argument("--no-single-file", dest="single_file", | 291 single_file_group.add_argument("--no-single-file", dest="single_file", |
282 action="store_false", | 292 action="store_false", |
283 help="Process all source files as a group") | 293 help="Process all source files as a group") |
284 parser.add_argument("-d", "--depends", nargs=argparse.ZERO_OR_MORE) | 294 parser.add_argument("-d", "--depends", nargs=argparse.ZERO_OR_MORE) |
285 parser.add_argument("-e", "--externs", nargs=argparse.ZERO_OR_MORE) | 295 parser.add_argument("-e", "--externs", nargs=argparse.ZERO_OR_MORE) |
286 parser.add_argument("-o", "--out_file", help="A place to output results") | 296 parser.add_argument("-o", "--out_file", help="A place to output results") |
297 parser.add_argument("-g", "--generate_output", | |
298 help="Whether or not an output file should be generated") | |
287 parser.add_argument("-v", "--verbose", action="store_true", | 299 parser.add_argument("-v", "--verbose", action="store_true", |
288 help="Show more information as this script runs") | 300 help="Show more information as this script runs") |
289 parser.add_argument("--strict", action="store_true", | 301 parser.add_argument("--strict", action="store_true", |
290 help="Enable strict type checking") | 302 help="Enable strict type checking") |
291 parser.add_argument("--success-stamp", | 303 parser.add_argument("--success-stamp", |
292 help="Timestamp file to update upon success") | 304 help="Timestamp file to update upon success") |
293 | 305 |
294 parser.set_defaults(single_file=True, strict=False) | 306 parser.set_defaults(single_file=True, strict=False) |
295 opts = parser.parse_args() | 307 opts = parser.parse_args() |
296 | 308 |
297 depends = opts.depends or [] | 309 depends = opts.depends or [] |
298 externs = opts.externs or set() | 310 externs = opts.externs or set() |
311 generate_output = False if (opts.generate_output == '0') else True | |
Dan Beam
2015/03/06 02:57:29
False if (condition) else True => can always be sh
| |
312 | |
313 # Clean up intermediate folders that get created by output.py or create the di rs if needed | |
Dan Beam
2015/03/06 02:57:29
end with .
Dan Beam
2015/03/06 02:57:29
80 col wrap
| |
314 out_dir = os.path.dirname(opts.out_file) | |
315 if not generate_output: | |
316 if os.path.exists(out_dir) and os.listdir(out_dir) == []: | |
Dan Beam
2015/03/06 02:57:29
why are we making empty directories?
Theresa
2015/03/06 03:07:19
I think output.py is creating them when it generat
| |
317 os.removedirs(out_dir) | |
318 else: | |
319 if not os.path.exists(out_dir): | |
320 os.makedirs(out_dir) | |
Dan Beam
2015/03/06 02:57:28
so closure doesn't do this automatically? make a
Theresa
2015/03/06 03:07:19
It might. I'll try removing this. I had copied it
| |
299 | 321 |
300 checker = Checker(verbose=opts.verbose, strict=opts.strict) | 322 checker = Checker(verbose=opts.verbose, strict=opts.strict) |
301 if opts.single_file: | 323 if opts.single_file: |
302 for source in opts.sources: | 324 for source in opts.sources: |
303 depends, externs = build.inputs.resolve_recursive_dependencies( | 325 depends, externs = build.inputs.resolve_recursive_dependencies( |
304 source, | 326 source, |
305 depends, | 327 depends, |
306 externs) | 328 externs) |
307 has_errors, _ = checker.check(source, depends=depends, externs=externs) | 329 has_errors, _ = checker.check(source, depends=depends, externs=externs, |
330 out_file=opts.out_file, generate_output=gene rate_output) | |
Dan Beam
2015/03/06 02:57:29
80 col wrap
| |
308 if has_errors: | 331 if has_errors: |
309 sys.exit(1) | 332 sys.exit(1) |
310 | 333 |
311 if opts.out_file: | |
312 out_dir = os.path.dirname(opts.out_file) | |
313 if not os.path.exists(out_dir): | |
314 os.makedirs(out_dir) | |
315 # TODO(dbeam): write compiled file to |opts.out_file|. | |
316 open(opts.out_file, "w").write("") | |
317 else: | 334 else: |
318 has_errors, errors = checker.check_multiple(opts.sources) | 335 has_errors, errors = checker.check_multiple(opts.sources) |
319 if has_errors: | 336 if has_errors: |
320 print errors | 337 print errors |
321 sys.exit(1) | 338 sys.exit(1) |
322 | 339 |
323 if opts.success_stamp: | 340 if opts.success_stamp: |
324 with open(opts.success_stamp, 'w'): | 341 with open(opts.success_stamp, 'w'): |
325 os.utime(opts.success_stamp, None) | 342 os.utime(opts.success_stamp, None) |
OLD | NEW |