Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: third_party/closure_compiler/compile.py

Issue 1152583011: Refactor compile_js.gypi to support script_args and closure_args (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: compile_test.py cleanup Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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 build.inputs 16 import build.inputs
17 import processor 17 import processor
18 import error_filter 18 import error_filter
19 19
20 20
21 _CURRENT_DIR = os.path.join(os.path.dirname(__file__)) 21 _CURRENT_DIR = os.path.join(os.path.dirname(__file__))
22 22
23 23
24 class Checker(object): 24 class Checker(object):
25 """Runs the Closure compiler on given source files to typecheck them 25 """Runs the Closure compiler on given source files to typecheck them
26 and produce minified output.""" 26 and produce minified output."""
27 27
28 _COMMON_JSCOMP_ERRORS = [
29 "accessControls",
30 "ambiguousFunctionDecl",
31 "checkStructDictInheritance",
32 "checkTypes",
33 "checkVars",
34 "constantProperty",
35 "deprecated",
36 "externsValidation",
37 "globalThis",
38 "invalidCasts",
39 "missingProperties",
40 "missingReturn",
41 "nonStandardJsDocs",
42 "suspiciousCode",
43 "undefinedNames",
44 "undefinedVars",
45 "unknownDefines",
46 "uselessCode",
47 "visibility",
48 ]
49
50 # Extra @jsDocAnnotations used when compiling polymer code.
51 _POLYMER_EXTRA_ANNOTATIONS = [
52 "attribute",
53 "status",
54 "element",
55 "homepage",
56 "submodule",
57 "group",
58 ]
59
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
69 ] + [
70 "--extra_annotation_name=%s" % a for a in _POLYMER_EXTRA_ANNOTATIONS
71 ]
72
73 # These are the extra flags used when compiling in strict mode.
74 # Flags that are normally disabled are turned on for strict mode.
75 _STRICT_CLOSURE_ARGS = [
76 "--jscomp_error=reportUnknownTypes",
77 "--jscomp_error=duplicate",
78 "--jscomp_error=misplacedTypeAnnotation",
79 ]
80
81 _DISABLED_CLOSURE_ARGS = [
82 # TODO(dbeam): happens when the same file is <include>d multiple times.
83 "--jscomp_off=duplicate",
84 # TODO(fukino): happens when cr.defineProperty() has a type annotation.
85 # Avoiding parse-time warnings needs 2 pass compiling. crbug.com/421562.
86 "--jscomp_off=misplacedTypeAnnotation",
87 ]
88
89 _JAR_COMMAND = [ 28 _JAR_COMMAND = [
90 "java", 29 "java",
91 "-jar", 30 "-jar",
92 "-Xms1024m", 31 "-Xms1024m",
93 "-client", 32 "-client",
94 "-XX:+TieredCompilation" 33 "-XX:+TieredCompilation"
95 ] 34 ]
96 35
97 _MAP_FILE_FORMAT = "%s.map" 36 _MAP_FILE_FORMAT = "%s.map"
98 37
(...skipping 29 matching lines...) Expand all
128 print "(INFO) %s" % msg 67 print "(INFO) %s" % msg
129 68
130 def _log_error(self, msg): 69 def _log_error(self, msg):
131 """Logs |msg| to stderr regardless of --flags. 70 """Logs |msg| to stderr regardless of --flags.
132 71
133 Args: 72 Args:
134 msg: An error message to log. 73 msg: An error message to log.
135 """ 74 """
136 print >> sys.stderr, "(ERROR) %s" % msg 75 print >> sys.stderr, "(ERROR) %s" % msg
137 76
138 def _common_args(self):
139 """Returns an array of the common closure compiler args."""
140 if self._strict:
141 return self._COMMON_CLOSURE_ARGS + self._STRICT_CLOSURE_ARGS
142 return self._COMMON_CLOSURE_ARGS + self._DISABLED_CLOSURE_ARGS
143
144 def _run_jar(self, jar, args): 77 def _run_jar(self, jar, args):
145 """Runs a .jar from the command line with arguments. 78 """Runs a .jar from the command line with arguments.
146 79
147 Args: 80 Args:
148 jar: A file path to a .jar file 81 jar: A file path to a .jar file
149 args: A list of command line arguments to be passed when running the .jar. 82 args: A list of command line arguments to be passed when running the .jar.
150 83
151 Return: 84 Return:
152 (exit_code, stderr) The exit code of the command (e.g. 0 for success) and 85 (exit_code, stderr) The exit code of the command (e.g. 0 for success) and
153 the stderr collected while running |jar| (as a string). 86 the stderr collected while running |jar| (as a string).
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
246 179
247 Return: 180 Return:
248 The filepath of the newly created, written, and closed temporary file. 181 The filepath of the newly created, written, and closed temporary file.
249 """ 182 """
250 with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file: 183 with tempfile.NamedTemporaryFile(mode="wt", delete=False) as tmp_file:
251 self._temp_files.append(tmp_file.name) 184 self._temp_files.append(tmp_file.name)
252 tmp_file.write(contents) 185 tmp_file.write(contents)
253 return tmp_file.name 186 return tmp_file.name
254 187
255 def _run_js_check(self, sources, out_file=None, externs=None, 188 def _run_js_check(self, sources, out_file=None, externs=None,
256 output_wrapper=None): 189 closure_args=None):
257 """Check |sources| for type errors. 190 """Check |sources| for type errors.
258 191
259 Args: 192 Args:
260 sources: Files to check. 193 sources: Files to check.
261 out_file: A file where the compiled output is written to. 194 out_file: A file where the compiled output is written to.
262 externs: @extern files that inform the compiler about custom globals. 195 externs: @extern files that inform the compiler about custom globals.
263 output_wrapper: Wraps output into this string at the place denoted by the 196 closure_args: Arguments passed directly to the Closure compiler.
264 marker token %output%.
265 197
266 Returns: 198 Returns:
267 (errors, stderr) A parsed list of errors (strings) found by the compiler 199 (errors, stderr) A parsed list of errors (strings) found by the compiler
268 and the raw stderr (as a string). 200 and the raw stderr (as a string).
269 """ 201 """
270 args = ["--js=%s" % s for s in sources] 202 args = ["--js=%s" % s for s in sources]
271 203
272 if out_file: 204 if out_file:
273 out_dir = os.path.dirname(out_file) 205 out_dir = os.path.dirname(out_file)
274 if not os.path.exists(out_dir): 206 if not os.path.exists(out_dir):
275 os.makedirs(out_dir) 207 os.makedirs(out_dir)
276 args += ["--js_output_file=%s" % out_file] 208 args += ["--js_output_file=%s" % out_file]
277 args += ["--create_source_map=%s" % (self._MAP_FILE_FORMAT % out_file)] 209 args += ["--create_source_map=%s" % (self._MAP_FILE_FORMAT % out_file)]
278 210
279 if externs: 211 if externs:
280 args += ["--externs=%s" % e for e in externs] 212 args += ["--externs=%s" % e for e in externs]
281 213
282 if output_wrapper: 214 if closure_args:
283 args += ['--output_wrapper="%s"' % output_wrapper] 215 for arg in closure_args:
216 args += ["--%s" % arg]
284 217
285 args_file_content = " %s" % " ".join(self._common_args() + args) 218 args_file_content = " %s" % " ".join(args)
286 self._log_debug("Args: %s" % args_file_content.strip()) 219 self._log_debug("Args: %s" % args_file_content.strip())
287 220
288 args_file = self._create_temp_file(args_file_content) 221 args_file = self._create_temp_file(args_file_content)
289 self._log_debug("Args file: %s" % args_file) 222 self._log_debug("Args file: %s" % args_file)
290 223
291 runner_args = ["--compiler-args-file=%s" % args_file] 224 runner_args = ["--compiler-args-file=%s" % args_file]
292 _, stderr = self._run_jar(self._runner_jar, runner_args) 225 _, stderr = self._run_jar(self._runner_jar, runner_args)
293 226
294 errors = stderr.strip().split("\n\n") 227 errors = stderr.strip().split("\n\n")
295 maybe_summary = errors.pop() 228 maybe_summary = errors.pop()
296 229
297 if re.search(".*error.*warning.*typed", maybe_summary): 230 if re.search(".*error.*warning.*typed", maybe_summary):
298 self._log_debug("Summary: %s" % maybe_summary) 231 self._log_debug("Summary: %s" % maybe_summary)
299 else: 232 else:
300 # Not a summary. Running the jar failed. Bail. 233 # Not a summary. Running the jar failed. Bail.
301 self._log_error(stderr) 234 self._log_error(stderr)
302 self._nuke_temp_files() 235 self._nuke_temp_files()
303 sys.exit(1) 236 sys.exit(1)
304 237
305 if errors and out_file: 238 if errors and out_file:
306 if os.path.exists(out_file): 239 if os.path.exists(out_file):
307 os.remove(out_file) 240 os.remove(out_file)
308 if os.path.exists(self._MAP_FILE_FORMAT % out_file): 241 if os.path.exists(self._MAP_FILE_FORMAT % out_file):
309 os.remove(self._MAP_FILE_FORMAT % out_file) 242 os.remove(self._MAP_FILE_FORMAT % out_file)
310 243
311 return errors, stderr 244 return errors, stderr
312 245
313 def check(self, source_file, out_file=None, depends=None, externs=None, 246 def check(self, source_file, out_file=None, depends=None, externs=None,
314 output_wrapper=None): 247 closure_args=None):
315 """Closure compiler |source_file| while checking for errors. 248 """Closure compiler |source_file| while checking for errors.
316 249
317 Args: 250 Args:
318 source_file: A file to check. 251 source_file: A file to check.
319 out_file: A file where the compiled output is written to. 252 out_file: A file where the compiled output is written to.
320 depends: Files that |source_file| requires to run (e.g. earlier <script>). 253 depends: Files that |source_file| requires to run (e.g. earlier <script>).
321 externs: @extern files that inform the compiler about custom globals. 254 externs: @extern files that inform the compiler about custom globals.
322 output_wrapper: Wraps output into this string at the place denoted by the 255 closure_args: Arguments passed directly to the Closure compiler.
323 marker token %output%.
324 256
325 Returns: 257 Returns:
326 (found_errors, stderr) A boolean indicating whether errors were found and 258 (found_errors, stderr) A boolean indicating whether errors were found and
327 the raw Closure compiler stderr (as a string). 259 the raw Closure compiler stderr (as a string).
328 """ 260 """
329 self._log_debug("FILE: %s" % source_file) 261 self._log_debug("FILE: %s" % source_file)
330 262
331 if source_file.endswith("_externs.js"): 263 if source_file.endswith("_externs.js"):
332 self._log_debug("Skipping externs: %s" % source_file) 264 self._log_debug("Skipping externs: %s" % source_file)
333 return 265 return
334 266
335 self._file_arg = source_file 267 self._file_arg = source_file
336 268
337 cwd, tmp_dir = os.getcwd(), tempfile.gettempdir() 269 cwd, tmp_dir = os.getcwd(), tempfile.gettempdir()
338 rel_path = lambda f: os.path.join(os.path.relpath(cwd, tmp_dir), f) 270 rel_path = lambda f: os.path.join(os.path.relpath(cwd, tmp_dir), f)
339 271
340 depends = depends or [] 272 depends = depends or []
341 includes = [rel_path(f) for f in depends + [source_file]] 273 includes = [rel_path(f) for f in depends + [source_file]]
342 contents = ['<include src="%s">' % i for i in includes] 274 contents = ['<include src="%s">' % i for i in includes]
343 meta_file = self._create_temp_file("\n".join(contents)) 275 meta_file = self._create_temp_file("\n".join(contents))
344 self._log_debug("Meta file: %s" % meta_file) 276 self._log_debug("Meta file: %s" % meta_file)
345 277
346 self._processor = processor.Processor(meta_file) 278 self._processor = processor.Processor(meta_file)
347 self._expanded_file = self._create_temp_file(self._processor.contents) 279 self._expanded_file = self._create_temp_file(self._processor.contents)
348 self._log_debug("Expanded file: %s" % self._expanded_file) 280 self._log_debug("Expanded file: %s" % self._expanded_file)
349 281
350 errors, stderr = self._run_js_check([self._expanded_file], 282 errors, stderr = self._run_js_check([self._expanded_file],
351 out_file=out_file, externs=externs, 283 out_file=out_file, externs=externs,
352 output_wrapper=output_wrapper) 284 closure_args=closure_args)
353 filtered_errors = self._filter_errors(errors) 285 filtered_errors = self._filter_errors(errors)
354 cleaned_errors = map(self._clean_up_error, filtered_errors) 286 cleaned_errors = map(self._clean_up_error, filtered_errors)
355 output = self._format_errors(cleaned_errors) 287 output = self._format_errors(cleaned_errors)
356 288
357 if cleaned_errors: 289 if cleaned_errors:
358 prefix = "\n" if output else "" 290 prefix = "\n" if output else ""
359 self._log_error("Error in: %s%s%s" % (source_file, prefix, output)) 291 self._log_error("Error in: %s%s%s" % (source_file, prefix, output))
360 elif output: 292 elif output:
361 self._log_debug("Output: %s" % output) 293 self._log_debug("Output: %s" % output)
362 294
363 self._nuke_temp_files() 295 self._nuke_temp_files()
364 return bool(cleaned_errors), stderr 296 return bool(cleaned_errors), stderr
365 297
366 def check_multiple(self, sources, out_file=None, output_wrapper=None, 298 def check_multiple(self, sources, out_file=None, externs=None,
367 externs=None): 299 closure_args=None):
368 """Closure compile a set of files and check for errors. 300 """Closure compile a set of files and check for errors.
369 301
370 Args: 302 Args:
371 sources: An array of files to check. 303 sources: An array of files to check.
372 out_file: A file where the compiled output is written to. 304 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
374 marker token %output%.
375 externs: @extern files that inform the compiler about custom globals. 305 externs: @extern files that inform the compiler about custom globals.
306 closure_args: Arguments passed directly to the Closure compiler.
376 307
377 Returns: 308 Returns:
378 (found_errors, stderr) A boolean indicating whether errors were found and 309 (found_errors, stderr) A boolean indicating whether errors were found and
379 the raw Closure Compiler stderr (as a string). 310 the raw Closure Compiler stderr (as a string).
380 """ 311 """
381 errors, stderr = self._run_js_check(sources, out_file=out_file, 312 errors, stderr = self._run_js_check(sources, out_file=out_file,
382 output_wrapper=output_wrapper, 313 externs=externs,
383 externs=externs) 314 closure_args=closure_args)
384 self._nuke_temp_files() 315 self._nuke_temp_files()
385 return bool(errors), stderr 316 return bool(errors), stderr
386 317
387 318
388 if __name__ == "__main__": 319 if __name__ == "__main__":
389 parser = argparse.ArgumentParser( 320 parser = argparse.ArgumentParser(
390 description="Typecheck JavaScript using Closure compiler") 321 description="Typecheck JavaScript using Closure compiler")
391 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE, 322 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE,
392 help="Path to a source file to typecheck") 323 help="Path to a source file to typecheck")
393 single_file_group = parser.add_mutually_exclusive_group() 324 single_file_group = parser.add_mutually_exclusive_group()
394 single_file_group.add_argument("--single-file", dest="single_file", 325 single_file_group.add_argument("--single-file", dest="single_file",
395 action="store_true", 326 action="store_true",
396 help="Process each source file individually") 327 help="Process each source file individually")
397 single_file_group.add_argument("--no-single-file", dest="single_file", 328 single_file_group.add_argument("--no-single-file", dest="single_file",
398 action="store_false", 329 action="store_false",
399 help="Process all source files as a group") 330 help="Process all source files as a group")
400 parser.add_argument("-d", "--depends", nargs=argparse.ZERO_OR_MORE) 331 parser.add_argument("-d", "--depends", nargs=argparse.ZERO_OR_MORE)
401 parser.add_argument("-e", "--externs", nargs=argparse.ZERO_OR_MORE) 332 parser.add_argument("-e", "--externs", nargs=argparse.ZERO_OR_MORE)
402 parser.add_argument("-o", "--out_file", 333 parser.add_argument("-o", "--out-file", dest="out_file",
403 help="A file where the compiled output is written to") 334 help="A file where the compiled output is written to")
404 parser.add_argument("-w", "--output_wrapper", 335 parser.add_argument("-c", "--closure-args", dest="closure_args",
405 help="Wraps output into this string at the place" 336 nargs=argparse.ZERO_OR_MORE,
406 + " denoted by the marker token %output%") 337 help="Arguments passed directly to the Closure compiler")
407 parser.add_argument("-v", "--verbose", action="store_true", 338 parser.add_argument("-v", "--verbose", action="store_true",
408 help="Show more information as this script runs") 339 help="Show more information as this script runs")
409 parser.add_argument("--strict", action="store_true",
410 help="Enable strict type checking")
411 parser.add_argument("--success-stamp",
412 help="Timestamp file to update upon success")
413 340
414 parser.set_defaults(single_file=True, strict=False) 341 parser.set_defaults(single_file=True, strict=False)
415 opts = parser.parse_args() 342 opts = parser.parse_args()
416 343
417 depends = opts.depends or [] 344 depends = opts.depends or []
418 externs = set(opts.externs or []) 345 externs = set(opts.externs or [])
419 346
420 polymer_externs = os.path.join(os.path.dirname(_CURRENT_DIR), 'polymer', 347 polymer_externs = os.path.join(os.path.dirname(_CURRENT_DIR), 'polymer',
421 'v1_0', 'components-chromium', 348 'v1_0', 'components-chromium',
422 'polymer-externs', 'polymer.externs.js') 349 'polymer-externs', 'polymer.externs.js')
423 externs.add(polymer_externs) 350 externs.add(polymer_externs)
424 351
425 checker = Checker(verbose=opts.verbose, strict=opts.strict) 352 checker = Checker(verbose=opts.verbose, strict=opts.strict)
426 if opts.single_file: 353 if opts.single_file:
427 for source in opts.sources: 354 for source in opts.sources:
428 depends, externs = build.inputs.resolve_recursive_dependencies( 355 depends, externs = build.inputs.resolve_recursive_dependencies(
429 source, depends, externs) 356 source, depends, externs)
430 found_errors, _ = checker.check(source, out_file=opts.out_file, 357 found_errors, _ = checker.check(source, out_file=opts.out_file,
431 depends=depends, externs=externs, 358 depends=depends, externs=externs,
432 output_wrapper=opts.output_wrapper) 359 closure_args=opts.closure_args)
433 if found_errors: 360 if found_errors:
434 sys.exit(1) 361 sys.exit(1)
435 else: 362 else:
436 found_errors, stderr = checker.check_multiple( 363 found_errors, stderr = checker.check_multiple(
437 opts.sources, 364 opts.sources,
438 out_file=opts.out_file, 365 out_file=opts.out_file,
439 output_wrapper=opts.output_wrapper, 366 externs=externs,
440 externs=externs) 367 closure_args=opts.closure_args)
441 if found_errors: 368 if found_errors:
442 print stderr 369 print stderr
443 sys.exit(1) 370 sys.exit(1)
444
445 if opts.success_stamp:
446 with open(opts.success_stamp, "w"):
447 os.utime(opts.success_stamp, None)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698