|
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 |