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

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: Added remoting/compile_js.gypi and decoupled target_name from source_files 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 args += closure_args
283 args += ['--output_wrapper="%s"' % output_wrapper]
284 215
285 args_file_content = " %s" % " ".join(self._common_args() + args) 216 args_file_content = " %s" % " ".join(args)
286 self._log_debug("Args: %s" % args_file_content.strip()) 217 self._log_debug("Args: %s" % args_file_content.strip())
287 218
288 args_file = self._create_temp_file(args_file_content) 219 args_file = self._create_temp_file(args_file_content)
289 self._log_debug("Args file: %s" % args_file) 220 self._log_debug("Args file: %s" % args_file)
290 221
291 runner_args = ["--compiler-args-file=%s" % args_file] 222 runner_args = ["--compiler-args-file=%s" % args_file]
292 _, stderr = self._run_jar(self._runner_jar, runner_args) 223 _, stderr = self._run_jar(self._runner_jar, runner_args)
293 224
294 errors = stderr.strip().split("\n\n") 225 errors = stderr.strip().split("\n\n")
295 maybe_summary = errors.pop() 226 maybe_summary = errors.pop()
296 227
297 if re.search(".*error.*warning.*typed", maybe_summary): 228 if re.search(".*error.*warning.*typed", maybe_summary):
298 self._log_debug("Summary: %s" % maybe_summary) 229 self._log_debug("Summary: %s" % maybe_summary)
299 else: 230 else:
300 # Not a summary. Running the jar failed. Bail. 231 # Not a summary. Running the jar failed. Bail.
301 self._log_error(stderr) 232 self._log_error(stderr)
302 self._nuke_temp_files() 233 self._nuke_temp_files()
303 sys.exit(1) 234 sys.exit(1)
304 235
305 if errors and out_file: 236 if errors and out_file:
306 if os.path.exists(out_file): 237 if os.path.exists(out_file):
307 os.remove(out_file) 238 os.remove(out_file)
308 if os.path.exists(self._MAP_FILE_FORMAT % out_file): 239 if os.path.exists(self._MAP_FILE_FORMAT % out_file):
309 os.remove(self._MAP_FILE_FORMAT % out_file) 240 os.remove(self._MAP_FILE_FORMAT % out_file)
310 241
311 return errors, stderr 242 return errors, stderr
312 243
313 def check(self, source_file, out_file=None, depends=None, externs=None, 244 def check(self, source_file, out_file=None, depends=None, externs=None,
314 output_wrapper=None): 245 closure_args=None):
315 """Closure compiler |source_file| while checking for errors. 246 """Closure compiler |source_file| while checking for errors.
316 247
317 Args: 248 Args:
318 source_file: A file to check. 249 source_file: A file to check.
319 out_file: A file where the compiled output is written to. 250 out_file: A file where the compiled output is written to.
320 depends: Files that |source_file| requires to run (e.g. earlier <script>). 251 depends: Files that |source_file| requires to run (e.g. earlier <script>).
321 externs: @extern files that inform the compiler about custom globals. 252 externs: @extern files that inform the compiler about custom globals.
322 output_wrapper: Wraps output into this string at the place denoted by the 253 closure_args: Arguments passed directly to the closure compiler.
323 marker token %output%.
324 254
325 Returns: 255 Returns:
326 (found_errors, stderr) A boolean indicating whether errors were found and 256 (found_errors, stderr) A boolean indicating whether errors were found and
327 the raw Closure compiler stderr (as a string). 257 the raw Closure compiler stderr (as a string).
328 """ 258 """
329 self._log_debug("FILE: %s" % source_file) 259 self._log_debug("FILE: %s" % source_file)
330 260
331 if source_file.endswith("_externs.js"): 261 if source_file.endswith("_externs.js"):
332 self._log_debug("Skipping externs: %s" % source_file) 262 self._log_debug("Skipping externs: %s" % source_file)
333 return 263 return
334 264
335 self._file_arg = source_file 265 self._file_arg = source_file
336 266
337 cwd, tmp_dir = os.getcwd(), tempfile.gettempdir() 267 cwd, tmp_dir = os.getcwd(), tempfile.gettempdir()
338 rel_path = lambda f: os.path.join(os.path.relpath(cwd, tmp_dir), f) 268 rel_path = lambda f: os.path.join(os.path.relpath(cwd, tmp_dir), f)
339 269
340 depends = depends or [] 270 depends = depends or []
341 includes = [rel_path(f) for f in depends + [source_file]] 271 includes = [rel_path(f) for f in depends + [source_file]]
342 contents = ['<include src="%s">' % i for i in includes] 272 contents = ['<include src="%s">' % i for i in includes]
343 meta_file = self._create_temp_file("\n".join(contents)) 273 meta_file = self._create_temp_file("\n".join(contents))
344 self._log_debug("Meta file: %s" % meta_file) 274 self._log_debug("Meta file: %s" % meta_file)
345 275
346 self._processor = processor.Processor(meta_file) 276 self._processor = processor.Processor(meta_file)
347 self._expanded_file = self._create_temp_file(self._processor.contents) 277 self._expanded_file = self._create_temp_file(self._processor.contents)
348 self._log_debug("Expanded file: %s" % self._expanded_file) 278 self._log_debug("Expanded file: %s" % self._expanded_file)
349 279
350 errors, stderr = self._run_js_check([self._expanded_file], 280 errors, stderr = self._run_js_check([self._expanded_file],
351 out_file=out_file, externs=externs, 281 out_file=out_file, externs=externs,
352 output_wrapper=output_wrapper) 282 closure_args=closure_args)
353 filtered_errors = self._filter_errors(errors) 283 filtered_errors = self._filter_errors(errors)
354 cleaned_errors = map(self._clean_up_error, filtered_errors) 284 cleaned_errors = map(self._clean_up_error, filtered_errors)
355 output = self._format_errors(cleaned_errors) 285 output = self._format_errors(cleaned_errors)
356 286
357 if cleaned_errors: 287 if cleaned_errors:
358 prefix = "\n" if output else "" 288 prefix = "\n" if output else ""
359 self._log_error("Error in: %s%s%s" % (source_file, prefix, output)) 289 self._log_error("Error in: %s%s%s" % (source_file, prefix, output))
360 elif output: 290 elif output:
361 self._log_debug("Output: %s" % output) 291 self._log_debug("Output: %s" % output)
362 292
363 self._nuke_temp_files() 293 self._nuke_temp_files()
364 return bool(cleaned_errors), stderr 294 return bool(cleaned_errors), stderr
365 295
366 def check_multiple(self, sources, out_file=None, output_wrapper=None, 296 def check_multiple(self, sources, out_file=None, closure_args=None,
367 externs=None): 297 externs=None):
368 """Closure compile a set of files and check for errors. 298 """Closure compile a set of files and check for errors.
369 299
370 Args: 300 Args:
371 sources: An array of files to check. 301 sources: An array of files to check.
372 out_file: A file where the compiled output is written to. 302 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 303 closure_args: Arguments passed directly to the closure compiler.
374 marker token %output%.
375 externs: @extern files that inform the compiler about custom globals. 304 externs: @extern files that inform the compiler about custom globals.
376 305
377 Returns: 306 Returns:
378 (found_errors, stderr) A boolean indicating whether errors were found and 307 (found_errors, stderr) A boolean indicating whether errors were found and
379 the raw Closure Compiler stderr (as a string). 308 the raw Closure Compiler stderr (as a string).
380 """ 309 """
381 errors, stderr = self._run_js_check(sources, out_file=out_file, 310 errors, stderr = self._run_js_check(sources, out_file=out_file,
382 output_wrapper=output_wrapper, 311 closure_args=closure_args,
383 externs=externs) 312 externs=externs)
384 self._nuke_temp_files() 313 self._nuke_temp_files()
385 return bool(errors), stderr 314 return bool(errors), stderr
386 315
387 316
388 if __name__ == "__main__": 317 if __name__ == "__main__":
389 parser = argparse.ArgumentParser( 318 parser = argparse.ArgumentParser(
390 description="Typecheck JavaScript using Closure compiler") 319 description="Typecheck JavaScript using Closure compiler")
391 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE, 320 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE,
392 help="Path to a source file to typecheck") 321 help="Path to a source file to typecheck")
393 single_file_group = parser.add_mutually_exclusive_group() 322 single_file_group = parser.add_mutually_exclusive_group()
394 single_file_group.add_argument("--single-file", dest="single_file", 323 single_file_group.add_argument("--single-file", dest="single_file",
395 action="store_true", 324 action="store_true",
396 help="Process each source file individually") 325 help="Process each source file individually")
397 single_file_group.add_argument("--no-single-file", dest="single_file", 326 single_file_group.add_argument("--no-single-file", dest="single_file",
398 action="store_false", 327 action="store_false",
399 help="Process all source files as a group") 328 help="Process all source files as a group")
400 parser.add_argument("-d", "--depends", nargs=argparse.ZERO_OR_MORE) 329 parser.add_argument("-d", "--depends", nargs=argparse.ZERO_OR_MORE)
401 parser.add_argument("-e", "--externs", nargs=argparse.ZERO_OR_MORE) 330 parser.add_argument("-e", "--externs", nargs=argparse.ZERO_OR_MORE)
402 parser.add_argument("-o", "--out_file", 331 parser.add_argument("-o", "--out_file",
403 help="A file where the compiled output is written to") 332 help="A file where the compiled output is written to")
404 parser.add_argument("-w", "--output_wrapper", 333 parser.add_argument("-c", "--closure_args",
Dan Beam 2015/06/01 22:57:39 i'd say all of these args should be --dash-form fo
Theresa 2015/06/02 22:02:12 Done.
405 help="Wraps output into this string at the place" 334 help="Arguments passed directly to the closure compiler")
406 + " denoted by the marker token %output%")
407 parser.add_argument("-v", "--verbose", action="store_true", 335 parser.add_argument("-v", "--verbose", action="store_true",
408 help="Show more information as this script runs") 336 help="Show more information as this script runs")
409 parser.add_argument("--strict", action="store_true", 337 parser.add_argument("--strict", action="store_true",
Dan Beam 2015/06/01 22:57:39 kill --strict
Theresa 2015/06/02 22:02:12 Done.
410 help="Enable strict type checking") 338 help="Enable strict type checking")
411 parser.add_argument("--success-stamp", 339 parser.add_argument("--success-stamp",
412 help="Timestamp file to update upon success") 340 help="Timestamp file to update upon success")
413 341
414 parser.set_defaults(single_file=True, strict=False) 342 parser.set_defaults(single_file=True, strict=False)
415 opts = parser.parse_args() 343 opts = parser.parse_args()
416 344
417 depends = opts.depends or [] 345 depends = opts.depends or []
418 externs = set(opts.externs or []) 346 externs = set(opts.externs or [])
419 347
420 polymer_externs = os.path.join(os.path.dirname(_CURRENT_DIR), 'polymer', 348 polymer_externs = os.path.join(os.path.dirname(_CURRENT_DIR), 'polymer',
421 'v0_8', 'components-chromium', 349 'v0_8', 'components-chromium',
422 'polymer-externs', 'polymer.externs.js') 350 'polymer-externs', 'polymer.externs.js')
423 externs.add(polymer_externs) 351 externs.add(polymer_externs)
424 352
425 checker = Checker(verbose=opts.verbose, strict=opts.strict) 353 checker = Checker(verbose=opts.verbose, strict=opts.strict)
426 if opts.single_file: 354 if opts.single_file:
427 for source in opts.sources: 355 for source in opts.sources:
428 depends, externs = build.inputs.resolve_recursive_dependencies( 356 depends, externs = build.inputs.resolve_recursive_dependencies(
429 source, depends, externs) 357 source, depends, externs)
430 found_errors, _ = checker.check(source, out_file=opts.out_file, 358 found_errors, _ = checker.check(source, out_file=opts.out_file,
431 depends=depends, externs=externs, 359 depends=depends, externs=externs,
432 output_wrapper=opts.output_wrapper) 360 closure_args=opts.closure_args)
433 if found_errors: 361 if found_errors:
434 sys.exit(1) 362 sys.exit(1)
435 else: 363 else:
436 found_errors, stderr = checker.check_multiple( 364 found_errors, stderr = checker.check_multiple(
437 opts.sources, 365 opts.sources,
438 out_file=opts.out_file, 366 out_file=opts.out_file,
439 output_wrapper=opts.output_wrapper, 367 closure_args=opts.closure_args,
440 externs=externs) 368 externs=externs)
441 if found_errors: 369 if found_errors:
442 print stderr 370 print stderr
443 sys.exit(1) 371 sys.exit(1)
444 372
445 if opts.success_stamp: 373 if opts.success_stamp:
446 with open(opts.success_stamp, "w"): 374 with open(opts.success_stamp, "w"):
447 os.utime(opts.success_stamp, None) 375 os.utime(opts.success_stamp, None)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698