laurent@371: #!/usr/bin/env python laurent@371: laurent@371: import sys laurent@371: import os laurent@371: import shutil laurent@371: from copy import copy laurent@371: from os.path import join, dirname, basename, abspath, split, isfile, isdir laurent@371: from optparse import OptionParser laurent@371: import pyjs laurent@371: from cStringIO import StringIO laurent@371: try: laurent@371: # Python 2.5 and above laurent@371: from hashlib import md5 laurent@371: except: laurent@371: import md5 laurent@371: import re laurent@371: laurent@371: usage = """ laurent@371: usage: %prog [options] laurent@371: laurent@371: This is the command line builder for the pyjamas project, which can laurent@371: be used to build Ajax applications from Python. laurent@371: For more information, see the website at http://pyjs.org/ laurent@371: """ laurent@371: laurent@371: # GWT1.2 Impl | GWT1.2 Output | Pyjamas 0.2 Platform | Pyjamas 0.2 Output laurent@371: # -------------+-----------------------+----------------------+---------------------- laurent@371: # IE6 | ie6 | IE6 | ie6 laurent@371: # Opera | opera | Opera | opera laurent@371: # Safari | safari | Safari | safari laurent@371: # -- | gecko1_8 | Mozilla | mozilla laurent@371: # -- | gecko | OldMoz | oldmoz laurent@371: # Standard | all | (default code) | all laurent@371: # Mozilla | gecko1_8, gecko | -- | -- laurent@371: # Old | safari, gecko, opera | -- | -- laurent@371: laurent@371: version = "%prog pyjamas version 2006-08-19" laurent@371: laurent@371: # these names in lowercase need match the strings laurent@371: # returned by "provider$user.agent" in order to be selected corretly laurent@371: app_platforms = ['IE6', 'Opera', 'OldMoz', 'Safari', 'Mozilla'] laurent@371: laurent@371: # usually defaults to e.g. /usr/share/pyjamas laurent@371: _data_dir = os.path.join(pyjs.prefix, "share/pyjamas") laurent@371: laurent@371: laurent@371: # .cache.html files produces look like this laurent@371: CACHE_HTML_PAT=re.compile('^[a-z]*.[0-9a-f]{32}\.cache\.html$') laurent@371: laurent@371: # ok these are the three "default" library directories, containing laurent@371: # the builtins (str, List, Dict, ord, round, len, range etc.) laurent@371: # the main pyjamas libraries (pyjamas.ui, pyjamas.Window etc.) laurent@371: # and the contributed addons laurent@371: laurent@371: for p in ["library/builtins", laurent@371: "library", laurent@371: "addons"]: laurent@371: p = os.path.join(_data_dir, p) laurent@371: if os.path.isdir(p): laurent@371: pyjs.path.append(p) laurent@371: laurent@371: laurent@371: def read_boilerplate(data_dir, filename): laurent@371: return open(join(data_dir, "builder/boilerplate", filename)).read() laurent@371: laurent@371: def copy_boilerplate(data_dir, filename, output_dir): laurent@371: filename = join(data_dir, "builder/boilerplate", filename) laurent@371: shutil.copy(filename, output_dir) laurent@371: laurent@371: laurent@371: # taken and modified from python2.4 laurent@371: def copytree_exists(src, dst, symlinks=False): laurent@371: if not os.path.exists(src): laurent@371: return laurent@371: laurent@371: names = os.listdir(src) laurent@371: try: laurent@371: os.mkdir(dst) laurent@371: except: laurent@371: pass laurent@371: laurent@371: errors = [] laurent@371: for name in names: laurent@371: if name.startswith('CVS'): laurent@371: continue laurent@371: if name.startswith('.git'): laurent@371: continue laurent@371: if name.startswith('.svn'): laurent@371: continue laurent@371: laurent@371: srcname = os.path.join(src, name) laurent@371: dstname = os.path.join(dst, name) laurent@371: try: laurent@371: if symlinks and os.path.islink(srcname): laurent@371: linkto = os.readlink(srcname) laurent@371: os.symlink(linkto, dstname) laurent@371: elif isdir(srcname): laurent@371: copytree_exists(srcname, dstname, symlinks) laurent@371: else: laurent@371: shutil.copy2(srcname, dstname) laurent@371: except (IOError, os.error), why: laurent@371: errors.append((srcname, dstname, why)) laurent@371: if errors: laurent@371: print errors laurent@371: laurent@371: def check_html_file(source_file, dest_path): laurent@371: """ Checks if a base HTML-file is available in the PyJamas laurent@371: output directory. laurent@371: If the HTML-file isn't available, it will be created. laurent@371: laurent@371: If a CSS-file with the same name is available laurent@371: in the output directory, a reference to this CSS-file laurent@371: is included. laurent@371: laurent@371: If no CSS-file is found, this function will look for a special laurent@371: CSS-file in the output directory, with the name laurent@371: "pyjamas_default.css", and if found it will be referenced laurent@371: in the generated HTML-file. laurent@371: laurent@371: [thank you to stef mientki for contributing this function] laurent@371: """ laurent@371: laurent@371: base_html = """\ laurent@371: laurent@371: laurent@371: laurent@371: laurent@371: %(css)s laurent@371: %(title)s laurent@371: laurent@371: laurent@371: laurent@371: laurent@371: laurent@371: """ laurent@371: laurent@371: filename = os.path.split ( source_file )[1] laurent@371: mod_name = os.path.splitext ( filename )[0] laurent@371: file_name = os.path.join ( dest_path, mod_name + '.html' ) laurent@371: laurent@371: # if html file in output directory exists, leave it alone. laurent@371: if os.path.exists ( file_name ): laurent@371: return 0 laurent@371: laurent@371: if os.path.exists ( laurent@371: os.path.join ( dest_path, mod_name + '.css' ) ) : laurent@371: css = "" laurent@371: elif os.path.exists ( laurent@371: os.path.join ( dest_path, 'pyjamas_default.css' ) ) : laurent@371: css = "" laurent@371: laurent@371: else: laurent@371: css = '' laurent@371: laurent@371: title = 'PyJamas Auto-Generated HTML file ' + mod_name laurent@371: laurent@371: base_html = base_html % {'modulename': mod_name, 'title': title, 'css': css} laurent@371: laurent@371: fh = open (file_name, 'w') laurent@371: fh.write (base_html) laurent@371: fh.close () laurent@371: laurent@371: return 1 laurent@371: laurent@371: laurent@371: def build(app_name, output, js_includes=(), debug=False, dynamic=0, laurent@371: data_dir=None, cache_buster=False, optimize=False): laurent@371: laurent@371: # make sure the output directory is always created in the current working laurent@371: # directory or at the place given if it is an absolute path. laurent@371: output = os.path.abspath(output) laurent@371: msg = "Building '%(app_name)s' to output directory '%(output)s'" % locals() laurent@371: if debug: laurent@371: msg += " with debugging statements" laurent@371: print msg laurent@371: laurent@371: # check the output directory laurent@371: if os.path.exists(output) and not os.path.isdir(output): laurent@371: print >>sys.stderr, "Output destination %s exists and is not a directory" % output laurent@371: return laurent@371: if not os.path.isdir(output): laurent@371: try: laurent@371: print "Creating output directory" laurent@371: os.mkdir(output) laurent@371: except StandardError, e: laurent@371: print >>sys.stderr, "Exception creating output directory %s: %s" % (output, e) laurent@371: laurent@371: ## public dir laurent@371: for p in pyjs.path: laurent@371: pub_dir = join(p, 'public') laurent@371: if isdir(pub_dir): laurent@371: print "Copying: public directory of library %r" % p laurent@371: copytree_exists(pub_dir, output) laurent@371: laurent@371: ## AppName.html - can be in current or public directory laurent@371: html_input_filename = app_name + ".html" laurent@371: html_output_filename = join(output, basename(html_input_filename)) laurent@371: if os.path.isfile(html_input_filename): laurent@371: if not os.path.isfile(html_output_filename) or \ laurent@371: os.path.getmtime(html_input_filename) > \ laurent@371: os.path.getmtime(html_output_filename): laurent@371: try: laurent@371: shutil.copy(html_input_filename, html_output_filename) laurent@371: except: laurent@371: print >>sys.stderr, "Warning: Missing module HTML file %s" % html_input_filename laurent@371: laurent@371: print "Copying: %(html_input_filename)s" % locals() laurent@371: laurent@371: if check_html_file(html_input_filename, output): laurent@371: print >>sys.stderr, "Warning: Module HTML file %s has been auto-generated" % html_input_filename laurent@371: laurent@371: ## pygwt.js laurent@371: laurent@371: print "Copying: pygwt.js" laurent@371: laurent@371: pygwt_js_template = read_boilerplate(data_dir, "pygwt.js") laurent@371: pygwt_js_output = open(join(output, "pygwt.js"), "w") laurent@371: laurent@371: print >>pygwt_js_output, pygwt_js_template laurent@371: laurent@371: pygwt_js_output.close() laurent@371: laurent@371: ## Images laurent@371: laurent@371: print "Copying: Images and History" laurent@371: copy_boilerplate(data_dir, "corner_dialog_topleft_black.png", output) laurent@371: copy_boilerplate(data_dir, "corner_dialog_topright_black.png", output) laurent@371: copy_boilerplate(data_dir, "corner_dialog_bottomright_black.png", output) laurent@371: copy_boilerplate(data_dir, "corner_dialog_bottomleft_black.png", output) laurent@371: copy_boilerplate(data_dir, "corner_dialog_edge_black.png", output) laurent@371: copy_boilerplate(data_dir, "corner_dialog_topleft.png", output) laurent@371: copy_boilerplate(data_dir, "corner_dialog_topright.png", output) laurent@371: copy_boilerplate(data_dir, "corner_dialog_bottomright.png", output) laurent@371: copy_boilerplate(data_dir, "corner_dialog_bottomleft.png", output) laurent@371: copy_boilerplate(data_dir, "corner_dialog_edge.png", output) laurent@371: copy_boilerplate(data_dir, "tree_closed.gif", output) laurent@371: copy_boilerplate(data_dir, "tree_open.gif", output) laurent@371: copy_boilerplate(data_dir, "tree_white.gif", output) laurent@371: copy_boilerplate(data_dir, "history.html", output) laurent@371: laurent@371: laurent@371: ## all.cache.html laurent@371: app_files = generateAppFiles(data_dir, js_includes, app_name, debug, laurent@371: output, dynamic, cache_buster, optimize) laurent@371: laurent@371: ## AppName.nocache.html laurent@371: laurent@371: print "Creating: %(app_name)s.nocache.html" % locals() laurent@371: laurent@371: home_nocache_html_template = read_boilerplate(data_dir, "home.nocache.html") laurent@371: home_nocache_html_output = open(join(output, app_name + ".nocache.html"), laurent@371: "w") laurent@371: laurent@371: # the selector templ is added to the selectScript function laurent@371: select_tmpl = """O(["true","%s"],"%s");""" laurent@371: script_selectors = StringIO() laurent@371: laurent@371: for platform, file_prefix in app_files: laurent@371: print >> script_selectors, select_tmpl % (platform, file_prefix) laurent@371: laurent@371: print >>home_nocache_html_output, home_nocache_html_template % dict( laurent@371: app_name = app_name, laurent@371: script_selectors = script_selectors.getvalue(), laurent@371: ) laurent@371: laurent@371: home_nocache_html_output.close() laurent@371: laurent@371: print "Done. You can run your app by opening '%(html_output_filename)s' in a browser" % locals() laurent@371: laurent@371: laurent@371: def generateAppFiles(data_dir, js_includes, app_name, debug, output, dynamic, laurent@371: cache_buster, optimize): laurent@371: laurent@371: all_cache_html_template = read_boilerplate(data_dir, "all.cache.html") laurent@371: mod_cache_html_template = read_boilerplate(data_dir, "mod.cache.html") laurent@371: laurent@371: # clean out the old ones first laurent@371: for name in os.listdir(output): laurent@371: if CACHE_HTML_PAT.match(name): laurent@371: p = join(output, name) laurent@371: print "Deleting existing app file %s" % p laurent@371: os.unlink(p) laurent@371: laurent@371: app_files = [] laurent@371: tmpl = read_boilerplate(data_dir, "all.cache.html") laurent@371: parser = pyjs.PlatformParser("platform") laurent@371: app_headers = '' laurent@371: scripts = [''%script \ laurent@371: for script in js_includes] laurent@371: app_body = '\n'.join(scripts) laurent@371: laurent@371: mod_code = {} laurent@371: mod_libs = {} laurent@371: modules = {} laurent@371: app_libs = {} laurent@371: early_app_libs = {} laurent@371: app_code = {} laurent@371: overrides = {} laurent@371: pover = {} laurent@371: app_modnames = {} laurent@371: mod_levels = {} laurent@371: laurent@371: # First, generate all the code. laurent@371: # Second, (dynamic only), post-analyse the places where modules laurent@371: # haven't changed laurent@371: # Third, write everything out. laurent@371: laurent@371: for platform in app_platforms: laurent@371: laurent@371: mod_code[platform] = {} laurent@371: mod_libs[platform] = {} laurent@371: modules[platform] = [] laurent@371: pover[platform] = {} laurent@371: app_libs[platform] = '' laurent@371: early_app_libs[platform] = '' laurent@371: app_code[platform] = {} laurent@371: app_modnames[platform] = {} laurent@371: laurent@371: # Application.Platform.cache.html laurent@371: laurent@371: parser.setPlatform(platform) laurent@371: app_translator = pyjs.AppTranslator( laurent@371: parser=parser, dynamic=dynamic, optimize=optimize) laurent@371: early_app_libs[platform], appcode = \ laurent@371: app_translator.translate(None, is_app=False, laurent@371: debug=debug, laurent@371: library_modules=['dynamicajax.js', laurent@371: '_pyjs.js', 'sys', laurent@371: 'pyjslib']) laurent@371: pover[platform].update(app_translator.overrides.items()) laurent@371: for mname, name in app_translator.overrides.items(): laurent@371: pd = overrides.setdefault(mname, {}) laurent@371: pd[platform] = name laurent@371: laurent@371: print appcode laurent@371: #mod_code[platform][app_name] = appcode laurent@371: laurent@371: # platform.Module.cache.js laurent@371: laurent@371: modules_done = ['pyjslib', 'sys', '_pyjs.js'] laurent@371: #modules_to_do = [app_name] + app_translator.library_modules laurent@371: modules_to_do = [app_name] + app_translator.library_modules laurent@371: laurent@371: dependencies = {} laurent@371: laurent@371: deps = map(pyjs.strip_py, modules_to_do) laurent@371: for d in deps: laurent@371: sublist = add_subdeps(dependencies, d) laurent@371: modules_to_do += sublist laurent@371: deps = uniquify(deps) laurent@371: #dependencies[app_name] = deps laurent@371: laurent@371: modules[platform] = modules_done + modules_to_do laurent@371: laurent@371: while modules_to_do: laurent@371: laurent@371: #print "modules to do", modules_to_do laurent@371: laurent@371: mn = modules_to_do.pop() laurent@371: mod_name = pyjs.strip_py(mn) laurent@371: laurent@371: if mod_name in modules_done: laurent@371: continue laurent@371: laurent@371: modules_done.append(mod_name) laurent@371: laurent@371: mod_cache_name = "%s.%s.cache.js" % (platform.lower(), mod_name) laurent@371: laurent@371: parser.setPlatform(platform) laurent@371: mod_translator = pyjs.AppTranslator(parser=parser, optimize=optimize) laurent@371: mod_libs[platform][mod_name], mod_code[platform][mod_name] = \ laurent@371: mod_translator.translate(mod_name, laurent@371: is_app=False, laurent@371: debug=debug) laurent@371: pover[platform].update(mod_translator.overrides.items()) laurent@371: for mname, name in mod_translator.overrides.items(): laurent@371: pd = overrides.setdefault(mname, {}) laurent@371: pd[platform] = name laurent@371: laurent@371: mods = mod_translator.library_modules laurent@371: modules_to_do += mods laurent@371: modules[platform] += mods laurent@371: laurent@371: deps = map(pyjs.strip_py, mods) laurent@371: sd = subdeps(mod_name) laurent@371: if len(sd) > 1: laurent@371: deps += sd[:-1] laurent@371: while mod_name in deps: laurent@371: deps.remove(mod_name) laurent@371: laurent@371: #print laurent@371: #print laurent@371: #print "modname preadd:", mod_name, deps laurent@371: #print laurent@371: #print laurent@371: for d in deps: laurent@371: sublist = add_subdeps(dependencies, d) laurent@371: modules_to_do += sublist laurent@371: modules_to_do += add_subdeps(dependencies, mod_name) laurent@371: #print "modname:", mod_name, deps laurent@371: deps = uniquify(deps) laurent@371: #print "modname:", mod_name, deps laurent@371: dependencies[mod_name] = deps laurent@371: laurent@371: # work out the dependency ordering of the modules laurent@371: laurent@371: mod_levels[platform] = make_deps(None, dependencies, modules_done) laurent@371: laurent@371: # now write everything out laurent@371: laurent@371: for platform in app_platforms: laurent@371: laurent@371: early_app_libs_ = early_app_libs[platform] laurent@371: app_libs_ = app_libs[platform] laurent@371: app_code_ = app_code[platform] laurent@371: #modules_ = filter_mods(app_name, modules[platform]) laurent@371: mods = flattenlist(mod_levels[platform]) laurent@371: mods.reverse() laurent@371: modules_ = filter_mods(None, mods) laurent@371: laurent@371: for mod_name in modules_: laurent@371: laurent@371: mod_code_ = mod_code[platform][mod_name] laurent@371: laurent@371: mod_name = pyjs.strip_py(mod_name) laurent@371: laurent@371: override_name = "%s.%s" % (platform.lower(), mod_name) laurent@371: if pover[platform].has_key(override_name): laurent@371: mod_cache_name = "%s.cache.js" % (override_name) laurent@371: else: laurent@371: mod_cache_name = "%s.cache.js" % (mod_name) laurent@371: laurent@371: print "Creating: " + mod_cache_name laurent@371: laurent@371: modlevels = make_deps(None, dependencies, dependencies[mod_name]) laurent@371: laurent@371: modnames = [] laurent@371: laurent@371: for md in modlevels: laurent@371: mnames = map(lambda x: "'%s'" % x, md) laurent@371: mnames = "new pyjslib.List([\n\t\t\t%s])" % ',\n\t\t\t'.join(mnames) laurent@371: modnames.append(mnames) laurent@371: laurent@371: modnames.reverse() laurent@371: modnames = "new pyjslib.List([\n\t\t%s\n\t])" % ',\n\t\t'.join(modnames) laurent@371: laurent@371: # convert the overrides laurent@371: laurent@371: overnames = map(lambda x: "'%s': '%s'" % x, pover[platform].items()) laurent@371: overnames = "new pyjslib.Dict({\n\t\t%s\n\t})" % ',\n\t\t'.join(overnames) laurent@371: laurent@371: if dynamic: laurent@371: mod_cache_html_output = open(join(output, mod_cache_name), "w") laurent@371: else: laurent@371: mod_cache_html_output = StringIO() laurent@371: laurent@371: print >>mod_cache_html_output, mod_cache_html_template % dict( laurent@371: mod_name = mod_name, laurent@371: app_name = app_name, laurent@371: modnames = modnames, laurent@371: overrides = overnames, laurent@371: mod_libs = mod_libs[platform][mod_name], laurent@371: dynamic = dynamic, laurent@371: mod_code = mod_code_, laurent@371: ) laurent@371: laurent@371: if dynamic: laurent@371: mod_cache_html_output.close() laurent@371: else: laurent@371: mod_cache_html_output.seek(0) laurent@371: app_libs_ += mod_cache_html_output.read() laurent@371: laurent@371: # write out the dependency ordering of the modules laurent@371: laurent@371: app_modnames = [] laurent@371: laurent@371: for md in mod_levels[platform]: laurent@371: mnames = map(lambda x: "'%s'" % x, md) laurent@371: mnames = "new pyjslib.List([\n\t\t\t%s])" % ',\n\t\t\t'.join(mnames) laurent@371: app_modnames.append(mnames) laurent@371: laurent@371: app_modnames.reverse() laurent@371: app_modnames = "new pyjslib.List([\n\t\t%s\n\t])" % ',\n\t\t'.join(app_modnames) laurent@371: laurent@371: # convert the overrides laurent@371: laurent@371: overnames = map(lambda x: "'%s': '%s'" % x, pover[platform].items()) laurent@371: overnames = "new pyjslib.Dict({\n\t\t%s\n\t})" % ',\n\t\t'.join(overnames) laurent@371: laurent@371: #print "platform names", platform, overnames laurent@371: #print pover laurent@371: laurent@371: # now write app.allcache including dependency-ordered list of laurent@371: # library modules laurent@371: laurent@371: file_contents = all_cache_html_template % dict( laurent@371: app_name = app_name, laurent@371: early_app_libs = early_app_libs_, laurent@371: app_libs = app_libs_, laurent@371: app_code = app_code_, laurent@371: app_body = app_body, laurent@371: overrides = overnames, laurent@371: platform = platform.lower(), laurent@371: dynamic = dynamic, laurent@371: app_modnames = app_modnames, laurent@371: app_headers = app_headers laurent@371: ) laurent@371: if cache_buster: laurent@371: digest = md5.new(file_contents).hexdigest() laurent@371: file_name = "%s.%s.%s" % (platform.lower(), app_name, digest) laurent@371: else: laurent@371: file_name = "%s.%s" % (platform.lower(), app_name) laurent@371: file_name += ".cache.html" laurent@371: out_path = join(output, file_name) laurent@371: out_file = open(out_path, 'w') laurent@371: out_file.write(file_contents) laurent@371: out_file.close() laurent@371: app_files.append((platform.lower(), file_name)) laurent@371: print "Created app file %s:%s: %s" % ( laurent@371: app_name, platform, out_path) laurent@371: laurent@371: return app_files laurent@371: laurent@371: def flattenlist(ll): laurent@371: res = [] laurent@371: for l in ll: laurent@371: res += l laurent@371: return res laurent@371: laurent@371: # creates sub-dependencies e.g. pyjamas.ui.Widget laurent@371: # creates pyjamas.ui.Widget, pyjamas.ui and pyjamas. laurent@371: def subdeps(m): laurent@371: d = [] laurent@371: m = m.split(".") laurent@371: for i in range(0, len(m)): laurent@371: d.append('.'.join(m[:i+1])) laurent@371: return d laurent@371: laurent@371: import time laurent@371: laurent@371: def add_subdeps(deps, mod_name): laurent@371: sd = subdeps(mod_name) laurent@371: if len(sd) == 1: laurent@371: return [] laurent@371: #print "subdeps", mod_name, sd laurent@371: #print "deps", deps laurent@371: res = [] laurent@371: for i in range(0, len(sd)-1): laurent@371: parent = sd[i] laurent@371: child = sd[i+1] laurent@371: l = deps.get(child, []) laurent@371: l.append(parent) laurent@371: deps[child] = l laurent@371: if parent not in res: laurent@371: res.append(parent) laurent@371: #print deps laurent@371: return res laurent@371: laurent@371: # makes unique and preserves list order laurent@371: def uniquify(md): laurent@371: res = [] laurent@371: for m in md: laurent@371: if m not in res: laurent@371: res.append(m) laurent@371: return res laurent@371: laurent@371: def filter_mods(app_name, md): laurent@371: while 'sys' in md: laurent@371: md.remove('sys') laurent@371: while 'pyjslib' in md: laurent@371: md.remove('pyjslib') laurent@371: while app_name in md: laurent@371: md.remove(app_name) laurent@371: md = filter(lambda x: not x.endswith('.js'), md) laurent@371: md = map(pyjs.strip_py, md) laurent@371: laurent@371: return uniquify(md) laurent@371: laurent@371: def filter_deps(app_name, deps): laurent@371: laurent@371: res = {} laurent@371: for (k, l) in deps.items(): laurent@371: mods = filter_mods(k, l) laurent@371: while k in mods: laurent@371: mods.remove(k) laurent@371: res[k] = mods laurent@371: return res laurent@371: laurent@371: def has_nodeps(mod, deps): laurent@371: if not deps.has_key(mod) or not deps[mod]: laurent@371: return True laurent@371: return False laurent@371: laurent@371: def nodeps_list(mod_list, deps): laurent@371: res = [] laurent@371: for mod in mod_list: laurent@371: if has_nodeps(mod, deps): laurent@371: res.append(mod) laurent@371: return res laurent@371: laurent@371: # this function takes a dictionary of dependent modules and laurent@371: # creates a list of lists. the first list will be modules laurent@371: # that have no dependencies; the second list will be those laurent@371: # modules that have the first list as dependencies; the laurent@371: # third will be those modules that have the first and second... laurent@371: # etc. laurent@371: laurent@371: laurent@371: def make_deps(app_name, deps, mod_list): laurent@371: print "Calculating Dependencies ..." laurent@371: mod_list = filter_mods(app_name, mod_list) laurent@371: deps = filter_deps(app_name, deps) laurent@371: laurent@371: if not mod_list: laurent@371: return [] laurent@371: laurent@371: #print mod_list laurent@371: #print deps laurent@371: laurent@371: ordered_deps = [] laurent@371: last_len = -1 laurent@371: while deps: laurent@371: l_deps = len(deps) laurent@371: #print l_deps laurent@371: if l_deps==last_len: laurent@371: for m, dl in deps.items(): laurent@371: for d in dl: laurent@371: if m in deps.get(d, []): laurent@371: raise Exception('Circular Imports found: \n%s %s -> %s %s' laurent@371: % (m, dl, d, deps[d])) laurent@371: #raise Exception('Could not calculate dependencies: \n%s' % deps) laurent@371: break laurent@371: last_len = l_deps laurent@371: #print "modlist", mod_list laurent@371: nodeps = nodeps_list(mod_list, deps) laurent@371: #print "nodeps", nodeps laurent@371: mod_list = filter(lambda x: x not in nodeps, mod_list) laurent@371: newdeps = {} laurent@371: for k in deps.keys(): laurent@371: depslist = deps[k] laurent@371: depslist = filter(lambda x: x not in nodeps, depslist) laurent@371: if depslist: laurent@371: newdeps[k] = depslist laurent@371: #print "newdeps", newdeps laurent@371: deps = newdeps laurent@371: ordered_deps.append(nodeps) laurent@371: #time.sleep(0) laurent@371: laurent@371: if mod_list: laurent@371: ordered_deps.append(mod_list) # last dependencies - usually the app(s) laurent@371: laurent@371: ordered_deps.reverse() laurent@371: laurent@371: return ordered_deps laurent@371: laurent@371: def main(): laurent@371: global app_platforms laurent@371: laurent@371: parser = OptionParser(usage = usage, version = version) laurent@371: parser.add_option("-o", "--output", dest="output", laurent@371: help="directory to which the webapp should be written") laurent@371: parser.add_option("-j", "--include-js", dest="js_includes", action="append", laurent@371: help="javascripts to load into the same frame as the rest of the script") laurent@371: parser.add_option("-I", "--library_dir", dest="library_dirs", laurent@371: action="append", help="additional paths appended to PYJSPATH") laurent@371: parser.add_option("-D", "--data_dir", dest="data_dir", laurent@371: help="path for data directory") laurent@371: parser.add_option("-m", "--dynamic-modules", action="store_true", laurent@371: dest="dynamic", default=False, laurent@371: help="Split output into separate dynamically-loaded modules (experimental)") laurent@371: parser.add_option("-P", "--platforms", dest="platforms", laurent@371: help="platforms to build for, comma-separated") laurent@371: parser.add_option("-d", "--debug", action="store_true", dest="debug") laurent@371: parser.add_option("-O", "--optimize", action="store_true", laurent@371: dest="optimize", default=False, laurent@371: help="Optimize generated code (removes all print statements)", laurent@371: ) laurent@371: parser.add_option("-c", "--cache_buster", action="store_true", laurent@371: dest="cache_buster", laurent@371: help="Enable browser cache-busting (MD5 hash added to output filenames)") laurent@371: laurent@371: parser.set_defaults(output = "output", js_includes=[], library_dirs=[], laurent@371: platforms=(','.join(app_platforms)), laurent@371: data_dir=os.path.join(sys.prefix, "share/pyjamas"), laurent@371: dynamic=False, laurent@371: cache_buster=False, laurent@371: debug=False) laurent@371: (options, args) = parser.parse_args() laurent@371: if len(args) != 1: laurent@371: parser.error("incorrect number of arguments") laurent@371: laurent@371: data_dir = abspath(options.data_dir) laurent@371: laurent@371: app_path = args[0] laurent@371: if app_path.endswith('.py'): laurent@371: app_path = abspath(app_path) laurent@371: if not isfile(app_path): laurent@371: parser.error("Application file not found %r" % app_path) laurent@371: app_path, app_name = split(app_path) laurent@371: app_name = app_name[:-3] laurent@371: pyjs.path.append(app_path) laurent@371: elif os.path.sep in app_path: laurent@371: parser.error("Not a valid module declaration %r" % app_path) laurent@371: else: laurent@371: app_name = app_path laurent@371: laurent@371: for d in options.library_dirs: laurent@371: pyjs.path.append(abspath(d)) laurent@371: laurent@371: if options.platforms: laurent@371: app_platforms = options.platforms.split(',') laurent@371: laurent@371: # this is mostly for getting boilerplate stuff laurent@371: data_dir = os.path.abspath(options.data_dir) laurent@371: laurent@371: build(app_name, options.output, options.js_includes, laurent@371: options.debug, options.dynamic and 1 or 0, data_dir, laurent@371: options.cache_buster, options.optimize) laurent@371: laurent@371: if __name__ == "__main__": laurent@371: main() laurent@371: