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.
6 """The frontend for the Mojo bindings system."""
15 # Disable lint check for finding modules:
16 # pylint: disable=F0401
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__)
23 path, tail = os.path.split(path)
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"))
33 sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
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
42 def LoadGenerators(generators_string):
43 if not generators_string:
44 return [] # No generators.
46 script_dir = os.path.dirname(os.path.abspath(__file__))
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"):
60 print "Unknown generator name %s" % generator_name
62 generator_module = imp.load_source(os.path.basename(generator_name)[:-3],
64 generators.append(generator_module)
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.)"""
72 reversed(["\n %s was imported by %s" % (a, b) for (a, b) in \
73 zip(imported_filename_stack[1:], imported_filename_stack)]))
76 # Disable Check for dangerous default arguments (they're "private" keyword
78 # pylint: disable=W0102
79 def ProcessFile(args, generator_modules, filename, _processed_files={},
80 _imported_filename_stack=[]):
82 if filename in _processed_files:
83 return _processed_files[filename]
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])
92 with open(filename) as f:
95 print "%s: Error: %s" % (e.filename, e.strerror) + \
96 MakeImportStackMessage(_imported_filename_stack + [filename])
100 tree = Parse(source, filename)
102 print str(e) + MakeImportStackMessage(_imported_filename_stack + [filename])
105 dirname, name = os.path.split(filename)
106 mojom = Translate(tree, name)
107 if args.debug_print_intermediate:
108 pprint.PrettyPrinter().pprint(mojom)
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])
119 module = OrderedModuleFromData(mojom)
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))
125 # Normalize to unix-style path here to keep the generators simpler.
126 module.path = module.path.replace('\\', '/')
128 for generator_module in generator_modules:
129 generator = generator_module.Generator(module, args.output_dir)
130 generator.GenerateFiles()
133 _processed_files[filename] = module
135 # pylint: enable=W0102
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()
156 generator_modules = LoadGenerators(args.generators_string)
158 if not os.path.exists(args.output_dir):
159 os.makedirs(args.output_dir)
161 for filename in args.filename:
162 ProcessFile(args, generator_modules, filename)
167 if __name__ == "__main__":