| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 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 |
| 11 import re | 11 import re |
| 12 import subprocess | 12 import subprocess |
| 13 import sys | 13 import sys |
| 14 import tempfile | 14 import tempfile |
| 15 | 15 |
| 16 import processor | 16 import processor |
| 17 import error_filter | |
| 18 | 17 |
| 19 | 18 |
| 20 _CURRENT_DIR = os.path.join(os.path.dirname(__file__)) | 19 _CURRENT_DIR = os.path.join(os.path.dirname(__file__)) |
| 21 | 20 |
| 22 | 21 |
| 23 class Checker(object): | 22 class Checker(object): |
| 24 """Runs the Closure compiler on given source files to typecheck them | 23 """Runs the Closure compiler on given source files to typecheck them |
| 25 and produce minified output.""" | 24 and produce minified output.""" |
| 26 | 25 |
| 27 _JAR_COMMAND = [ | 26 _JAR_COMMAND = [ |
| (...skipping 10 matching lines...) Expand all Loading... |
| 38 | 37 |
| 39 def __init__(self, verbose=False): | 38 def __init__(self, verbose=False): |
| 40 """ | 39 """ |
| 41 Args: | 40 Args: |
| 42 verbose: Whether this class should output diagnostic messages. | 41 verbose: Whether this class should output diagnostic messages. |
| 43 """ | 42 """ |
| 44 self._compiler_jar = os.path.join(_CURRENT_DIR, "compiler", "compiler.jar") | 43 self._compiler_jar = os.path.join(_CURRENT_DIR, "compiler", "compiler.jar") |
| 45 self._target = None | 44 self._target = None |
| 46 self._temp_files = [] | 45 self._temp_files = [] |
| 47 self._verbose = verbose | 46 self._verbose = verbose |
| 48 self._error_filter = error_filter.PromiseErrorFilter() | |
| 49 | 47 |
| 50 def _nuke_temp_files(self): | 48 def _nuke_temp_files(self): |
| 51 """Deletes any temp files this class knows about.""" | 49 """Deletes any temp files this class knows about.""" |
| 52 if not self._temp_files: | 50 if not self._temp_files: |
| 53 return | 51 return |
| 54 | 52 |
| 55 self._log_debug("Deleting temp files: %s" % ", ".join(self._temp_files)) | 53 self._log_debug("Deleting temp files: %s" % ", ".join(self._temp_files)) |
| 56 for f in self._temp_files: | 54 for f in self._temp_files: |
| 57 os.remove(f) | 55 os.remove(f) |
| 58 self._temp_files = [] | 56 self._temp_files = [] |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 115 | 113 |
| 116 Args: | 114 Args: |
| 117 match: A re.MatchObject from matching against a line number regex. | 115 match: A re.MatchObject from matching against a line number regex. |
| 118 | 116 |
| 119 Returns: | 117 Returns: |
| 120 The fixed up /file and :line number. | 118 The fixed up /file and :line number. |
| 121 """ | 119 """ |
| 122 real_file = self._processor.get_file_from_line(match.group(1)) | 120 real_file = self._processor.get_file_from_line(match.group(1)) |
| 123 return "%s:%d" % (os.path.abspath(real_file.file), real_file.line_number) | 121 return "%s:%d" % (os.path.abspath(real_file.file), real_file.line_number) |
| 124 | 122 |
| 125 def _filter_errors(self, errors): | |
| 126 """Removes some extraneous errors. For example, we ignore: | |
| 127 | |
| 128 Variable x first declared in /tmp/expanded/file | |
| 129 | |
| 130 Because it's just a duplicated error (it'll only ever show up 2+ times). | |
| 131 We also ignore Promise-based errors: | |
| 132 | |
| 133 found : function (VolumeInfo): (Promise<(DirectoryEntry|null)>|null) | |
| 134 required: (function (Promise<VolumeInfo>): ?|null|undefined) | |
| 135 | |
| 136 as templates don't work with Promises in all cases yet. See | |
| 137 https://github.com/google/closure-compiler/issues/715 for details. | |
| 138 | |
| 139 Args: | |
| 140 errors: A list of string errors extracted from Closure Compiler output. | |
| 141 | |
| 142 Return: | |
| 143 A slimmer, sleeker list of relevant errors (strings). | |
| 144 """ | |
| 145 first_declared_in = lambda e: " first declared in " not in e | |
| 146 return self._error_filter.filter(filter(first_declared_in, errors)) | |
| 147 | |
| 148 def _clean_up_error(self, error): | 123 def _clean_up_error(self, error): |
| 149 """Reverse the effects that funky <include> preprocessing steps have on | 124 """Reverse the effects that funky <include> preprocessing steps have on |
| 150 errors messages. | 125 errors messages. |
| 151 | 126 |
| 152 Args: | 127 Args: |
| 153 error: A Closure compiler error (2 line string with error and source). | 128 error: A Closure compiler error (2 line string with error and source). |
| 154 | 129 |
| 155 Return: | 130 Return: |
| 156 The fixed up error string. | 131 The fixed up error string. |
| 157 """ | 132 """ |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 288 if os.path.exists(self._MAP_FILE_FORMAT % out_file): | 263 if os.path.exists(self._MAP_FILE_FORMAT % out_file): |
| 289 os.remove(self._MAP_FILE_FORMAT % out_file) | 264 os.remove(self._MAP_FILE_FORMAT % out_file) |
| 290 elif checks_only: | 265 elif checks_only: |
| 291 # Compile succeeded but --checks_only disables --js_output_file from | 266 # Compile succeeded but --checks_only disables --js_output_file from |
| 292 # actually writing a file. Write a file ourselves so incremental builds | 267 # actually writing a file. Write a file ourselves so incremental builds |
| 293 # still work. | 268 # still work. |
| 294 with open(out_file, 'w') as f: | 269 with open(out_file, 'w') as f: |
| 295 f.write('') | 270 f.write('') |
| 296 | 271 |
| 297 if process_includes: | 272 if process_includes: |
| 298 filtered_errors = self._filter_errors(errors) | 273 errors = map(self._clean_up_error, errors) |
| 299 errors = map(self._clean_up_error, filtered_errors) | |
| 300 output = self._format_errors(errors) | 274 output = self._format_errors(errors) |
| 301 | 275 |
| 302 if errors: | 276 if errors: |
| 303 prefix = "\n" if output else "" | 277 prefix = "\n" if output else "" |
| 304 self._log_error("Error in: %s%s%s" % (self._target, prefix, output)) | 278 self._log_error("Error in: %s%s%s" % (self._target, prefix, output)) |
| 305 elif output: | 279 elif output: |
| 306 self._log_debug("Output: %s" % output) | 280 self._log_debug("Output: %s" % output) |
| 307 | 281 |
| 308 self._nuke_temp_files() | 282 self._nuke_temp_files() |
| 309 return bool(errors), stderr | 283 return bool(errors), stderr |
| (...skipping 21 matching lines...) Expand all Loading... |
| 331 | 305 |
| 332 found_errors, stderr = checker.check(opts.sources, out_file=opts.out_file, | 306 found_errors, stderr = checker.check(opts.sources, out_file=opts.out_file, |
| 333 closure_args=opts.closure_args, | 307 closure_args=opts.closure_args, |
| 334 custom_sources=opts.custom_sources, | 308 custom_sources=opts.custom_sources, |
| 335 custom_includes=opts.custom_includes) | 309 custom_includes=opts.custom_includes) |
| 336 | 310 |
| 337 if found_errors: | 311 if found_errors: |
| 338 if opts.custom_sources: | 312 if opts.custom_sources: |
| 339 print stderr | 313 print stderr |
| 340 sys.exit(1) | 314 sys.exit(1) |
| OLD | NEW |