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 and ps_stderr attributes which, for
     31 applications linked with ppapi_simple, will cause stdout and stderr to be sent
     32 to javascript via postMessage.  Also, the postMessage listener assumes that
     33 all messages sent via postMessage are strings to be displayed in the output
     34 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" ps_stdout="/dev/tty" ps_stderr="/dev/tty"
     50            width=640 height=480 />
     51   </div>
     52 
     53   <p>Standard output/error:</p>
     54   <textarea id="stdout" rows="25" cols="80">
     55 </textarea>
     56 
     57   <script>
     58 listenerDiv = document.getElementById("listener")
     59 stdout = document.getElementById("stdout")
     60 nacl_module = document.getElementById("nacl_module")
     61 
     62 function updateStatus(message) {
     63   document.getElementById("status").innerHTML = message
     64 }
     65 
     66 function addToStdout(message) {
     67   stdout.value += message;
     68   stdout.scrollTop = stdout.scrollHeight;
     69 }
     70 
     71 function handleMessage(message) {
     72   addToStdout(message.data)
     73 }
     74 
     75 function handleCrash(event) {
     76   updateStatus("Crashed/exited with status: " + nacl_module.exitStatus)
     77 }
     78 
     79 function handleLoad(event) {
     80   updateStatus("Loaded")
     81 }
     82 
     83 listenerDiv.addEventListener("load", handleLoad, true);
     84 listenerDiv.addEventListener("message", handleMessage, true);
     85 listenerDiv.addEventListener("crash", handleCrash, true);
     86   </script>
     87 </body>
     88 </html>
     89 '''
     90 
     91 
     92 class Error(Exception):
     93   pass
     94 
     95 
     96 def Log(msg):
     97   if Log.enabled:
     98     sys.stderr.write(str(msg) + '\n')
     99 Log.enabled = False
    100 
    101 
    102 def CreateHTML(filenames, options):
    103   nmf = None
    104 
    105   for filename in filenames:
    106     if not os.path.exists(filename):
    107       raise Error('file not found: %s' % filename)
    108 
    109     if not os.path.isfile(filename):
    110       raise Error('specified input is not a file: %s' % filename)
    111 
    112     basename, ext = os.path.splitext(filename)
    113     if ext not in ('.nexe', '.pexe', '.nmf'):
    114       raise Error('input file must be .nexe, .pexe or .nmf: %s' % filename)
    115 
    116     if ext == '.nmf':
    117       if len(filenames) > 1:
    118         raise Error('Only one .nmf argument can be specified')
    119       nmf = filename
    120     elif len(filenames) > 1 and not options.output:
    121       raise Error('When specifying muliple input files -o must'
    122                   ' also be specified.')
    123 
    124   htmlfile = options.output
    125   if not htmlfile:
    126     htmlfile = basename + '.html'
    127   basename = os.path.splitext(os.path.basename(htmlfile))[0]
    128 
    129   if not nmf:
    130     nmf = os.path.splitext(htmlfile)[0] + '.nmf'
    131     Log('creating nmf: %s' % nmf)
    132     create_nmf = os.path.join(SCRIPT_DIR, 'create_nmf.py')
    133     staging = os.path.dirname(nmf)
    134     if not staging:
    135       staging = '.'
    136     cmd = [create_nmf, '-s', staging, '-o', nmf] + filenames
    137     if options.verbose:
    138       cmd.append('-v')
    139     if options.debug_libs:
    140       cmd.append('--debug-libs')
    141     Log(cmd)
    142     try:
    143       subprocess.check_call(cmd)
    144     except subprocess.CalledProcessError:
    145       raise Error('create_nmf failed')
    146 
    147   Log('creating html: %s' % htmlfile)
    148   with open(htmlfile, 'w') as outfile:
    149     args = {}
    150     args['title'] = basename
    151     args['module_name'] = basename
    152     args['nmf'] = os.path.basename(nmf)
    153     outfile.write(HTML_TEMPLATE % args)
    154 
    155 
    156 def main(argv):
    157   usage = 'Usage: %prog [options] <.nexe/.pexe or .nmf>'
    158   description = __doc__
    159   epilog = 'Example: create_html.py -o index.html my_nexe.nexe'
    160   parser = optparse.OptionParser(usage, description=description, epilog=epilog)
    161   parser.add_option('-v', '--verbose', action='store_true',
    162                     help='Verbose output')
    163   parser.add_option('-d', '--debug-libs', action='store_true',
    164                     help='When calling create_nmf request debug libaries')
    165   parser.add_option('-o', '--output', dest='output',
    166                     help='Name of html file to write (default is '
    167                          'input name with .html extension)',
    168                     metavar='FILE')
    169 
    170   options, args = parser.parse_args(argv)
    171 
    172   if not args:
    173     parser.error('no input file specified')
    174 
    175   if options.verbose:
    176     Log.enabled = True
    177 
    178   CreateHTML(args, options)
    179   return 0
    180 
    181 
    182 if __name__ == '__main__':
    183   try:
    184     rtn = main(sys.argv[1:])
    185   except Error, e:
    186     sys.stderr.write('%s: %s\n' % (os.path.basename(__file__), e))
    187     rtn = 1
    188   except KeyboardInterrupt:
    189     sys.stderr.write('%s: interrupted\n' % os.path.basename(__file__))
    190     rtn = 1
    191   sys.exit(rtn)
    192