michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: # michael@0: # ToCAsciiArray and ToCArray are from V8's js2c.py. michael@0: # michael@0: # Copyright 2012 the V8 project authors. All rights reserved. michael@0: # Redistribution and use in source and binary forms, with or without michael@0: # modification, are permitted provided that the following conditions are michael@0: # met: michael@0: # michael@0: # * Redistributions of source code must retain the above copyright michael@0: # notice, this list of conditions and the following disclaimer. michael@0: # * Redistributions in binary form must reproduce the above michael@0: # copyright notice, this list of conditions and the following michael@0: # disclaimer in the documentation and/or other materials provided michael@0: # with the distribution. michael@0: # * Neither the name of Google Inc. nor the names of its michael@0: # contributors may be used to endorse or promote products derived michael@0: # from this software without specific prior written permission. michael@0: # michael@0: # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: # This utility converts JS files containing self-hosted builtins into a C michael@0: # header file that can be embedded into SpiderMonkey. michael@0: # michael@0: # It uses the C preprocessor to process its inputs. michael@0: michael@0: from __future__ import with_statement michael@0: import re, sys, os, fileinput, subprocess michael@0: import shlex michael@0: from optparse import OptionParser michael@0: michael@0: def ToCAsciiArray(lines): michael@0: result = [] michael@0: for chr in lines: michael@0: value = ord(chr) michael@0: assert value < 128 michael@0: result.append(str(value)) michael@0: return ", ".join(result) michael@0: michael@0: def ToCArray(lines): michael@0: result = [] michael@0: for chr in lines: michael@0: result.append(str(ord(chr))) michael@0: return ", ".join(result) michael@0: michael@0: HEADER_TEMPLATE = """\ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: namespace js { michael@0: namespace selfhosted { michael@0: static const %(sources_type)s data[] = { %(sources_data)s }; michael@0: michael@0: static const %(sources_type)s * const %(sources_name)s = reinterpret_cast(data); michael@0: michael@0: uint32_t GetCompressedSize() { michael@0: return %(compressed_total_length)i; michael@0: } michael@0: michael@0: uint32_t GetRawScriptsSize() { michael@0: return %(raw_total_length)i; michael@0: } michael@0: } // selfhosted michael@0: } // js michael@0: """ michael@0: michael@0: def embed(cpp, msgs, sources, c_out, js_out, env): michael@0: # Clang seems to complain and not output anything if the extension of the michael@0: # input is not something it recognizes, so just fake a .h here. michael@0: tmp = 'selfhosted.js.h' michael@0: with open(tmp, 'wb') as output: michael@0: output.write('\n'.join([msgs] + ['#include "%(s)s"' % { 's': source } for source in sources])) michael@0: cmdline = cpp + ['-D%(k)s=%(v)s' % { 'k': k, 'v': env[k] } for k in env] + [tmp] michael@0: p = subprocess.Popen(cmdline, stdout=subprocess.PIPE) michael@0: processed = '' michael@0: for line in p.stdout: michael@0: if not line.startswith('#'): michael@0: processed += line michael@0: os.remove(tmp) michael@0: with open(js_out, 'w') as output: michael@0: output.write(processed) michael@0: with open(c_out, 'w') as output: michael@0: if 'USE_ZLIB' in env: michael@0: import zlib michael@0: compressed = zlib.compress(processed) michael@0: data = ToCArray(compressed) michael@0: output.write(HEADER_TEMPLATE % { michael@0: 'sources_type': 'unsigned char', michael@0: 'sources_data': data, michael@0: 'sources_name': 'compressedSources', michael@0: 'compressed_total_length': len(compressed), michael@0: 'raw_total_length': len(processed) michael@0: }) michael@0: else: michael@0: data = ToCAsciiArray(processed) michael@0: output.write(HEADER_TEMPLATE % { michael@0: 'sources_type': 'char', michael@0: 'sources_data': data, michael@0: 'sources_name': 'rawSources', michael@0: 'compressed_total_length': 0, michael@0: 'raw_total_length': len(processed) michael@0: }) michael@0: michael@0: def process_msgs(cpp, msgs): michael@0: # Clang seems to complain and not output anything if the extension of the michael@0: # input is not something it recognizes, so just fake a .h here. michael@0: tmp = 'selfhosted.msg.h' michael@0: with open(tmp, 'wb') as output: michael@0: output.write("""\ michael@0: #define hash # michael@0: #define id(x) x michael@0: #define hashify(x) id(hash)x michael@0: #define MSG_DEF(name, id, argc, ex, msg) hashify(define) name id michael@0: #include "%(msgs)s" michael@0: """ % { 'msgs': msgs }) michael@0: p = subprocess.Popen(cpp + [tmp], stdout=subprocess.PIPE) michael@0: processed = p.communicate()[0] michael@0: os.remove(tmp) michael@0: return processed michael@0: michael@0: def main(): michael@0: env = {} michael@0: def define_env(option, opt, value, parser): michael@0: pair = value.split('=', 1) michael@0: if len(pair) == 1: michael@0: pair.append(1) michael@0: env[pair[0]] = pair[1] michael@0: p = OptionParser(usage="%prog [options] file") michael@0: p.add_option('-D', action='callback', callback=define_env, type="string", michael@0: metavar='var=[val]', help='Define a variable') michael@0: p.add_option('-m', type='string', metavar='jsmsg', default='../js.msg', michael@0: help='js.msg file') michael@0: p.add_option('-p', type='string', metavar='cpp', help='Path to C preprocessor') michael@0: p.add_option('-o', type='string', metavar='filename', default='selfhosted.out.h', michael@0: help='C array header file') michael@0: p.add_option('-s', type='string', metavar='jsfilename', default='selfhosted.js', michael@0: help='Combined postprocessed JS file') michael@0: (options, sources) = p.parse_args() michael@0: if not (options.p and sources): michael@0: p.print_help() michael@0: exit(1) michael@0: cpp = shlex.split(options.p) michael@0: embed(cpp, process_msgs(cpp, options.m), michael@0: sources, options.o, options.s, env) michael@0: michael@0: if __name__ == "__main__": michael@0: main()