blob: ad3889c5355ffaf865945fc93dbaa37bf9b0d3fe [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 version 2 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('-p', '--python-sdk-dir', dest='python_sdk_dir',
default=None,
help='Location of the compiled python bindings')
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("-d", "--depth", dest="depth", 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=True)
return parser.parse_known_args()
# We assume this script is located in the Mojo SDK in tools/bindings.
THIS_DIR = os.path.abspath(os.path.dirname(__file__))
SDK_ROOT = os.path.abspath(os.path.join(THIS_DIR, os.pardir, os.pardir))
PYTHON_SDK_DIR = os.path.abspath(os.path.join(SDK_ROOT, "python"))
def _FixPath():
# We parse command line args before imports in case the caller wishes to
# specify an alternative location for the Mojo Python SDK.
# TODO(rudominer) Consider removing this flexibility as I can think of
# no good reason for it.
args, _ = _ParseCLIArgs()
python_sdk_dir = args.python_sdk_dir
if not python_sdk_dir:
python_sdk_dir = PYTHON_SDK_DIR
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(THIS_DIR, "pylib"))
_FixPath()
from mojom.generate.generated import mojom_files_mojom
from mojom.generate import mojom_translator
from mojo_bindings import serialization
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() == "dart":
generator_name = os.path.join(script_dir, "generators",
"mojom_dart_generator.py")
elif generator_name.lower() == "go":
generator_name = os.path.join(script_dir, "generators",
"mojom_go_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 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.
"""
data = bytearray(fp.read())
context = serialization.RootDeserializationContext(data, [])
return mojom_files_mojom.MojomFileGraph.Deserialize(context)
def FixModulePath(module, 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_root: {str} absolute path to the root of the source tree.
"""
module.path = os.path.relpath(module.path, src_root_path)
if not hasattr(module, 'imports'):
return
for import_dict in module.imports:
FixModulePath(import_dict['module'], 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)
modules = mojom_translator.TranslateFileGraph(mojom_file_graph)
generator_modules = LoadGenerators(args.generators_string)
filenames = set(os.path.basename(filename) for filename in args.filenames)
for _, module in modules.iteritems():
# If filenames are specified on the command line, only generate code for
# the specified mojom files.
# TODO(azani): This is not as robust as we would like since files with the
# same name but different paths or files resolved through links might not
# be correctly identified by module.name.
if args.no_gen_imports and filenames and module.name not in filenames:
continue
FixModulePath(module, os.path.abspath(args.depth))
for generator_module in generator_modules:
generator = generator_module.Generator(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())