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 49import json 50from datetime import datetime 51import os.path 52import sys 53import optparse 54import re 55_script_path = os.path.realpath(__file__) 56 57sys.path.insert(0, os.path.normpath(_script_path + "/../../json_comment_eater")) 58try: 59 import json_comment_eater 60finally: 61 sys.path.pop(0) 62 63import struct_generator 64import element_generator 65 66HEAD = """// 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 77def _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 83def _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 f.write('#include <cstddef>\n') 103 f.write('\n') 104 105 header_guard = _GenerateHeaderGuard(h_filename) 106 f.write('#ifndef %s\n' % header_guard) 107 f.write('#define %s\n' % header_guard) 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 136def _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 168def _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 176if __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