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

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

Issue 369643002: Lay groudwork to Closure compile JavaScript (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: more nits Created 6 years, 5 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 from collections import defaultdict
6 import os
7 import re
8 import subprocess
9 import sys
10 import tempfile
11
12
13
14 class LineNumber(object):
15 def __init__(self, file, line_number):
16 self.file = file
17 self.line_number = int(line_number)
18
19
20 class FileCache(object):
21 _cache = defaultdict(str)
22
23 def _read(self, file):
24 file = os.path.abspath(file)
25 self._cache[file] = self._cache[file] or open(file, "r").read()
26 return self._cache[file]
27
28 @staticmethod
29 def read(file):
30 return FileCache()._read(file)
31
32
33 class Flattener(object):
34 _INCLUDE_REG = "<include[^>]+src=['\"]([^>]*)['\"]>"
35
36 def __init__(self, file):
37 self.index = 0
38 self.lines = self._get_file(file)
39
40 while self.index < len(self.lines):
41 current_line = self.lines[self.index]
42 match = re.search(self._INCLUDE_REG, current_line[2])
43 if match:
44 file_dir = os.path.dirname(current_line[0])
45 self._inline_file(os.path.join(file_dir, match.group(1)))
46 else:
47 self.index += 1
48
49 self.contents = "\n".join(l[2] for l in self.lines)
50
51 # Returns a list of tuples in the format: (file, line number, line contents).
52 def _get_file(self, file):
53 lines = FileCache.read(file).splitlines()
54 return [(file, lnum + 1, line) for lnum, line in enumerate(lines)]
55
56 def _inline_file(self, file):
57 lines = self._get_file(file)
58 self.lines = self.lines[:self.index] + lines + self.lines[self.index + 1:]
59
60 def get_file_from_line(self, line_number):
61 line_number = int(line_number) - 1
62 return LineNumber(self.lines[line_number][0], self.lines[line_number][1])
63
64
65 class Checker(object):
66 _common_closure_args = [
67 "--accept_const_keyword",
68 "--language_in=ECMASCRIPT5",
69 "--summary_detail_level=3",
70 "--warning_level=VERBOSE",
71 "--jscomp_error=accessControls",
72 "--jscomp_error=ambiguousFunctionDecl",
73 "--jscomp_error=checkStructDictInheritance",
74 "--jscomp_error=checkTypes",
75 "--jscomp_error=checkVars",
76 "--jscomp_error=constantProperty",
77 "--jscomp_error=deprecated",
78 "--jscomp_error=externsValidation",
79 "--jscomp_error=globalThis",
80 "--jscomp_error=invalidCasts",
81 "--jscomp_error=misplacedTypeAnnotation",
82 "--jscomp_error=missingProperties",
83 "--jscomp_error=missingReturn",
84 "--jscomp_error=nonStandardJsDocs",
85 "--jscomp_error=suspiciousCode",
86 "--jscomp_error=undefinedNames",
87 "--jscomp_error=undefinedVars",
88 "--jscomp_error=unknownDefines",
89 "--jscomp_error=uselessCode",
90 "--jscomp_error=visibility",
91 ]
92
93 _found_java = False
94
95 _jar_command = [
96 "java",
97 "-jar",
98 "-Xms1024m",
99 "-server",
100 "-XX:+TieredCompilation"
101 ]
102
103 def __init__(self, verbose=False):
104 current_dir = os.path.join(os.path.dirname(__file__))
105 self._compiler_jar = os.path.join(current_dir, "lib", "compiler.jar")
106 self._runner_jar = os.path.join(current_dir, "runner", "runner.jar")
107 self._temp_files = []
108 self._verbose = verbose
109
110 def _clean_up(self):
111 if not self._temp_files:
112 return
113
114 self._debug("Deleting temporary files: " + ", ".join(self._temp_files))
115 for f in self._temp_files:
116 os.remove(f)
117 self._temp_files = []
118
119 def _debug(self, msg, error=False):
120 if self._verbose:
121 print "(INFO) " + msg
122
123 def _fatal(self, msg):
124 print >> sys.stderr, "(FATAL) " + msg
125 self._clean_up()
126 sys.exit(1)
127
128 def _run_command(self, cmd):
129 cmd_str = " ".join(cmd)
130 self._debug("Running command: " + cmd_str)
131
132 devnull = open(os.devnull, "w")
133 return subprocess.Popen(
134 cmd_str, stdout=devnull, stderr=subprocess.PIPE, shell=True)
135
136 def _check_java_path(self):
137 if self._found_java:
138 return
139
140 proc = self._run_command(["which", "java"])
141 proc.communicate()
142 if proc.returncode == 0:
143 self._found_java = True
144 else:
145 self._fatal("Cannot find java (`which java` => %s)" % proc.returncode)
146
147 def _run_jar(self, jar, args=[]):
148 self._check_java_path()
149 return self._run_command(self._jar_command + [jar] + args)
150
151 def _fix_line_number(self, match):
152 real_file = self._flattener.get_file_from_line(match.group(1))
153 return "%s:%d" % (os.path.abspath(real_file.file), real_file.line_number)
154
155 def _fix_up_error(self, error):
156 if " first declared in " in error:
157 # Ignore "Variable x first declared in /same/file".
158 return ""
159
160 file = self._expanded_file
161 fixed = re.sub("%s:(\d+)" % file, self._fix_line_number, error)
162 return fixed.replace(file, os.path.abspath(self._file_arg))
163
164 def _format_errors(self, errors):
165 errors = filter(None, errors)
166 contents = ("\n" + "## ").join("\n\n".join(errors).splitlines())
167 return "## " + contents if contents else ""
168
169 def _create_temp_file(self, contents):
170 with tempfile.NamedTemporaryFile(mode='wt', delete=False) as tmp_file:
171 self._temp_files.append(tmp_file.name)
172 tmp_file.write(contents)
173 return tmp_file.name
174
175 def check(self, file, depends=[], externs=[]):
176 self._debug("FILE: " + file)
177
178 if file.endswith("_externs.js"):
179 self._debug("Skipping externs: " + file)
180 return
181
182 self._file_arg = file
183
184 tmp_dir = tempfile.gettempdir()
185 rel_path = lambda f: os.path.join(os.path.relpath(os.getcwd(), tmp_dir), f)
186
187 contents = ['<include src="%s">' % rel_path(f) for f in depends + [file]]
188 meta_file = self._create_temp_file("\n".join(contents))
189 self._debug("Meta file: " + meta_file)
190
191 self._flattener = Flattener(meta_file)
192 self._expanded_file = self._create_temp_file(self._flattener.contents)
193 self._debug("Expanded file: " + self._expanded_file)
194
195 args = ["--js=" + self._expanded_file] + ["--externs=" + e for e in externs]
196 args_file_content = " " + " ".join(self._common_closure_args + args)
197 self._debug("Args: " + args_file_content.strip())
198
199 args_file = self._create_temp_file(args_file_content)
200 self._debug("Args file: " + args_file)
201
202 runner_args = ["--compiler-args-file=" + args_file]
203 runner_cmd = self._run_jar(self._runner_jar, args=runner_args)
204 (_, stderr) = runner_cmd.communicate()
205
206 errors = stderr.strip().split("\n\n")
207 self._debug("Summary: " + errors.pop())
208
209 output = self._format_errors(map(self._fix_up_error, errors))
210 if runner_cmd.returncode:
211 self._fatal("Error in: " + file + ("\n" + output if output else ""))
212 elif output:
213 self._debug("Output: " + output)
214
215 self._clean_up()
216
217 return runner_cmd.returncode == 0
OLDNEW
« no previous file with comments | « third_party/closure_compiler/README.chromium ('k') | third_party/closure_compiler/checker_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698