| 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 |