Update To 11.40.268.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     elif generator_name.lower() == "java":
57       generator_name = os.path.join(script_dir, "generators",
58                                     "mojom_java_generator.py")
59     elif generator_name.lower() == "python":
60       generator_name = os.path.join(script_dir, "generators",
61                                     "mojom_python_generator.py")
62     # Specified generator python module:
63     elif generator_name.endswith(".py"):
64       pass
65     else:
66       print "Unknown generator name %s" % generator_name
67       sys.exit(1)
68     generator_module = imp.load_source(os.path.basename(generator_name)[:-3],
69                                        generator_name)
70     generators.append(generator_module)
71   return generators
72
73
74 def MakeImportStackMessage(imported_filename_stack):
75   """Make a (human-readable) message listing a chain of imports. (Returned
76   string begins with a newline (if nonempty) and does not end with one.)"""
77   return ''.join(
78       reversed(["\n  %s was imported by %s" % (a, b) for (a, b) in \
79                     zip(imported_filename_stack[1:], imported_filename_stack)]))
80
81
82 def FindImportFile(dir_name, file_name, search_dirs):
83   for search_dir in [dir_name] + search_dirs:
84     path = os.path.join(search_dir, file_name)
85     if os.path.isfile(path):
86       return path
87   return os.path.join(dir_name, file_name)
88
89 class MojomProcessor(object):
90   def __init__(self, should_generate):
91     self._should_generate = should_generate
92     self._processed_files = {}
93
94   def ProcessFile(self, args, remaining_args, generator_modules, filename,
95                   _imported_filename_stack=None):
96     # Memoized results.
97     if filename in self._processed_files:
98       return self._processed_files[filename]
99
100     if _imported_filename_stack is None:
101       _imported_filename_stack = []
102
103     # Ensure we only visit each file once.
104     if filename in _imported_filename_stack:
105       print "%s: Error: Circular dependency" % filename + \
106           MakeImportStackMessage(_imported_filename_stack + [filename])
107       sys.exit(1)
108
109     try:
110       with open(filename) as f:
111         source = f.read()
112     except IOError as e:
113       print "%s: Error: %s" % (e.filename, e.strerror) + \
114           MakeImportStackMessage(_imported_filename_stack + [filename])
115       sys.exit(1)
116
117     try:
118       tree = Parse(source, filename)
119     except Error as e:
120       full_stack = _imported_filename_stack + [filename]
121       print str(e) + MakeImportStackMessage(full_stack)
122       sys.exit(1)
123
124     dirname, name = os.path.split(filename)
125     mojom = Translate(tree, name)
126     if args.debug_print_intermediate:
127       pprint.PrettyPrinter().pprint(mojom)
128
129     # Process all our imports first and collect the module object for each.
130     # We use these to generate proper type info.
131     for import_data in mojom['imports']:
132       import_filename = FindImportFile(dirname,
133                                        import_data['filename'],
134                                        args.import_directories)
135       import_data['module'] = self.ProcessFile(
136           args, remaining_args, generator_modules, import_filename,
137           _imported_filename_stack=_imported_filename_stack + [filename])
138
139     module = OrderedModuleFromData(mojom)
140
141     # Set the path as relative to the source root.
142     module.path = os.path.relpath(os.path.abspath(filename),
143                                   os.path.abspath(args.depth))
144
145     # Normalize to unix-style path here to keep the generators simpler.
146     module.path = module.path.replace('\\', '/')
147
148     if self._should_generate(filename):
149       for generator_module in generator_modules:
150         generator = generator_module.Generator(module, args.output_dir)
151         filtered_args = []
152         if hasattr(generator_module, 'GENERATOR_PREFIX'):
153           prefix = '--' + generator_module.GENERATOR_PREFIX + '_'
154           filtered_args = [arg for arg in remaining_args
155                            if arg.startswith(prefix)]
156         generator.GenerateFiles(filtered_args)
157
158     # Save result.
159     self._processed_files[filename] = module
160     return module
161
162 def main():
163   parser = argparse.ArgumentParser(
164       description="Generate bindings from mojom files.")
165   parser.add_argument("filename", nargs="+",
166                       help="mojom input file")
167   parser.add_argument("-d", "--depth", dest="depth", default=".",
168                       help="depth from source root")
169   parser.add_argument("-o", "--output_dir", dest="output_dir", default=".",
170                       help="output directory for generated files")
171   parser.add_argument("-g", "--generators", dest="generators_string",
172                       metavar="GENERATORS",
173                       default="c++,javascript,java,python",
174                       help="comma-separated list of generators")
175   parser.add_argument("--debug_print_intermediate", action="store_true",
176                       help="print the intermediate representation")
177   parser.add_argument("-I", dest="import_directories", action="append",
178                       metavar="directory", default=[],
179                       help="add a directory to be searched for import files")
180   parser.add_argument("--use_chromium_bundled_pylibs", action="store_true",
181                       help="use Python modules bundled in the Chromium source")
182   (args, remaining_args) = parser.parse_known_args()
183
184   generator_modules = LoadGenerators(args.generators_string)
185
186   if not os.path.exists(args.output_dir):
187     os.makedirs(args.output_dir)
188
189   processor = MojomProcessor(lambda filename: filename in args.filename)
190   for filename in args.filename:
191     processor.ProcessFile(args, remaining_args, generator_modules, filename)
192
193   return 0
194
195
196 if __name__ == "__main__":
197   sys.exit(main())