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

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: Fix inputs.py 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 args += ["--%s" % arg for arg in closure_args]
284 216
285 args_file_content = " %s" % " ".join(self._common_args() + args) 217 args_file_content = " %s" % " ".join(args)
286 self._log_debug("Args: %s" % args_file_content.strip()) 218 self._log_debug("Args: %s" % args_file_content.strip())
287 219
288 args_file = self._create_temp_file(args_file_content) 220 args_file = self._create_temp_file(args_file_content)
289 self._log_debug("Args file: %s" % args_file) 221 self._log_debug("Args file: %s" % args_file)
290 222
291 runner_args = ["--compiler-args-file=%s" % args_file] 223 runner_args = ["--compiler-args-file=%s" % args_file]
292 _, stderr = self._run_jar(self._runner_jar, runner_args) 224 _, stderr = self._run_jar(self._runner_jar, runner_args)
293 225
294 errors = stderr.strip().split("\n\n") 226 errors = stderr.strip().split("\n\n")
295 maybe_summary = errors.pop() 227 maybe_summary = errors.pop()
296 228
297 if re.search(".*error.*warning.*typed", maybe_summary): 229 if re.search(".*error.*warning.*typed", maybe_summary):
298 self._log_debug("Summary: %s" % maybe_summary) 230 self._log_debug("Summary: %s" % maybe_summary)
299 else: 231 else:
300 # Not a summary. Running the jar failed. Bail. 232 # Not a summary. Running the jar failed. Bail.
301 self._log_error(stderr) 233 self._log_error(stderr)
302 self._nuke_temp_files() 234 self._nuke_temp_files()
303 sys.exit(1) 235 sys.exit(1)
304 236
305 if errors and out_file: 237 if errors and out_file:
306 if os.path.exists(out_file): 238 if os.path.exists(out_file):
307 os.remove(out_file) 239 os.remove(out_file)
308 if os.path.exists(self._MAP_FILE_FORMAT % out_file): 240 if os.path.exists(self._MAP_FILE_FORMAT % out_file):
309 os.remove(self._MAP_FILE_FORMAT % out_file) 241 os.remove(self._MAP_FILE_FORMAT % out_file)
310 242
311 return errors, stderr 243 return errors, stderr
312 244
313 def check(self, source_file, out_file=None, depends=None, externs=None, 245 def check(self, source_file, out_file=None, depends=None, externs=None,
314 output_wrapper=None): 246 closure_args=None):
315 """Closure compiler |source_file| while checking for errors. 247 """Closure compiler |source_file| while checking for errors.
316 248
317 Args: 249 Args:
318 source_file: A file to check. 250 source_file: A file to check.
319 out_file: A file where the compiled output is written to. 251 out_file: A file where the compiled output is written to.
320 depends: Files that |source_file| requires to run (e.g. earlier <script>). 252 depends: Files that |source_file| requires to run (e.g. earlier <script>).
321 externs: @extern files that inform the compiler about custom globals. 253 externs: @extern files that inform the compiler about custom globals.
322 output_wrapper: Wraps output into this string at the place denoted by the 254 closure_args: Arguments passed directly to the Closure compiler.
323 marker token %output%.
324 255
325 Returns: 256 Returns:
326 (found_errors, stderr) A boolean indicating whether errors were found and 257 (found_errors, stderr) A boolean indicating whether errors were found and
327 the raw Closure compiler stderr (as a string). 258 the raw Closure compiler stderr (as a string).
328 """ 259 """
329 self._log_debug("FILE: %s" % source_file) 260 self._log_debug("FILE: %s" % source_file)
330 261
331 if source_file.endswith("_externs.js"): 262 if source_file.endswith("_externs.js"):
332 self._log_debug("Skipping externs: %s" % source_file) 263 self._log_debug("Skipping externs: %s" % source_file)
333 return 264 return
334 265
335 self._file_arg = source_file 266 self._file_arg = source_file
336 267
337 cwd, tmp_dir = os.getcwd(), tempfile.gettempdir() 268 cwd, tmp_dir = os.getcwd(), tempfile.gettempdir()
338 rel_path = lambda f: os.path.join(os.path.relpath(cwd, tmp_dir), f) 269 rel_path = lambda f: os.path.join(os.path.relpath(cwd, tmp_dir), f)
339 270
340 depends = depends or [] 271 depends = depends or []
341 includes = [rel_path(f) for f in depends + [source_file]] 272 includes = [rel_path(f) for f in depends + [source_file]]
342 contents = ['<include src="%s">' % i for i in includes] 273 contents = ['<include src="%s">' % i for i in includes]
343 meta_file = self._create_temp_file("\n".join(contents)) 274 meta_file = self._create_temp_file("\n".join(contents))
344 self._log_debug("Meta file: %s" % meta_file) 275 self._log_debug("Meta file: %s" % meta_file)
345 276
346 self._processor = processor.Processor(meta_file) 277 self._processor = processor.Processor(meta_file)
347 self._expanded_file = self._create_temp_file(self._processor.contents) 278 self._expanded_file = self._create_temp_file(self._processor.contents)
348 self._log_debug("Expanded file: %s" % self._expanded_file) 279 self._log_debug("Expanded file: %s" % self._expanded_file)
349 280
350 errors, stderr = self._run_js_check([self._expanded_file], 281 errors, stderr = self._run_js_check([self._expanded_file],
351 out_file=out_file, externs=externs, 282 out_file=out_file, externs=externs,
352 output_wrapper=output_wrapper) 283 closure_args=closure_args)
353 filtered_errors = self._filter_errors(errors) 284 filtered_errors = self._filter_errors(errors)
354 cleaned_errors = map(self._clean_up_error, filtered_errors) 285 cleaned_errors = map(self._clean_up_error, filtered_errors)
355 output = self._format_errors(cleaned_errors) 286 output = self._format_errors(cleaned_errors)
356 287
357 if cleaned_errors: 288 if cleaned_errors:
358 prefix = "\n" if output else "" 289 prefix = "\n" if output else ""
359 self._log_error("Error in: %s%s%s" % (source_file, prefix, output)) 290 self._log_error("Error in: %s%s%s" % (source_file, prefix, output))
360 elif output: 291 elif output:
361 self._log_debug("Output: %s" % output) 292 self._log_debug("Output: %s" % output)
362 293
363 self._nuke_temp_files() 294 self._nuke_temp_files()
364 return bool(cleaned_errors), stderr 295 return bool(cleaned_errors), stderr
365 296
366 def check_multiple(self, sources, out_file=None, output_wrapper=None, 297 def check_multiple(self, sources, out_file=None, externs=None,
367 externs=None): 298 closure_args=None):
368 """Closure compile a set of files and check for errors. 299 """Closure compile a set of files and check for errors.
369 300
370 Args: 301 Args:
371 sources: An array of files to check. 302 sources: An array of files to check.
372 out_file: A file where the compiled output is written to. 303 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. 304 externs: @extern files that inform the compiler about custom globals.
305 closure_args: Arguments passed directly to the Closure compiler.
376 306
377 Returns: 307 Returns:
378 (found_errors, stderr) A boolean indicating whether errors were found and 308 (found_errors, stderr) A boolean indicating whether errors were found and
379 the raw Closure Compiler stderr (as a string). 309 the raw Closure Compiler stderr (as a string).
380 """ 310 """
381 errors, stderr = self._run_js_check(sources, out_file=out_file, 311 errors, stderr = self._run_js_check(sources, out_file=out_file,
382 output_wrapper=output_wrapper, 312 externs=externs,
383 externs=externs) 313 closure_args=closure_args)
384 self._nuke_temp_files() 314 self._nuke_temp_files()
385 return bool(errors), stderr 315 return bool(errors), stderr
386 316
387 317
388 if __name__ == "__main__": 318 if __name__ == "__main__":
389 parser = argparse.ArgumentParser( 319 parser = argparse.ArgumentParser(
390 description="Typecheck JavaScript using Closure compiler") 320 description="Typecheck JavaScript using Closure compiler")
391 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE, 321 parser.add_argument("sources", nargs=argparse.ONE_OR_MORE,
392 help="Path to a source file to typecheck") 322 help="Path to a source file to typecheck")
393 single_file_group = parser.add_mutually_exclusive_group() 323 single_file_group = parser.add_mutually_exclusive_group()
394 single_file_group.add_argument("--single-file", dest="single_file", 324 single_file_group.add_argument("--single-file", dest="single_file",
395 action="store_true", 325 action="store_true",
396 help="Process each source file individually") 326 help="Process each source file individually")
327 # TODO(twellington): remove --no-single-file and use len(opts.sources).
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