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 8This script is designed to make the process of creating running 9Native Client executables in the browers simple by creating 10boilderplate a .html (and optionally a .nmf) file for a given 11Native Client executable (.nexe). 12 13If the script if given a .nexe file it will produce both html 14the nmf files. If it is given an nmf it will only create 15the html file. 16""" 17 18import optparse 19import os 20import sys 21import subprocess 22 23SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 24HTML_TEMPLATE = '''\ 25<!DOCTYPE html> 26<!-- 27Sample html container for embedded NaCl module. This file was auto-generated 28by the create_html tool which is part of the NaCl SDK. 29 30The embed tag is setup with PS_STDOUT, PS_STDERR and PS_TTY_PREFIX attributes 31which, for applications linked with ppapi_simple, will cause stdout and stderr 32to be sent to javascript via postMessage. Also, the postMessage listener 33assumes that all messages sent via postMessage are strings to be displayed in 34the 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> 60listenerDiv = document.getElementById("listener") 61stdout = document.getElementById("stdout") 62nacl_module = document.getElementById("nacl_module") 63 64function updateStatus(message) { 65 document.getElementById("status").innerHTML = message 66} 67 68function addToStdout(message) { 69 stdout.value += message; 70 stdout.scrollTop = stdout.scrollHeight; 71} 72 73function 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 81function handleCrash(event) { 82 updateStatus("Crashed/exited with status: " + nacl_module.exitStatus) 83} 84 85function handleLoad(event) { 86 updateStatus("Loaded") 87} 88 89listenerDiv.addEventListener("load", handleLoad, true); 90listenerDiv.addEventListener("message", handleMessage, true); 91listenerDiv.addEventListener("crash", handleCrash, true); 92 </script> 93</body> 94</html> 95''' 96 97 98class Error(Exception): 99 pass 100 101 102def Log(msg): 103 if Log.enabled: 104 sys.stderr.write(str(msg) + '\n') 105Log.enabled = False 106 107 108def 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 162def 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 196if __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