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