svgui/pyjs/build.py
changeset 1784 64beb9e9c749
parent 1783 3311eea28d56
child 1826 91796f408540
equal deleted inserted replaced
1729:31e63e25b4cc 1784:64beb9e9c749
     5 import shutil
     5 import shutil
     6 from copy import copy
     6 from copy import copy
     7 from os.path import join, dirname, basename, abspath, split, isfile, isdir
     7 from os.path import join, dirname, basename, abspath, split, isfile, isdir
     8 from optparse import OptionParser
     8 from optparse import OptionParser
     9 import pyjs
     9 import pyjs
       
    10 import time
    10 from cStringIO import StringIO
    11 from cStringIO import StringIO
    11 try:
    12 try:
    12     # Python 2.5 and above
    13     # Python 2.5 and above
    13     from hashlib import md5
    14     from hashlib import md5
    14 except:
    15 except Exception:
    15     import md5
    16     import md5
    16 import re
    17 import re
    17 
    18 
    18 usage = """
    19 usage = """
    19   usage: %prog [options] <application module name or path>
    20   usage: %prog [options] <application module name or path>
    43 # usually defaults to e.g. /usr/share/pyjamas
    44 # usually defaults to e.g. /usr/share/pyjamas
    44 _data_dir = os.path.join(pyjs.prefix, "share/pyjamas")
    45 _data_dir = os.path.join(pyjs.prefix, "share/pyjamas")
    45 
    46 
    46 
    47 
    47 # .cache.html files produces look like this
    48 # .cache.html files produces look like this
    48 CACHE_HTML_PAT=re.compile('^[a-z]*.[0-9a-f]{32}\.cache\.html$')
    49 CACHE_HTML_PAT = re.compile('^[a-z]*.[0-9a-f]{32}\.cache\.html$')
    49 
    50 
    50 # ok these are the three "default" library directories, containing
    51 # ok these are the three "default" library directories, containing
    51 # the builtins (str, List, Dict, ord, round, len, range etc.)
    52 # the builtins (str, List, Dict, ord, round, len, range etc.)
    52 # the main pyjamas libraries (pyjamas.ui, pyjamas.Window etc.)
    53 # the main pyjamas libraries (pyjamas.ui, pyjamas.Window etc.)
    53 # and the contributed addons
    54 # and the contributed addons
    61 
    62 
    62 
    63 
    63 def read_boilerplate(data_dir, filename):
    64 def read_boilerplate(data_dir, filename):
    64     return open(join(data_dir, "builder/boilerplate", filename)).read()
    65     return open(join(data_dir, "builder/boilerplate", filename)).read()
    65 
    66 
       
    67 
    66 def copy_boilerplate(data_dir, filename, output_dir):
    68 def copy_boilerplate(data_dir, filename, output_dir):
    67     filename = join(data_dir, "builder/boilerplate", filename)
    69     filename = join(data_dir, "builder/boilerplate", filename)
    68     shutil.copy(filename, output_dir)
    70     shutil.copy(filename, output_dir)
    69 
    71 
    70 
    72 
    74         return
    76         return
    75 
    77 
    76     names = os.listdir(src)
    78     names = os.listdir(src)
    77     try:
    79     try:
    78         os.mkdir(dst)
    80         os.mkdir(dst)
    79     except:
    81     except Exception:
    80         pass
    82         pass
    81 
    83 
    82     errors = []
    84     errors = []
    83     for name in names:
    85     for name in names:
    84         if name.startswith('CVS'):
    86         if name.startswith('CVS'):
   100                 shutil.copy2(srcname, dstname)
   102                 shutil.copy2(srcname, dstname)
   101         except (IOError, os.error), why:
   103         except (IOError, os.error), why:
   102             errors.append((srcname, dstname, why))
   104             errors.append((srcname, dstname, why))
   103     if errors:
   105     if errors:
   104         print errors
   106         print errors
       
   107 
   105 
   108 
   106 def check_html_file(source_file, dest_path):
   109 def check_html_file(source_file, dest_path):
   107     """ Checks if a base HTML-file is available in the PyJamas
   110     """ Checks if a base HTML-file is available in the PyJamas
   108         output directory.
   111         output directory.
   109         If the HTML-file isn't available, it will be created.
   112         If the HTML-file isn't available, it will be created.
   134       <script language="javascript" src="pygwt.js"></script>
   137       <script language="javascript" src="pygwt.js"></script>
   135     </body>
   138     </body>
   136 </html>
   139 </html>
   137 """
   140 """
   138 
   141 
   139     filename = os.path.split    ( source_file )[1]
   142     filename = os.path.split(source_file)[1]
   140     mod_name = os.path.splitext ( filename    )[0]
   143     mod_name = os.path.splitext(filename)[0]
   141     file_name = os.path.join     ( dest_path, mod_name + '.html' )
   144     file_name = os.path.join(dest_path, mod_name + '.html')
   142 
   145 
   143     # if html file in output directory exists, leave it alone.
   146     # if html file in output directory exists, leave it alone.
   144     if os.path.exists ( file_name ):
   147     if os.path.exists(file_name):
   145         return 0
   148         return 0
   146 
   149 
   147     if os.path.exists (
   150     if os.path.exists(os.path.join(dest_path, mod_name + '.css')):
   148         os.path.join ( dest_path, mod_name + '.css' ) ) :
       
   149         css = "<link rel='stylesheet' href='" + mod_name + ".css'>"
   151         css = "<link rel='stylesheet' href='" + mod_name + ".css'>"
   150     elif os.path.exists (
   152     elif os.path.exists(os.path.join(dest_path, 'pyjamas_default.css')):
   151         os.path.join ( dest_path, 'pyjamas_default.css' ) ) :
       
   152         css = "<link rel='stylesheet' href='pyjamas_default.css'>"
   153         css = "<link rel='stylesheet' href='pyjamas_default.css'>"
   153 
   154 
   154     else:
   155     else:
   155         css = ''
   156         css = ''
   156 
   157 
   157     title = 'PyJamas Auto-Generated HTML file ' + mod_name
   158     title = 'PyJamas Auto-Generated HTML file ' + mod_name
   158 
   159 
   159     base_html = base_html % {'modulename': mod_name, 'title': title, 'css': css}
   160     base_html = base_html % {'modulename': mod_name, 'title': title, 'css': css}
   160 
   161 
   161     fh = open (file_name, 'w')
   162     fh = open(file_name, 'w')
   162     fh.write  (base_html)
   163     fh.write(base_html)
   163     fh.close  ()
   164     fh.close()
   164 
   165 
   165     return 1
   166     return 1
   166 
   167 
   167 
   168 
   168 def build(app_name, output, js_includes=(), debug=False, dynamic=0,
   169 def build(app_name, output, js_includes=(), debug=False, dynamic=0,
   185             print "Creating output directory"
   186             print "Creating output directory"
   186             os.mkdir(output)
   187             os.mkdir(output)
   187         except StandardError, e:
   188         except StandardError, e:
   188             print >>sys.stderr, "Exception creating output directory %s: %s" % (output, e)
   189             print >>sys.stderr, "Exception creating output directory %s: %s" % (output, e)
   189 
   190 
   190     ## public dir
   191     # public dir
   191     for p in pyjs.path:
   192     for p in pyjs.path:
   192         pub_dir = join(p, 'public')
   193         pub_dir = join(p, 'public')
   193         if isdir(pub_dir):
   194         if isdir(pub_dir):
   194             print "Copying: public directory of library %r" % p
   195             print "Copying: public directory of library %r" % p
   195             copytree_exists(pub_dir, output)
   196             copytree_exists(pub_dir, output)
   196 
   197 
   197     ## AppName.html - can be in current or public directory
   198     # AppName.html - can be in current or public directory
   198     html_input_filename = app_name + ".html"
   199     html_input_filename = app_name + ".html"
   199     html_output_filename = join(output, basename(html_input_filename))
   200     html_output_filename = join(output, basename(html_input_filename))
   200     if os.path.isfile(html_input_filename):
   201     if os.path.isfile(html_input_filename):
   201         if not os.path.isfile(html_output_filename) or \
   202         if not os.path.isfile(html_output_filename) or \
   202                os.path.getmtime(html_input_filename) > \
   203                os.path.getmtime(html_input_filename) > \
   203                os.path.getmtime(html_output_filename):
   204                os.path.getmtime(html_output_filename):
   204             try:
   205             try:
   205                 shutil.copy(html_input_filename, html_output_filename)
   206                 shutil.copy(html_input_filename, html_output_filename)
   206             except:
   207             except Exception:
   207                 print >>sys.stderr, "Warning: Missing module HTML file %s" % html_input_filename
   208                 print >>sys.stderr, "Warning: Missing module HTML file %s" % html_input_filename
   208 
   209 
   209             print "Copying: %(html_input_filename)s" % locals()
   210             print "Copying: %(html_input_filename)s" % locals()
   210 
   211 
   211     if check_html_file(html_input_filename, output):
   212     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         print >>sys.stderr, "Warning: Module HTML file %s has been auto-generated" % html_input_filename
   213 
   214 
   214     ## pygwt.js
   215     # pygwt.js
   215 
   216 
   216     print "Copying: pygwt.js"
   217     print "Copying: pygwt.js"
   217 
   218 
   218     pygwt_js_template = read_boilerplate(data_dir, "pygwt.js")
   219     pygwt_js_template = read_boilerplate(data_dir, "pygwt.js")
   219     pygwt_js_output = open(join(output, "pygwt.js"), "w")
   220     pygwt_js_output = open(join(output, "pygwt.js"), "w")
   220 
   221 
   221     print >>pygwt_js_output, pygwt_js_template
   222     print >>pygwt_js_output, pygwt_js_template
   222 
   223 
   223     pygwt_js_output.close()
   224     pygwt_js_output.close()
   224 
   225 
   225     ## Images
   226     # Images
   226 
   227 
   227     print "Copying: Images and History"
   228     print "Copying: Images and History"
   228     copy_boilerplate(data_dir, "corner_dialog_topleft_black.png", output)
   229     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_topright_black.png", output)
   230     copy_boilerplate(data_dir, "corner_dialog_bottomright_black.png", output)
   231     copy_boilerplate(data_dir, "corner_dialog_bottomright_black.png", output)
   238     copy_boilerplate(data_dir, "tree_closed.gif", output)
   239     copy_boilerplate(data_dir, "tree_closed.gif", output)
   239     copy_boilerplate(data_dir, "tree_open.gif", output)
   240     copy_boilerplate(data_dir, "tree_open.gif", output)
   240     copy_boilerplate(data_dir, "tree_white.gif", output)
   241     copy_boilerplate(data_dir, "tree_white.gif", output)
   241     copy_boilerplate(data_dir, "history.html", output)
   242     copy_boilerplate(data_dir, "history.html", output)
   242 
   243 
   243 
   244     # all.cache.html
   244     ## all.cache.html
       
   245     app_files = generateAppFiles(data_dir, js_includes, app_name, debug,
   245     app_files = generateAppFiles(data_dir, js_includes, app_name, debug,
   246                                  output, dynamic, cache_buster, optimize)
   246                                  output, dynamic, cache_buster, optimize)
   247 
   247 
   248     ## AppName.nocache.html
   248     # AppName.nocache.html
   249 
   249 
   250     print "Creating: %(app_name)s.nocache.html" % locals()
   250     print "Creating: %(app_name)s.nocache.html" % locals()
   251 
   251 
   252     home_nocache_html_template = read_boilerplate(data_dir, "home.nocache.html")
   252     home_nocache_html_template = read_boilerplate(data_dir, "home.nocache.html")
   253     home_nocache_html_output = open(join(output, app_name + ".nocache.html"),
   253     home_nocache_html_output = open(join(output, app_name + ".nocache.html"),
   259 
   259 
   260     for platform, file_prefix in app_files:
   260     for platform, file_prefix in app_files:
   261         print >> script_selectors, select_tmpl % (platform, file_prefix)
   261         print >> script_selectors, select_tmpl % (platform, file_prefix)
   262 
   262 
   263     print >>home_nocache_html_output, home_nocache_html_template % dict(
   263     print >>home_nocache_html_output, home_nocache_html_template % dict(
   264         app_name = app_name,
   264         app_name=app_name,
   265         script_selectors = script_selectors.getvalue(),
   265         script_selectors=script_selectors.getvalue(),
   266     )
   266     )
   267 
   267 
   268     home_nocache_html_output.close()
   268     home_nocache_html_output.close()
   269 
   269 
   270     print "Done. You can run your app by opening '%(html_output_filename)s' in a browser" % locals()
   270     print "Done. You can run your app by opening '%(html_output_filename)s' in a browser" % locals()
   285 
   285 
   286     app_files = []
   286     app_files = []
   287     tmpl = read_boilerplate(data_dir, "all.cache.html")
   287     tmpl = read_boilerplate(data_dir, "all.cache.html")
   288     parser = pyjs.PlatformParser("platform")
   288     parser = pyjs.PlatformParser("platform")
   289     app_headers = ''
   289     app_headers = ''
   290     scripts = ['<script type="text/javascript" src="%s"></script>'%script \
   290     scripts = ['<script type="text/javascript" src="%s"></script>' %
   291                                                   for script in js_includes]
   291                script for script in js_includes]
   292     app_body = '\n'.join(scripts)
   292     app_body = '\n'.join(scripts)
   293 
   293 
   294     mod_code = {}
   294     mod_code = {}
   295     mod_libs = {}
   295     mod_libs = {}
   296     modules = {}
   296     modules = {}
   304 
   304 
   305     # First, generate all the code.
   305     # First, generate all the code.
   306     # Second, (dynamic only), post-analyse the places where modules
   306     # Second, (dynamic only), post-analyse the places where modules
   307     # haven't changed
   307     # haven't changed
   308     # Third, write everything out.
   308     # Third, write everything out.
   309     
   309 
   310     for platform in app_platforms:
   310     for platform in app_platforms:
   311 
   311 
   312         mod_code[platform] = {}
   312         mod_code[platform] = {}
   313         mod_libs[platform] = {}
   313         mod_libs[platform] = {}
   314         modules[platform] = []
   314         modules[platform] = []
   322 
   322 
   323         parser.setPlatform(platform)
   323         parser.setPlatform(platform)
   324         app_translator = pyjs.AppTranslator(
   324         app_translator = pyjs.AppTranslator(
   325             parser=parser, dynamic=dynamic, optimize=optimize)
   325             parser=parser, dynamic=dynamic, optimize=optimize)
   326         early_app_libs[platform], appcode = \
   326         early_app_libs[platform], appcode = \
   327                      app_translator.translate(None, is_app=False,
   327             app_translator.translate(None, is_app=False,
   328                                               debug=debug,
   328                                      debug=debug,
   329                                       library_modules=['dynamicajax.js',
   329                                      library_modules=['dynamicajax.js',
   330                                                     '_pyjs.js', 'sys',
   330                                                       '_pyjs.js', 'sys',
   331                                                      'pyjslib'])
   331                                                       'pyjslib'])
   332         pover[platform].update(app_translator.overrides.items())
   332         pover[platform].update(app_translator.overrides.items())
   333         for mname, name in app_translator.overrides.items():
   333         for mname, name in app_translator.overrides.items():
   334             pd = overrides.setdefault(mname, {})
   334             pd = overrides.setdefault(mname, {})
   335             pd[platform] = name
   335             pd[platform] = name
   336 
   336 
   337         print appcode
   337         print appcode
   338         #mod_code[platform][app_name] = appcode
   338         # mod_code[platform][app_name] = appcode
   339 
   339 
   340         # platform.Module.cache.js 
   340         # platform.Module.cache.js
   341 
   341 
   342         modules_done = ['pyjslib', 'sys', '_pyjs.js']
   342         modules_done = ['pyjslib', 'sys', '_pyjs.js']
   343         #modules_to_do = [app_name] + app_translator.library_modules
   343         # modules_to_do = [app_name] + app_translator.library_modules
   344         modules_to_do = [app_name] + app_translator.library_modules 
   344         modules_to_do = [app_name] + app_translator.library_modules
   345 
   345 
   346         dependencies = {}
   346         dependencies = {}
   347 
   347 
   348         deps = map(pyjs.strip_py, modules_to_do)
   348         deps = map(pyjs.strip_py, modules_to_do)
   349         for d in deps:
   349         for d in deps:
   350             sublist = add_subdeps(dependencies, d)
   350             sublist = add_subdeps(dependencies, d)
   351             modules_to_do += sublist
   351             modules_to_do += sublist
   352         deps = uniquify(deps)
   352         deps = uniquify(deps)
   353         #dependencies[app_name] = deps
   353         # dependencies[app_name] = deps
   354 
   354 
   355         modules[platform] = modules_done + modules_to_do
   355         modules[platform] = modules_done + modules_to_do
   356 
   356 
   357         while modules_to_do:
   357         while modules_to_do:
   358 
   358 
   359             #print "modules to do", modules_to_do
   359             # print "modules to do", modules_to_do
   360 
   360 
   361             mn = modules_to_do.pop()
   361             mn = modules_to_do.pop()
   362             mod_name = pyjs.strip_py(mn)
   362             mod_name = pyjs.strip_py(mn)
   363 
   363 
   364             if mod_name in modules_done:
   364             if mod_name in modules_done:
   369             mod_cache_name = "%s.%s.cache.js" % (platform.lower(), mod_name)
   369             mod_cache_name = "%s.%s.cache.js" % (platform.lower(), mod_name)
   370 
   370 
   371             parser.setPlatform(platform)
   371             parser.setPlatform(platform)
   372             mod_translator = pyjs.AppTranslator(parser=parser, optimize=optimize)
   372             mod_translator = pyjs.AppTranslator(parser=parser, optimize=optimize)
   373             mod_libs[platform][mod_name], mod_code[platform][mod_name] = \
   373             mod_libs[platform][mod_name], mod_code[platform][mod_name] = \
   374                               mod_translator.translate(mod_name,
   374                 mod_translator.translate(mod_name,
   375                                                   is_app=False,
   375                                          is_app=False,
   376                                                   debug=debug)
   376                                          debug=debug)
   377             pover[platform].update(mod_translator.overrides.items())
   377             pover[platform].update(mod_translator.overrides.items())
   378             for mname, name in mod_translator.overrides.items():
   378             for mname, name in mod_translator.overrides.items():
   379                 pd = overrides.setdefault(mname, {})
   379                 pd = overrides.setdefault(mname, {})
   380                 pd[platform] = name
   380                 pd[platform] = name
   381 
   381 
   388             if len(sd) > 1:
   388             if len(sd) > 1:
   389                 deps += sd[:-1]
   389                 deps += sd[:-1]
   390             while mod_name in deps:
   390             while mod_name in deps:
   391                 deps.remove(mod_name)
   391                 deps.remove(mod_name)
   392 
   392 
   393             #print
   393             # print
   394             #print
   394             # print
   395             #print "modname preadd:", mod_name, deps
   395             # print "modname preadd:", mod_name, deps
   396             #print
   396             # print
   397             #print
   397             # print
   398             for d in deps:
   398             for d in deps:
   399                 sublist = add_subdeps(dependencies, d)
   399                 sublist = add_subdeps(dependencies, d)
   400                 modules_to_do += sublist
   400                 modules_to_do += sublist
   401             modules_to_do += add_subdeps(dependencies, mod_name)
   401             modules_to_do += add_subdeps(dependencies, mod_name)
   402             #print "modname:", mod_name, deps
   402             # print "modname:", mod_name, deps
   403             deps = uniquify(deps)
   403             deps = uniquify(deps)
   404             #print "modname:", mod_name, deps
   404             # print "modname:", mod_name, deps
   405             dependencies[mod_name] = deps
   405             dependencies[mod_name] = deps
   406             
   406 
   407         # work out the dependency ordering of the modules
   407         # work out the dependency ordering of the modules
   408     
   408 
   409         mod_levels[platform] = make_deps(None, dependencies, modules_done)
   409         mod_levels[platform] = make_deps(None, dependencies, modules_done)
   410 
   410 
   411     # now write everything out
   411     # now write everything out
   412 
   412 
   413     for platform in app_platforms:
   413     for platform in app_platforms:
   414 
   414 
   415         early_app_libs_ = early_app_libs[platform]
   415         early_app_libs_ = early_app_libs[platform]
   416         app_libs_ = app_libs[platform]
   416         app_libs_ = app_libs[platform]
   417         app_code_ = app_code[platform]
   417         app_code_ = app_code[platform]
   418         #modules_ = filter_mods(app_name, modules[platform])
   418         # modules_ = filter_mods(app_name, modules[platform])
   419         mods = flattenlist(mod_levels[platform])
   419         mods = flattenlist(mod_levels[platform])
   420         mods.reverse()
   420         mods.reverse()
   421         modules_ = filter_mods(None, mods)
   421         modules_ = filter_mods(None, mods)
   422 
   422 
   423         for mod_name in modules_:
   423         for mod_name in modules_:
   425             mod_code_ = mod_code[platform][mod_name]
   425             mod_code_ = mod_code[platform][mod_name]
   426 
   426 
   427             mod_name = pyjs.strip_py(mod_name)
   427             mod_name = pyjs.strip_py(mod_name)
   428 
   428 
   429             override_name = "%s.%s" % (platform.lower(), mod_name)
   429             override_name = "%s.%s" % (platform.lower(), mod_name)
   430             if pover[platform].has_key(override_name):
   430             if override_name in pover[platform]:
   431                 mod_cache_name = "%s.cache.js" % (override_name)
   431                 mod_cache_name = "%s.cache.js" % (override_name)
   432             else:
   432             else:
   433                 mod_cache_name = "%s.cache.js" % (mod_name)
   433                 mod_cache_name = "%s.cache.js" % (mod_name)
   434 
   434 
   435             print "Creating: " + mod_cache_name
   435             print "Creating: " + mod_cache_name
   455                 mod_cache_html_output = open(join(output, mod_cache_name), "w")
   455                 mod_cache_html_output = open(join(output, mod_cache_name), "w")
   456             else:
   456             else:
   457                 mod_cache_html_output = StringIO()
   457                 mod_cache_html_output = StringIO()
   458 
   458 
   459             print >>mod_cache_html_output, mod_cache_html_template % dict(
   459             print >>mod_cache_html_output, mod_cache_html_template % dict(
   460                 mod_name = mod_name,
   460                 mod_name=mod_name,
   461                 app_name = app_name,
   461                 app_name=app_name,
   462                 modnames = modnames,
   462                 modnames=modnames,
   463                 overrides = overnames,
   463                 overrides=overnames,
   464                 mod_libs = mod_libs[platform][mod_name],
   464                 mod_libs=mod_libs[platform][mod_name],
   465                 dynamic = dynamic,
   465                 dynamic=dynamic,
   466                 mod_code = mod_code_,
   466                 mod_code=mod_code_,
   467             )
   467             )
   468 
   468 
   469             if dynamic:
   469             if dynamic:
   470                 mod_cache_html_output.close()
   470                 mod_cache_html_output.close()
   471             else:
   471             else:
   472                 mod_cache_html_output.seek(0)
   472                 mod_cache_html_output.seek(0)
   473                 app_libs_ += mod_cache_html_output.read()
   473                 app_libs_ += mod_cache_html_output.read()
   474 
   474 
   475         # write out the dependency ordering of the modules
   475         # write out the dependency ordering of the modules
   476     
   476 
   477         app_modnames = []
   477         app_modnames = []
   478 
   478 
   479         for md in mod_levels[platform]:
   479         for md in mod_levels[platform]:
   480             mnames = map(lambda x: "'%s'" % x, md)
   480             mnames = map(lambda x: "'%s'" % x, md)
   481             mnames = "new pyjslib.List([\n\t\t\t%s])" % ',\n\t\t\t'.join(mnames)
   481             mnames = "new pyjslib.List([\n\t\t\t%s])" % ',\n\t\t\t'.join(mnames)
   487         # convert the overrides
   487         # convert the overrides
   488 
   488 
   489         overnames = map(lambda x: "'%s': '%s'" % x, pover[platform].items())
   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)
   490         overnames = "new pyjslib.Dict({\n\t\t%s\n\t})" % ',\n\t\t'.join(overnames)
   491 
   491 
   492         #print "platform names", platform, overnames
   492         # print "platform names", platform, overnames
   493         #print pover
   493         # print pover
   494 
   494 
   495         # now write app.allcache including dependency-ordered list of
   495         # now write app.allcache including dependency-ordered list of
   496         # library modules
   496         # library modules
   497 
   497 
   498         file_contents = all_cache_html_template % dict(
   498         file_contents = all_cache_html_template % dict(
   499             app_name = app_name,
   499             app_name=app_name,
   500             early_app_libs = early_app_libs_,
   500             early_app_libs=early_app_libs_,
   501             app_libs = app_libs_,
   501             app_libs=app_libs_,
   502             app_code = app_code_,
   502             app_code=app_code_,
   503             app_body = app_body,
   503             app_body=app_body,
   504             overrides = overnames,
   504             overrides=overnames,
   505             platform = platform.lower(),
   505             platform=platform.lower(),
   506             dynamic = dynamic,
   506             dynamic=dynamic,
   507             app_modnames = app_modnames,
   507             app_modnames=app_modnames,
   508             app_headers = app_headers
   508             app_headers=app_headers
   509         )
   509         )
   510         if cache_buster:
   510         if cache_buster:
   511             digest = md5.new(file_contents).hexdigest()
   511             digest = md5.new(file_contents).hexdigest()
   512             file_name = "%s.%s.%s" % (platform.lower(), app_name, digest)
   512             file_name = "%s.%s.%s" % (platform.lower(), app_name, digest)
   513         else:
   513         else:
   514             file_name = "%s.%s" % (platform.lower(), app_name)
   514             file_name = "%s.%s" % (platform.lower(), app_name)
   515         file_name += ".cache.html" 
   515         file_name += ".cache.html"
   516         out_path = join(output, file_name)
   516         out_path = join(output, file_name)
   517         out_file = open(out_path, 'w')
   517         out_file = open(out_path, 'w')
   518         out_file.write(file_contents)
   518         out_file.write(file_contents)
   519         out_file.close()
   519         out_file.close()
   520         app_files.append((platform.lower(), file_name))
   520         app_files.append((platform.lower(), file_name))
   521         print "Created app file %s:%s: %s" % (
   521         print "Created app file %s:%s: %s" % (
   522             app_name, platform, out_path)
   522             app_name, platform, out_path)
   523 
   523 
   524     return app_files
   524     return app_files
   525 
   525 
       
   526 
   526 def flattenlist(ll):
   527 def flattenlist(ll):
   527     res = []
   528     res = []
   528     for l in ll:
   529     for l in ll:
   529         res += l
   530         res += l
   530     return res
   531     return res
   531 
   532 
   532 # creates sub-dependencies e.g. pyjamas.ui.Widget
   533 
   533 # creates pyjamas.ui.Widget, pyjamas.ui and pyjamas.
       
   534 def subdeps(m):
   534 def subdeps(m):
       
   535     """
       
   536     creates sub-dependencies e.g. pyjamas.ui.Widget
       
   537     creates pyjamas.ui.Widget, pyjamas.ui and pyjamas.
       
   538     """
   535     d = []
   539     d = []
   536     m = m.split(".")
   540     m = m.split(".")
   537     for i in range(0, len(m)):
   541     for i in range(0, len(m)):
   538         d.append('.'.join(m[:i+1]))
   542         d.append('.'.join(m[:i+1]))
   539     return d
   543     return d
   540 
   544 
   541 import time
       
   542 
   545 
   543 def add_subdeps(deps, mod_name):
   546 def add_subdeps(deps, mod_name):
   544     sd = subdeps(mod_name)
   547     sd = subdeps(mod_name)
   545     if len(sd) == 1:
   548     if len(sd) == 1:
   546         return []
   549         return []
   547     #print "subdeps", mod_name, sd
   550     # print "subdeps", mod_name, sd
   548     #print "deps", deps
   551     # print "deps", deps
   549     res = []
   552     res = []
   550     for i in range(0, len(sd)-1):
   553     for i in range(0, len(sd)-1):
   551         parent = sd[i]
   554         parent = sd[i]
   552         child = sd[i+1]
   555         child = sd[i+1]
   553         l = deps.get(child, [])
   556         k = deps.get(child, [])
   554         l.append(parent)
   557         k.append(parent)
   555         deps[child] = l
   558         deps[child] = k
   556         if parent not in res:
   559         if parent not in res:
   557             res.append(parent)
   560             res.append(parent)
   558     #print deps
   561     # print deps
   559     return res
   562     return res
   560 
   563 
   561 # makes unique and preserves list order
   564 
   562 def uniquify(md):
   565 def uniquify(md):
       
   566     """
       
   567     makes unique and preserves list order
       
   568     """
   563     res = []
   569     res = []
   564     for m in md:
   570     for m in md:
   565         if m not in res:
   571         if m not in res:
   566             res.append(m)
   572             res.append(m)
   567     return res
   573     return res
       
   574 
   568 
   575 
   569 def filter_mods(app_name, md):
   576 def filter_mods(app_name, md):
   570     while 'sys' in md:
   577     while 'sys' in md:
   571         md.remove('sys')
   578         md.remove('sys')
   572     while 'pyjslib' in md:
   579     while 'pyjslib' in md:
   576     md = filter(lambda x: not x.endswith('.js'), md)
   583     md = filter(lambda x: not x.endswith('.js'), md)
   577     md = map(pyjs.strip_py, md)
   584     md = map(pyjs.strip_py, md)
   578 
   585 
   579     return uniquify(md)
   586     return uniquify(md)
   580 
   587 
       
   588 
   581 def filter_deps(app_name, deps):
   589 def filter_deps(app_name, deps):
   582 
   590 
   583     res = {}
   591     res = {}
   584     for (k, l) in deps.items():
   592     for (k, l) in deps.items():
   585         mods = filter_mods(k, l)
   593         mods = filter_mods(k, l)
   586         while k in mods:
   594         while k in mods:
   587             mods.remove(k)
   595             mods.remove(k)
   588         res[k] = mods
   596         res[k] = mods
   589     return res
   597     return res
   590 
   598 
       
   599 
   591 def has_nodeps(mod, deps):
   600 def has_nodeps(mod, deps):
   592     if not deps.has_key(mod) or not deps[mod]:
   601     if mod not in deps or not deps[mod]:
   593         return True
   602         return True
   594     return False
   603     return False
       
   604 
   595 
   605 
   596 def nodeps_list(mod_list, deps):
   606 def nodeps_list(mod_list, deps):
   597     res = []
   607     res = []
   598     for mod in mod_list:
   608     for mod in mod_list:
   599         if has_nodeps(mod, deps):
   609         if has_nodeps(mod, deps):
   600             res.append(mod)
   610             res.append(mod)
   601     return res
   611     return res
   602         
   612 
   603 # this function takes a dictionary of dependent modules and
   613 # this function takes a dictionary of dependent modules and
   604 # creates a list of lists.  the first list will be modules
   614 # creates a list of lists.  the first list will be modules
   605 # that have no dependencies; the second list will be those
   615 # that have no dependencies; the second list will be those
   606 # modules that have the first list as dependencies; the
   616 # modules that have the first list as dependencies; the
   607 # third will be those modules that have the first and second...
   617 # third will be those modules that have the first and second...
   614     deps = filter_deps(app_name, deps)
   624     deps = filter_deps(app_name, deps)
   615 
   625 
   616     if not mod_list:
   626     if not mod_list:
   617         return []
   627         return []
   618 
   628 
   619     #print mod_list
   629     # print mod_list
   620     #print deps
   630     # print deps
   621 
   631 
   622     ordered_deps = []
   632     ordered_deps = []
   623     last_len = -1
   633     last_len = -1
   624     while deps:
   634     while deps:
   625         l_deps = len(deps)
   635         l_deps = len(deps)
   626         #print l_deps
   636         # print l_deps
   627         if l_deps==last_len:
   637         if l_deps == last_len:
   628             for m, dl in deps.items():
   638             for m, dl in deps.items():
   629                 for d in dl:
   639                 for d in dl:
   630                     if m in deps.get(d, []):
   640                     if m in deps.get(d, []):
   631                         raise Exception('Circular Imports found: \n%s %s -> %s %s'
   641                         raise Exception('Circular Imports found: \n%s %s -> %s %s'
   632                                         % (m, dl, d, deps[d]))
   642                                         % (m, dl, d, deps[d]))
   633             #raise Exception('Could not calculate dependencies: \n%s' % deps)
   643             # raise Exception('Could not calculate dependencies: \n%s' % deps)
   634             break
   644             break
   635         last_len = l_deps
   645         last_len = l_deps
   636         #print "modlist", mod_list
   646         # print "modlist", mod_list
   637         nodeps = nodeps_list(mod_list, deps)
   647         nodeps = nodeps_list(mod_list, deps)
   638         #print "nodeps", nodeps
   648         # print "nodeps", nodeps
   639         mod_list = filter(lambda x: x not in nodeps, mod_list)
   649         mod_list = filter(lambda x: x not in nodeps, mod_list)
   640         newdeps = {}
   650         newdeps = {}
   641         for k in deps.keys():
   651         for k in deps.keys():
   642             depslist = deps[k]
   652             depslist = deps[k]
   643             depslist = filter(lambda x: x not in nodeps, depslist)
   653             depslist = filter(lambda x: x not in nodeps, depslist)
   644             if depslist:
   654             if depslist:
   645                 newdeps[k] = depslist
   655                 newdeps[k] = depslist
   646         #print "newdeps", newdeps
   656         # print "newdeps", newdeps
   647         deps = newdeps
   657         deps = newdeps
   648         ordered_deps.append(nodeps)
   658         ordered_deps.append(nodeps)
   649         #time.sleep(0)
   659         # time.sleep(0)
   650 
   660 
   651     if mod_list:
   661     if mod_list:
   652         ordered_deps.append(mod_list) # last dependencies - usually the app(s)
   662         ordered_deps.append(mod_list)  # last dependencies - usually the app(s)
   653 
   663 
   654     ordered_deps.reverse()
   664     ordered_deps.reverse()
   655 
   665 
   656     return ordered_deps
   666     return ordered_deps
       
   667 
   657 
   668 
   658 def main():
   669 def main():
   659     global app_platforms
   670     global app_platforms
   660 
   671 
   661     parser = OptionParser(usage = usage, version = version)
   672     parser = OptionParser(usage=usage, version=version)
   662     parser.add_option("-o", "--output", dest="output",
   673     parser.add_option(
   663         help="directory to which the webapp should be written")
   674         "-o",
   664     parser.add_option("-j", "--include-js", dest="js_includes", action="append",
   675         "--output",
   665         help="javascripts to load into the same frame as the rest of the script")
   676         dest="output",
   666     parser.add_option("-I", "--library_dir", dest="library_dirs",
   677         help="directory to which the webapp should be written"
   667         action="append", help="additional paths appended to PYJSPATH")
   678     )
   668     parser.add_option("-D", "--data_dir", dest="data_dir",
   679     parser.add_option(
   669         help="path for data directory")
   680         "-j",
   670     parser.add_option("-m", "--dynamic-modules", action="store_true",
   681         "--include-js",
   671         dest="dynamic", default=False,
   682         dest="js_includes",
   672         help="Split output into separate dynamically-loaded modules (experimental)")
   683         action="append",
   673     parser.add_option("-P", "--platforms", dest="platforms",
   684         help="javascripts to load into the same frame as the rest of the script"
   674         help="platforms to build for, comma-separated")
   685     )
   675     parser.add_option("-d", "--debug", action="store_true", dest="debug")
   686     parser.add_option(
   676     parser.add_option("-O", "--optimize", action="store_true",
   687         "-I",
   677                       dest="optimize", default=False,
   688         "--library_dir",
   678                       help="Optimize generated code (removes all print statements)",
   689         dest="library_dirs",
   679                       )
   690         action="append",
   680     parser.add_option("-c", "--cache_buster", action="store_true",
   691         help="additional paths appended to PYJSPATH"
   681                   dest="cache_buster",
   692     )
   682         help="Enable browser cache-busting (MD5 hash added to output filenames)")
   693     parser.add_option(
   683 
   694         "-D",
   684     parser.set_defaults(output = "output", js_includes=[], library_dirs=[],
   695         "--data_dir",
       
   696         dest="data_dir",
       
   697         help="path for data directory"
       
   698     )
       
   699     parser.add_option(
       
   700         "-m",
       
   701         "--dynamic-modules",
       
   702         action="store_true",
       
   703         dest="dynamic",
       
   704         default=False,
       
   705         help="Split output into separate dynamically-loaded modules (experimental)"
       
   706     )
       
   707     parser.add_option(
       
   708         "-P",
       
   709         "--platforms",
       
   710         dest="platforms",
       
   711         help="platforms to build for, comma-separated"
       
   712     )
       
   713     parser.add_option(
       
   714         "-d",
       
   715         "--debug",
       
   716         action="store_true",
       
   717         dest="debug"
       
   718     )
       
   719     parser.add_option(
       
   720         "-O",
       
   721         "--optimize",
       
   722         action="store_true",
       
   723         dest="optimize",
       
   724         default=False,
       
   725         help="Optimize generated code (removes all print statements)",
       
   726     )
       
   727     parser.add_option(
       
   728         "-c",
       
   729         "--cache_buster",
       
   730         action="store_true",
       
   731         dest="cache_buster",
       
   732         help="Enable browser cache-busting (MD5 hash added to output filenames)"
       
   733     )
       
   734 
       
   735     parser.set_defaults(output="output", js_includes=[], library_dirs=[],
   685                         platforms=(','.join(app_platforms)),
   736                         platforms=(','.join(app_platforms)),
   686                         data_dir=os.path.join(sys.prefix, "share/pyjamas"),
   737                         data_dir=os.path.join(sys.prefix, "share/pyjamas"),
   687                         dynamic=False,
   738                         dynamic=False,
   688                         cache_buster=False,
   739                         cache_buster=False,
   689                         debug=False)
   740                         debug=False)
   708 
   759 
   709     for d in options.library_dirs:
   760     for d in options.library_dirs:
   710         pyjs.path.append(abspath(d))
   761         pyjs.path.append(abspath(d))
   711 
   762 
   712     if options.platforms:
   763     if options.platforms:
   713        app_platforms = options.platforms.split(',')
   764         app_platforms = options.platforms.split(',')
   714 
   765 
   715     # this is mostly for getting boilerplate stuff
   766     # this is mostly for getting boilerplate stuff
   716     data_dir = os.path.abspath(options.data_dir)
   767     data_dir = os.path.abspath(options.data_dir)
   717 
   768 
   718     build(app_name, options.output, options.js_includes,
   769     build(app_name, options.output, options.js_includes,
   719           options.debug, options.dynamic and 1 or 0, data_dir,
   770           options.debug, options.dynamic and 1 or 0, data_dir,
   720           options.cache_buster, options.optimize)
   771           options.cache_buster, options.optimize)
   721 
   772 
       
   773 
   722 if __name__ == "__main__":
   774 if __name__ == "__main__":
   723     main()
   775     main()
   724