blob: cccc42a514e6ac97e39ee029fea57ebb536eb346 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2013 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.
"""The frontend for the Mojo bindings system."""
import argparse
import imp
import os
import pprint
import sys
# Disable lint check for finding modules:
# pylint: disable=F0401
def _GetDirAbove(dirname):
"""Returns the directory "above" this file containing |dirname| (which must
also be "above" this file)."""
path = os.path.abspath(__file__)
while True:
path, tail = os.path.split(path)
assert tail
if tail == dirname:
return path
# Manually check for the command-line flag. (This isn't quite right, since it
# ignores, e.g., "--", but it's close enough.)
if "--use_chromium_bundled_pylibs" in sys.argv[1:]:
sys.path.insert(0, os.path.join(_GetDirAbove("mojo"), "third_party"))
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"pylib"))
from mojom.error import Error
from mojom.generate.data import OrderedModuleFromData
from mojom.parse.parser import Parse
from mojom.parse.translate import Translate
def LoadGenerators(generators_string):
if not generators_string:
return [] # No generators.
script_dir = os.path.dirname(os.path.abspath(__file__))
generators = []
for generator_name in [s.strip() for s in generators_string.split(",")]:
# "Built-in" generators:
if generator_name.lower() == "c++":
generator_name = os.path.join(script_dir, "generators",
"mojom_cpp_generator.py")
elif generator_name.lower() == "javascript":
generator_name = os.path.join(script_dir, "generators",
"mojom_js_generator.py")
elif generator_name.lower() == "java":
generator_name = os.path.join(script_dir, "generators",
"mojom_java_generator.py")
elif generator_name.lower() == "python":
generator_name = os.path.join(script_dir, "generators",
"mojom_python_generator.py")
# Specified generator python module:
elif generator_name.endswith(".py"):
pass
else:
print "Unknown generator name %s" % generator_name
sys.exit(1)
generator_module = imp.load_source(os.path.basename(generator_name)[:-3],
generator_name)
generators.append(generator_module)
return generators
def MakeImportStackMessage(imported_filename_stack):
"""Make a (human-readable) message listing a chain of imports. (Returned
string begins with a newline (if nonempty) and does not end with one.)"""
return ''.join(
reversed(["\n %s was imported by %s" % (a, b) for (a, b) in \
zip(imported_filename_stack[1:], imported_filename_stack)]))
def FindImportFile(dir_name, file_name, search_dirs):
for search_dir in [dir_name] + search_dirs:
path = os.path.join(search_dir, file_name)
if os.path.isfile(path):
return path
return os.path.join(dir_name, file_name)
class MojomProcessor(object):
def __init__(self, should_generate):
self._should_generate = should_generate
self._processed_files = {}
def ProcessFile(self, args, remaining_args, generator_modules, filename,
_imported_filename_stack=None):
# Memoized results.
if filename in self._processed_files:
return self._processed_files[filename]
if _imported_filename_stack is None:
_imported_filename_stack = []
# Ensure we only visit each file once.
if filename in _imported_filename_stack:
print "%s: Error: Circular dependency" % filename + \
MakeImportStackMessage(_imported_filename_stack + [filename])
sys.exit(1)
try:
with open(filename) as f:
source = f.read()
except IOError as e:
print "%s: Error: %s" % (e.filename, e.strerror) + \
MakeImportStackMessage(_imported_filename_stack + [filename])
sys.exit(1)
try:
tree = Parse(source, filename)
except Error as e:
full_stack = _imported_filename_stack + [filename]
print str(e) + MakeImportStackMessage(full_stack)
sys.exit(1)
dirname, name = os.path.split(filename)
mojom = Translate(tree, name)
if args.debug_print_intermediate:
pprint.PrettyPrinter().pprint(mojom)
# Process all our imports first and collect the module object for each.
# We use these to generate proper type info.
for import_data in mojom['imports']:
import_filename = FindImportFile(dirname,
import_data['filename'],
args.import_directories)
import_data['module'] = self.ProcessFile(
args, remaining_args, generator_modules, import_filename,
_imported_filename_stack=_imported_filename_stack + [filename])
module = OrderedModuleFromData(mojom)
# Set the path as relative to the source root.
module.path = os.path.relpath(os.path.abspath(filename),
os.path.abspath(args.depth))
# Normalize to unix-style path here to keep the generators simpler.
module.path = module.path.replace('\\', '/')
if self._should_generate(filename):
for generator_module in generator_modules:
generator = generator_module.Generator(module, args.output_dir)
filtered_args = []
if hasattr(generator_module, 'GENERATOR_PREFIX'):
prefix = '--' + generator_module.GENERATOR_PREFIX + '_'
filtered_args = [arg for arg in remaining_args
if arg.startswith(prefix)]
generator.GenerateFiles(filtered_args)
# Save result.
self._processed_files[filename] = module
return module
def main():
parser = argparse.ArgumentParser(
description="Generate bindings from mojom files.")
parser.add_argument("filename", nargs="+",
help="mojom input file")
parser.add_argument("-d", "--depth", dest="depth", default=".",
help="depth from source root")
parser.add_argument("-o", "--output_dir", dest="output_dir", default=".",
help="output directory for generated files")
parser.add_argument("-g", "--generators", dest="generators_string",
metavar="GENERATORS",
default="c++,javascript,java,python",
help="comma-separated list of generators")
parser.add_argument("--debug_print_intermediate", action="store_true",
help="print the intermediate representation")
parser.add_argument("-I", dest="import_directories", action="append",
metavar="directory", default=[],
help="add a directory to be searched for import files")
parser.add_argument("--use_chromium_bundled_pylibs", action="store_true",
help="use Python modules bundled in the Chromium source")
(args, remaining_args) = parser.parse_known_args()
generator_modules = LoadGenerators(args.generators_string)
if not os.path.exists(args.output_dir):
os.makedirs(args.output_dir)
processor = MojomProcessor(lambda filename: filename in args.filename)
for filename in args.filename:
processor.ProcessFile(args, remaining_args, generator_modules, filename)
return 0
if __name__ == "__main__":
sys.exit(main())