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

Side by Side Diff: tools/testing/legpad/legpad.py

Issue 9866022: legpad.py (python script that generates legpad.html files) (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: legpad.py Created 8 years, 8 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2
3 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
4 # for details. All rights reserved. Use of this source code is governed by a
5 # BSD-style license that can be found in the LICENSE file.
6 #
7
8 """
9 Legpad is used to compile .dart files to javascript, using the leg (now
10 called dart2js) compiler.
11
12 This is accomplished by creating an html file (usually called
13 <something>.legpad.html) that executes the leg compiler when the page
14 is loaded by a web browser (or DumpRenderTree).
15
16 The <something>.legpad.html file contains:
17
18 1. all the dart files that compose a user's dart program
19 2. all the dart files of dart:core and other standard dart libraries
20 (or any other symbol that can follow "dart:" in an import statement
21 3. legpad.dart (compiled to javascript)
22
23 The contents of each dart file is placed in a separate <script> tag.
24
25 When the html page is loaded by a browser, the leg compiler is invoked
26 and the dart program is compiled to javascript. The generated javascript is
27 placed in a <pre> element with id "output".
28
29 When the html page is passed to DumpRenderTree, the dumped output will
30 have the generated javascript.
31 """
32
33 import logging
34 import optparse
35 import os.path
36 import platform
37 import re
38 import subprocess
39 import sys
40
41
42 class FileNotFoundException(Exception):
43 def __init__(self, file_name):
44 self._name = file_name
45
46 def __str__(self):
47 return self._name
48
49
50 class CommandFailedException(Exception):
51 def __init__(self, message):
52 self._message = message
53
54 def GetMessage(self):
55 return self._message
56
57
58 # Template for the legpad.html page we're going to generate.
59 HTML = """<!DOCTYPE html>
60 <html>
61 <head>
62 <style type="text/css">
63 textarea {
64 width: 100%;
65 height: 200px;
66 }
67 .label {
68 margin-top: 5px;
69 }
70 pre {
71 border: 2px solid black;
72 }
73 </style>
74 {{script_tags}}
75 <script type="text/javascript">
76 if (window.layoutTestController) {
77 layoutTestController.dumpAsText();
78 }
79 </script>
80 </head>
81 <body>
82 <h1>Legpad</h1>
83 <div class="label">Input:</div>
84 <textarea id="input"></textarea>
85 <div class="label">Compiler Messages:</div>
86 <pre id="warnings"></pre>
87 <div class="label">Timing:</div>
88 <pre id="timing"></pre>
89 <div class="label">Output:</div>
90 <pre id="output"></pre>
91 <script type="text/javascript">
92 {{LEGPAD_JS}}
93 </script>
94 </body>
95 </html>
96 """
97
98 # This finds everything after the word "Output:" in the html page.
99 # (Note, because the javascript we're fishing out spans multiple lines
100 # we need to use the DOTALL switch here.)
101 OUTPUT_JAVASCRIPT_REGEX = re.compile(".*\nOutput:\n(.*)\n#EOF", re.DOTALL)
102
103 # If the legpad.dart encounters a compilation error, the generated
104 # javascript will contains the words "legpad compilation error".
105 COMPILATION_ERROR_REGEX = re.compile(".*legpad compilation error.*", re.DOTALL)
106
107 # We use "application/inert" here to make the browser ignore the
108 # these script tags. (legpad.dart will fish out the contents as needed.)
109 #
110 SCRIPT_TAG = """<script type="application/inert" id="{{id}}">
111 {{contents}}
112 </script>
113 """
114
115 # Regex that finds #import, #source and #native directives in .dart files.
116 # match.group(1) = "import", "source" or "native"
117 # match.group(2) = url of file being imported
118 DIRECTIVE_RE = re.compile(r"^#(import|source|native)\([\"']([^\"']*)[\"']")
119
120 # id of script tag that holds name of the top dart file to be compiled,
121 # (This file name passed will be passed to the leg compiler by legpad.dart.)
122 MAIN_ID = "main_id"
123
124 # TODO - read this from some config file once ahe/zundel create it
floitsch 2012/03/28 05:48:49 TODO(ldap):
mattsh 2012/03/28 15:29:11 Done.
125 DART_LIBRARIES = {
126 "core" : "frog/leg/lib/core.dart",
127 "coreimpl" : "frog/leg/lib/coreimpl.dart",
128 "dom" : "lib/dom/frog/dom_frog.dart",
129 "html" : "lib/html/frog/html_frog.dart",
130 "io" : "frog/leg/lib/io.dart",
131 "isolate" : "lib/isolate/isolate_leg.dart",
132 "json" : "lib/json/json.dart",
133 "uri" : "lib/uri/uri.dart",
134 "utf" : "lib/utf/utf.dart",
135 }
136
137 class Pad(object):
138 """
139 Accumulates all source files that are needed to compile a dart program,
140 and places them in <script> tags on an html page.
141 """
142
143 def __init__(self, argv):
144 parser = optparse.OptionParser(usage=
145 "%prog [options] file_to_compile.dart"
146 )
147 parser.add_option("-o", "--out",
148 help="name of javascript output file")
149 parser.add_option("-v", "--verbose", action="store_true",
150 help="more verbose logging")
151 (options, args) = parser.parse_args(argv)
152
153 log_level = logging.INFO
154 if options.verbose:
155 log_level = logging.DEBUG
156 logging.basicConfig(level=log_level)
157
158 if len(args) < 2:
159 parser.print_help()
160 sys.exit(1)
161
162 self.main_file = os.path.abspath(args[1])
163
164 # directory of this script
165 self.legpad_dir = os.path.abspath(os.path.dirname(argv[0]))
166
167 # root of dart source repo
168 self.dart_dir = os.path.dirname(os.path.dirname(os.path.dirname(
169 self.legpad_dir)))
170
171 logging.debug("dart_dir: '%s'" % self.dart_dir)
172
173 if options.out:
174 # user has specified an output file name
175 self.js_file = os.path.abspath(options.out)
176 else:
177 # User didn't specify an output file, so use the input
178 # file name as the base of the output file name.
179 self.js_file = self.main_file + ".legpad.js"
180
181 logging.debug("js_file: '%s" % self.js_file)
182
183 # this is the html file that we pass to DumpRenderTree
184 self.html_file = self.js_file + ".legpad.html"
185 logging.debug("html_file: '%s'" % self.html_file)
186
187 # map from file name to File object (contains entries for all corelib
188 # and all other dart files needed to compile main_file)
189 self.name_to_file = {}
190
191 # map from script tag id to File object
192 self.id_to_file = {}
193
194 self.load_libraries()
195 self.load_file(self.main_file)
196
197 html = self.generate_html()
198 write_file(self.html_file, html)
199
200 js = self.generate_js()
201 write_file(self.js_file, js)
202
203 line_count = len(js.splitlines())
204 logging.debug("generated '%s' (%d lines)", self.js_file, line_count)
205
206 match = COMPILATION_ERROR_REGEX.match(js)
207 if match:
208 sys.exit(1)
209
210 def generate_html(self):
211 tags = []
212 for f in self.id_to_file.values():
213 tags.append(self._create_tag(f.id, f.contents))
214 tags.append(self._create_tag(MAIN_ID, self.main_file))
215 html = HTML.replace("{{script_tags}}", "".join(tags))
216
217 legpad_js = os.path.join(self.legpad_dir, "legpad.dart.dart2js.js")
218 check_exists(legpad_js)
219
220 html = html.replace("{{LEGPAD_JS}}", read_file(legpad_js))
221 return html
222
223 def generate_js(self):
224 drt = os.path.join(self.dart_dir, "client/tests/drt/DumpRenderTree")
225 if platform.system() == 'Darwin':
226 drt += ".app"
227 elif platform.system() == 'Windows':
228 raise Exception("legpad does not run on Windows")
229
230 check_exists(drt)
231 args = []
232 args.append(drt)
233 args.append(self.html_file)
234
235 stdout = run_command(args)
236 match = OUTPUT_JAVASCRIPT_REGEX.match(stdout)
237 if not match:
238 raise Exception("can't find regex in DumpRenderTree output")
239 return match.group(1)
240
241 @staticmethod
242 def _create_tag(id, contents):
243 s = SCRIPT_TAG
244 s = s.replace("{{id}}", id)
245 # TODO(mattsh) - need to html escape here
246 s = s.replace("{{contents}}", contents)
247 return s
248
249 def dart_library(self, name):
250 path = DART_LIBRARIES[name]
251 if not path:
252 raise Exception("unrecognized 'dart:%s'", name)
253 return os.path.join(self.dart_dir, path)
254
255 def load_libraries(self):
256 for name in DART_LIBRARIES:
257 self.load_file(self.dart_library(name))
258
259 def load_file(self, name):
260 name = os.path.abspath(name)
261 if name in self.name_to_file:
262 return
263 f = File(self, name)
264 self.name_to_file[f.name] = f
265 if f.id in self.id_to_file:
266 raise Exception("ambiguous id '%s'" % f.id)
267 self.id_to_file[f.id] = f
268 f.directives()
269
270 class File(object):
271 def __init__(self, pad, name):
272 self.pad = pad
273 self.name = name
274 self.id = self._make_id()
275 check_exists(name)
276 with open(self.name, "r") as f:
277 self.contents = f.read()
278
279 def _make_id(self):
280 """
281 Generates an id (based on the file name) for the <script> tag that will
282 hold the contents of this file.
283 """
284 name = self.name.replace(self.pad.dart_dir, "dartdir")
285 return name.replace("/", "_").replace(".", "_")
286
287 def directives(self):
288 """Load files referenced by #source, #import and #native directives."""
289 lines = self.contents.split("\n")
290 self.line_number = 0
291 for line in lines:
292 self.line_number += 1
293 self._directive(line)
294
295 def _directive(self, line):
296 match = DIRECTIVE_RE.match(line)
297 if not match:
298 return
299 url = match.group(2)
300 if url.startswith("dart:"):
301 path = self.pad.dart_library(url[len("dart:"):])
302 else:
303 path = os.path.join(os.path.dirname(self.name), url)
304 self.pad.load_file(path)
305
306 def read_file(file_name):
307 check_exists(file_name)
308 with open(file_name, "r") as input:
309 contents = input.read()
310 logging.debug("read_file '%s' (%d bytes)" % (file_name, len(contents)))
311 return contents
312
313 def write_file(file_name, contents):
314 with open(file_name, "w") as output:
315 output.write(contents)
316
317 check_exists(file_name)
318 logging.debug("write_file '%s' (%d bytes)" % (file_name, len(contents)))
319
320
321 def check_exists(file_name):
322 if not os.path.exists(file_name):
323 raise FileNotFoundException(file_name)
324
325
326 def format_command(args):
327 return ' '.join(args)
328
329
330 def run_command(args):
331 """
332 Args:
333 command: comamnd with arguments to exec
334 Returns:
335 all output that this command sent to stdout
336 """
337
338 command = format_command(args)
339 logging.info("RUNNING: '%s'" % command)
340 child = subprocess.Popen(args,
341 stdout=subprocess.PIPE,
342 stderr=subprocess.PIPE,
343 close_fds=True)
344 (stdout, stderr) = child.communicate()
345 exit_code = child.wait()
346 if exit_code:
347 for line in stderr.splitlines():
348 logging.info(line)
349 msg = "FAILURE (exit_code=%d): '%s'" % (exit_code, command)
350 logging.error(msg)
351 raise CommandFailedException(msg)
352 logging.debug("SUCCEEDED (%d bytes)" % len(stdout))
353 return stdout
354
355
356 def main(argv):
357 Pad(argv)
358
359 if __name__ == "__main__":
360 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698