#!/usr/bin/env python # Copyright (c) 2012 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Server for viewing the compiled C++ code from tools/json_schema_compiler. """ import cc_generator import code import cpp_type_generator import cpp_util import h_generator import idl_schema import json_schema import model import optparse import os import shlex import urlparse from highlighters import ( pygments_highlighter, none_highlighter, hilite_me_highlighter) from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from cpp_namespace_environment import CppNamespaceEnvironment from schema_loader import SchemaLoader class CompilerHandler(BaseHTTPRequestHandler): """A HTTPRequestHandler that outputs the result of tools/json_schema_compiler. """ def do_GET(self): parsed_url = urlparse.urlparse(self.path) request_path = self._GetRequestPath(parsed_url) chromium_favicon = 'http://codereview.chromium.org/static/favicon.ico' head = code.Code() head.Append('' % chromium_favicon) head.Append('' % chromium_favicon) body = code.Code() try: if os.path.isdir(request_path): self._ShowPanels(parsed_url, head, body) else: self._ShowCompiledFile(parsed_url, head, body) finally: self.wfile.write('') self.wfile.write(head.Render()) self.wfile.write('') self.wfile.write(body.Render()) self.wfile.write('') def _GetRequestPath(self, parsed_url, strip_nav=False): """Get the relative path from the current directory to the requested file. """ path = parsed_url.path if strip_nav: path = parsed_url.path.replace('/nav', '') return os.path.normpath(os.curdir + path) def _ShowPanels(self, parsed_url, head, body): """Show the previewer frame structure. Code panes are populated via XHR after links in the nav pane are clicked. """ (head.Append('') ) body.Append( '' '
' '
' % self._RenderNavPane(parsed_url.path[1:]) ) # The Javascript that interacts with the nav pane and panes to show the # compiled files as the URL or highlighting options change. body.Append('''''') def _ShowCompiledFile(self, parsed_url, head, body): """Show the compiled version of a json or idl file given the path to the compiled file. """ api_model = model.Model() request_path = self._GetRequestPath(parsed_url) (file_root, file_ext) = os.path.splitext(request_path) (filedir, filename) = os.path.split(file_root) schema_loader = SchemaLoader("./", filedir, self.server.include_rules, self.server.cpp_namespace_pattern) try: # Get main file. namespace = schema_loader.ResolveNamespace(filename) type_generator = cpp_type_generator.CppTypeGenerator( api_model, schema_loader, namespace) # Generate code if file_ext == '.h': cpp_code = (h_generator.HGenerator(type_generator) .Generate(namespace).Render()) elif file_ext == '.cc': cpp_code = (cc_generator.CCGenerator(type_generator) .Generate(namespace).Render()) else: self.send_error(404, "File not found: %s" % request_path) return # Do highlighting on the generated code (highlighter_param, style_param) = self._GetHighlighterParams(parsed_url) head.Append('') body.Append(self.server.highlighters[highlighter_param] .GetCodeElement(cpp_code, style_param)) except IOError: self.send_error(404, "File not found: %s" % request_path) return except (TypeError, KeyError, AttributeError, AssertionError, NotImplementedError) as error: body.Append('
')
      body.Append('compiler error: %s' % error)
      body.Append('Check server log for more details')
      body.Append('
') raise def _GetHighlighterParams(self, parsed_url): """Get the highlighting parameters from a parsed url. """ query_dict = urlparse.parse_qs(parsed_url.query) return (query_dict.get('highlighter', ['pygments'])[0], query_dict.get('style', ['colorful'])[0]) def _RenderNavPane(self, path): """Renders an HTML nav pane. This consists of a select element to set highlight style, and a list of all files at |path| with the appropriate onclick handlers to open either subdirectories or JSON files. """ html = code.Code() # Highlighter chooser. html.Append('') html.Append('
') # Style for each highlighter. # The correct highlighting will be shown by Javascript. for name, highlighter in self.server.highlighters.items(): styles = sorted(highlighter.GetStyles()) if not styles: continue html.Append('') html.Append('
') # The files, with appropriate handlers. html.Append('') return html.Render() class PreviewHTTPServer(HTTPServer, object): def __init__(self, server_address, handler, highlighters, include_rules, cpp_namespace_pattern): super(PreviewHTTPServer, self).__init__(server_address, handler) self.highlighters = highlighters self.include_rules = include_rules self.cpp_namespace_pattern = cpp_namespace_pattern if __name__ == '__main__': parser = optparse.OptionParser( description='Runs a server to preview the json_schema_compiler output.', usage='usage: %prog [option]...') parser.add_option('-p', '--port', default='8000', help='port to run the server on') parser.add_option('-n', '--namespace', default='generated_api_schemas', help='C++ namespace for generated files. e.g extensions::api.') parser.add_option('-I', '--include-rules', help='A list of paths to include when searching for referenced objects,' ' with the namespace separated by a \':\'. Example: ' '/foo/bar:Foo::Bar::%(namespace)s') (opts, argv) = parser.parse_args() def split_path_and_namespace(path_and_namespace): if ':' not in path_and_namespace: raise ValueError('Invalid include rule "%s". Rules must be of ' 'the form path:namespace' % path_and_namespace) return path_and_namespace.split(':', 1) include_rules = [] if opts.include_rules: include_rules = map(split_path_and_namespace, shlex.split(opts.include_rules)) try: print('Starting previewserver on port %s' % opts.port) print('The extension documentation can be found at:') print('') print(' http://localhost:%s/chrome/common/extensions/api' % opts.port) print('') highlighters = { 'hilite': hilite_me_highlighter.HiliteMeHighlighter(), 'none': none_highlighter.NoneHighlighter() } try: highlighters['pygments'] = pygments_highlighter.PygmentsHighlighter() except ImportError as e: pass server = PreviewHTTPServer(('', int(opts.port)), CompilerHandler, highlighters, include_rules, opts.namespace) server.serve_forever() except KeyboardInterrupt: server.socket.close()