svgui/pyjs/build.py
changeset 728 e0424e96e3fd
parent 721 ecf4d203c4d4
child 1730 64d8f52bc8c8
equal deleted inserted replaced
727:3edd2f19bce2 728:e0424e96e3fd
       
     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