Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """Creates simple HTML for running a NaCl module.
      7 
      8 This script is designed to make the process of creating running
      9 Native Client executables in the browers simple by creating
     10 boilderplate a .html (and optionally a .nmf) file for a given
     11 Native Client executable (.nexe).
     12 
     13 If the script if given a .nexe file it will produce both html
     14 the nmf files.  If it is given an nmf it will only create
     15 the html file.
     16 """
     17 
     18 import optparse
     19 import os
     20 import sys
     21 import subprocess
     22 
     23 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
     24 HTML_TEMPLATE = '''\
     25 <!DOCTYPE html>
     26 <!--
     27 Sample html container for embedded NaCl module.  This file was auto-generated
     28 by the create_html tool which is part of the NaCl SDK.
     29 
     30 The embed tag is setup with PS_STDOUT, PS_STDERR and PS_TTY_PREFIX attributes
     31 which, for applications linked with ppapi_simple, will cause stdout and stderr
     32 to be sent to javascript via postMessage.  Also, the postMessage listener
     33 assumes that all messages sent via postMessage are strings to be displayed in
     34 the output textarea.
     35 -->
     36 <html>
     37 <head>
     38   <meta http-equiv="Pragma" content="no-cache">
     39   <meta http-equiv="Expires" content="-1">
     40   <title>%(title)s</title>
     41 
     42 </head>
     43 <body>
     44   <h2>Native Client Module: %(module_name)s</h2>
     45   <p>Status: <code id="status">Loading</code></p>
     46 
     47   <div id="listener">
     48     <embed id="nacl_module" name="%(module_name)s" src="%(nmf)s"
     49            type="application/x-nacl" width=640 height=480
     50            PS_TTY_PREFIX="tty:"
     51            PS_STDOUT="/dev/tty"
     52            PS_STDERR="/dev/tty" >/
     53   </div>
     54 
     55   <p>Standard output/error:</p>
     56   <textarea id="stdout" rows="25" cols="80">
     57 </textarea>
     58 
     59   <script>
     60 listenerDiv = document.getElementById("listener")
     61 stdout = document.getElementById("stdout")
     62 nacl_module = document.getElementById("nacl_module")
     63 
     64 function updateStatus(message) {
     65   document.getElementById("status").innerHTML = message
     66 }
     67 
     68 function addToStdout(message) {
     69   stdout.value += message;
     70   stdout.scrollTop = stdout.scrollHeight;
     71 }
     72 
     73 function handleMessage(message) {
     74   var payload = message.data;
     75   var prefix = "tty:";
     76   if (typeof(payload) == 'string' && payload.indexOf(prefix) == 0) {
     77     addToStdout(payload.slice(prefix.length));
     78   }
     79 }
     80 
     81 function handleCrash(event) {
     82   updateStatus("Crashed/exited with status: " + nacl_module.exitStatus)
     83 }
     84 
     85 function handleLoad(event) {
     86   updateStatus("Loaded")
     87 }
     88 
     89 listenerDiv.addEventListener("load", handleLoad, true);
     90 listenerDiv.addEventListener("message", handleMessage, true);
     91 listenerDiv.addEventListener("crash", handleCrash, true);
     92   </script>
     93 </body>
     94 </html>
     95 '''
     96 
     97 
     98 class Error(Exception):
     99   pass
    100 
    101 
    102 def Log(msg):
    103   if Log.enabled:
    104     sys.stderr.write(str(msg) + '\n')
    105 Log.enabled = False
    106 
    107 
    108 def CreateHTML(filenames, options):
    109   nmf = None
    110 
    111   for filename in filenames:
    112     if not os.path.exists(filename):
    113       raise Error('file not found: %s' % filename)
    114 
    115     if not os.path.isfile(filename):
    116       raise Error('specified input is not a file: %s' % filename)
    117 
    118     basename, ext = os.path.splitext(filename)
    119     if ext not in ('.nexe', '.pexe', '.nmf'):
    120       raise Error('input file must be .nexe, .pexe or .nmf: %s' % filename)
    121 
    122     if ext == '.nmf':
    123       if len(filenames) > 1:
    124         raise Error('Only one .nmf argument can be specified')
    125       nmf = filename
    126     elif len(filenames) > 1 and not options.output:
    127       raise Error('When specifying muliple input files -o must'
    128                   ' also be specified.')
    129 
    130   htmlfile = options.output
    131   if not htmlfile:
    132     htmlfile = basename + '.html'
    133   basename = os.path.splitext(os.path.basename(htmlfile))[0]
    134 
    135   if not nmf:
    136     nmf = os.path.splitext(htmlfile)[0] + '.nmf'
    137     Log('creating nmf: %s' % nmf)
    138     create_nmf = os.path.join(SCRIPT_DIR, 'create_nmf.py')
    139     staging = os.path.dirname(nmf)
    140     if not staging:
    141       staging = '.'
    142     cmd = [create_nmf, '-s', staging, '-o', nmf] + filenames
    143     if options.verbose:
    144       cmd.append('-v')
    145     if options.debug_libs:
    146       cmd.append('--debug-libs')
    147     Log(cmd)
    148     try:
    149       subprocess.check_call(cmd)
    150     except subprocess.CalledProcessError:
    151       raise Error('create_nmf failed')
    152 
    153   Log('creating html: %s' % htmlfile)
    154   with open(htmlfile, 'w') as outfile:
    155     args = {}
    156     args['title'] = basename
    157     args['module_name'] = basename
    158     args['nmf'] = os.path.basename(nmf)
    159     outfile.write(HTML_TEMPLATE % args)
    160 
    161 
    162 def main(argv):
    163   usage = 'Usage: %prog [options] <.nexe/.pexe or .nmf>'
    164   epilog = 'Example: create_html.py -o index.html my_nexe.nexe'
    165   parser = optparse.OptionParser(usage, description=__doc__, epilog=epilog)
    166   parser.add_option('-v', '--verbose', action='store_true',
    167                     help='Verbose output')
    168   parser.add_option('-d', '--debug-libs', action='store_true',
    169                     help='When calling create_nmf request debug libaries')
    170   parser.add_option('-o', '--output', dest='output',
    171                     help='Name of html file to write (default is '
    172                          'input name with .html extension)',
    173                     metavar='FILE')
    174 
    175   # To enable bash completion for this command first install optcomplete
    176   # and then add this line to your .bashrc:
    177   #  complete -F _optcomplete create_html.py
    178   try:
    179     import optcomplete
    180     optcomplete.autocomplete(parser)
    181   except ImportError:
    182     pass
    183 
    184   options, args = parser.parse_args(argv)
    185 
    186   if not args:
    187     parser.error('no input file specified')
    188 
    189   if options.verbose:
    190     Log.enabled = True
    191 
    192   CreateHTML(args, options)
    193   return 0
    194 
    195 
    196 if __name__ == '__main__':
    197   try:
    198     rtn = main(sys.argv[1:])
    199   except Error, e:
    200     sys.stderr.write('%s: %s\n' % (os.path.basename(__file__), e))
    201     rtn = 1
    202   except KeyboardInterrupt:
    203     sys.stderr.write('%s: interrupted\n' % os.path.basename(__file__))
    204     rtn = 1
    205   sys.exit(rtn)
    206