Backed out changeset 7272b7396c78 (bug 1932758) for causing fenix debug failures...
[gecko.git] / dom / canvas / test / webgl-conf / checkout / closure-library / closure / bin / build / closurebuilder.py
blob9e4e2eb339b60b8badabbb54992b521b764b8d30
1 #!/usr/bin/env python
3 # Copyright 2009 The Closure Library Authors. All Rights Reserved.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS-IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Utility for Closure Library dependency calculation.
19 ClosureBuilder scans source files to build dependency info. From the
20 dependencies, the script can produce a manifest in dependency order,
21 a concatenated script, or compiled output from the Closure Compiler.
23 Paths to files can be expressed as individual arguments to the tool (intended
24 for use with find and xargs). As a convenience, --root can be used to specify
25 all JS files below a directory.
27 usage: %prog [options] [file1.js file2.js ...]
28 """
30 __author__ = 'nnaze@google.com (Nathan Naze)'
33 import logging
34 import optparse
35 import os
36 import sys
38 import depstree
39 import jscompiler
40 import source
41 import treescan
44 def _GetOptionsParser():
45 """Get the options parser."""
47 parser = optparse.OptionParser(__doc__)
48 parser.add_option('-i',
49 '--input',
50 dest='inputs',
51 action='append',
52 default=[],
53 help='One or more input files to calculate dependencies '
54 'for. The namespaces in this file will be combined with '
55 'those given with the -n flag to form the set of '
56 'namespaces to find dependencies for.')
57 parser.add_option('-n',
58 '--namespace',
59 dest='namespaces',
60 action='append',
61 default=[],
62 help='One or more namespaces to calculate dependencies '
63 'for. These namespaces will be combined with those given '
64 'with the -i flag to form the set of namespaces to find '
65 'dependencies for. A Closure namespace is a '
66 'dot-delimited path expression declared with a call to '
67 'goog.provide() (e.g. "goog.array" or "foo.bar").')
68 parser.add_option('--root',
69 dest='roots',
70 action='append',
71 default=[],
72 help='The paths that should be traversed to build the '
73 'dependencies.')
74 parser.add_option('-o',
75 '--output_mode',
76 dest='output_mode',
77 type='choice',
78 action='store',
79 choices=['list', 'script', 'compiled'],
80 default='list',
81 help='The type of output to generate from this script. '
82 'Options are "list" for a list of filenames, "script" '
83 'for a single script containing the contents of all the '
84 'files, or "compiled" to produce compiled output with '
85 'the Closure Compiler. Default is "list".')
86 parser.add_option('-c',
87 '--compiler_jar',
88 dest='compiler_jar',
89 action='store',
90 help='The location of the Closure compiler .jar file.')
91 parser.add_option('-f',
92 '--compiler_flags',
93 dest='compiler_flags',
94 default=[],
95 action='append',
96 help='Additional flags to pass to the Closure compiler. '
97 'To pass multiple flags, --compiler_flags has to be '
98 'specified multiple times.')
99 parser.add_option('-j',
100 '--jvm_flags',
101 dest='jvm_flags',
102 default=[],
103 action='append',
104 help='Additional flags to pass to the JVM compiler. '
105 'To pass multiple flags, --jvm_flags has to be '
106 'specified multiple times.')
107 parser.add_option('--output_file',
108 dest='output_file',
109 action='store',
110 help=('If specified, write output to this path instead of '
111 'writing to standard output.'))
113 return parser
116 def _GetInputByPath(path, sources):
117 """Get the source identified by a path.
119 Args:
120 path: str, A path to a file that identifies a source.
121 sources: An iterable collection of source objects.
123 Returns:
124 The source from sources identified by path, if found. Converts to
125 real paths for comparison.
127 for js_source in sources:
128 # Convert both to real paths for comparison.
129 if os.path.realpath(path) == os.path.realpath(js_source.GetPath()):
130 return js_source
133 def _GetClosureBaseFile(sources):
134 """Given a set of sources, returns the one base.js file.
136 Note that if zero or two or more base.js files are found, an error message
137 will be written and the program will be exited.
139 Args:
140 sources: An iterable of _PathSource objects.
142 Returns:
143 The _PathSource representing the base Closure file.
145 base_files = [
146 js_source for js_source in sources if _IsClosureBaseFile(js_source)]
148 if not base_files:
149 logging.error('No Closure base.js file found.')
150 sys.exit(1)
151 if len(base_files) > 1:
152 logging.error('More than one Closure base.js files found at these paths:')
153 for base_file in base_files:
154 logging.error(base_file.GetPath())
155 sys.exit(1)
156 return base_files[0]
159 def _IsClosureBaseFile(js_source):
160 """Returns true if the given _PathSource is the Closure base.js source."""
161 return (os.path.basename(js_source.GetPath()) == 'base.js' and
162 js_source.provides == set(['goog']))
165 class _PathSource(source.Source):
166 """Source file subclass that remembers its file path."""
168 def __init__(self, path):
169 """Initialize a source.
171 Args:
172 path: str, Path to a JavaScript file. The source string will be read
173 from this file.
175 super(_PathSource, self).__init__(source.GetFileContents(path))
177 self._path = path
179 def __str__(self):
180 return 'PathSource %s' % self._path
182 def GetPath(self):
183 """Returns the path."""
184 return self._path
187 def _WrapGoogModuleSource(src):
188 return ('goog.loadModule(function(exports) {{'
189 '"use strict";'
190 '{0}'
191 '\n' # terminate any trailing single line comment.
192 ';return exports'
193 '}});\n').format(src)
196 def main():
197 logging.basicConfig(format=(sys.argv[0] + ': %(message)s'),
198 level=logging.INFO)
199 options, args = _GetOptionsParser().parse_args()
201 # Make our output pipe.
202 if options.output_file:
203 out = open(options.output_file, 'w')
204 else:
205 out = sys.stdout
207 sources = set()
209 logging.info('Scanning paths...')
210 for path in options.roots:
211 for js_path in treescan.ScanTreeForJsFiles(path):
212 sources.add(_PathSource(js_path))
214 # Add scripts specified on the command line.
215 for js_path in args:
216 sources.add(_PathSource(js_path))
218 logging.info('%s sources scanned.', len(sources))
220 # Though deps output doesn't need to query the tree, we still build it
221 # to validate dependencies.
222 logging.info('Building dependency tree..')
223 tree = depstree.DepsTree(sources)
225 input_namespaces = set()
226 inputs = options.inputs or []
227 for input_path in inputs:
228 js_input = _GetInputByPath(input_path, sources)
229 if not js_input:
230 logging.error('No source matched input %s', input_path)
231 sys.exit(1)
232 input_namespaces.update(js_input.provides)
234 input_namespaces.update(options.namespaces)
236 if not input_namespaces:
237 logging.error('No namespaces found. At least one namespace must be '
238 'specified with the --namespace or --input flags.')
239 sys.exit(2)
241 # The Closure Library base file must go first.
242 base = _GetClosureBaseFile(sources)
243 deps = [base] + tree.GetDependencies(input_namespaces)
245 output_mode = options.output_mode
246 if output_mode == 'list':
247 out.writelines([js_source.GetPath() + '\n' for js_source in deps])
248 elif output_mode == 'script':
249 for js_source in deps:
250 src = js_source.GetSource()
251 if js_source.is_goog_module:
252 src = _WrapGoogModuleSource(src)
253 out.write(src + '\n')
254 elif output_mode == 'compiled':
255 logging.warning("""\
256 Closure Compiler now natively understands and orders Closure dependencies and
257 is prefererred over using this script for performing JavaScript compilation.
259 Please migrate your codebase.
261 See:
262 https://github.com/google/closure-compiler/wiki/Manage-Closure-Dependencies
263 """)
265 # Make sure a .jar is specified.
266 if not options.compiler_jar:
267 logging.error('--compiler_jar flag must be specified if --output is '
268 '"compiled"')
269 sys.exit(2)
271 # Will throw an error if the compilation fails.
272 compiled_source = jscompiler.Compile(
273 options.compiler_jar,
274 [js_source.GetPath() for js_source in deps],
275 jvm_flags=options.jvm_flags,
276 compiler_flags=options.compiler_flags)
278 logging.info('JavaScript compilation succeeded.')
279 out.write(compiled_source)
281 else:
282 logging.error('Invalid value for --output flag.')
283 sys.exit(2)
286 if __name__ == '__main__':
287 main()