1#!/usr/bin/env python 2# Copyright (c) 2012 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"""Generator for C++ structs from api json files. 6 7The purpose of this tool is to remove the need for hand-written code that 8converts to and from base::Value types when receiving javascript api calls. 9Originally written for generating code for extension apis. Reference schemas 10are in chrome/common/extensions/api. 11 12Usage example: 13 compiler.py --root /home/Work/src --namespace extensions windows.json 14 tabs.json 15 compiler.py --destdir gen --root /home/Work/src 16 --namespace extensions windows.json tabs.json 17""" 18 19import optparse 20import os 21import sys 22 23from cpp_bundle_generator import CppBundleGenerator 24from cpp_generator import CppGenerator 25from cpp_type_generator import CppTypeGenerator 26from dart_generator import DartGenerator 27import json_schema 28from model import Model 29from ppapi_generator import PpapiGenerator 30from schema_loader import SchemaLoader 31 32# Names of supported code generators, as specified on the command-line. 33# First is default. 34GENERATORS = ['cpp', 'cpp-bundle', 'dart', 'ppapi'] 35 36def GenerateSchema(generator, 37 filenames, 38 root, 39 destdir, 40 root_namespace, 41 dart_overrides_dir, 42 impl_dir): 43 # Merge the source files into a single list of schemas. 44 api_defs = [] 45 for filename in filenames: 46 schema = os.path.normpath(filename) 47 schema_loader = SchemaLoader( 48 os.path.dirname(os.path.relpath(os.path.normpath(filename), root)), 49 os.path.dirname(filename)) 50 api_def = schema_loader.LoadSchema(os.path.split(schema)[1]) 51 52 # If compiling the C++ model code, delete 'nocompile' nodes. 53 if generator == 'cpp': 54 api_def = json_schema.DeleteNodes(api_def, 'nocompile') 55 api_defs.extend(api_def) 56 57 api_model = Model() 58 59 # For single-schema compilation make sure that the first (i.e. only) schema 60 # is the default one. 61 default_namespace = None 62 63 # If we have files from multiple source paths, we'll use the common parent 64 # path as the source directory. 65 src_path = None 66 67 # Load the actual namespaces into the model. 68 for target_namespace, schema_filename in zip(api_defs, filenames): 69 relpath = os.path.relpath(os.path.normpath(schema_filename), root) 70 namespace = api_model.AddNamespace(target_namespace, 71 relpath, 72 include_compiler_options=True) 73 74 if default_namespace is None: 75 default_namespace = namespace 76 77 if src_path is None: 78 src_path = namespace.source_file_dir 79 else: 80 src_path = os.path.commonprefix((src_path, namespace.source_file_dir)) 81 82 path, filename = os.path.split(schema_filename) 83 short_filename, extension = os.path.splitext(filename) 84 85 # Construct the type generator with all the namespaces in this model. 86 type_generator = CppTypeGenerator(api_model, 87 schema_loader, 88 default_namespace=default_namespace) 89 if generator == 'cpp-bundle': 90 cpp_bundle_generator = CppBundleGenerator(root, 91 api_model, 92 api_defs, 93 type_generator, 94 root_namespace, 95 src_path, 96 impl_dir) 97 generators = [ 98 ('generated_api.cc', cpp_bundle_generator.api_cc_generator), 99 ('generated_api.h', cpp_bundle_generator.api_h_generator), 100 ('generated_schemas.cc', cpp_bundle_generator.schemas_cc_generator), 101 ('generated_schemas.h', cpp_bundle_generator.schemas_h_generator) 102 ] 103 elif generator == 'cpp': 104 cpp_generator = CppGenerator(type_generator, root_namespace) 105 generators = [ 106 ('%s.h' % short_filename, cpp_generator.h_generator), 107 ('%s.cc' % short_filename, cpp_generator.cc_generator) 108 ] 109 elif generator == 'dart': 110 generators = [ 111 ('%s.dart' % namespace.unix_name, DartGenerator( 112 dart_overrides_dir)) 113 ] 114 elif generator == 'ppapi': 115 generator = PpapiGenerator() 116 generators = [ 117 (os.path.join('api', 'ppb_%s.idl' % namespace.unix_name), 118 generator.idl_generator), 119 ] 120 else: 121 raise Exception('Unrecognised generator %s' % generator) 122 123 output_code = [] 124 for filename, generator in generators: 125 code = generator.Generate(namespace).Render() 126 if destdir: 127 output_dir = os.path.join(destdir, src_path) 128 if not os.path.exists(output_dir): 129 os.makedirs(output_dir) 130 with open(os.path.join(output_dir, filename), 'w') as f: 131 f.write(code) 132 output_code += [filename, '', code, ''] 133 134 return '\n'.join(output_code) 135 136 137if __name__ == '__main__': 138 parser = optparse.OptionParser( 139 description='Generates a C++ model of an API from JSON schema', 140 usage='usage: %prog [option]... schema') 141 parser.add_option('-r', '--root', default='.', 142 help='logical include root directory. Path to schema files from specified' 143 ' dir will be the include path.') 144 parser.add_option('-d', '--destdir', 145 help='root directory to output generated files.') 146 parser.add_option('-n', '--namespace', default='generated_api_schemas', 147 help='C++ namespace for generated files. e.g extensions::api.') 148 parser.add_option('-g', '--generator', default=GENERATORS[0], 149 choices=GENERATORS, 150 help='The generator to use to build the output code. Supported values are' 151 ' %s' % GENERATORS) 152 parser.add_option('-D', '--dart-overrides-dir', dest='dart_overrides_dir', 153 help='Adds custom dart from files in the given directory (Dart only).') 154 parser.add_option('-i', '--impl-dir', dest='impl_dir', 155 help='The root path of all API implementations') 156 157 (opts, filenames) = parser.parse_args() 158 159 if not filenames: 160 sys.exit(0) # This is OK as a no-op 161 162 # Unless in bundle mode, only one file should be specified. 163 if opts.generator != 'cpp-bundle' and len(filenames) > 1: 164 # TODO(sashab): Could also just use filenames[0] here and not complain. 165 raise Exception( 166 "Unless in bundle mode, only one file can be specified at a time.") 167 168 result = GenerateSchema(opts.generator, filenames, opts.root, opts.destdir, 169 opts.namespace, opts.dart_overrides_dir, 170 opts.impl_dir) 171 if not opts.destdir: 172 print result 173