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.
7 The purpose of this tool is to remove the need for hand-written code that
8 converts to and from base::Value types when receiving javascript api calls.
9 Originally written for generating code for extension apis. Reference schemas
10 are in chrome/common/extensions/api.
13 compiler.py --root /home/Work/src --namespace extensions windows.json
15 compiler.py --destdir gen --root /home/Work/src
16 --namespace extensions windows.json tabs.json
23 from cpp_bundle_generator
import CppBundleGenerator
24 from cpp_generator
import CppGenerator
25 from cpp_type_generator
import CppTypeGenerator
26 from dart_generator
import DartGenerator
28 from model
import Model
29 from ppapi_generator
import PpapiGenerator
30 from schema_loader
import SchemaLoader
32 # Names of supported code generators, as specified on the command-line.
34 GENERATORS
= ['cpp', 'cpp-bundle', 'dart', 'ppapi']
36 def GenerateSchema(generator
,
43 # Merge the source files into a single list of schemas.
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])
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
)
59 # For single-schema compilation make sure that the first (i.e. only) schema
61 default_namespace
= None
63 # If we have files from multiple source paths, we'll use the common parent
64 # path as the source directory.
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
,
72 include_compiler_options
=True)
74 if default_namespace
is None:
75 default_namespace
= namespace
78 src_path
= namespace
.source_file_dir
80 src_path
= os
.path
.commonprefix((src_path
, namespace
.source_file_dir
))
82 path
, filename
= os
.path
.split(schema_filename
)
83 short_filename
, extension
= os
.path
.splitext(filename
)
85 # Construct the type generator with all the namespaces in this model.
86 type_generator
= CppTypeGenerator(api_model
,
88 default_namespace
=default_namespace
)
89 if generator
== 'cpp-bundle':
90 cpp_bundle_generator
= CppBundleGenerator(root
,
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
)
103 elif generator
== 'cpp':
104 cpp_generator
= CppGenerator(type_generator
, root_namespace
)
106 ('%s.h' % short_filename
, cpp_generator
.h_generator
),
107 ('%s.cc' % short_filename
, cpp_generator
.cc_generator
)
109 elif generator
== 'dart':
111 ('%s.dart' % namespace
.unix_name
, DartGenerator(
114 elif generator
== 'ppapi':
115 generator
= PpapiGenerator()
117 (os
.path
.join('api', 'ppb_%s.idl' % namespace
.unix_name
),
118 generator
.idl_generator
),
121 raise Exception('Unrecognised generator %s' % generator
)
124 for filename
, generator
in generators
:
125 code
= generator
.Generate(namespace
).Render()
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
:
132 output_code
+= [filename
, '', code
, '']
134 return '\n'.join(output_code
)
137 if __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],
150 help='The generator to use to build the output code. Supported values are'
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')
157 (opts
, filenames
) = parser
.parse_args()
160 sys
.exit(0) # This is OK as a no-op
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.
166 "Unless in bundle mode, only one file can be specified at a time.")
168 result
= GenerateSchema(opts
.generator
, filenames
, opts
.root
, opts
.destdir
,
169 opts
.namespace
, opts
.dart_overrides_dir
,