summaryrefslogtreecommitdiffstats
path: root/tools/json_schema_compiler/compiler.py
blob: f782f724f1d4e091c0341166b79ab4f443c1e22c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generator for C++ structs from api json files.

The purpose of this tool is to remove the need for hand-written code that
converts to and from base::Value types when receiving javascript api calls.
Originally written for generating code for extension apis. Reference schemas
are in chrome/common/extensions/api.

Usage example:
  compiler.py --root /home/Work/src --namespace extensions windows.json
    tabs.json
  compiler.py --destdir gen --root /home/Work/src
    --namespace extensions windows.json tabs.json
"""

import cc_generator
import cpp_type_generator
import h_generator
import idl_schema
import json_schema
import model
import optparse
import os.path
import sys

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.')

  (opts, args) = parser.parse_args()
  if not args:
    sys.exit(parser.get_usage())
  dest_dir = opts.destdir
  root_namespace = opts.namespace

  schema = os.path.normpath(args[0])

  api_model = model.Model()

  # Actually generate for source file.
  schema_filename, schema_extension = os.path.splitext(schema)
  if schema_extension == '.json':
    api_defs = json_schema.Load(schema)
  elif schema_extension == '.idl':
    api_defs = idl_schema.Load(schema)
  else:
    sys.exit("Did not recognize file extension %s for schema %s" %
             (schema_extension, schema))

  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:
      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)
    if not namespace:
      continue

    # The output filename must match the input filename for gyp to deal with it
    # properly.
    out_file = namespace.name
    type_generator = cpp_type_generator.CppTypeGenerator(
        root_namespace, namespace, namespace.unix_name)
    for referenced_namespace in api_model.namespaces.values():
      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