371
|
1 |
#!/usr/bin/env python
|
|
2 |
|
|
3 |
import sys
|
|
4 |
import os
|
|
5 |
import shutil
|
|
6 |
from copy import copy
|
|
7 |
from os.path import join, dirname, basename, abspath, split, isfile, isdir
|
|
8 |
from optparse import OptionParser
|
|
9 |
import pyjs
|
|
10 |
from cStringIO import StringIO
|
|
11 |
try:
|
|
12 |
# Python 2.5 and above
|
|
13 |
from hashlib import md5
|
|
14 |
except:
|
|
15 |
import md5
|
|
16 |
import re
|
|
17 |
|
|
18 |
usage = """
|
|
19 |
usage: %prog [options] <application module name or path>
|
|
20 |
|
|
21 |
This is the command line builder for the pyjamas project, which can
|
|
22 |
be used to build Ajax applications from Python.
|
|
23 |
For more information, see the website at http://pyjs.org/
|
|
24 |
"""
|
|
25 |
|
|
26 |
# GWT1.2 Impl | GWT1.2 Output | Pyjamas 0.2 Platform | Pyjamas 0.2 Output
|
|
27 |
# -------------+-----------------------+----------------------+----------------------
|
|
28 |
# IE6 | ie6 | IE6 | ie6
|
|
29 |
# Opera | opera | Opera | opera
|
|
30 |
# Safari | safari | Safari | safari
|
|
31 |
# -- | gecko1_8 | Mozilla | mozilla
|
|
32 |
# -- | gecko | OldMoz | oldmoz
|
|
33 |
# Standard | all | (default code) | all
|
|
34 |
# Mozilla | gecko1_8, gecko | -- | --
|
|
35 |
# Old | safari, gecko, opera | -- | --
|
|
36 |
|
|
37 |
version = "%prog pyjamas version 2006-08-19"
|
|
38 |
|
|
39 |
# these names in lowercase need match the strings
|
|
40 |
# returned by "provider$user.agent" in order to be selected corretly
|
|
41 |
app_platforms = ['IE6', 'Opera', 'OldMoz', 'Safari', 'Mozilla']
|
|
42 |
|
|
43 |
# usually defaults to e.g. /usr/share/pyjamas
|
|
44 |
_data_dir = os.path.join(pyjs.prefix, "share/pyjamas")
|
|
45 |
|
|
46 |
|
|
47 |
# .cache.html files produces look like this
|
|
48 |
CACHE_HTML_PAT=re.compile('^[a-z]*.[0-9a-f]{32}\.cache\.html$')
|
|
49 |
|
|
50 |
# ok these are the three "default" library directories, containing
|
|
51 |
# the builtins (str, List, Dict, ord, round, len, range etc.)
|
|
52 |
# the main pyjamas libraries (pyjamas.ui, pyjamas.Window etc.)
|
|
53 |
# and the contributed addons
|
|
54 |
|
|
55 |
for p in ["library/builtins",
|
|
56 |
"library",
|
|
57 |
"addons"]:
|
|
58 |
p = os.path.join(_data_dir, p)
|
|
59 |
if os.path.isdir(p):
|
|
60 |
pyjs.path.append(p)
|
|
61 |
|
|
62 |
|
|
63 |
def read_boilerplate(data_dir, filename):
|
|
64 |
return open(join(data_dir, "builder/boilerplate", filename)).read()
|
|
65 |
|
|
66 |
def copy_boilerplate(data_dir, filename, output_dir):
|
|
67 |
filename = join(data_dir, "builder/boilerplate", filename)
|
|
68 |
shutil.copy(filename, output_dir)
|
|
69 |
|
|
70 |
|
|
71 |
# taken and modified from python2.4
|
|
72 |
def copytree_exists(src, dst, symlinks=False):
|
|
73 |
if not os.path.exists(src):
|
|
74 |
return
|
|
75 |
|
|
76 |
names = os.listdir(src)
|
|
77 |
try:
|
|
78 |
os.mkdir(dst)
|
|
79 |
except:
|
|
80 |
pass
|
|
81 |
|
|
82 |
errors = []
|
|
83 |
for name in names:
|
|
84 |
if name.startswith('CVS'):
|
|
85 |
continue
|
|
86 |
if name.startswith('.git'):
|
|
87 |
continue
|
|
88 |
if name.startswith('.svn'):
|
|
89 |
continue
|
|
90 |
|
|
91 |
srcname = os.path.join(src, name)
|
|
92 |
dstname = os.path.join(dst, name)
|
|
93 |
try:
|
|
94 |
if symlinks and os.path.islink(srcname):
|
|
95 |
linkto = os.readlink(srcname)
|
|
96 |
os.symlink(linkto, dstname)
|
|
97 |
elif isdir(srcname):
|
|
98 |
copytree_exists(srcname, dstname, symlinks)
|
|
99 |
else:
|
|
100 |
shutil.copy2(srcname, dstname)
|
|
101 |
except (IOError, os.error), why:
|
|
102 |
errors.append((srcname, dstname, why))
|
|
103 |
if errors:
|
|
104 |
print errors
|
|
105 |
|
|
106 |
def check_html_file(source_file, dest_path):
|
|
107 |
""" Checks if a base HTML-file is available in the PyJamas
|
|
108 |
output directory.
|
|
109 |
If the HTML-file isn't available, it will be created.
|
|
110 |
|
|
111 |
If a CSS-file with the same name is available
|
|
112 |
in the output directory, a reference to this CSS-file
|
|
113 |
is included.
|
|
114 |
|
|
115 |
If no CSS-file is found, this function will look for a special
|
|
116 |
CSS-file in the output directory, with the name
|
|
117 |
"pyjamas_default.css", and if found it will be referenced
|
|
118 |
in the generated HTML-file.
|
|
119 |
|
|
120 |
[thank you to stef mientki for contributing this function]
|
|
121 |
"""
|
|
122 |
|
|
123 |
base_html = """\
|
|
124 |
<html>
|
|
125 |
<!-- auto-generated html - you should consider editing and
|
|
126 |
adapting this to suit your requirements
|
|
127 |
-->
|
|
128 |
<head>
|
|
129 |
<meta name="pygwt:module" content="%(modulename)s">
|
|
130 |
%(css)s
|
|
131 |
<title>%(title)s</title>
|
|
132 |
</head>
|
|
133 |
<body bgcolor="white">
|
|
134 |
<script language="javascript" src="pygwt.js"></script>
|
|
135 |
</body>
|
|
136 |
</html>
|
|
137 |
"""
|
|
138 |
|
|
139 |
filename = os.path.split ( source_file )[1]
|
|
140 |
mod_name = os.path.splitext ( filename )[0]
|
|
141 |
file_name = os.path.join ( dest_path, mod_name + '.html' )
|
|
142 |
|
|
143 |
# if html file in output directory exists, leave it alone.
|
|
144 |
if os.path.exists ( file_name ):
|
|
145 |
return 0
|
|
146 |
|
|
147 |
if os.path.exists (
|
|
148 |
os.path.join ( dest_path, mod_name + '.css' ) ) :
|
|
149 |
css = "<link rel='stylesheet' href='" + mod_name + ".css'>"
|
|
150 |
elif os.path.exists (
|
|
151 |
os.path.join ( dest_path, 'pyjamas_default.css' ) ) :
|
|
152 |
css = "<link rel='stylesheet' href='pyjamas_default.css'>"
|
|
153 |
|
|
154 |
else:
|
|
155 |
css = ''
|
|
156 |
|
|
157 |
title = 'PyJamas Auto-Generated HTML file ' + mod_name
|
|
158 |
|
|
159 |
base_html = base_html % {'modulename': mod_name, 'title': title, 'css': css}
|
|
160 |
|
|
161 |
fh = open (file_name, 'w')
|
|
162 |
fh.write (base_html)
|
|
163 |
fh.close ()
|
|
164 |
|
|
165 |
return 1
|
|
166 |
|
|
167 |
|
|
168 |
def build(app_name, output, js_includes=(), debug=False, dynamic=0,
|
|
169 |
data_dir=None, cache_buster=False, optimize=False):
|
|
170 |
|
|
171 |
# make sure the output directory is always created in the current working
|
|
172 |
# directory or at the place given if it is an absolute path.
|
|
173 |
output = os.path.abspath(output)
|
|
174 |
msg = "Building '%(app_name)s' to output directory '%(output)s'" % locals()
|
|
175 |
if debug:
|
|
176 |
msg += " with debugging statements"
|
|
177 |
print msg
|
|
178 |
|
|
179 |
# check the output directory
|
|
180 |
if os.path.exists(output) and not os.path.isdir(output):
|
|
181 |
print >>sys.stderr, "Output destination %s exists and is not a directory" % output
|
|
182 |
return
|
|
183 |
if not os.path.isdir(output):
|
|
184 |
try:
|
|
185 |
print "Creating output directory"
|
|
186 |
os.mkdir(output)
|
|
187 |
except StandardError, e:
|
|
188 |
print >>sys.stderr, "Exception creating output directory %s: %s" % (output, e)
|
|
189 |
|
|
190 |
## public dir
|
|
191 |
for p in pyjs.path:
|
|
192 |
pub_dir = join(p, 'public')
|
|
193 |
if isdir(pub_dir):
|
|
194 |
print "Copying: public directory of library %r" % p
|
|
195 |
copytree_exists(pub_dir, output)
|
|
196 |
|
|
197 |
## AppName.html - can be in current or public directory
|
|
198 |
html_input_filename = app_name + ".html"
|
|
199 |
html_output_filename = join(output, basename(html_input_filename))
|
|
200 |
if os.path.isfile(html_input_filename):
|
|
201 |
if not os.path.isfile(html_output_filename) or \
|
|
202 |
os.path.getmtime(html_input_filename) > \
|
|
203 |
os.path.getmtime(html_output_filename):
|
|
204 |
try:
|
|
205 |
shutil.copy(html_input_filename, html_output_filename)
|
|
206 |
except:
|
|
207 |
print >>sys.stderr, "Warning: Missing module HTML file %s" % html_input_filename
|
|
208 |
|
|
209 |
print "Copying: %(html_input_filename)s" % locals()
|
|
210 |
|
|
211 |
if check_html_file(html_input_filename, output):
|
|
212 |
print >>sys.stderr, "Warning: Module HTML file %s has been auto-generated" % html_input_filename
|
|
213 |
|
|
214 |
## pygwt.js
|
|
215 |
|
|
216 |
print "Copying: pygwt.js"
|
|
217 |
|
|
218 |
pygwt_js_template = read_boilerplate(data_dir, "pygwt.js")
|
|
219 |
pygwt_js_output = open(join(output, "pygwt.js"), "w")
|
|
220 |
|
|
221 |
print >>pygwt_js_output, pygwt_js_template
|
|
222 |
|
|
223 |
pygwt_js_output.close()
|
|
224 |
|
|
225 |
## Images
|
|
226 |
|
|
227 |
print "Copying: Images and History"
|
|
228 |
copy_boilerplate(data_dir, "corner_dialog_topleft_black.png", output)
|
|
229 |
copy_boilerplate(data_dir, "corner_dialog_topright_black.png", output)
|
|
230 |
copy_boilerplate(data_dir, "corner_dialog_bottomright_black.png", output)
|
|
231 |
copy_boilerplate(data_dir, "corner_dialog_bottomleft_black.png", output)
|
|
232 |
copy_boilerplate(data_dir, "corner_dialog_edge_black.png", output)
|
|
233 |
copy_boilerplate(data_dir, "corner_dialog_topleft.png", output)
|
|
234 |
copy_boilerplate(data_dir, "corner_dialog_topright.png", output)
|
|
235 |
copy_boilerplate(data_dir, "corner_dialog_bottomright.png", output)
|
|
236 |
copy_boilerplate(data_dir, "corner_dialog_bottomleft.png", output)
|
|
237 |
copy_boilerplate(data_dir, "corner_dialog_edge.png", output)
|
|
238 |
copy_boilerplate(data_dir, "tree_closed.gif", output)
|
|
239 |
copy_boilerplate(data_dir, "tree_open.gif", output)
|
|
240 |
copy_boilerplate(data_dir, "tree_white.gif", output)
|
|
241 |
copy_boilerplate(data_dir, "history.html", output)
|
|
242 |
|
|
243 |
|
|
244 |
## all.cache.html
|
|
245 |
app_files = generateAppFiles(data_dir, js_includes, app_name, debug,
|
|
246 |
output, dynamic, cache_buster, optimize)
|
|
247 |
|
|
248 |
## AppName.nocache.html
|
|
249 |
|
|
250 |
print "Creating: %(app_name)s.nocache.html" % locals()
|
|
251 |
|
|
252 |
home_nocache_html_template = read_boilerplate(data_dir, "home.nocache.html")
|
|
253 |
home_nocache_html_output = open(join(output, app_name + ".nocache.html"),
|
|
254 |
"w")
|
|
255 |
|
|
256 |
# the selector templ is added to the selectScript function
|
|
257 |
select_tmpl = """O(["true","%s"],"%s");"""
|
|
258 |
script_selectors = StringIO()
|
|
259 |
|
|
260 |
for platform, file_prefix in app_files:
|
|
261 |
print >> script_selectors, select_tmpl % (platform, file_prefix)
|
|
262 |
|
|
263 |
print >>home_nocache_html_output, home_nocache_html_template % dict(
|
|
264 |
app_name = app_name,
|
|
265 |
script_selectors = script_selectors.getvalue(),
|
|
266 |
)
|
|
267 |
|
|
268 |
home_nocache_html_output.close()
|
|
269 |
|
|
270 |
print "Done. You can run your app by opening '%(html_output_filename)s' in a browser" % locals()
|
|
271 |
|
|
272 |
|
|
273 |
def generateAppFiles(data_dir, js_includes, app_name, debug, output, dynamic,
|
|
274 |
cache_buster, optimize):
|
|
275 |
|
|
276 |
all_cache_html_template = read_boilerplate(data_dir, "all.cache.html")
|
|
277 |
mod_cache_html_template = read_boilerplate(data_dir, "mod.cache.html")
|
|
278 |
|
|
279 |
# clean out the old ones first
|
|
280 |
for name in os.listdir(output):
|
|
281 |
if CACHE_HTML_PAT.match(name):
|
|
282 |
p = join(output, name)
|
|
283 |
print "Deleting existing app file %s" % p
|
|
284 |
os.unlink(p)
|
|
285 |
|
|
286 |
app_files = []
|
|
287 |
tmpl = read_boilerplate(data_dir, "all.cache.html")
|
|
288 |
parser = pyjs.PlatformParser("platform")
|
|
289 |
app_headers = ''
|
|
290 |
scripts = ['<script type="text/javascript" src="%s"></script>'%script \
|
|
291 |
for script in js_includes]
|
|
292 |
app_body = '\n'.join(scripts)
|
|
293 |
|
|
294 |
mod_code = {}
|
|
295 |
mod_libs = {}
|
|
296 |
modules = {}
|
|
297 |
app_libs = {}
|
|
298 |
early_app_libs = {}
|
|
299 |
app_code = {}
|
|
300 |
overrides = {}
|
|
301 |
pover = {}
|
|
302 |
app_modnames = {}
|
|
303 |
mod_levels = {}
|
|
304 |
|
|
305 |
# First, generate all the code.
|
|
306 |
# Second, (dynamic only), post-analyse the places where modules
|
|
307 |
# haven't changed
|
|
308 |
# Third, write everything out.
|
|
309 |
|
|
310 |
for platform in app_platforms:
|
|
311 |
|
|
312 |
mod_code[platform] = {}
|
|
313 |
mod_libs[platform] = {}
|
|
314 |
modules[platform] = []
|
|
315 |
pover[platform] = {}
|
|
316 |
app_libs[platform] = ''
|
|
317 |
early_app_libs[platform] = ''
|
|
318 |
app_code[platform] = {}
|
|
319 |
app_modnames[platform] = {}
|
|
320 |
|
|
321 |
# Application.Platform.cache.html
|
|
322 |
|
|
323 |
parser.setPlatform(platform)
|
|
324 |
app_translator = pyjs.AppTranslator(
|
|
325 |
parser=parser, dynamic=dynamic, optimize=optimize)
|
|
326 |
early_app_libs[platform], appcode = \
|
|
327 |
app_translator.translate(None, is_app=False,
|
|
328 |
debug=debug,
|
|
329 |
library_modules=['dynamicajax.js',
|
|
330 |
'_pyjs.js', 'sys',
|
|
331 |
'pyjslib'])
|
|
332 |
pover[platform].update(app_translator.overrides.items())
|
|
333 |
for mname, name in app_translator.overrides.items():
|
|
334 |
pd = overrides.setdefault(mname, {})
|
|
335 |
pd[platform] = name
|
|
336 |
|
|
337 |
print appcode
|
|
338 |
#mod_code[platform][app_name] = appcode
|
|
339 |
|
|
340 |
# platform.Module.cache.js
|
|
341 |
|
|
342 |
modules_done = ['pyjslib', 'sys', '_pyjs.js']
|
|
343 |
#modules_to_do = [app_name] + app_translator.library_modules
|
|
344 |
modules_to_do = [app_name] + app_translator.library_modules
|
|
345 |
|
|
346 |
dependencies = {}
|
|
347 |
|
|
348 |
deps = map(pyjs.strip_py, modules_to_do)
|
|
349 |
for d in deps:
|
|
350 |
sublist = add_subdeps(dependencies, d)
|
|
351 |
modules_to_do += sublist
|
|
352 |
deps = uniquify(deps)
|
|
353 |
#dependencies[app_name] = deps
|
|
354 |
|
|
355 |
modules[platform] = modules_done + modules_to_do
|
|
356 |
|
|
357 |
while modules_to_do:
|
|
358 |
|
|
359 |
#print "modules to do", modules_to_do
|
|
360 |
|
|
361 |
mn = modules_to_do.pop()
|
|
362 |
mod_name = pyjs.strip_py(mn)
|
|
363 |
|
|
364 |
if mod_name in modules_done:
|
|
365 |
continue
|
|
366 |
|
|
367 |
modules_done.append(mod_name)
|
|
368 |
|
|
369 |
mod_cache_name = "%s.%s.cache.js" % (platform.lower(), mod_name)
|
|
370 |
|
|
371 |
parser.setPlatform(platform)
|
|
372 |
mod_translator = pyjs.AppTranslator(parser=parser, optimize=optimize)
|
|
373 |
mod_libs[platform][mod_name], mod_code[platform][mod_name] = \
|
|
374 |
mod_translator.translate(mod_name,
|
|
375 |
is_app=False,
|
|
376 |
debug=debug)
|
|
377 |
pover[platform].update(mod_translator.overrides.items())
|
|
378 |
for mname, name in mod_translator.overrides.items():
|
|
379 |
pd = overrides.setdefault(mname, {})
|
|
380 |
pd[platform] = name
|
|
381 |
|
|
382 |
mods = mod_translator.library_modules
|
|
383 |
modules_to_do += mods
|
|
384 |
modules[platform] += mods
|
|
385 |
|
|
386 |
deps = map(pyjs.strip_py, mods)
|
|
387 |
sd = subdeps(mod_name)
|
|
388 |
if len(sd) > 1:
|
|
389 |
deps += sd[:-1]
|
|
390 |
while mod_name in deps:
|
|
391 |
deps.remove(mod_name)
|
|
392 |
|
|
393 |
#print
|
|
394 |
#print
|
|
395 |
#print "modname preadd:", mod_name, deps
|
|
396 |
#print
|
|
397 |
#print
|
|
398 |
for d in deps:
|
|
399 |
sublist = add_subdeps(dependencies, d)
|
|
400 |
modules_to_do += sublist
|
|
401 |
modules_to_do += add_subdeps(dependencies, mod_name)
|
|
402 |
#print "modname:", mod_name, deps
|
|
403 |
deps = uniquify(deps)
|
|
404 |
#print "modname:", mod_name, deps
|
|
405 |
dependencies[mod_name] = deps
|
|
406 |
|
|
407 |
# work out the dependency ordering of the modules
|
|
408 |
|
|
409 |
mod_levels[platform] = make_deps(None, dependencies, modules_done)
|
|
410 |
|
|
411 |
# now write everything out
|
|
412 |
|
|
413 |
for platform in app_platforms:
|
|
414 |
|
|
415 |
early_app_libs_ = early_app_libs[platform]
|
|
416 |
app_libs_ = app_libs[platform]
|
|
417 |
app_code_ = app_code[platform]
|
|
418 |
#modules_ = filter_mods(app_name, modules[platform])
|
|
419 |
mods = flattenlist(mod_levels[platform])
|
|
420 |
mods.reverse()
|
|
421 |
modules_ = filter_mods(None, mods)
|
|
422 |
|
|
423 |
for mod_name in modules_:
|
|
424 |
|
|
425 |
mod_code_ = mod_code[platform][mod_name]
|
|
426 |
|
|
427 |
mod_name = pyjs.strip_py(mod_name)
|
|
428 |
|
|
429 |
override_name = "%s.%s" % (platform.lower(), mod_name)
|
|
430 |
if pover[platform].has_key(override_name):
|
|
431 |
mod_cache_name = "%s.cache.js" % (override_name)
|
|
432 |
else:
|
|
433 |
mod_cache_name = "%s.cache.js" % (mod_name)
|
|
434 |
|
|
435 |
print "Creating: " + mod_cache_name
|
|
436 |
|
|
437 |
modlevels = make_deps(None, dependencies, dependencies[mod_name])
|
|
438 |
|
|
439 |
modnames = []
|
|
440 |
|
|
441 |
for md in modlevels:
|
|
442 |
mnames = map(lambda x: "'%s'" % x, md)
|
|
443 |
mnames = "new pyjslib.List([\n\t\t\t%s])" % ',\n\t\t\t'.join(mnames)
|
|
444 |
modnames.append(mnames)
|
|
445 |
|
|
446 |
modnames.reverse()
|
|
447 |
modnames = "new pyjslib.List([\n\t\t%s\n\t])" % ',\n\t\t'.join(modnames)
|
|
448 |
|
|
449 |
# convert the overrides
|
|
450 |
|
|
451 |
overnames = map(lambda x: "'%s': '%s'" % x, pover[platform].items())
|
|
452 |
overnames = "new pyjslib.Dict({\n\t\t%s\n\t})" % ',\n\t\t'.join(overnames)
|
|
453 |
|
|
454 |
if dynamic:
|
|
455 |
mod_cache_html_output = open(join(output, mod_cache_name), "w")
|
|
456 |
else:
|
|
457 |
mod_cache_html_output = StringIO()
|
|
458 |
|
|
459 |
print >>mod_cache_html_output, mod_cache_html_template % dict(
|
|
460 |
mod_name = mod_name,
|
|
461 |
app_name = app_name,
|
|
462 |
modnames = modnames,
|
|
463 |
overrides = overnames,
|
|
464 |
mod_libs = mod_libs[platform][mod_name],
|
|
465 |
dynamic = dynamic,
|
|
466 |
mod_code = mod_code_,
|
|
467 |
)
|
|
468 |
|
|
469 |
if dynamic:
|
|
470 |
mod_cache_html_output.close()
|
|
471 |
else:
|
|
472 |
mod_cache_html_output.seek(0)
|
|
473 |
app_libs_ += mod_cache_html_output.read()
|
|
474 |
|
|
475 |
# write out the dependency ordering of the modules
|
|
476 |
|
|
477 |
app_modnames = []
|
|
478 |
|
|
479 |
for md in mod_levels[platform]:
|
|
480 |
mnames = map(lambda x: "'%s'" % x, md)
|
|
481 |
mnames = "new pyjslib.List([\n\t\t\t%s])" % ',\n\t\t\t'.join(mnames)
|
|
482 |
app_modnames.append(mnames)
|
|
483 |
|
|
484 |
app_modnames.reverse()
|
|
485 |
app_modnames = "new pyjslib.List([\n\t\t%s\n\t])" % ',\n\t\t'.join(app_modnames)
|
|
486 |
|
|
487 |
# convert the overrides
|
|
488 |
|
|
489 |
overnames = map(lambda x: "'%s': '%s'" % x, pover[platform].items())
|
|
490 |
overnames = "new pyjslib.Dict({\n\t\t%s\n\t})" % ',\n\t\t'.join(overnames)
|
|
491 |
|
|
492 |
#print "platform names", platform, overnames
|
|
493 |
#print pover
|
|
494 |
|
|
495 |
# now write app.allcache including dependency-ordered list of
|
|
496 |
# library modules
|
|
497 |
|
|
498 |
file_contents = all_cache_html_template % dict(
|
|
499 |
app_name = app_name,
|
|
500 |
early_app_libs = early_app_libs_,
|
|
501 |
app_libs = app_libs_,
|
|
502 |
app_code = app_code_,
|
|
503 |
app_body = app_body,
|
|
504 |
overrides = overnames,
|
|
505 |
platform = platform.lower(),
|
|
506 |
dynamic = dynamic,
|
|
507 |
app_modnames = app_modnames,
|
|
508 |
app_headers = app_headers
|
|
509 |
)
|
|
510 |
if cache_buster:
|
|
511 |
digest = md5.new(file_contents).hexdigest()
|
|
512 |
file_name = "%s.%s.%s" % (platform.lower(), app_name, digest)
|
|
513 |
else:
|
|
514 |
file_name = "%s.%s" % (platform.lower(), app_name)
|
|
515 |
file_name += ".cache.html"
|
|
516 |
out_path = join(output, file_name)
|
|
517 |
out_file = open(out_path, 'w')
|
|
518 |
out_file.write(file_contents)
|
|
519 |
out_file.close()
|
|
520 |
app_files.append((platform.lower(), file_name))
|
|
521 |
print "Created app file %s:%s: %s" % (
|
|
522 |
app_name, platform, out_path)
|
|
523 |
|
|
524 |
return app_files
|
|
525 |
|
|
526 |
def flattenlist(ll):
|
|
527 |
res = []
|
|
528 |
for l in ll:
|
|
529 |
res += l
|
|
530 |
return res
|
|
531 |
|
|
532 |
# creates sub-dependencies e.g. pyjamas.ui.Widget
|
|
533 |
# creates pyjamas.ui.Widget, pyjamas.ui and pyjamas.
|
|
534 |
def subdeps(m):
|
|
535 |
d = []
|
|
536 |
m = m.split(".")
|
|
537 |
for i in range(0, len(m)):
|
|
538 |
d.append('.'.join(m[:i+1]))
|
|
539 |
return d
|
|
540 |
|
|
541 |
import time
|
|
542 |
|
|
543 |
def add_subdeps(deps, mod_name):
|
|
544 |
sd = subdeps(mod_name)
|
|
545 |
if len(sd) == 1:
|
|
546 |
return []
|
|
547 |
#print "subdeps", mod_name, sd
|
|
548 |
#print "deps", deps
|
|
549 |
res = []
|
|
550 |
for i in range(0, len(sd)-1):
|
|
551 |
parent = sd[i]
|
|
552 |
child = sd[i+1]
|
|
553 |
l = deps.get(child, [])
|
|
554 |
l.append(parent)
|
|
555 |
deps[child] = l
|
|
556 |
if parent not in res:
|
|
557 |
res.append(parent)
|
|
558 |
#print deps
|
|
559 |
return res
|
|
560 |
|
|
561 |
# makes unique and preserves list order
|
|
562 |
def uniquify(md):
|
|
563 |
res = []
|
|
564 |
for m in md:
|
|
565 |
if m not in res:
|
|
566 |
res.append(m)
|
|
567 |
return res
|
|
568 |
|
|
569 |
def filter_mods(app_name, md):
|
|
570 |
while 'sys' in md:
|
|
571 |
md.remove('sys')
|
|
572 |
while 'pyjslib' in md:
|
|
573 |
md.remove('pyjslib')
|
|
574 |
while app_name in md:
|
|
575 |
md.remove(app_name)
|
|
576 |
md = filter(lambda x: not x.endswith('.js'), md)
|
|
577 |
md = map(pyjs.strip_py, md)
|
|
578 |
|
|
579 |
return uniquify(md)
|
|
580 |
|
|
581 |
def filter_deps(app_name, deps):
|
|
582 |
|
|
583 |
res = {}
|
|
584 |
for (k, l) in deps.items():
|
|
585 |
mods = filter_mods(k, l)
|
|
586 |
while k in mods:
|
|
587 |
mods.remove(k)
|
|
588 |
res[k] = mods
|
|
589 |
return res
|
|
590 |
|
|
591 |
def has_nodeps(mod, deps):
|
|
592 |
if not deps.has_key(mod) or not deps[mod]:
|
|
593 |
return True
|
|
594 |
return False
|
|
595 |
|
|
596 |
def nodeps_list(mod_list, deps):
|
|
597 |
res = []
|
|
598 |
for mod in mod_list:
|
|
599 |
if has_nodeps(mod, deps):
|
|
600 |
res.append(mod)
|
|
601 |
return res
|
|
602 |
|
|
603 |
# this function takes a dictionary of dependent modules and
|
|
604 |
# creates a list of lists. the first list will be modules
|
|
605 |
# that have no dependencies; the second list will be those
|
|
606 |
# modules that have the first list as dependencies; the
|
|
607 |
# third will be those modules that have the first and second...
|
|
608 |
# etc.
|
|
609 |
|
|
610 |
|
|
611 |
def make_deps(app_name, deps, mod_list):
|
|
612 |
print "Calculating Dependencies ..."
|
|
613 |
mod_list = filter_mods(app_name, mod_list)
|
|
614 |
deps = filter_deps(app_name, deps)
|
|
615 |
|
|
616 |
if not mod_list:
|
|
617 |
return []
|
|
618 |
|
|
619 |
#print mod_list
|
|
620 |
#print deps
|
|
621 |
|
|
622 |
ordered_deps = []
|
|
623 |
last_len = -1
|
|
624 |
while deps:
|
|
625 |
l_deps = len(deps)
|
|
626 |
#print l_deps
|
|
627 |
if l_deps==last_len:
|
|
628 |
for m, dl in deps.items():
|
|
629 |
for d in dl:
|
|
630 |
if m in deps.get(d, []):
|
|
631 |
raise Exception('Circular Imports found: \n%s %s -> %s %s'
|
|
632 |
% (m, dl, d, deps[d]))
|
|
633 |
#raise Exception('Could not calculate dependencies: \n%s' % deps)
|
|
634 |
break
|
|
635 |
last_len = l_deps
|
|
636 |
#print "modlist", mod_list
|
|
637 |
nodeps = nodeps_list(mod_list, deps)
|
|
638 |
#print "nodeps", nodeps
|
|
639 |
mod_list = filter(lambda x: x not in nodeps, mod_list)
|
|
640 |
newdeps = {}
|
|
641 |
for k in deps.keys():
|
|
642 |
depslist = deps[k]
|
|
643 |
depslist = filter(lambda x: x not in nodeps, depslist)
|
|
644 |
if depslist:
|
|
645 |
newdeps[k] = depslist
|
|
646 |
#print "newdeps", newdeps
|
|
647 |
deps = newdeps
|
|
648 |
ordered_deps.append(nodeps)
|
|
649 |
#time.sleep(0)
|
|
650 |
|
|
651 |
if mod_list:
|
|
652 |
ordered_deps.append(mod_list) # last dependencies - usually the app(s)
|
|
653 |
|
|
654 |
ordered_deps.reverse()
|
|
655 |
|
|
656 |
return ordered_deps
|
|
657 |
|
|
658 |
def main():
|
|
659 |
global app_platforms
|
|
660 |
|
|
661 |
parser = OptionParser(usage = usage, version = version)
|
|
662 |
parser.add_option("-o", "--output", dest="output",
|
|
663 |
help="directory to which the webapp should be written")
|
|
664 |
parser.add_option("-j", "--include-js", dest="js_includes", action="append",
|
|
665 |
help="javascripts to load into the same frame as the rest of the script")
|
|
666 |
parser.add_option("-I", "--library_dir", dest="library_dirs",
|
|
667 |
action="append", help="additional paths appended to PYJSPATH")
|
|
668 |
parser.add_option("-D", "--data_dir", dest="data_dir",
|
|
669 |
help="path for data directory")
|
|
670 |
parser.add_option("-m", "--dynamic-modules", action="store_true",
|
|
671 |
dest="dynamic", default=False,
|
|
672 |
help="Split output into separate dynamically-loaded modules (experimental)")
|
|
673 |
parser.add_option("-P", "--platforms", dest="platforms",
|
|
674 |
help="platforms to build for, comma-separated")
|
|
675 |
parser.add_option("-d", "--debug", action="store_true", dest="debug")
|
|
676 |
parser.add_option("-O", "--optimize", action="store_true",
|
|
677 |
dest="optimize", default=False,
|
|
678 |
help="Optimize generated code (removes all print statements)",
|
|
679 |
)
|
|
680 |
parser.add_option("-c", "--cache_buster", action="store_true",
|
|
681 |
dest="cache_buster",
|
|
682 |
help="Enable browser cache-busting (MD5 hash added to output filenames)")
|
|
683 |
|
|
684 |
parser.set_defaults(output = "output", js_includes=[], library_dirs=[],
|
|
685 |
platforms=(','.join(app_platforms)),
|
|
686 |
data_dir=os.path.join(sys.prefix, "share/pyjamas"),
|
|
687 |
dynamic=False,
|
|
688 |
cache_buster=False,
|
|
689 |
debug=False)
|
|
690 |
(options, args) = parser.parse_args()
|
|
691 |
if len(args) != 1:
|
|
692 |
parser.error("incorrect number of arguments")
|
|
693 |
|
|
694 |
data_dir = abspath(options.data_dir)
|
|
695 |
|
|
696 |
app_path = args[0]
|
|
697 |
if app_path.endswith('.py'):
|
|
698 |
app_path = abspath(app_path)
|
|
699 |
if not isfile(app_path):
|
|
700 |
parser.error("Application file not found %r" % app_path)
|
|
701 |
app_path, app_name = split(app_path)
|
|
702 |
app_name = app_name[:-3]
|
|
703 |
pyjs.path.append(app_path)
|
|
704 |
elif os.path.sep in app_path:
|
|
705 |
parser.error("Not a valid module declaration %r" % app_path)
|
|
706 |
else:
|
|
707 |
app_name = app_path
|
|
708 |
|
|
709 |
for d in options.library_dirs:
|
|
710 |
pyjs.path.append(abspath(d))
|
|
711 |
|
|
712 |
if options.platforms:
|
|
713 |
app_platforms = options.platforms.split(',')
|
|
714 |
|
|
715 |
# this is mostly for getting boilerplate stuff
|
|
716 |
data_dir = os.path.abspath(options.data_dir)
|
|
717 |
|
|
718 |
build(app_name, options.output, options.js_includes,
|
|
719 |
options.debug, options.dynamic and 1 or 0, data_dir,
|
|
720 |
options.cache_buster, options.optimize)
|
|
721 |
|
|
722 |
if __name__ == "__main__":
|
|
723 |
main()
|
|
724 |
|