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