Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / tools / json_schema_compiler / compiler.py
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
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.
11
12 Usage 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
19 import optparse
20 import os
21 import shlex
22 import sys
23
24 from cpp_bundle_generator import CppBundleGenerator
25 from cpp_generator import CppGenerator
26 from cpp_type_generator import CppTypeGenerator
27 from dart_generator import DartGenerator
28 import json_schema
29 from cpp_namespace_environment import CppNamespaceEnvironment
30 from model import Model
31 from schema_loader import SchemaLoader
32
33 # Names of supported code generators, as specified on the command-line.
34 # First is default.
35 GENERATORS = ['cpp', 'cpp-bundle-registration', 'cpp-bundle-schema', 'dart']
36
37 def GenerateSchema(generator_name,
38                    file_paths,
39                    root,
40                    destdir,
41                    cpp_namespace_pattern,
42                    dart_overrides_dir,
43                    impl_dir,
44                    include_rules):
45   # Merge the source files into a single list of schemas.
46   api_defs = []
47   for file_path in file_paths:
48     schema = os.path.relpath(file_path, root)
49     schema_loader = SchemaLoader(
50         root,
51         os.path.dirname(schema),
52         include_rules,
53         cpp_namespace_pattern)
54     api_def = schema_loader.LoadSchema(schema)
55
56     # If compiling the C++ model code, delete 'nocompile' nodes.
57     if generator_name == 'cpp':
58       api_def = json_schema.DeleteNodes(api_def, 'nocompile')
59     api_defs.extend(api_def)
60
61   api_model = Model()
62
63   # For single-schema compilation make sure that the first (i.e. only) schema
64   # is the default one.
65   default_namespace = None
66
67   # If we have files from multiple source paths, we'll use the common parent
68   # path as the source directory.
69   src_path = None
70
71   # Load the actual namespaces into the model.
72   for target_namespace, file_path in zip(api_defs, file_paths):
73     relpath = os.path.relpath(os.path.normpath(file_path), root)
74     namespace = api_model.AddNamespace(target_namespace,
75                                        relpath,
76                                        include_compiler_options=True,
77                                        environment=CppNamespaceEnvironment(
78                                            cpp_namespace_pattern))
79
80     if default_namespace is None:
81       default_namespace = namespace
82
83     if src_path is None:
84       src_path = namespace.source_file_dir
85     else:
86       src_path = os.path.commonprefix((src_path, namespace.source_file_dir))
87
88     path, filename = os.path.split(file_path)
89     filename_base, _ = os.path.splitext(filename)
90
91   # Construct the type generator with all the namespaces in this model.
92   type_generator = CppTypeGenerator(api_model,
93                                     schema_loader,
94                                     default_namespace)
95   if generator_name in ('cpp-bundle-registration', 'cpp-bundle-schema'):
96     cpp_bundle_generator = CppBundleGenerator(root,
97                                               api_model,
98                                               api_defs,
99                                               type_generator,
100                                               cpp_namespace_pattern,
101                                               src_path,
102                                               impl_dir)
103     if generator_name == 'cpp-bundle-registration':
104       generators = [
105         ('generated_api_registration.cc',
106          cpp_bundle_generator.api_cc_generator),
107         ('generated_api_registration.h', cpp_bundle_generator.api_h_generator),
108       ]
109     elif generator_name == 'cpp-bundle-schema':
110       generators = [
111         ('generated_schemas.cc', cpp_bundle_generator.schemas_cc_generator),
112         ('generated_schemas.h', cpp_bundle_generator.schemas_h_generator)
113       ]
114   elif generator_name == 'cpp':
115     cpp_generator = CppGenerator(type_generator)
116     generators = [
117       ('%s.h' % filename_base, cpp_generator.h_generator),
118       ('%s.cc' % filename_base, cpp_generator.cc_generator)
119     ]
120   elif generator_name == 'dart':
121     generators = [
122       ('%s.dart' % namespace.unix_name, DartGenerator(
123           dart_overrides_dir))
124     ]
125   else:
126     raise Exception('Unrecognised generator %s' % generator)
127
128   output_code = []
129   for filename, generator in generators:
130     code = generator.Generate(namespace).Render()
131     if destdir:
132       if generator_name == 'cpp-bundle-registration':
133         # Function registrations must be output to impl_dir, since they link in
134         # API implementations.
135         output_dir = os.path.join(destdir, impl_dir)
136       else:
137         output_dir = os.path.join(destdir, src_path)
138       if not os.path.exists(output_dir):
139         os.makedirs(output_dir)
140       with open(os.path.join(output_dir, filename), 'w') as f:
141         f.write(code)
142     output_code += [filename, '', code, '']
143
144   return '\n'.join(output_code)
145
146
147 if __name__ == '__main__':
148   parser = optparse.OptionParser(
149       description='Generates a C++ model of an API from JSON schema',
150       usage='usage: %prog [option]... schema')
151   parser.add_option('-r', '--root', default='.',
152       help='logical include root directory. Path to schema files from specified'
153       ' dir will be the include path.')
154   parser.add_option('-d', '--destdir',
155       help='root directory to output generated files.')
156   parser.add_option('-n', '--namespace', default='generated_api_schemas',
157       help='C++ namespace for generated files. e.g extensions::api.')
158   parser.add_option('-g', '--generator', default=GENERATORS[0],
159       choices=GENERATORS,
160       help='The generator to use to build the output code. Supported values are'
161       ' %s' % GENERATORS)
162   parser.add_option('-D', '--dart-overrides-dir', dest='dart_overrides_dir',
163       help='Adds custom dart from files in the given directory (Dart only).')
164   parser.add_option('-i', '--impl-dir', dest='impl_dir',
165       help='The root path of all API implementations')
166   parser.add_option('-I', '--include-rules',
167       help='A list of paths to include when searching for referenced objects,'
168       ' with the namespace separated by a \':\'. Example: '
169       '/foo/bar:Foo::Bar::%(namespace)s')
170
171   (opts, file_paths) = parser.parse_args()
172
173   if not file_paths:
174     sys.exit(0) # This is OK as a no-op
175
176   # Unless in bundle mode, only one file should be specified.
177   if (opts.generator not in ('cpp-bundle-registration', 'cpp-bundle-schema') and
178       len(file_paths) > 1):
179     # TODO(sashab): Could also just use file_paths[0] here and not complain.
180     raise Exception(
181         "Unless in bundle mode, only one file can be specified at a time.")
182
183   def split_path_and_namespace(path_and_namespace):
184     if ':' not in path_and_namespace:
185       raise ValueError('Invalid include rule "%s". Rules must be of '
186                        'the form path:namespace' % path_and_namespace)
187     return path_and_namespace.split(':', 1)
188
189   include_rules = []
190   if opts.include_rules:
191     include_rules = map(split_path_and_namespace,
192                         shlex.split(opts.include_rules))
193
194   result = GenerateSchema(opts.generator, file_paths, opts.root, opts.destdir,
195                           opts.namespace, opts.dart_overrides_dir,
196                           opts.impl_dir, include_rules)
197   if not opts.destdir:
198     print result