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 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 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 179 | 179 |
| 180 Return: | 180 Return: |
| 181 The filepath of the newly created, written, and closed temporary file. | 181 The filepath of the newly created, written, and closed temporary file. |
| 182 """ | 182 """ |
| 183 with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file: | 183 with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file: |
| 184 self._temp_files.append(tmp_file.name) | 184 self._temp_files.append(tmp_file.name) |
| 185 tmp_file.write(contents) | 185 tmp_file.write(contents) |
| 186 return tmp_file.name | 186 return tmp_file.name |
| 187 | 187 |
| 188 def _run_js_check(self, sources, out_file=None, externs=None, | 188 def _run_js_check(self, sources, out_file=None, externs=None, |
| 189 closure_args=None): | 189 runner_args=None, closure_args=None): |
| 190 """Check |sources| for type errors. | 190 """Check |sources| for type errors. |
| 191 | 191 |
| 192 Args: | 192 Args: |
| 193 sources: Files to check. | 193 sources: Files to check. |
| 194 out_file: A file where the compiled output is written to. | 194 out_file: A file where the compiled output is written to. |
| 195 externs: @extern files that inform the compiler about custom globals. | 195 externs: @extern files that inform the compiler about custom globals. |
| 196 runner_args: Arguments passed to runner.jar. | |
| 196 closure_args: Arguments passed directly to the Closure compiler. | 197 closure_args: Arguments passed directly to the Closure compiler. |
| 197 | 198 |
| 198 Returns: | 199 Returns: |
| 199 (errors, stderr) A parsed list of errors (strings) found by the compiler | 200 (errors, stderr) A parsed list of errors (strings) found by the compiler |
| 200 and the raw stderr (as a string). | 201 and the raw stderr (as a string). |
| 201 """ | 202 """ |
| 202 args = ["--js=%s" % s for s in sources] | 203 args = ["--js=%s" % s for s in sources] |
| 203 | 204 |
| 204 if out_file: | 205 if out_file: |
| 205 out_dir = os.path.dirname(out_file) | 206 out_dir = os.path.dirname(out_file) |
| 206 if not os.path.exists(out_dir): | 207 if not os.path.exists(out_dir): |
| 207 os.makedirs(out_dir) | 208 os.makedirs(out_dir) |
| 208 args += ["--js_output_file=%s" % out_file] | 209 args += ["--js_output_file=%s" % out_file] |
| 209 args += ["--create_source_map=%s" % (self._MAP_FILE_FORMAT % out_file)] | 210 args += ["--create_source_map=%s" % (self._MAP_FILE_FORMAT % out_file)] |
| 210 | 211 |
| 211 args += ["--externs=%s" % e for e in externs or []] | 212 args += ["--externs=%s" % e for e in externs or []] |
| 212 | 213 |
| 213 closure_args = closure_args or [] | 214 closure_args = closure_args or [] |
| 214 closure_args += ["summary_detail_level=3"] | 215 closure_args += ["summary_detail_level=3"] |
| 215 args += ["--%s" % arg for arg in closure_args] | 216 args += ["--%s" % arg for arg in closure_args] |
| 216 | 217 |
| 217 args_file_content = " %s" % " ".join(args) | 218 args_file_content = " %s" % " ".join(args) |
| 218 self._log_debug("Args: %s" % args_file_content.strip()) | 219 self._log_debug("Args: %s" % args_file_content.strip()) |
| 219 | 220 |
| 220 args_file = self._create_temp_file(args_file_content) | 221 args_file = self._create_temp_file(args_file_content) |
| 221 self._log_debug("Args file: %s" % args_file) | 222 self._log_debug("Args file: %s" % args_file) |
| 222 | 223 |
| 223 runner_args = ["--compiler-args-file=%s" % args_file] | 224 runner_args = ["--%s" % arg for arg in runner_args or []] |
|
aberent
2016/08/02 10:52:24
Using the same variable for both the processed and
Dan Beam
2016/08/02 19:36:33
Done.
| |
| 225 runner_args += ["--compiler-args-file=%s" % args_file] | |
| 224 _, stderr = self._run_jar(self._runner_jar, runner_args) | 226 _, stderr = self._run_jar(self._runner_jar, runner_args) |
| 225 | 227 |
| 226 errors = stderr.strip().split("\n\n") | 228 errors = stderr.strip().split("\n\n") |
| 227 maybe_summary = errors.pop() | 229 maybe_summary = errors.pop() |
| 228 | 230 |
| 229 if re.search(".*error.*warning.*typed", maybe_summary): | 231 if re.search(".*error.*warning.*typed", maybe_summary): |
| 230 self._log_debug("Summary: %s" % maybe_summary) | 232 self._log_debug("Summary: %s" % maybe_summary) |
| 231 else: | 233 else: |
| 232 # Not a summary. Running the jar failed. Bail. | 234 # Not a summary. Running the jar failed. Bail. |
| 233 self._log_error(stderr) | 235 self._log_error(stderr) |
| 234 self._nuke_temp_files() | 236 self._nuke_temp_files() |
| 235 sys.exit(1) | 237 sys.exit(1) |
| 236 | 238 |
| 237 if errors and out_file: | 239 if errors and out_file: |
| 238 if os.path.exists(out_file): | 240 if os.path.exists(out_file): |
| 239 os.remove(out_file) | 241 os.remove(out_file) |
| 240 if os.path.exists(self._MAP_FILE_FORMAT % out_file): | 242 if os.path.exists(self._MAP_FILE_FORMAT % out_file): |
| 241 os.remove(self._MAP_FILE_FORMAT % out_file) | 243 os.remove(self._MAP_FILE_FORMAT % out_file) |
| 242 | 244 |
| 243 return errors, stderr | 245 return errors, stderr |
| 244 | 246 |
| 245 def check(self, source_file, out_file=None, depends=None, externs=None, | 247 def check(self, source_file, out_file=None, depends=None, externs=None, |
| 246 closure_args=None): | 248 runner_args=None, closure_args=None): |
| 247 """Closure compiler |source_file| while checking for errors. | 249 """Closure compiler |source_file| while checking for errors. |
| 248 | 250 |
| 249 Args: | 251 Args: |
| 250 source_file: A file to check. | 252 source_file: A file to check. |
| 251 out_file: A file where the compiled output is written to. | 253 out_file: A file where the compiled output is written to. |
| 252 depends: Files that |source_file| requires to run (e.g. earlier <script>). | 254 depends: Files that |source_file| requires to run (e.g. earlier <script>). |
| 253 externs: @extern files that inform the compiler about custom globals. | 255 externs: @extern files that inform the compiler about custom globals. |
| 256 runner_args: Arguments passed to runner.jar. | |
| 254 closure_args: Arguments passed directly to the Closure compiler. | 257 closure_args: Arguments passed directly to the Closure compiler. |
| 255 | 258 |
| 256 Returns: | 259 Returns: |
| 257 (found_errors, stderr) A boolean indicating whether errors were found and | 260 (found_errors, stderr) A boolean indicating whether errors were found and |
| 258 the raw Closure compiler stderr (as a string). | 261 the raw Closure compiler stderr (as a string). |
| 259 """ | 262 """ |
| 260 self._log_debug("FILE: %s" % source_file) | 263 self._log_debug("FILE: %s" % source_file) |
| 261 | 264 |
| 262 if source_file.endswith("_externs.js"): | 265 if source_file.endswith("_externs.js"): |
| 263 self._log_debug("Skipping externs: %s" % source_file) | 266 self._log_debug("Skipping externs: %s" % source_file) |
| 264 return | 267 return |
| 265 | 268 |
| 266 self._file_arg = source_file | 269 self._file_arg = source_file |
| 267 | 270 |
| 268 cwd, tmp_dir = os.getcwd(), tempfile.gettempdir() | 271 cwd, tmp_dir = os.getcwd(), tempfile.gettempdir() |
| 269 rel_path = lambda f: os.path.join(os.path.relpath(cwd, tmp_dir), f) | 272 rel_path = lambda f: os.path.join(os.path.relpath(cwd, tmp_dir), f) |
| 270 | 273 |
| 271 depends = depends or [] | 274 depends = depends or [] |
| 272 includes = [rel_path(f) for f in depends + [source_file]] | 275 includes = [rel_path(f) for f in depends + [source_file]] |
| 273 contents = ['<include src="%s">' % i for i in includes] | 276 contents = ['<include src="%s">' % i for i in includes] |
| 274 meta_file = self._create_temp_file("\n".join(contents)) | 277 meta_file = self._create_temp_file("\n".join(contents)) |
| 275 self._log_debug("Meta file: %s" % meta_file) | 278 self._log_debug("Meta file: %s" % meta_file) |
| 276 | 279 |
| 277 self._processor = processor.Processor(meta_file) | 280 self._processor = processor.Processor(meta_file) |
| 278 self._expanded_file = self._create_temp_file(self._processor.contents) | 281 self._expanded_file = self._create_temp_file(self._processor.contents) |
| 279 self._log_debug("Expanded file: %s" % self._expanded_file) | 282 self._log_debug("Expanded file: %s" % self._expanded_file) |
| 280 | 283 |
| 281 errors, stderr = self._run_js_check([self._expanded_file], | 284 errors, stderr = self._run_js_check([self._expanded_file], |
| 282 out_file=out_file, externs=externs, | 285 out_file=out_file, externs=externs, |
| 286 runner_args=runner_args, | |
| 283 closure_args=closure_args) | 287 closure_args=closure_args) |
| 284 filtered_errors = self._filter_errors(errors) | 288 filtered_errors = self._filter_errors(errors) |
| 285 cleaned_errors = map(self._clean_up_error, filtered_errors) | 289 cleaned_errors = map(self._clean_up_error, filtered_errors) |
| 286 output = self._format_errors(cleaned_errors) | 290 output = self._format_errors(cleaned_errors) |
| 287 | 291 |
| 288 if cleaned_errors: | 292 if cleaned_errors: |
| 289 prefix = "\n" if output else "" | 293 prefix = "\n" if output else "" |
| 290 self._log_error("Error in: %s%s%s" % (source_file, prefix, output)) | 294 self._log_error("Error in: %s%s%s" % (source_file, prefix, output)) |
| 291 elif output: | 295 elif output: |
| 292 self._log_debug("Output: %s" % output) | 296 self._log_debug("Output: %s" % output) |
| 293 | 297 |
| 294 self._nuke_temp_files() | 298 self._nuke_temp_files() |
| 295 return bool(cleaned_errors), stderr | 299 return bool(cleaned_errors), stderr |
| 296 | 300 |
| 297 def check_multiple(self, sources, out_file=None, externs=None, | 301 def check_multiple(self, sources, out_file=None, externs=None, |
| 298 closure_args=None): | 302 runner_args=None, closure_args=None): |
| 299 """Closure compile a set of files and check for errors. | 303 """Closure compile a set of files and check for errors. |
| 300 | 304 |
| 301 Args: | 305 Args: |
| 302 sources: An array of files to check. | 306 sources: An array of files to check. |
| 303 out_file: A file where the compiled output is written to. | 307 out_file: A file where the compiled output is written to. |
| 304 externs: @extern files that inform the compiler about custom globals. | 308 externs: @extern files that inform the compiler about custom globals. |
| 309 runner_args: Arguments passed to runner.jar. | |
| 305 closure_args: Arguments passed directly to the Closure compiler. | 310 closure_args: Arguments passed directly to the Closure compiler. |
| 306 | 311 |
| 307 Returns: | 312 Returns: |
| 308 (found_errors, stderr) A boolean indicating whether errors were found and | 313 (found_errors, stderr) A boolean indicating whether errors were found and |
| 309 the raw Closure Compiler stderr (as a string). | 314 the raw Closure Compiler stderr (as a string). |
| 310 """ | 315 """ |
| 311 errors, stderr = self._run_js_check(sources, out_file=out_file, | 316 errors, stderr = self._run_js_check(sources, out_file=out_file, |
| 312 externs=externs, | 317 externs=externs, |
| 318 runner_args=runner_args, | |
| 313 closure_args=closure_args) | 319 closure_args=closure_args) |
| 314 self._nuke_temp_files() | 320 self._nuke_temp_files() |
| 315 return bool(errors), stderr | 321 return bool(errors), stderr |
| 316 | 322 |
| 317 | 323 |
| 318 if __name__ == "__main__": | 324 if __name__ == "__main__": |
| 319 parser = argparse.ArgumentParser( | 325 parser = argparse.ArgumentParser( |
| 320 description="Typecheck JavaScript using Closure compiler") | 326 description="Typecheck JavaScript using Closure compiler") |
| 321 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE, | 327 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE, |
| 322 help="Path to a source file to typecheck") | 328 help="Path to a source file to typecheck") |
| 323 single_file_group = parser.add_mutually_exclusive_group() | 329 single_file_group = parser.add_mutually_exclusive_group() |
| 324 single_file_group.add_argument("--single-file", dest="single_file", | 330 single_file_group.add_argument("--single-file", dest="single_file", |
| 325 action="store_true", | 331 action="store_true", |
| 326 help="Process each source file individually") | 332 help="Process each source file individually") |
| 327 # TODO(twellington): remove --no-single-file and use len(opts.sources). | 333 # TODO(twellington): remove --no-single-file and use len(opts.sources). |
| 328 single_file_group.add_argument("--no-single-file", dest="single_file", | 334 single_file_group.add_argument("--no-single-file", dest="single_file", |
| 329 action="store_false", | 335 action="store_false", |
| 330 help="Process all source files as a group") | 336 help="Process all source files as a group") |
| 331 parser.add_argument("-d", "--depends", nargs=argparse.ZERO_OR_MORE) | 337 parser.add_argument("-d", "--depends", nargs=argparse.ZERO_OR_MORE) |
| 332 parser.add_argument("-e", "--externs", nargs=argparse.ZERO_OR_MORE) | 338 parser.add_argument("-e", "--externs", nargs=argparse.ZERO_OR_MORE) |
| 333 parser.add_argument("-o", "--out-file", dest="out_file", | 339 parser.add_argument("-o", "--out_file", dest="out_file", |
|
aberent
2016/08/02 10:52:24
Why the change? A '-' is conventional for multi-wo
Dan Beam
2016/08/02 19:36:33
switching to _ consistently everywhere to:
a) be
| |
| 334 help="A file where the compiled output is written to") | 340 help="A file where the compiled output is written to") |
| 335 parser.add_argument("-c", "--closure-args", dest="closure_args", | 341 parser.add_argument("-r", "--runner_args", dest="runner_args", |
| 342 nargs=argparse.ZERO_OR_MORE, | |
| 343 help="Arguments passed to runner.jar") | |
| 344 parser.add_argument("-c", "--closure_args", dest="closure_args", | |
|
aberent
2016/08/02 10:52:24
Ditto.
Dan Beam
2016/08/02 19:36:33
Done.
| |
| 336 nargs=argparse.ZERO_OR_MORE, | 345 nargs=argparse.ZERO_OR_MORE, |
| 337 help="Arguments passed directly to the Closure compiler") | 346 help="Arguments passed directly to the Closure compiler") |
| 338 parser.add_argument("-v", "--verbose", action="store_true", | 347 parser.add_argument("-v", "--verbose", action="store_true", |
| 339 help="Show more information as this script runs") | 348 help="Show more information as this script runs") |
| 340 | 349 |
| 341 parser.set_defaults(single_file=True, strict=False) | 350 parser.set_defaults(single_file=True, strict=False) |
| 342 opts = parser.parse_args() | 351 opts = parser.parse_args() |
| 343 | 352 |
| 344 depends = opts.depends or [] | 353 depends = opts.depends or [] |
| 345 # TODO(devlin): should we run normpath() on this first and/or do this for | 354 # TODO(devlin): should we run normpath() on this first and/or do this for |
| 346 # depends as well? | 355 # depends as well? |
| 347 externs = set(opts.externs or []) | 356 externs = set(opts.externs or []) |
| 348 sources = set(opts.sources) | 357 sources = set(opts.sources) |
| 349 | 358 |
| 350 externs.add(os.path.join(_CURRENT_DIR, "externs", "polymer-1.0.js")) | 359 externs.add(os.path.join(_CURRENT_DIR, "externs", "polymer-1.0.js")) |
| 351 | 360 |
| 352 checker = Checker(verbose=opts.verbose, strict=opts.strict) | 361 checker = Checker(verbose=opts.verbose, strict=opts.strict) |
| 353 if opts.single_file: | 362 if opts.single_file: |
| 354 for source in sources: | 363 for source in sources: |
| 355 # Normalize source to the current directory. | 364 # Normalize source to the current directory. |
| 356 source = os.path.normpath(os.path.join(os.getcwd(), source)) | 365 source = os.path.normpath(os.path.join(os.getcwd(), source)) |
| 357 depends, externs = build.inputs.resolve_recursive_dependencies( | 366 depends, externs = build.inputs.resolve_recursive_dependencies( |
| 358 source, depends, externs) | 367 source, depends, externs) |
| 359 | 368 |
| 360 found_errors, _ = checker.check(source, out_file=opts.out_file, | 369 found_errors, _ = checker.check(source, out_file=opts.out_file, |
| 361 depends=depends, externs=externs, | 370 depends=depends, externs=externs, |
| 371 runner_args=opts.runner_args, | |
| 362 closure_args=opts.closure_args) | 372 closure_args=opts.closure_args) |
| 363 if found_errors: | 373 if found_errors: |
| 364 sys.exit(1) | 374 sys.exit(1) |
| 365 else: | 375 else: |
| 366 found_errors, stderr = checker.check_multiple( | 376 found_errors, stderr = checker.check_multiple( |
| 367 sources, | 377 sources, |
| 368 out_file=opts.out_file, | 378 out_file=opts.out_file, |
| 369 externs=externs, | 379 externs=externs, |
| 380 runner_args=opts.runner_args, | |
| 370 closure_args=opts.closure_args) | 381 closure_args=opts.closure_args) |
| 371 if found_errors: | 382 if found_errors: |
| 372 print stderr | 383 print stderr |
| 373 sys.exit(1) | 384 sys.exit(1) |
| OLD | NEW |