1 #!/usr/bin/env python |
|
2 |
|
3 |
|
4 from __future__ import absolute_import |
|
5 from __future__ import print_function |
|
6 import sys |
|
7 import shutil |
|
8 import re |
|
9 import os |
|
10 from os.path import join, basename, abspath, split, isfile, isdir |
|
11 from hashlib import md5 |
|
12 from optparse import OptionParser |
|
13 from six.moves import cStringIO |
|
14 |
|
15 from svgui.pyjs import pyjs |
|
16 |
|
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(r'^[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 |
|
67 def copy_boilerplate(data_dir, filename, output_dir): |
|
68 filename = join(data_dir, "builder/boilerplate", filename) |
|
69 shutil.copy(filename, output_dir) |
|
70 |
|
71 |
|
72 # taken and modified from python2.4 |
|
73 def copytree_exists(src, dst, symlinks=False): |
|
74 if not os.path.exists(src): |
|
75 return |
|
76 |
|
77 names = os.listdir(src) |
|
78 try: |
|
79 os.mkdir(dst) |
|
80 except Exception: |
|
81 pass |
|
82 |
|
83 errors = [] |
|
84 for name in names: |
|
85 if name.startswith('CVS'): |
|
86 continue |
|
87 if name.startswith('.git'): |
|
88 continue |
|
89 if name.startswith('.svn'): |
|
90 continue |
|
91 |
|
92 srcname = os.path.join(src, name) |
|
93 dstname = os.path.join(dst, name) |
|
94 try: |
|
95 if symlinks and os.path.islink(srcname): |
|
96 linkto = os.readlink(srcname) |
|
97 os.symlink(linkto, dstname) |
|
98 elif isdir(srcname): |
|
99 copytree_exists(srcname, dstname, symlinks) |
|
100 else: |
|
101 shutil.copy2(srcname, dstname) |
|
102 except (IOError, os.error) as why: |
|
103 errors.append((srcname, dstname, why)) |
|
104 if errors: |
|
105 print(errors) |
|
106 |
|
107 |
|
108 def check_html_file(source_file, dest_path): |
|
109 """ Checks if a base HTML-file is available in the PyJamas |
|
110 output directory. |
|
111 If the HTML-file isn't available, it will be created. |
|
112 |
|
113 If a CSS-file with the same name is available |
|
114 in the output directory, a reference to this CSS-file |
|
115 is included. |
|
116 |
|
117 If no CSS-file is found, this function will look for a special |
|
118 CSS-file in the output directory, with the name |
|
119 "pyjamas_default.css", and if found it will be referenced |
|
120 in the generated HTML-file. |
|
121 |
|
122 [thank you to stef mientki for contributing this function] |
|
123 """ |
|
124 |
|
125 base_html = """\ |
|
126 <html> |
|
127 <!-- auto-generated html - you should consider editing and |
|
128 adapting this to suit your requirements |
|
129 --> |
|
130 <head> |
|
131 <meta name="pygwt:module" content="%(modulename)s"> |
|
132 %(css)s |
|
133 <title>%(title)s</title> |
|
134 </head> |
|
135 <body bgcolor="white"> |
|
136 <script language="javascript" src="pygwt.js"></script> |
|
137 </body> |
|
138 </html> |
|
139 """ |
|
140 |
|
141 filename = os.path.split(source_file)[1] |
|
142 mod_name = os.path.splitext(filename)[0] |
|
143 file_name = os.path.join(dest_path, mod_name + '.html') |
|
144 |
|
145 # if html file in output directory exists, leave it alone. |
|
146 if os.path.exists(file_name): |
|
147 return 0 |
|
148 |
|
149 if os.path.exists(os.path.join(dest_path, mod_name + '.css')): |
|
150 css = "<link rel='stylesheet' href='" + mod_name + ".css'>" |
|
151 elif os.path.exists(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("Output destination %s exists and is not a directory" % output, file=sys.stderr) |
|
182 return |
|
183 if not os.path.isdir(output): |
|
184 try: |
|
185 print("Creating output directory") |
|
186 os.mkdir(output) |
|
187 except OSError as e: |
|
188 print("Exception creating output directory %s: %s" % (output, e), file=sys.stderr) |
|
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 Exception: |
|
207 print("Warning: Missing module HTML file %s" % html_input_filename, file=sys.stderr) |
|
208 |
|
209 print("Copying: %(html_input_filename)s" % locals()) |
|
210 |
|
211 if check_html_file(html_input_filename, output): |
|
212 print("Warning: Module HTML file %s has been auto-generated" % html_input_filename, file=sys.stderr) |
|
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_template, file=pygwt_js_output) |
|
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 # all.cache.html |
|
244 app_files = generateAppFiles(data_dir, js_includes, app_name, debug, |
|
245 output, dynamic, cache_buster, optimize) |
|
246 |
|
247 # AppName.nocache.html |
|
248 |
|
249 print("Creating: %(app_name)s.nocache.html" % locals()) |
|
250 |
|
251 home_nocache_html_template = read_boilerplate(data_dir, "home.nocache.html") |
|
252 home_nocache_html_output = open(join(output, app_name + ".nocache.html"), |
|
253 "w") |
|
254 |
|
255 # the selector templ is added to the selectScript function |
|
256 select_tmpl = """O(["true","%s"],"%s");""" |
|
257 script_selectors = cStringIO() |
|
258 |
|
259 for platform, file_prefix in app_files: |
|
260 print(select_tmpl % (platform, file_prefix), file=script_selectors) |
|
261 |
|
262 print( |
|
263 home_nocache_html_template % dict( |
|
264 app_name=app_name, |
|
265 script_selectors=script_selectors.getvalue(), |
|
266 ), file=home_nocache_html_output) |
|
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 parser = pyjs.PlatformParser("platform") |
|
288 app_headers = '' |
|
289 scripts = ['<script type="text/javascript" src="%s"></script>' % |
|
290 script for script in js_includes] |
|
291 app_body = '\n'.join(scripts) |
|
292 |
|
293 mod_code = {} |
|
294 mod_libs = {} |
|
295 modules = {} |
|
296 app_libs = {} |
|
297 early_app_libs = {} |
|
298 app_code = {} |
|
299 overrides = {} |
|
300 pover = {} |
|
301 app_modnames = {} |
|
302 mod_levels = {} |
|
303 |
|
304 # First, generate all the code. |
|
305 # Second, (dynamic only), post-analyse the places where modules |
|
306 # haven't changed |
|
307 # Third, write everything out. |
|
308 |
|
309 for platform in app_platforms: |
|
310 |
|
311 mod_code[platform] = {} |
|
312 mod_libs[platform] = {} |
|
313 modules[platform] = [] |
|
314 pover[platform] = {} |
|
315 app_libs[platform] = '' |
|
316 early_app_libs[platform] = '' |
|
317 app_code[platform] = {} |
|
318 app_modnames[platform] = {} |
|
319 |
|
320 # Application.Platform.cache.html |
|
321 |
|
322 parser.setPlatform(platform) |
|
323 app_translator = pyjs.AppTranslator( |
|
324 parser=parser, dynamic=dynamic, optimize=optimize) |
|
325 early_app_libs[platform], appcode = \ |
|
326 app_translator.translate(None, is_app=False, |
|
327 debug=debug, |
|
328 library_modules=['dynamicajax.js', |
|
329 '_pyjs.js', 'sys', |
|
330 'pyjslib']) |
|
331 pover[platform].update(app_translator.overrides.items()) |
|
332 for mname, name in app_translator.overrides.items(): |
|
333 pd = overrides.setdefault(mname, {}) |
|
334 pd[platform] = name |
|
335 |
|
336 print(appcode) |
|
337 # mod_code[platform][app_name] = appcode |
|
338 |
|
339 # platform.Module.cache.js |
|
340 |
|
341 modules_done = ['pyjslib', 'sys', '_pyjs.js'] |
|
342 # modules_to_do = [app_name] + app_translator.library_modules |
|
343 modules_to_do = [app_name] + app_translator.library_modules |
|
344 |
|
345 dependencies = {} |
|
346 |
|
347 deps = map(pyjs.strip_py, modules_to_do) |
|
348 for d in deps: |
|
349 sublist = add_subdeps(dependencies, d) |
|
350 modules_to_do += sublist |
|
351 deps = uniquify(deps) |
|
352 # dependencies[app_name] = deps |
|
353 |
|
354 modules[platform] = modules_done + modules_to_do |
|
355 |
|
356 while modules_to_do: |
|
357 |
|
358 # print "modules to do", modules_to_do |
|
359 |
|
360 mn = modules_to_do.pop() |
|
361 mod_name = pyjs.strip_py(mn) |
|
362 |
|
363 if mod_name in modules_done: |
|
364 continue |
|
365 |
|
366 modules_done.append(mod_name) |
|
367 |
|
368 mod_cache_name = "%s.%s.cache.js" % (platform.lower(), mod_name) |
|
369 |
|
370 parser.setPlatform(platform) |
|
371 mod_translator = pyjs.AppTranslator(parser=parser, optimize=optimize) |
|
372 mod_libs[platform][mod_name], mod_code[platform][mod_name] = \ |
|
373 mod_translator.translate(mod_name, |
|
374 is_app=False, |
|
375 debug=debug) |
|
376 pover[platform].update(mod_translator.overrides.items()) |
|
377 for mname, name in mod_translator.overrides.items(): |
|
378 pd = overrides.setdefault(mname, {}) |
|
379 pd[platform] = name |
|
380 |
|
381 mods = mod_translator.library_modules |
|
382 modules_to_do += mods |
|
383 modules[platform] += mods |
|
384 |
|
385 deps = map(pyjs.strip_py, mods) |
|
386 sd = subdeps(mod_name) |
|
387 if len(sd) > 1: |
|
388 deps += sd[:-1] |
|
389 while mod_name in deps: |
|
390 deps.remove(mod_name) |
|
391 |
|
392 # print |
|
393 # print |
|
394 # print "modname preadd:", mod_name, deps |
|
395 # print |
|
396 # print |
|
397 for d in deps: |
|
398 sublist = add_subdeps(dependencies, d) |
|
399 modules_to_do += sublist |
|
400 modules_to_do += add_subdeps(dependencies, mod_name) |
|
401 # print "modname:", mod_name, deps |
|
402 deps = uniquify(deps) |
|
403 # print "modname:", mod_name, deps |
|
404 dependencies[mod_name] = deps |
|
405 |
|
406 # work out the dependency ordering of the modules |
|
407 |
|
408 mod_levels[platform] = make_deps(None, dependencies, modules_done) |
|
409 |
|
410 # now write everything out |
|
411 |
|
412 for platform in app_platforms: |
|
413 |
|
414 early_app_libs_ = early_app_libs[platform] |
|
415 app_libs_ = app_libs[platform] |
|
416 app_code_ = app_code[platform] |
|
417 # modules_ = filter_mods(app_name, modules[platform]) |
|
418 mods = flattenlist(mod_levels[platform]) |
|
419 mods.reverse() |
|
420 modules_ = filter_mods(None, mods) |
|
421 |
|
422 for mod_name in modules_: |
|
423 |
|
424 mod_code_ = mod_code[platform][mod_name] |
|
425 |
|
426 mod_name = pyjs.strip_py(mod_name) |
|
427 |
|
428 override_name = "%s.%s" % (platform.lower(), mod_name) |
|
429 if override_name in pover[platform]: |
|
430 mod_cache_name = "%s.cache.js" % (override_name) |
|
431 else: |
|
432 mod_cache_name = "%s.cache.js" % (mod_name) |
|
433 |
|
434 print("Creating: " + mod_cache_name) |
|
435 |
|
436 modlevels = make_deps(None, dependencies, dependencies[mod_name]) |
|
437 |
|
438 modnames = [] |
|
439 |
|
440 for md in modlevels: |
|
441 mnames = map(lambda x: "'%s'" % x, md) |
|
442 mnames = "new pyjslib.List([\n\t\t\t%s])" % ',\n\t\t\t'.join(mnames) |
|
443 modnames.append(mnames) |
|
444 |
|
445 modnames.reverse() |
|
446 modnames = "new pyjslib.List([\n\t\t%s\n\t])" % ',\n\t\t'.join(modnames) |
|
447 |
|
448 # convert the overrides |
|
449 |
|
450 overnames = map(lambda x: "'%s': '%s'" % x, pover[platform].items()) |
|
451 overnames = "new pyjslib.Dict({\n\t\t%s\n\t})" % ',\n\t\t'.join(overnames) |
|
452 |
|
453 if dynamic: |
|
454 mod_cache_html_output = open(join(output, mod_cache_name), "w") |
|
455 else: |
|
456 mod_cache_html_output = cStringIO() |
|
457 |
|
458 print(mod_cache_html_template % dict( |
|
459 mod_name=mod_name, |
|
460 app_name=app_name, |
|
461 modnames=modnames, |
|
462 overrides=overnames, |
|
463 mod_libs=mod_libs[platform][mod_name], |
|
464 dynamic=dynamic, |
|
465 mod_code=mod_code_, |
|
466 ), file=mod_cache_html_output) |
|
467 |
|
468 if dynamic: |
|
469 mod_cache_html_output.close() |
|
470 else: |
|
471 mod_cache_html_output.seek(0) |
|
472 app_libs_ += mod_cache_html_output.read() |
|
473 |
|
474 # write out the dependency ordering of the modules |
|
475 |
|
476 app_modnames = [] |
|
477 |
|
478 for md in mod_levels[platform]: |
|
479 mnames = map(lambda x: "'%s'" % x, md) |
|
480 mnames = "new pyjslib.List([\n\t\t\t%s])" % ',\n\t\t\t'.join(mnames) |
|
481 app_modnames.append(mnames) |
|
482 |
|
483 app_modnames.reverse() |
|
484 app_modnames = "new pyjslib.List([\n\t\t%s\n\t])" % ',\n\t\t'.join(app_modnames) |
|
485 |
|
486 # convert the overrides |
|
487 |
|
488 overnames = map(lambda x: "'%s': '%s'" % x, pover[platform].items()) |
|
489 overnames = "new pyjslib.Dict({\n\t\t%s\n\t})" % ',\n\t\t'.join(overnames) |
|
490 |
|
491 # print "platform names", platform, overnames |
|
492 # print pover |
|
493 |
|
494 # now write app.allcache including dependency-ordered list of |
|
495 # library modules |
|
496 |
|
497 file_contents = all_cache_html_template % dict( |
|
498 app_name=app_name, |
|
499 early_app_libs=early_app_libs_, |
|
500 app_libs=app_libs_, |
|
501 app_code=app_code_, |
|
502 app_body=app_body, |
|
503 overrides=overnames, |
|
504 platform=platform.lower(), |
|
505 dynamic=dynamic, |
|
506 app_modnames=app_modnames, |
|
507 app_headers=app_headers |
|
508 ) |
|
509 if cache_buster: |
|
510 digest = md5.new(file_contents).hexdigest() |
|
511 file_name = "%s.%s.%s" % (platform.lower(), app_name, digest) |
|
512 else: |
|
513 file_name = "%s.%s" % (platform.lower(), app_name) |
|
514 file_name += ".cache.html" |
|
515 out_path = join(output, file_name) |
|
516 out_file = open(out_path, 'w') |
|
517 out_file.write(file_contents) |
|
518 out_file.close() |
|
519 app_files.append((platform.lower(), file_name)) |
|
520 print("Created app file %s:%s: %s" % ( |
|
521 app_name, platform, out_path)) |
|
522 |
|
523 return app_files |
|
524 |
|
525 |
|
526 def flattenlist(ll): |
|
527 res = [] |
|
528 for l in ll: |
|
529 res += l |
|
530 return res |
|
531 |
|
532 |
|
533 def subdeps(m): |
|
534 """ |
|
535 creates sub-dependencies e.g. pyjamas.ui.Widget |
|
536 creates pyjamas.ui.Widget, pyjamas.ui and pyjamas. |
|
537 """ |
|
538 d = [] |
|
539 m = m.split(".") |
|
540 for i in range(0, len(m)): |
|
541 d.append('.'.join(m[:i+1])) |
|
542 return d |
|
543 |
|
544 |
|
545 def add_subdeps(deps, mod_name): |
|
546 sd = subdeps(mod_name) |
|
547 if len(sd) == 1: |
|
548 return [] |
|
549 # print "subdeps", mod_name, sd |
|
550 # print "deps", deps |
|
551 res = [] |
|
552 for i in range(0, len(sd)-1): |
|
553 parent = sd[i] |
|
554 child = sd[i+1] |
|
555 k = deps.get(child, []) |
|
556 k.append(parent) |
|
557 deps[child] = k |
|
558 if parent not in res: |
|
559 res.append(parent) |
|
560 # print deps |
|
561 return res |
|
562 |
|
563 |
|
564 def uniquify(md): |
|
565 """ |
|
566 makes unique and preserves list order |
|
567 """ |
|
568 res = [] |
|
569 for m in md: |
|
570 if m not in res: |
|
571 res.append(m) |
|
572 return res |
|
573 |
|
574 |
|
575 def filter_mods(app_name, md): |
|
576 while 'sys' in md: |
|
577 md.remove('sys') |
|
578 while 'pyjslib' in md: |
|
579 md.remove('pyjslib') |
|
580 while app_name in md: |
|
581 md.remove(app_name) |
|
582 md = filter(lambda x: not x.endswith('.js'), md) |
|
583 md = map(pyjs.strip_py, md) |
|
584 |
|
585 return uniquify(md) |
|
586 |
|
587 |
|
588 def filter_deps(app_name, deps): |
|
589 |
|
590 res = {} |
|
591 for (k, l) in deps.items(): |
|
592 mods = filter_mods(k, l) |
|
593 while k in mods: |
|
594 mods.remove(k) |
|
595 res[k] = mods |
|
596 return res |
|
597 |
|
598 |
|
599 def has_nodeps(mod, deps): |
|
600 if mod not in deps or not deps[mod]: |
|
601 return True |
|
602 return False |
|
603 |
|
604 |
|
605 def nodeps_list(mod_list, deps): |
|
606 res = [] |
|
607 for mod in mod_list: |
|
608 if has_nodeps(mod, deps): |
|
609 res.append(mod) |
|
610 return res |
|
611 |
|
612 # this function takes a dictionary of dependent modules and |
|
613 # creates a list of lists. the first list will be modules |
|
614 # that have no dependencies; the second list will be those |
|
615 # modules that have the first list as dependencies; the |
|
616 # third will be those modules that have the first and second... |
|
617 # etc. |
|
618 |
|
619 |
|
620 def make_deps(app_name, deps, mod_list): |
|
621 print("Calculating Dependencies ...") |
|
622 mod_list = filter_mods(app_name, mod_list) |
|
623 deps = filter_deps(app_name, deps) |
|
624 |
|
625 if not mod_list: |
|
626 return [] |
|
627 |
|
628 # print mod_list |
|
629 # print deps |
|
630 |
|
631 ordered_deps = [] |
|
632 last_len = -1 |
|
633 while deps: |
|
634 l_deps = len(deps) |
|
635 # print l_deps |
|
636 if l_deps == last_len: |
|
637 for m, dl in deps.items(): |
|
638 for d in dl: |
|
639 if m in deps.get(d, []): |
|
640 raise Exception('Circular Imports found: \n%s %s -> %s %s' |
|
641 % (m, dl, d, deps[d])) |
|
642 # raise Exception('Could not calculate dependencies: \n%s' % deps) |
|
643 break |
|
644 last_len = l_deps |
|
645 # print "modlist", mod_list |
|
646 nodeps = nodeps_list(mod_list, deps) |
|
647 # print "nodeps", nodeps |
|
648 mod_list = filter(lambda x: x not in nodeps, mod_list) |
|
649 newdeps = {} |
|
650 for k in deps.keys(): |
|
651 depslist = deps[k] |
|
652 depslist = filter(lambda x: x not in nodeps, depslist) |
|
653 if depslist: |
|
654 newdeps[k] = depslist |
|
655 # print "newdeps", newdeps |
|
656 deps = newdeps |
|
657 ordered_deps.append(nodeps) |
|
658 # time.sleep(0) |
|
659 |
|
660 if mod_list: |
|
661 ordered_deps.append(mod_list) # last dependencies - usually the app(s) |
|
662 |
|
663 ordered_deps.reverse() |
|
664 |
|
665 return ordered_deps |
|
666 |
|
667 |
|
668 def main(): |
|
669 global app_platforms |
|
670 |
|
671 parser = OptionParser(usage=usage, version=version) |
|
672 parser.add_option( |
|
673 "-o", |
|
674 "--output", |
|
675 dest="output", |
|
676 help="directory to which the webapp should be written" |
|
677 ) |
|
678 parser.add_option( |
|
679 "-j", |
|
680 "--include-js", |
|
681 dest="js_includes", |
|
682 action="append", |
|
683 help="javascripts to load into the same frame as the rest of the script" |
|
684 ) |
|
685 parser.add_option( |
|
686 "-I", |
|
687 "--library_dir", |
|
688 dest="library_dirs", |
|
689 action="append", |
|
690 help="additional paths appended to PYJSPATH" |
|
691 ) |
|
692 parser.add_option( |
|
693 "-D", |
|
694 "--data_dir", |
|
695 dest="data_dir", |
|
696 help="path for data directory" |
|
697 ) |
|
698 parser.add_option( |
|
699 "-m", |
|
700 "--dynamic-modules", |
|
701 action="store_true", |
|
702 dest="dynamic", |
|
703 default=False, |
|
704 help="Split output into separate dynamically-loaded modules (experimental)" |
|
705 ) |
|
706 parser.add_option( |
|
707 "-P", |
|
708 "--platforms", |
|
709 dest="platforms", |
|
710 help="platforms to build for, comma-separated" |
|
711 ) |
|
712 parser.add_option( |
|
713 "-d", |
|
714 "--debug", |
|
715 action="store_true", |
|
716 dest="debug" |
|
717 ) |
|
718 parser.add_option( |
|
719 "-O", |
|
720 "--optimize", |
|
721 action="store_true", |
|
722 dest="optimize", |
|
723 default=False, |
|
724 help="Optimize generated code (removes all print statements)", |
|
725 ) |
|
726 parser.add_option( |
|
727 "-c", |
|
728 "--cache_buster", |
|
729 action="store_true", |
|
730 dest="cache_buster", |
|
731 help="Enable browser cache-busting (MD5 hash added to output filenames)" |
|
732 ) |
|
733 |
|
734 parser.set_defaults(output="output", js_includes=[], library_dirs=[], |
|
735 platforms=(','.join(app_platforms)), |
|
736 data_dir=os.path.join(sys.prefix, "share/pyjamas"), |
|
737 dynamic=False, |
|
738 cache_buster=False, |
|
739 debug=False) |
|
740 (options, args) = parser.parse_args() |
|
741 if len(args) != 1: |
|
742 parser.error("incorrect number of arguments") |
|
743 |
|
744 data_dir = abspath(options.data_dir) |
|
745 |
|
746 app_path = args[0] |
|
747 if app_path.endswith('.py'): |
|
748 app_path = abspath(app_path) |
|
749 if not isfile(app_path): |
|
750 parser.error("Application file not found %r" % app_path) |
|
751 app_path, app_name = split(app_path) |
|
752 app_name = app_name[:-3] |
|
753 pyjs.path.append(app_path) |
|
754 elif os.path.sep in app_path: |
|
755 parser.error("Not a valid module declaration %r" % app_path) |
|
756 else: |
|
757 app_name = app_path |
|
758 |
|
759 for d in options.library_dirs: |
|
760 pyjs.path.append(abspath(d)) |
|
761 |
|
762 if options.platforms: |
|
763 app_platforms = options.platforms.split(',') |
|
764 |
|
765 # this is mostly for getting boilerplate stuff |
|
766 data_dir = os.path.abspath(options.data_dir) |
|
767 |
|
768 build(app_name, options.output, options.js_includes, |
|
769 options.debug, options.dynamic and 1 or 0, data_dir, |
|
770 options.cache_buster, options.optimize) |
|
771 |
|
772 |
|
773 if __name__ == "__main__": |
|
774 main() |
|