Index: tools/json_schema_compiler/compiler.py
|
diff --git a/tools/json_schema_compiler/compiler.py b/tools/json_schema_compiler/compiler.py
|
index a270756bf31bd127fa8d4a7203d44fad1b1dc94a..4e41e3853ac42da86e2a5d7deec35f9b02870bd5 100755
|
--- a/tools/json_schema_compiler/compiler.py
|
+++ b/tools/json_schema_compiler/compiler.py
|
@@ -16,18 +16,22 @@ Usage example:
|
--namespace extensions windows.json tabs.json
|
"""
|
|
-import cc_generator
|
-import cpp_type_generator
|
-import h_generator
|
+from cpp_generator import CppGenerator
|
+from cpp_type_generator import CppTypeGenerator
|
+from dart_generator import DartGenerator
|
+from cpp_bundle_generator import CppBundleGenerator
|
+from model import Model
|
import idl_schema
|
import json_schema
|
-import model
|
-import schema_bundle_generator
|
|
import optparse
|
import os.path
|
import sys
|
|
+# Names of supported code generators, as specified on the command-line.
|
+# First is default.
|
+GENERATORS = ['cpp', 'cpp-bundle', 'dart']
|
+
|
def load_schema(schema):
|
schema_filename, schema_extension = os.path.splitext(schema)
|
|
@@ -36,106 +40,98 @@ def load_schema(schema):
|
elif schema_extension == '.idl':
|
api_defs = idl_schema.Load(schema)
|
else:
|
- sys.exit("Did not recognize file extension %s for schema %s" %
|
+ sys.exit('Did not recognize file extension %s for schema %s' %
|
(schema_extension, schema))
|
if len(api_defs) != 1:
|
- sys.exit("File %s has multiple schemas. Files are only allowed to contain a"
|
- " single schema." % schema)
|
+ sys.exit('File %s has multiple schemas. Files are only allowed to contain a'
|
+ ' single schema.' % schema)
|
|
return api_defs
|
|
-def handle_single_schema(filename, dest_dir, root, root_namespace):
|
- schema = os.path.normpath(filename)
|
- schema_filename, schema_extension = os.path.splitext(schema)
|
- path, short_filename = os.path.split(schema_filename)
|
- api_defs = json_schema.DeleteNocompileNodes(load_schema(schema))
|
-
|
- api_model = model.Model()
|
-
|
- for target_namespace in api_defs:
|
- referenced_schemas = target_namespace.get('dependencies', [])
|
- # Load type dependencies into the model.
|
- # TODO(miket): do we need this in IDL?
|
- for referenced_schema in referenced_schemas:
|
- split_schema = referenced_schema.split(':', 1)
|
- if len(split_schema) > 1:
|
- if split_schema[0] != 'api':
|
- continue
|
- else:
|
- referenced_schema = split_schema[1]
|
-
|
- referenced_schema_path = os.path.join(
|
- os.path.dirname(schema), referenced_schema + '.json')
|
- referenced_api_defs = json_schema.Load(referenced_schema_path)
|
-
|
- for namespace in referenced_api_defs:
|
- api_model.AddNamespace(
|
- namespace,
|
- os.path.relpath(referenced_schema_path, opts.root))
|
-
|
- # Gets the relative path from opts.root to the schema to correctly determine
|
- # the include path.
|
- relpath = os.path.relpath(schema, opts.root)
|
- namespace = api_model.AddNamespace(target_namespace,
|
- relpath,
|
- include_compiler_options=True)
|
- if not namespace:
|
- continue
|
+if __name__ == '__main__':
|
+ parser = optparse.OptionParser(
|
+ description='Generates a C++ model of an API from JSON schema',
|
+ usage='usage: %prog [option]... schema')
|
+ parser.add_option('-r', '--root', default='.',
|
+ help='logical include root directory. Path to schema files from specified'
|
+ 'dir will be the include path.')
|
+ parser.add_option('-d', '--destdir',
|
+ help='root directory to output generated files.')
|
+ parser.add_option('-n', '--namespace', default='generated_api_schemas',
|
+ help='C++ namespace for generated files. e.g extensions::api.')
|
+ parser.add_option('-g', '--generator', default=GENERATORS[0],
|
+ choices=GENERATORS,
|
+ help='The generator to use to build the output code. Supported values are'
|
+ ' %s' % GENERATORS)
|
+ parser.add_option('-D', '--dart-overrides-dir', dest='dart_overrides_dir',
|
+ help='Adds custom dart from files in the given directory (Dart only).')
|
|
- if short_filename != namespace.unix_name:
|
- sys.exit("Filename %s is illegal. Name files using unix_hacker style." %
|
- filename)
|
+ (opts, filenames) = parser.parse_args()
|
|
- # The output filename must match the input filename for gyp to deal with it
|
- # properly.
|
- out_file = namespace.unix_name
|
- type_generator = cpp_type_generator.CppTypeGenerator(
|
- root_namespace, namespace, namespace.unix_name)
|
- for referenced_namespace in api_model.namespaces.values():
|
- if referenced_namespace == namespace:
|
- continue
|
- type_generator.AddNamespace(
|
- referenced_namespace,
|
- referenced_namespace.unix_name)
|
-
|
- h_code = (h_generator.HGenerator(namespace, type_generator)
|
- .Generate().Render())
|
- cc_code = (cc_generator.CCGenerator(namespace, type_generator)
|
- .Generate().Render())
|
-
|
- if dest_dir:
|
- with open(
|
- os.path.join(dest_dir, namespace.source_file_dir, out_file + '.cc'),
|
- 'w') as cc_file:
|
- cc_file.write(cc_code)
|
- with open(
|
- os.path.join(dest_dir, namespace.source_file_dir, out_file + '.h'),
|
- 'w') as h_file:
|
- h_file.write(h_code)
|
- else:
|
- print '%s.h' % out_file
|
- print
|
- print h_code
|
- print
|
- print '%s.cc' % out_file
|
- print
|
- print cc_code
|
+ if not filenames:
|
+ sys.exit(0) # This is OK as a no-op
|
+
|
+ # Unless in bundle mode, only one file should be specified.
|
+ if opts.generator != 'cpp-bundle' and len(filenames) > 1:
|
+ # TODO(sashab): Could also just use filenames[0] here and not complain.
|
+ raise Exception(
|
+ "Unless in bundle mode, only one file can be specified at a time.")
|
|
-def handle_bundle_schema(filenames, dest_dir, root, root_namespace):
|
# Merge the source files into a single list of schemas.
|
api_defs = []
|
for filename in filenames:
|
schema = os.path.normpath(filename)
|
schema_filename, schema_extension = os.path.splitext(schema)
|
- api_defs.extend(load_schema(schema))
|
-
|
- api_model = model.Model()
|
- relpath = os.path.relpath(os.path.normpath(filenames[0]), root)
|
-
|
+ path, short_filename = os.path.split(schema_filename)
|
+ api_def = load_schema(schema)
|
+
|
+ # If compiling the C++ model code, delete 'nocompile' nodes.
|
+ if opts.generator == 'cpp':
|
+ api_def = json_schema.DeleteNodes(api_def, 'nocompile')
|
+ api_defs.extend(api_def)
|
+
|
+ api_model = Model()
|
+
|
+ # Load type dependencies into the model.
|
+ #
|
+ # HACK(kalman): bundle mode doesn't work with dependencies, because not all
|
+ # schemas work in bundle mode.
|
+ #
|
+ # TODO(kalman): load dependencies lazily (get rid of the 'dependencies' list)
|
+ # and this problem will go away.
|
+ if opts.generator != 'cpp-bundle':
|
+ for target_namespace in api_defs:
|
+ for referenced_schema in target_namespace.get('dependencies', []):
|
+ split_schema = referenced_schema.split(':', 1)
|
+ if len(split_schema) > 1:
|
+ if split_schema[0] != 'api':
|
+ continue
|
+ else:
|
+ referenced_schema = split_schema[1]
|
+
|
+ referenced_schema_path = os.path.join(
|
+ os.path.dirname(schema), referenced_schema + '.json')
|
+ referenced_api_defs = json_schema.Load(referenced_schema_path)
|
+
|
+ for namespace in referenced_api_defs:
|
+ api_model.AddNamespace(
|
+ namespace,
|
+ os.path.relpath(referenced_schema_path, opts.root),
|
+ include_compiler_options=True)
|
+
|
+ # For single-schema compilation make sure that the first (i.e. only) schema
|
+ # is the default one.
|
+ default_namespace = None
|
+
|
+ # Load the actual namespaces into the model.
|
for target_namespace, schema_filename in zip(api_defs, filenames):
|
+ relpath = os.path.relpath(os.path.normpath(schema_filename), opts.root)
|
namespace = api_model.AddNamespace(target_namespace,
|
relpath,
|
include_compiler_options=True)
|
+ if default_namespace is None:
|
+ default_namespace = namespace
|
+
|
path, filename = os.path.split(schema_filename)
|
short_filename, extension = os.path.splitext(filename)
|
|
@@ -146,62 +142,47 @@ def handle_bundle_schema(filenames, dest_dir, root, root_namespace):
|
sys.exit("Filename %s is illegal. Name files using unix_hacker style." %
|
schema_filename)
|
|
- type_generator = cpp_type_generator.CppTypeGenerator(root_namespace)
|
- for referenced_namespace in api_model.namespaces.values():
|
- type_generator.AddNamespace(
|
- referenced_namespace,
|
- referenced_namespace.unix_name)
|
-
|
- generator = schema_bundle_generator.SchemaBundleGenerator(
|
- root, api_model, api_defs, type_generator)
|
- api_h_code = generator.GenerateAPIHeader().Render()
|
- schemas_h_code = generator.GenerateSchemasHeader().Render()
|
- schemas_cc_code = generator.GenerateSchemasCC().Render()
|
-
|
- if dest_dir:
|
- basedir = os.path.join(dest_dir, 'chrome/common/extensions/api')
|
- with open(os.path.join(basedir, 'generated_api.h'), 'w') as h_file:
|
- h_file.write(api_h_code)
|
- with open(os.path.join(basedir, 'generated_schemas.h'), 'w') as h_file:
|
- h_file.write(schemas_h_code)
|
- with open(os.path.join(basedir, 'generated_schemas.cc'), 'w') as cc_file:
|
- cc_file.write(schemas_cc_code)
|
- else:
|
- print 'generated_api.h'
|
- print
|
- print api_h_code
|
- print
|
- print 'generated_schemas.h'
|
- print
|
- print schemas_h_code
|
- print
|
- print 'generated_schemas.cc'
|
- print
|
- print schemas_cc_code
|
-
|
-if __name__ == '__main__':
|
- parser = optparse.OptionParser(
|
- description='Generates a C++ model of an API from JSON schema',
|
- usage='usage: %prog [option]... schema')
|
- parser.add_option('-r', '--root', default='.',
|
- help='logical include root directory. Path to schema files from specified'
|
- 'dir will be the include path.')
|
- parser.add_option('-d', '--destdir',
|
- help='root directory to output generated files.')
|
- parser.add_option('-n', '--namespace', default='generated_api_schemas',
|
- help='C++ namespace for generated files. e.g extensions::api.')
|
- parser.add_option('-b', '--bundle', action="store_true", help=
|
-'''if supplied, causes compiler to generate bundle files for the given set of
|
-source files.''')
|
-
|
- (opts, args) = parser.parse_args()
|
-
|
- if not args:
|
- sys.exit(0) # This is OK as a no-op
|
- dest_dir = opts.destdir
|
- root_namespace = opts.namespace
|
+ # The output filename must match the input filename for gyp to deal with it
|
+ # properly.
|
+ out_file = namespace.unix_name
|
|
- if opts.bundle:
|
- handle_bundle_schema(args, dest_dir, opts.root, root_namespace)
|
+ # Construct the type generator with all the namespaces in this model.
|
+ type_generator = CppTypeGenerator(api_model,
|
+ default_namespace=default_namespace)
|
+
|
+ if opts.generator == 'cpp-bundle':
|
+ cpp_bundle_generator = CppBundleGenerator(opts.root,
|
+ api_model,
|
+ api_defs,
|
+ type_generator,
|
+ opts.namespace)
|
+ generators = [
|
+ ('generated_api.h', cpp_bundle_generator.api_h_generator),
|
+ ('generated_schemas.cc', cpp_bundle_generator.schemas_cc_generator),
|
+ ('generated_schemas.h', cpp_bundle_generator.schemas_h_generator)
|
+ ]
|
+ elif opts.generator == 'cpp':
|
+ cpp_generator = CppGenerator(type_generator, opts.namespace)
|
+ generators = [
|
+ ('%s.h' % namespace.unix_name, cpp_generator.h_generator),
|
+ ('%s.cc' % namespace.unix_name, cpp_generator.cc_generator)
|
+ ]
|
+ elif opts.generator == 'dart':
|
+ generators = [
|
+ ('%s.dart' % namespace.unix_name, DartGenerator(
|
+ opts.dart_overrides_dir))
|
+ ]
|
else:
|
- handle_single_schema(args[0], dest_dir, opts.root, root_namespace)
|
+ raise Exception('Unrecognised generator %s' % opts.generator)
|
+
|
+ for filename, generator in generators:
|
+ code = generator.Generate(namespace).Render()
|
+ if opts.destdir:
|
+ with open(os.path.join(opts.destdir, namespace.source_file_dir,
|
+ filename), 'w') as f:
|
+ f.write(code)
|
+ else:
|
+ print filename
|
+ print
|
+ print code
|
+ print
|
|