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_bundled_pylibs" in sys
.argv
[1:]:
31 sys
.path
.insert(0, os
.path
.join(_GetDirAbove("public"), "public/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 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"):
69 print "Unknown generator name %s" % generator_name
71 generator_module
= imp
.load_source(os
.path
.basename(generator_name
)[:-3],
73 generators
.append(generator_module
)
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.)"""
81 reversed(["\n %s was imported by %s" % (a
, b
) for (a
, b
) in \
82 zip(imported_filename_stack
[1:], imported_filename_stack
)]))
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
):
90 return os
.path
.join(dir_name
, file_name
)
92 class MojomProcessor(object):
93 def __init__(self
, should_generate
):
94 self
._should
_generate
= should_generate
95 self
._processed
_files
= {}
96 self
._parsed
_files
= {}
98 def ProcessFile(self
, args
, remaining_args
, generator_modules
, filename
):
99 self
._ParseFileAndImports
(filename
, args
.import_directories
, [])
101 return self
._GenerateModule
(args
, remaining_args
, generator_modules
,
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
]
110 dirname
, name
= os
.path
.split(filename
)
111 mojom
= Translate(tree
, name
)
112 if args
.debug_print_intermediate
:
113 pprint
.PrettyPrinter().pprint(mojom
)
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
)
124 module
= OrderedModuleFromData(mojom
)
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
))
130 # Normalize to unix-style path here to keep the generators simpler.
131 module
.path
= module
.path
.replace('\\', '/')
133 if self
._should
_generate
(filename
):
134 for generator_module
in generator_modules
:
135 generator
= generator_module
.Generator(module
, args
.output_dir
)
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
)
144 self
._processed
_files
[filename
] = module
147 def _ParseFileAndImports(self
, filename
, import_directories
,
148 imported_filename_stack
):
149 # Ignore already-parsed files.
150 if filename
in self
._parsed
_files
:
153 if filename
in imported_filename_stack
:
154 print "%s: Error: Circular dependency" % filename
+ \
155 MakeImportStackMessage(imported_filename_stack
+ [filename
])
159 with
open(filename
) as f
:
162 print "%s: Error: %s" % (e
.filename
, e
.strerror
) + \
163 MakeImportStackMessage(imported_filename_stack
+ [filename
])
167 tree
= Parse(source
, filename
)
169 full_stack
= imported_filename_stack
+ [filename
]
170 print str(e
) + MakeImportStackMessage(full_stack
)
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
])
180 self
._parsed
_files
[filename
] = tree
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()
205 generator_modules
= LoadGenerators(args
.generators_string
)
207 if not os
.path
.exists(args
.output_dir
):
208 os
.makedirs(args
.output_dir
)
210 processor
= MojomProcessor(lambda filename
: filename
in args
.filename
)
211 for filename
in args
.filename
:
212 processor
.ProcessFile(args
, remaining_args
, generator_modules
, filename
)
217 if __name__
== "__main__":