svgui/pyjs/build.py
changeset 3359 2c924cf26161
parent 3358 7478d0c0dc1c
child 3360 746e3e3f6537
equal deleted inserted replaced
3358:7478d0c0dc1c 3359:2c924cf26161
     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()