blob: 684ed08ed37d385263cb07ce7df21a63745a4292 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2015 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.
#
# This script accepts the output of the mojom parser and uses that
# data to invoke the code generators.
import argparse
import imp
import os
import sys
def _ParseCLIArgs():
"""Parses the command line arguments.
Returns:
tuple<Namespace, list<str>> The first value of the tuple is a Namespace
holding the value of the optional args. The second value of the tuple is
a list of the remaining arguments.
"""
parser = argparse.ArgumentParser(
description='Generate bindings from mojom parser output.')
parser.add_argument('filenames', nargs='*',
help='Filter on the set of .mojom files for which code '
'will be generated.')
parser.add_argument('-f', '--file-graph', dest='file_graph',
help='Location of the parser output. "-" for stdin. '
'(default "-")', default='-')
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++,dart,go,javascript,java,python",
help="comma-separated list of generators")
parser.add_argument("-s", "--src-root-path", dest="src_root_path",
default=".",
help="relative path to the root of the source tree.")
parser.add_argument("--no-gen-imports", action="store_true",
help="Generate code only for the files that are "
"specified on the command line. By default, code "
"is generated for all specified files and their "
"transitive imports.")
parser.add_argument("--generate-type-info", dest="generate_type_info",
action="store_true",
help="generate mojom type descriptors")
parser.add_argument("--no-generate-type-info", dest="generate_type_info",
action="store_false",
help="do not generate mojom type descriptors")
parser.set_defaults(generate_type_info=False)
return parser.parse_known_args()
# We assume this script is located in the Mojo SDK in tools/bindings.
# If __file__ is a link, we look for the real location of the script.
BINDINGS_DIR = os.path.dirname(os.path.realpath(os.path.abspath(__file__)))
SDK_ROOT = os.path.abspath(os.path.join(BINDINGS_DIR, os.pardir, os.pardir))
PYTHON_SDK_DIR = os.path.abspath(os.path.join(SDK_ROOT, "python"))
sys.path.insert(0, PYTHON_SDK_DIR)
# In order to use mojom_files_mojom we need to make sure the dummy mojo_system
# can be found on the python path.
sys.path.insert(0, os.path.join(PYTHON_SDK_DIR, "dummy_mojo_system"))
sys.path.insert(0, os.path.join(BINDINGS_DIR, "pylib"))
from mojom.generate.generated import mojom_files_mojom
from mojom.generate import mojom_translator
from mojom.parse import parser_runner
from mojo_bindings import serialization
def LoadGenerators(generators_string):
if not generators_string:
return [] # No generators.
generators_dir = os.path.join(BINDINGS_DIR, "generators")
generators = []
for generator_name in [s.strip() for s in generators_string.split(",")]:
generator_name_lower = generator_name.lower()
# "Built-in" generators:
if generator_name_lower == "c++":
generator_py_name = os.path.join(generators_dir,
"mojom_cpp_generator.py")
elif generator_name_lower == "dart":
generator_py_name = os.path.join(generators_dir,
"mojom_dart_generator.py")
elif generator_name_lower == "go":
generator_py_name = os.path.join(generators_dir,
"mojom_go_generator.py")
elif generator_name_lower == "javascript":
generator_py_name = os.path.join(generators_dir,
"mojom_js_generator.py")
elif generator_name_lower == "java":
generator_py_name = os.path.join(generators_dir,
"mojom_java_generator.py")
elif generator_name_lower == "python":
generator_py_name = os.path.join(generators_dir,
"mojom_python_generator.py")
# Specified generator python module:
elif generator_name.endswith(".py"):
generator_py_name = generator_name
else:
print "Unknown generator name %s" % generator_name
sys.exit(1)
generator_module = imp.load_source(os.path.basename(generator_py_name)[:-3],
generator_py_name)
generators.append(generator_module)
return generators
def ReadMojomFileGraphFromFile(fp):
"""Reads a mojom_files_mojom.MojomFileGraph from a file.
Args:
fp: A file pointer from which a serialized mojom_fileS_mojom.MojomFileGraph
can be read.
Returns:
The mojom_files_mojom.MojomFileGraph that was deserialized from the file.
"""
return parser_runner.DeserializeMojomFileGraph(fp.read())
def FixModulePath(module, abs_src_root_path):
"""Fix the path attribute of the provided module and its imports.
The path provided for the various modules is the absolute path to the mojom
file which the module represents. But the generators expect the path to be
relative to the root of the source tree.
Args:
module: {module.Module} whose path is to be updated.
abs_src_root_path: {str} absolute path to the root of the source tree.
"""
module.path = os.path.relpath(module.path, abs_src_root_path)
if not hasattr(module, 'imports'):
return
for import_dict in module.imports:
FixModulePath(import_dict['module'], abs_src_root_path)
def main():
args, remaining_args = _ParseCLIArgs()
if args.file_graph == '-':
fp = sys.stdin
else:
fp = open(args.file_graph)
mojom_file_graph = ReadMojomFileGraphFromFile(fp)
mojom_modules = mojom_translator.TranslateFileGraph(mojom_file_graph)
# Note that we are using the word "module" in two unrelated ways here.
# A mojom module is the Python data structure defined in module.py that
# represents a Mojom file (sometimes referred to as a Mojom module.)
# A generator module is a Python module in the sense of the entity the Python
# runtime loads corresponding to a .py file.
generator_modules = LoadGenerators(args.generators_string)
abs_src_root_path = os.path.abspath(args.src_root_path)
for _, mojom_module in mojom_modules.iteritems():
# If --no-gen-imports is specified then skip the code generation step for
# any modules that do not have the |specified_name| field set. This field
# being set indicates that the module was translated from a .mojom file
# whose name was explicitly requested during parsing. Otherwise the module
# is included only becuase of a mojom import statement.
if args.no_gen_imports and not mojom_module.specified_name:
continue
FixModulePath(mojom_module, abs_src_root_path)
for generator_module in generator_modules:
generator = generator_module.Generator(mojom_module, args.output_dir)
# Look at unparsed args for generator-specific args.
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)]
if args.generate_type_info:
filtered_args.append("--generate_type_info")
generator.GenerateFiles(filtered_args)
if __name__ == "__main__":
sys.exit(main())