Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / mojo / public / tools / bindings / mojom_bindings_generator.py
1 #!/usr/bin/env python
2 # Copyright 2013 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 """The frontend for the Mojo bindings system."""
7
8
9 import argparse
10 import imp
11 import os
12 import pprint
13 import sys
14
15 # Disable lint check for finding modules:
16 # pylint: disable=F0401
17
18 def _GetDirAbove(dirname):
19   """Returns the directory "above" this file containing |dirname| (which must
20   also be "above" this file)."""
21   path = os.path.abspath(__file__)
22   while True:
23     path, tail = os.path.split(path)
24     assert tail
25     if tail == dirname:
26       return path
27
28 # Manually check for the command-line flag. (This isn't quite right, since it
29 # ignores, e.g., "--", but it's close enough.)
30 if "--use_chromium_bundled_pylibs" in sys.argv[1:]:
31   sys.path.insert(0, os.path.join(_GetDirAbove("mojo"), "third_party"))
32
33 sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
34                                 "pylib"))
35
36 from mojom.error import Error
37 from mojom.generate.data import OrderedModuleFromData
38 from mojom.parse.parser import Parse
39 from mojom.parse.translate import Translate
40
41
42 def LoadGenerators(generators_string):
43   if not generators_string:
44     return []  # No generators.
45
46   script_dir = os.path.dirname(os.path.abspath(__file__))
47   generators = []
48   for generator_name in [s.strip() for s in generators_string.split(",")]:
49     # "Built-in" generators:
50     if generator_name.lower() == "c++":
51       generator_name = os.path.join(script_dir, "generators",
52                                     "mojom_cpp_generator.py")
53     elif generator_name.lower() == "javascript":
54       generator_name = os.path.join(script_dir, "generators",
55                                     "mojom_js_generator.py")
56     # Specified generator python module:
57     elif generator_name.endswith(".py"):
58       pass
59     else:
60       print "Unknown generator name %s" % generator_name
61       sys.exit(1)
62     generator_module = imp.load_source(os.path.basename(generator_name)[:-3],
63                                        generator_name)
64     generators.append(generator_module)
65   return generators
66
67
68 def MakeImportStackMessage(imported_filename_stack):
69   """Make a (human-readable) message listing a chain of imports. (Returned
70   string begins with a newline (if nonempty) and does not end with one.)"""
71   return ''.join(
72       reversed(["\n  %s was imported by %s" % (a, b) for (a, b) in \
73                     zip(imported_filename_stack[1:], imported_filename_stack)]))
74
75
76 # Disable Check for dangerous default arguments (they're "private" keyword
77 # arguments):
78 # pylint: disable=W0102
79 def ProcessFile(args, generator_modules, filename, _processed_files={},
80                 _imported_filename_stack=[]):
81   # Memoized results.
82   if filename in _processed_files:
83     return _processed_files[filename]
84
85   # Ensure we only visit each file once.
86   if filename in _imported_filename_stack:
87     print "%s: Error: Circular dependency" % filename + \
88         MakeImportStackMessage(_imported_filename_stack + [filename])
89     sys.exit(1)
90
91   try:
92     with open(filename) as f:
93       source = f.read()
94   except IOError as e:
95     print "%s: Error: %s" % (e.filename, e.strerror) + \
96         MakeImportStackMessage(_imported_filename_stack + [filename])
97     sys.exit(1)
98
99   try:
100     tree = Parse(source, filename)
101   except Error as e:
102     print str(e) + MakeImportStackMessage(_imported_filename_stack + [filename])
103     sys.exit(1)
104
105   dirname, name = os.path.split(filename)
106   mojom = Translate(tree, name)
107   if args.debug_print_intermediate:
108     pprint.PrettyPrinter().pprint(mojom)
109
110   # Process all our imports first and collect the module object for each.
111   # We use these to generate proper type info.
112   for import_data in mojom['imports']:
113     import_filename = os.path.join(dirname, import_data['filename'])
114     import_data['module'] = ProcessFile(
115         args, generator_modules, import_filename,
116         _processed_files=_processed_files,
117         _imported_filename_stack=_imported_filename_stack + [filename])
118
119   module = OrderedModuleFromData(mojom)
120
121   # Set the path as relative to the source root.
122   module.path = os.path.relpath(os.path.abspath(filename),
123                                 os.path.abspath(args.depth))
124
125   # Normalize to unix-style path here to keep the generators simpler.
126   module.path = module.path.replace('\\', '/')
127
128   for generator_module in generator_modules:
129     generator = generator_module.Generator(module, args.output_dir)
130     generator.GenerateFiles()
131
132   # Save result.
133   _processed_files[filename] = module
134   return module
135 # pylint: enable=W0102
136
137
138 def main():
139   parser = argparse.ArgumentParser(
140       description="Generate bindings from mojom files.")
141   parser.add_argument("filename", nargs="+",
142                       help="mojom input file")
143   parser.add_argument("-d", "--depth", dest="depth", default=".",
144                       help="depth from source root")
145   parser.add_argument("-o", "--output_dir", dest="output_dir", default=".",
146                       help="output directory for generated files")
147   parser.add_argument("-g", "--generators", dest="generators_string",
148                       metavar="GENERATORS", default="c++,javascript",
149                       help="comma-separated list of generators")
150   parser.add_argument("--debug_print_intermediate", action="store_true",
151                       help="print the intermediate representation")
152   parser.add_argument("--use_chromium_bundled_pylibs", action="store_true",
153                       help="use Python modules bundled in the Chromium source")
154   args = parser.parse_args()
155
156   generator_modules = LoadGenerators(args.generators_string)
157
158   if not os.path.exists(args.output_dir):
159     os.makedirs(args.output_dir)
160
161   for filename in args.filename:
162     ProcessFile(args, generator_modules, filename)
163
164   return 0
165
166
167 if __name__ == "__main__":
168   sys.exit(main())