1 #!/usr/bin/env python 2 # Copyright 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 6 # Format for the JSON schema file: 7 # { 8 # "type_name": "DesiredCStructName", 9 # "headers": [ // Optional list of headers to be included by the .h. 10 # "path/to/header.h" 11 # ], 12 # "schema": [ // Fields of the generated structure. 13 # { 14 # "field": "my_enum_field", 15 # "type": "enum", // Either: int, string, string16, enum, array. 16 # "default": "RED", // Optional. Cannot be used for array. 17 # "ctype": "Color" // Only for enum, specify the C type. 18 # }, 19 # { 20 # "field": "my_int_array_field", // my_int_array_field_size will also 21 # "type": "array", // be generated. 22 # "contents": { 23 # "type": "int" // Either: int, string, string16, enum, array. 24 # } 25 # }, 26 # ... 27 # ] 28 # } 29 # 30 # Format for the JSON description file: 31 # { 32 # "int_variables": { // An optional list of constant int variables. 33 # "kDesiredConstantName": 45 34 # }, 35 # "elements": { // All the elements for which to create static 36 # // initialization code in the .cc file. 37 # "my_const_variable": { 38 # "my_int_field": 10, 39 # "my_string_field": "foo bar", 40 # "my_enum_field": "BLACK", 41 # "my_int_array_field": [ 1, 2, 3, 5, 7 ], 42 # }, 43 # "my_other_const_variable": { 44 # ... 45 # } 46 # } 47 # } 48 49 import json 50 from datetime import datetime 51 import os.path 52 import sys 53 import optparse 54 import re 55 _script_path = os.path.realpath(__file__) 56 57 sys.path.insert(0, os.path.normpath(_script_path + "/../../json_comment_eater")) 58 try: 59 import json_comment_eater 60 finally: 61 sys.path.pop(0) 62 63 import struct_generator 64 import element_generator 65 66 HEAD = """// Copyright %d The Chromium Authors. All rights reserved. 67 // Use of this source code is governed by a BSD-style license that can be 68 // found in the LICENSE file. 69 70 // GENERATED FROM THE SCHEMA DEFINITION AND DESCRIPTION IN 71 // %s 72 // %s 73 // DO NOT EDIT. 74 75 """ 76 77 def _GenerateHeaderGuard(h_filename): 78 """Generates the string used in #ifndef guarding the header file. 79 """ 80 result = re.sub('[%s\\\\.]' % os.sep, '_', h_filename.upper()) 81 return re.sub('^_*', '', result) + '_' # Remove leading underscores. 82 83 def _GenerateH(basepath, fileroot, head, namespace, schema, description): 84 """Generates the .h file containing the definition of the structure specified 85 by the schema. 86 87 Args: 88 basepath: The base directory in which files are generated. 89 fileroot: The filename and path, relative to basepath, of the file to 90 create, without an extension. 91 head: The string to output as the header of the .h file. 92 namespace: A string corresponding to the C++ namespace to use. 93 schema: A dict containing the schema. See comment at the top of this file. 94 description: A dict containing the description. See comment at the top of 95 this file. 96 """ 97 98 h_filename = fileroot + '.h' 99 with open(os.path.join(basepath, h_filename), 'w') as f: 100 f.write(head) 101 102 header_guard = _GenerateHeaderGuard(h_filename) 103 f.write('#ifndef %s\n' % header_guard) 104 f.write('#define %s\n' % header_guard) 105 f.write('\n') 106 107 f.write('#include <cstddef>\n') 108 f.write('\n') 109 110 for header in schema.get('headers', []): 111 f.write('#include "%s"\n' % header) 112 f.write('\n') 113 114 if namespace: 115 f.write('namespace %s {\n' % namespace) 116 f.write('\n') 117 118 f.write(struct_generator.GenerateStruct( 119 schema['type_name'], schema['schema'])) 120 f.write('\n') 121 122 for var_name, value in description.get('int_variables', []).items(): 123 f.write('extern const int %s;\n' % var_name) 124 f.write('\n') 125 126 for element_name, element in description['elements'].items(): 127 f.write('extern const %s %s;\n' % (schema['type_name'], element_name)) 128 129 if namespace: 130 f.write('\n') 131 f.write('} // namespace %s\n' % namespace) 132 133 f.write('\n') 134 f.write( '#endif // %s\n' % header_guard) 135 136 def _GenerateCC(basepath, fileroot, head, namespace, schema, description): 137 """Generates the .cc file containing the static initializers for the 138 of the elements specified in the description. 139 140 Args: 141 basepath: The base directory in which files are generated. 142 fileroot: The filename and path, relative to basepath, of the file to 143 create, without an extension. 144 head: The string to output as the header of the .cc file. 145 namespace: A string corresponding to the C++ namespace to use. 146 schema: A dict containing the schema. See comment at the top of this file. 147 description: A dict containing the description. See comment at the top of 148 this file. 149 """ 150 151 with open(os.path.join(basepath, fileroot + '.cc'), 'w') as f: 152 f.write(head) 153 154 f.write('#include "%s"\n' % (fileroot + '.h')) 155 f.write('\n') 156 157 if namespace: 158 f.write('namespace %s {\n' % namespace) 159 f.write('\n') 160 161 f.write(element_generator.GenerateElements(schema['type_name'], 162 schema['schema'], description)) 163 164 if namespace: 165 f.write('\n') 166 f.write('} // namespace %s\n' % namespace) 167 168 def _Load(filename): 169 """Loads a JSON file int a Python object and return this object. 170 """ 171 # TODO(beaudoin): When moving to Python 2.7 use object_pairs_hook=OrderedDict. 172 with open(filename, 'r') as handle: 173 result = json.loads(json_comment_eater.Nom(handle.read())) 174 return result 175 176 if __name__ == '__main__': 177 parser = optparse.OptionParser( 178 description='Generates an C++ array of struct from a JSON description.', 179 usage='usage: %prog [option] -s schema description') 180 parser.add_option('-b', '--destbase', 181 help='base directory of generated files.') 182 parser.add_option('-d', '--destdir', 183 help='directory to output generated files, relative to destbase.') 184 parser.add_option('-n', '--namespace', 185 help='C++ namespace for generated files. e.g search_providers.') 186 parser.add_option('-s', '--schema', help='path to the schema file, ' 187 'mandatory.') 188 (opts, args) = parser.parse_args() 189 190 if not opts.schema: 191 parser.error('You must specify a --schema.') 192 193 description_filename = os.path.normpath(args[0]) 194 root, ext = os.path.splitext(description_filename) 195 shortroot = os.path.split(root)[1] 196 if opts.destdir: 197 output_root = os.path.join(os.path.normpath(opts.destdir), shortroot) 198 else: 199 output_root = shortroot 200 201 if opts.destbase: 202 basepath = os.path.normpath(opts.destbase) 203 else: 204 basepath = '' 205 206 schema = _Load(opts.schema) 207 description = _Load(description_filename) 208 209 head = HEAD % (datetime.now().year, opts.schema, description_filename) 210 _GenerateH(basepath, output_root, head, opts.namespace, schema, description) 211 _GenerateCC(basepath, output_root, head, opts.namespace, schema, description) 212