Chromium Code Reviews| 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 |
| 11 import subprocess | 11 import subprocess |
| 12 import sys | 12 import sys |
| 13 import tempfile | 13 import tempfile |
| 14 | 14 |
| 15 import build.inputs | 15 import build.inputs |
| 16 import processor | 16 import processor |
| 17 import error_filter | 17 import error_filter |
| 18 | 18 |
| 19 | 19 |
| 20 class Checker(object): | 20 class Checker(object): |
|
Dan Beam
2015/03/07 01:55:26
i guess i'll have to rename this to Compile eventu
| |
| 21 """Runs the Closure compiler on a given source file and returns the | 21 """Runs the Closure compiler on a given source file and returns the |
| 22 success/errors.""" | 22 success/errors.""" |
| 23 | 23 |
| 24 _COMMON_CLOSURE_ARGS = [ | 24 _COMMON_CLOSURE_ARGS = [ |
| 25 "--accept_const_keyword", | 25 "--accept_const_keyword", |
| 26 "--jscomp_error=accessControls", | 26 "--jscomp_error=accessControls", |
| 27 "--jscomp_error=ambiguousFunctionDecl", | 27 "--jscomp_error=ambiguousFunctionDecl", |
| 28 "--jscomp_error=checkStructDictInheritance", | 28 "--jscomp_error=checkStructDictInheritance", |
| 29 "--jscomp_error=checkTypes", | 29 "--jscomp_error=checkTypes", |
| 30 "--jscomp_error=checkVars", | 30 "--jscomp_error=checkVars", |
| 31 "--jscomp_error=constantProperty", | 31 "--jscomp_error=constantProperty", |
| 32 "--jscomp_error=deprecated", | 32 "--jscomp_error=deprecated", |
| 33 "--jscomp_error=externsValidation", | 33 "--jscomp_error=externsValidation", |
| 34 "--jscomp_error=globalThis", | 34 "--jscomp_error=globalThis", |
| 35 "--jscomp_error=invalidCasts", | 35 "--jscomp_error=invalidCasts", |
| 36 "--jscomp_error=missingProperties", | 36 "--jscomp_error=missingProperties", |
| 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", | |
| 48 "--source_map_format=V3", | |
| 47 ] | 49 ] |
| 48 | 50 |
| 49 # These are the extra flags used when compiling in 'strict' mode. | 51 # These are the extra flags used when compiling in 'strict' mode. |
| 50 # Flags that are normally disabled are turned on for strict mode. | 52 # Flags that are normally disabled are turned on for strict mode. |
| 51 _STRICT_CLOSURE_ARGS = [ | 53 _STRICT_CLOSURE_ARGS = [ |
| 52 "--jscomp_error=reportUnknownTypes", | 54 "--jscomp_error=reportUnknownTypes", |
| 53 "--jscomp_error=duplicate", | 55 "--jscomp_error=duplicate", |
| 54 "--jscomp_error=misplacedTypeAnnotation", | 56 "--jscomp_error=misplacedTypeAnnotation", |
| 55 ] | 57 ] |
| 56 | 58 |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 170 errors = filter(None, errors) | 172 errors = filter(None, errors) |
| 171 contents = "\n## ".join("\n\n".join(errors).splitlines()) | 173 contents = "\n## ".join("\n\n".join(errors).splitlines()) |
| 172 return "## %s" % contents if contents else "" | 174 return "## %s" % contents if contents else "" |
| 173 | 175 |
| 174 def _create_temp_file(self, contents): | 176 def _create_temp_file(self, contents): |
| 175 with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file: | 177 with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file: |
| 176 self._temp_files.append(tmp_file.name) | 178 self._temp_files.append(tmp_file.name) |
| 177 tmp_file.write(contents) | 179 tmp_file.write(contents) |
| 178 return tmp_file.name | 180 return tmp_file.name |
| 179 | 181 |
| 180 def run_js_check(self, sources, externs=None): | 182 def _run_js_check(self, sources, out_file=None, externs=None): |
| 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 out_file: | |
| 189 args += ["--js_output_file=%s" % out_file] | |
| 190 args += ["--create_source_map=%s.map" % out_file] | |
| 191 | |
| 185 if externs: | 192 if externs: |
| 186 args += ["--externs=%s" % e for e in externs] | 193 args += ["--externs=%s" % e for e in externs] |
| 187 args_file_content = " %s" % " ".join(self._common_args() + args) | 194 args_file_content = " %s" % " ".join(self._common_args() + args) |
| 188 self._debug("Args: %s" % args_file_content.strip()) | 195 self._debug("Args: %s" % args_file_content.strip()) |
| 189 | 196 |
| 190 args_file = self._create_temp_file(args_file_content) | 197 args_file = self._create_temp_file(args_file_content) |
| 191 self._debug("Args file: %s" % args_file) | 198 self._debug("Args file: %s" % args_file) |
| 192 | 199 |
| 193 runner_args = ["--compiler-args-file=%s" % args_file] | 200 runner_args = ["--compiler-args-file=%s" % args_file] |
| 194 runner_cmd = self._run_jar(self._runner_jar, args=runner_args) | 201 runner_cmd = self._run_jar(self._runner_jar, args=runner_args) |
| 195 _, stderr = runner_cmd.communicate() | 202 _, stderr = runner_cmd.communicate() |
| 196 | 203 |
| 197 errors = stderr.strip().split("\n\n") | 204 errors = stderr.strip().split("\n\n") |
| 198 self._debug("Summary: %s" % errors.pop()) | 205 self._debug("Summary: %s" % errors.pop()) |
| 199 | 206 |
| 200 self._clean_up() | 207 self._clean_up() |
| 201 | 208 |
| 202 return errors, stderr | 209 return errors, stderr |
| 203 | 210 |
| 204 def check(self, source_file, depends=None, externs=None): | 211 def check(self, source_file, out_file=None, depends=None, externs=None): |
| 205 """Closure compile a file and check for errors. | 212 """Closure compile a file and check for errors. |
| 206 | 213 |
| 207 Args: | 214 Args: |
| 208 source_file: A file to check. | 215 source_file: A file to check. |
| 216 out_file: A file to output results. | |
|
Dan Beam
2015/03/07 01:55:26
A file where the compiled output is written to.
Theresa
2015/03/07 02:50:59
Done.
| |
| 209 depends: Other files that would be included with a <script> earlier in | 217 depends: Other files that would be included with a <script> earlier in |
| 210 the page. | 218 the page. |
| 211 externs: @extern files that inform the compiler about custom globals. | 219 externs: @extern files that inform the compiler about custom globals. |
| 212 | 220 |
| 213 Returns: | 221 Returns: |
| 214 (has_errors, output) A boolean indicating if there were errors and the | 222 (has_errors, output) A boolean indicating if there were errors and the |
| 215 Closure compiler output (as a string). | 223 Closure compiler output (as a string). |
| 216 """ | 224 """ |
| 217 depends = depends or [] | 225 depends = depends or [] |
| 218 externs = externs or set() | 226 externs = externs or set() |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 233 | 241 |
| 234 includes = [rel_path(f) for f in depends + [source_file]] | 242 includes = [rel_path(f) for f in depends + [source_file]] |
| 235 contents = ['<include src="%s">' % i for i in includes] | 243 contents = ['<include src="%s">' % i for i in includes] |
| 236 meta_file = self._create_temp_file("\n".join(contents)) | 244 meta_file = self._create_temp_file("\n".join(contents)) |
| 237 self._debug("Meta file: %s" % meta_file) | 245 self._debug("Meta file: %s" % meta_file) |
| 238 | 246 |
| 239 self._processor = processor.Processor(meta_file) | 247 self._processor = processor.Processor(meta_file) |
| 240 self._expanded_file = self._create_temp_file(self._processor.contents) | 248 self._expanded_file = self._create_temp_file(self._processor.contents) |
| 241 self._debug("Expanded file: %s" % self._expanded_file) | 249 self._debug("Expanded file: %s" % self._expanded_file) |
| 242 | 250 |
| 243 errors, stderr = self.run_js_check([self._expanded_file], externs) | 251 errors, stderr = self._run_js_check([self._expanded_file], |
| 252 out_file=out_file, externs=externs) | |
| 244 | 253 |
| 245 # Filter out false-positive promise chain errors. | 254 # Filter out false-positive promise chain errors. |
| 246 # See https://github.com/google/closure-compiler/issues/715 for details. | 255 # See https://github.com/google/closure-compiler/issues/715 for details. |
| 247 errors = self._error_filter.filter(errors); | 256 errors = self._error_filter.filter(errors); |
| 248 | 257 |
| 249 output = self._format_errors(map(self._fix_up_error, errors)) | 258 output = self._format_errors(map(self._fix_up_error, errors)) |
| 250 if errors: | 259 if errors: |
| 251 prefix = "\n" if output else "" | 260 prefix = "\n" if output else "" |
| 252 self._error("Error in: %s%s%s" % (source_file, prefix, output)) | 261 self._error("Error in: %s%s%s" % (source_file, prefix, output)) |
| 253 elif output: | 262 elif output: |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 290 help="Enable strict type checking") | 299 help="Enable strict type checking") |
| 291 parser.add_argument("--success-stamp", | 300 parser.add_argument("--success-stamp", |
| 292 help="Timestamp file to update upon success") | 301 help="Timestamp file to update upon success") |
| 293 | 302 |
| 294 parser.set_defaults(single_file=True, strict=False) | 303 parser.set_defaults(single_file=True, strict=False) |
| 295 opts = parser.parse_args() | 304 opts = parser.parse_args() |
| 296 | 305 |
| 297 depends = opts.depends or [] | 306 depends = opts.depends or [] |
| 298 externs = opts.externs or set() | 307 externs = opts.externs or set() |
| 299 | 308 |
| 309 if opts.out_file: | |
| 310 out_dir = os.path.dirname(opts.out_file) | |
| 311 if not os.path.exists(out_dir): | |
| 312 os.makedirs(out_dir) | |
| 313 | |
| 300 checker = Checker(verbose=opts.verbose, strict=opts.strict) | 314 checker = Checker(verbose=opts.verbose, strict=opts.strict) |
| 301 if opts.single_file: | 315 if opts.single_file: |
| 302 for source in opts.sources: | 316 for source in opts.sources: |
| 303 depends, externs = build.inputs.resolve_recursive_dependencies( | 317 depends, externs = build.inputs.resolve_recursive_dependencies( |
| 304 source, | 318 source, |
| 305 depends, | 319 depends, |
| 306 externs) | 320 externs) |
| 307 has_errors, _ = checker.check(source, depends=depends, externs=externs) | 321 has_errors, _ = checker.check(source, out_file=opts.out_file, |
| 322 depends=depends, externs=externs) | |
| 308 if has_errors: | 323 if has_errors: |
| 309 sys.exit(1) | 324 sys.exit(1) |
| 310 | 325 |
| 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: | 326 else: |
| 318 has_errors, errors = checker.check_multiple(opts.sources) | 327 has_errors, errors = checker.check_multiple(opts.sources) |
| 319 if has_errors: | 328 if has_errors: |
| 320 print errors | 329 print errors |
| 321 sys.exit(1) | 330 sys.exit(1) |
| 322 | 331 |
| 323 if opts.success_stamp: | 332 if opts.success_stamp: |
| 324 with open(opts.success_stamp, 'w'): | 333 with open(opts.success_stamp, 'w'): |
| 325 os.utime(opts.success_stamp, None) | 334 os.utime(opts.success_stamp, None) |
| OLD | NEW |