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 JavaScript files to check for errors and produce | 6 """Runs Closure compiler on JavaScript files to check for errors and produce |
7 minified output.""" | 7 minified output.""" |
8 | 8 |
9 import argparse | 9 import argparse |
10 import os | 10 import os |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
51 _POLYMER_EXTRA_ANNOTATIONS = [ | 51 _POLYMER_EXTRA_ANNOTATIONS = [ |
52 "attribute", | 52 "attribute", |
53 "status", | 53 "status", |
54 "element", | 54 "element", |
55 "homepage", | 55 "homepage", |
56 "submodule", | 56 "submodule", |
57 "group", | 57 "group", |
58 ] | 58 ] |
59 | 59 |
60 _COMMON_CLOSURE_ARGS = [ | 60 _COMMON_CLOSURE_ARGS = [ |
61 "--accept_const_keyword", | |
62 "--language_in=ECMASCRIPT5_STRICT", | |
63 "--summary_detail_level=3", | |
64 "--compilation_level=SIMPLE_OPTIMIZATIONS", | |
65 "--source_map_format=V3", | |
66 "--polymer_pass", | |
67 ] + [ | |
68 "--jscomp_error=%s" % err for err in _COMMON_JSCOMP_ERRORS | 61 "--jscomp_error=%s" % err for err in _COMMON_JSCOMP_ERRORS |
69 ] + [ | 62 ] + [ |
70 "--extra_annotation_name=%s" % a for a in _POLYMER_EXTRA_ANNOTATIONS | 63 "--extra_annotation_name=%s" % a for a in _POLYMER_EXTRA_ANNOTATIONS |
71 ] | 64 ] |
72 | 65 |
73 # These are the extra flags used when compiling in strict mode. | 66 # These are the extra flags used when compiling in strict mode. |
74 # Flags that are normally disabled are turned on for strict mode. | 67 # Flags that are normally disabled are turned on for strict mode. |
75 _STRICT_CLOSURE_ARGS = [ | 68 _STRICT_CLOSURE_ARGS = [ |
76 "--jscomp_error=reportUnknownTypes", | 69 "--jscomp_error=reportUnknownTypes", |
77 "--jscomp_error=duplicate", | 70 "--jscomp_error=duplicate", |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 | 239 |
247 Return: | 240 Return: |
248 The filepath of the newly created, written, and closed temporary file. | 241 The filepath of the newly created, written, and closed temporary file. |
249 """ | 242 """ |
250 with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file: | 243 with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file: |
251 self._temp_files.append(tmp_file.name) | 244 self._temp_files.append(tmp_file.name) |
252 tmp_file.write(contents) | 245 tmp_file.write(contents) |
253 return tmp_file.name | 246 return tmp_file.name |
254 | 247 |
255 def _run_js_check(self, sources, out_file=None, externs=None, | 248 def _run_js_check(self, sources, out_file=None, externs=None, |
256 output_wrapper=None): | 249 closure_args=None): |
257 """Check |sources| for type errors. | 250 """Check |sources| for type errors. |
258 | 251 |
259 Args: | 252 Args: |
260 sources: Files to check. | 253 sources: Files to check. |
261 out_file: A file where the compiled output is written to. | 254 out_file: A file where the compiled output is written to. |
262 externs: @extern files that inform the compiler about custom globals. | 255 externs: @extern files that inform the compiler about custom globals. |
263 output_wrapper: Wraps output into this string at the place denoted by the | 256 closure_args: Arguments passed directly to the closure compiler. |
264 marker token %output%. | |
265 | 257 |
266 Returns: | 258 Returns: |
267 (errors, stderr) A parsed list of errors (strings) found by the compiler | 259 (errors, stderr) A parsed list of errors (strings) found by the compiler |
268 and the raw stderr (as a string). | 260 and the raw stderr (as a string). |
269 """ | 261 """ |
270 args = ["--js=%s" % s for s in sources] | 262 args = ["--js=%s" % s for s in sources] |
271 | 263 |
272 if out_file: | 264 if out_file: |
273 out_dir = os.path.dirname(out_file) | 265 out_dir = os.path.dirname(out_file) |
274 if not os.path.exists(out_dir): | 266 if not os.path.exists(out_dir): |
275 os.makedirs(out_dir) | 267 os.makedirs(out_dir) |
276 args += ["--js_output_file=%s" % out_file] | 268 args += ["--js_output_file=%s" % out_file] |
277 args += ["--create_source_map=%s" % (self._MAP_FILE_FORMAT % out_file)] | 269 args += ["--create_source_map=%s" % (self._MAP_FILE_FORMAT % out_file)] |
278 | 270 |
279 if externs: | 271 if externs: |
280 args += ["--externs=%s" % e for e in externs] | 272 args += ["--externs=%s" % e for e in externs] |
281 | 273 |
282 if output_wrapper: | 274 args += closure_args |
283 args += ['--output_wrapper="%s"' % output_wrapper] | |
284 | 275 |
285 args_file_content = " %s" % " ".join(self._common_args() + args) | 276 args_file_content = " %s" % " ".join(self._common_args() + args) |
286 self._log_debug("Args: %s" % args_file_content.strip()) | 277 self._log_debug("Args: %s" % args_file_content.strip()) |
287 | 278 |
288 args_file = self._create_temp_file(args_file_content) | 279 args_file = self._create_temp_file(args_file_content) |
289 self._log_debug("Args file: %s" % args_file) | 280 self._log_debug("Args file: %s" % args_file) |
290 | 281 |
291 runner_args = ["--compiler-args-file=%s" % args_file] | 282 runner_args = ["--compiler-args-file=%s" % args_file] |
292 _, stderr = self._run_jar(self._runner_jar, runner_args) | 283 _, stderr = self._run_jar(self._runner_jar, runner_args) |
293 | 284 |
(...skipping 10 matching lines...) Expand all Loading... |
304 | 295 |
305 if errors and out_file: | 296 if errors and out_file: |
306 if os.path.exists(out_file): | 297 if os.path.exists(out_file): |
307 os.remove(out_file) | 298 os.remove(out_file) |
308 if os.path.exists(self._MAP_FILE_FORMAT % out_file): | 299 if os.path.exists(self._MAP_FILE_FORMAT % out_file): |
309 os.remove(self._MAP_FILE_FORMAT % out_file) | 300 os.remove(self._MAP_FILE_FORMAT % out_file) |
310 | 301 |
311 return errors, stderr | 302 return errors, stderr |
312 | 303 |
313 def check(self, source_file, out_file=None, depends=None, externs=None, | 304 def check(self, source_file, out_file=None, depends=None, externs=None, |
314 output_wrapper=None): | 305 closure_args=None): |
315 """Closure compiler |source_file| while checking for errors. | 306 """Closure compiler |source_file| while checking for errors. |
316 | 307 |
317 Args: | 308 Args: |
318 source_file: A file to check. | 309 source_file: A file to check. |
319 out_file: A file where the compiled output is written to. | 310 out_file: A file where the compiled output is written to. |
320 depends: Files that |source_file| requires to run (e.g. earlier <script>). | 311 depends: Files that |source_file| requires to run (e.g. earlier <script>). |
321 externs: @extern files that inform the compiler about custom globals. | 312 externs: @extern files that inform the compiler about custom globals. |
322 output_wrapper: Wraps output into this string at the place denoted by the | 313 closure_args: Arguments passed directly to the closure compiler. |
323 marker token %output%. | |
324 | 314 |
325 Returns: | 315 Returns: |
326 (found_errors, stderr) A boolean indicating whether errors were found and | 316 (found_errors, stderr) A boolean indicating whether errors were found and |
327 the raw Closure compiler stderr (as a string). | 317 the raw Closure compiler stderr (as a string). |
328 """ | 318 """ |
329 self._log_debug("FILE: %s" % source_file) | 319 self._log_debug("FILE: %s" % source_file) |
330 | 320 |
331 if source_file.endswith("_externs.js"): | 321 if source_file.endswith("_externs.js"): |
332 self._log_debug("Skipping externs: %s" % source_file) | 322 self._log_debug("Skipping externs: %s" % source_file) |
333 return | 323 return |
334 | 324 |
335 self._file_arg = source_file | 325 self._file_arg = source_file |
336 | 326 |
337 cwd, tmp_dir = os.getcwd(), tempfile.gettempdir() | 327 cwd, tmp_dir = os.getcwd(), tempfile.gettempdir() |
338 rel_path = lambda f: os.path.join(os.path.relpath(cwd, tmp_dir), f) | 328 rel_path = lambda f: os.path.join(os.path.relpath(cwd, tmp_dir), f) |
339 | 329 |
340 depends = depends or [] | 330 depends = depends or [] |
341 includes = [rel_path(f) for f in depends + [source_file]] | 331 includes = [rel_path(f) for f in depends + [source_file]] |
342 contents = ['<include src="%s">' % i for i in includes] | 332 contents = ['<include src="%s">' % i for i in includes] |
343 meta_file = self._create_temp_file("\n".join(contents)) | 333 meta_file = self._create_temp_file("\n".join(contents)) |
344 self._log_debug("Meta file: %s" % meta_file) | 334 self._log_debug("Meta file: %s" % meta_file) |
345 | 335 |
346 self._processor = processor.Processor(meta_file) | 336 self._processor = processor.Processor(meta_file) |
347 self._expanded_file = self._create_temp_file(self._processor.contents) | 337 self._expanded_file = self._create_temp_file(self._processor.contents) |
348 self._log_debug("Expanded file: %s" % self._expanded_file) | 338 self._log_debug("Expanded file: %s" % self._expanded_file) |
349 | 339 |
350 errors, stderr = self._run_js_check([self._expanded_file], | 340 errors, stderr = self._run_js_check([self._expanded_file], |
351 out_file=out_file, externs=externs, | 341 out_file=out_file, externs=externs, |
352 output_wrapper=output_wrapper) | 342 closure_args=closure_args) |
353 filtered_errors = self._filter_errors(errors) | 343 filtered_errors = self._filter_errors(errors) |
354 cleaned_errors = map(self._clean_up_error, filtered_errors) | 344 cleaned_errors = map(self._clean_up_error, filtered_errors) |
355 output = self._format_errors(cleaned_errors) | 345 output = self._format_errors(cleaned_errors) |
356 | 346 |
357 if cleaned_errors: | 347 if cleaned_errors: |
358 prefix = "\n" if output else "" | 348 prefix = "\n" if output else "" |
359 self._log_error("Error in: %s%s%s" % (source_file, prefix, output)) | 349 self._log_error("Error in: %s%s%s" % (source_file, prefix, output)) |
360 elif output: | 350 elif output: |
361 self._log_debug("Output: %s" % output) | 351 self._log_debug("Output: %s" % output) |
362 | 352 |
363 self._nuke_temp_files() | 353 self._nuke_temp_files() |
364 return bool(cleaned_errors), stderr | 354 return bool(cleaned_errors), stderr |
365 | 355 |
366 def check_multiple(self, sources, out_file=None, output_wrapper=None, | 356 def check_multiple(self, sources, out_file=None, closure_args=None, |
367 externs=None): | 357 externs=None): |
368 """Closure compile a set of files and check for errors. | 358 """Closure compile a set of files and check for errors. |
369 | 359 |
370 Args: | 360 Args: |
371 sources: An array of files to check. | 361 sources: An array of files to check. |
372 out_file: A file where the compiled output is written to. | 362 out_file: A file where the compiled output is written to. |
373 output_wrapper: Wraps output into this string at the place denoted by the | 363 closure_args: Arguments passed directly to the closure compiler. |
374 marker token %output%. | |
375 externs: @extern files that inform the compiler about custom globals. | 364 externs: @extern files that inform the compiler about custom globals. |
376 | 365 |
377 Returns: | 366 Returns: |
378 (found_errors, stderr) A boolean indicating whether errors were found and | 367 (found_errors, stderr) A boolean indicating whether errors were found and |
379 the raw Closure Compiler stderr (as a string). | 368 the raw Closure Compiler stderr (as a string). |
380 """ | 369 """ |
381 errors, stderr = self._run_js_check(sources, out_file=out_file, | 370 errors, stderr = self._run_js_check(sources, out_file=out_file, |
382 output_wrapper=output_wrapper, | 371 closure_args=closure_args, |
383 externs=externs) | 372 externs=externs) |
384 self._nuke_temp_files() | 373 self._nuke_temp_files() |
385 return bool(errors), stderr | 374 return bool(errors), stderr |
386 | 375 |
387 | 376 |
388 if __name__ == "__main__": | 377 if __name__ == "__main__": |
389 parser = argparse.ArgumentParser( | 378 parser = argparse.ArgumentParser( |
390 description="Typecheck JavaScript using Closure compiler") | 379 description="Typecheck JavaScript using Closure compiler") |
391 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE, | 380 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE, |
392 help="Path to a source file to typecheck") | 381 help="Path to a source file to typecheck") |
393 single_file_group = parser.add_mutually_exclusive_group() | 382 single_file_group = parser.add_mutually_exclusive_group() |
394 single_file_group.add_argument("--single-file", dest="single_file", | 383 single_file_group.add_argument("--single-file", dest="single_file", |
395 action="store_true", | 384 action="store_true", |
396 help="Process each source file individually") | 385 help="Process each source file individually") |
397 single_file_group.add_argument("--no-single-file", dest="single_file", | 386 single_file_group.add_argument("--no-single-file", dest="single_file", |
398 action="store_false", | 387 action="store_false", |
399 help="Process all source files as a group") | 388 help="Process all source files as a group") |
400 parser.add_argument("-d", "--depends", nargs=argparse.ZERO_OR_MORE) | 389 parser.add_argument("-d", "--depends", nargs=argparse.ZERO_OR_MORE) |
401 parser.add_argument("-e", "--externs", nargs=argparse.ZERO_OR_MORE) | 390 parser.add_argument("-e", "--externs", nargs=argparse.ZERO_OR_MORE) |
402 parser.add_argument("-o", "--out_file", | 391 parser.add_argument("-o", "--out_file", |
403 help="A file where the compiled output is written to") | 392 help="A file where the compiled output is written to") |
404 parser.add_argument("-w", "--output_wrapper", | 393 parser.add_argument("-c", "--closure_args", |
405 help="Wraps output into this string at the place" | 394 help="Arguments passed directly to the closure compiler") |
406 + " denoted by the marker token %output%") | |
407 parser.add_argument("-v", "--verbose", action="store_true", | 395 parser.add_argument("-v", "--verbose", action="store_true", |
408 help="Show more information as this script runs") | 396 help="Show more information as this script runs") |
409 parser.add_argument("--strict", action="store_true", | 397 parser.add_argument("--strict", action="store_true", |
410 help="Enable strict type checking") | 398 help="Enable strict type checking") |
411 parser.add_argument("--success-stamp", | 399 parser.add_argument("--success-stamp", |
412 help="Timestamp file to update upon success") | 400 help="Timestamp file to update upon success") |
413 | 401 |
414 parser.set_defaults(single_file=True, strict=False) | 402 parser.set_defaults(single_file=True, strict=False) |
415 opts = parser.parse_args() | 403 opts = parser.parse_args() |
416 | 404 |
417 depends = opts.depends or [] | 405 depends = opts.depends or [] |
418 externs = set(opts.externs or []) | 406 externs = set(opts.externs or []) |
419 | 407 |
420 polymer_externs = os.path.join(os.path.dirname(_CURRENT_DIR), 'polymer', | 408 polymer_externs = os.path.join(os.path.dirname(_CURRENT_DIR), 'polymer', |
421 'v0_8', 'components-chromium', | 409 'v0_8', 'components-chromium', |
422 'polymer-externs', 'polymer.externs.js') | 410 'polymer-externs', 'polymer.externs.js') |
423 externs.add(polymer_externs) | 411 externs.add(polymer_externs) |
424 | 412 |
425 checker = Checker(verbose=opts.verbose, strict=opts.strict) | 413 checker = Checker(verbose=opts.verbose, strict=opts.strict) |
426 if opts.single_file: | 414 if opts.single_file: |
427 for source in opts.sources: | 415 for source in opts.sources: |
428 depends, externs = build.inputs.resolve_recursive_dependencies( | 416 depends, externs = build.inputs.resolve_recursive_dependencies( |
429 source, depends, externs) | 417 source, depends, externs) |
430 found_errors, _ = checker.check(source, out_file=opts.out_file, | 418 found_errors, _ = checker.check(source, out_file=opts.out_file, |
431 depends=depends, externs=externs, | 419 depends=depends, externs=externs, |
432 output_wrapper=opts.output_wrapper) | 420 closure_args=opts.closure_args) |
433 if found_errors: | 421 if found_errors: |
434 sys.exit(1) | 422 sys.exit(1) |
435 else: | 423 else: |
436 found_errors, stderr = checker.check_multiple( | 424 found_errors, stderr = checker.check_multiple( |
437 opts.sources, | 425 opts.sources, |
438 out_file=opts.out_file, | 426 out_file=opts.out_file, |
439 output_wrapper=opts.output_wrapper, | 427 closure_args=opts.closure_args, |
440 externs=externs) | 428 externs=externs) |
441 if found_errors: | 429 if found_errors: |
442 print stderr | 430 print stderr |
443 sys.exit(1) | 431 sys.exit(1) |
444 | 432 |
445 if opts.success_stamp: | 433 if opts.success_stamp: |
446 with open(opts.success_stamp, "w"): | 434 with open(opts.success_stamp, "w"): |
447 os.utime(opts.success_stamp, None) | 435 os.utime(opts.success_stamp, None) |
OLD | NEW |