Skip to content

Commit b4ec0a2

Browse files
committed
Include closurebuilder.py from the closure-library.
Add the minimal part of closure-library to pull in closurebuilder.py. closurebuilder.py depends on a few other .js files as well as goog.base. These are included as well. The directory tree mimics that of the closure-library to keep things easy to figure out.
1 parent 13945ac commit b4ec0a2

File tree

7 files changed

+3115
-0
lines changed

7 files changed

+3115
-0
lines changed
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
#!/usr/bin/env python
2+
#
3+
# Copyright 2009 The Closure Library Authors. All Rights Reserved.
4+
#
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
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
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.
16+
17+
"""Utility for Closure Library dependency calculation.
18+
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.
22+
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.
26+
27+
usage: %prog [options] [file1.js file2.js ...]
28+
"""
29+
30+
__author__ = '[email protected] (Nathan Naze)'
31+
32+
33+
import logging
34+
import optparse
35+
import os
36+
import sys
37+
38+
import depstree
39+
import jscompiler
40+
import source
41+
import treescan
42+
43+
44+
def _GetOptionsParser():
45+
"""Get the options parser."""
46+
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('--output_file',
100+
dest='output_file',
101+
action='store',
102+
help=('If specified, write output to this path instead of '
103+
'writing to standard output.'))
104+
105+
return parser
106+
107+
108+
def _GetInputByPath(path, sources):
109+
"""Get the source identified by a path.
110+
111+
Args:
112+
path: str, A path to a file that identifies a source.
113+
sources: An iterable collection of source objects.
114+
115+
Returns:
116+
The source from sources identified by path, if found. Converts to
117+
absolute paths for comparison.
118+
"""
119+
for js_source in sources:
120+
# Convert both to absolute paths for comparison.
121+
if os.path.abspath(path) == os.path.abspath(js_source.GetPath()):
122+
return js_source
123+
124+
125+
def _GetClosureBaseFile(sources):
126+
"""Given a set of sources, returns the one base.js file.
127+
128+
Note that if zero or two or more base.js files are found, an error message
129+
will be written and the program will be exited.
130+
131+
Args:
132+
sources: An iterable of _PathSource objects.
133+
134+
Returns:
135+
The _PathSource representing the base Closure file.
136+
"""
137+
base_files = [
138+
js_source for js_source in sources if _IsClosureBaseFile(js_source)]
139+
140+
if not base_files:
141+
logging.error('No Closure base.js file found.')
142+
sys.exit(1)
143+
if len(base_files) > 1:
144+
logging.error('More than one Closure base.js files found at these paths:')
145+
for base_file in base_files:
146+
logging.error(base_file.GetPath())
147+
sys.exit(1)
148+
return base_files[0]
149+
150+
151+
def _IsClosureBaseFile(js_source):
152+
"""Returns true if the given _PathSource is the Closure base.js source."""
153+
return (os.path.basename(js_source.GetPath()) == 'base.js' and
154+
js_source.provides == set(['goog']))
155+
156+
157+
class _PathSource(source.Source):
158+
"""Source file subclass that remembers its file path."""
159+
160+
def __init__(self, path):
161+
"""Initialize a source.
162+
163+
Args:
164+
path: str, Path to a JavaScript file. The source string will be read
165+
from this file.
166+
"""
167+
super(_PathSource, self).__init__(source.GetFileContents(path))
168+
169+
self._path = path
170+
171+
def GetPath(self):
172+
"""Returns the path."""
173+
return self._path
174+
175+
176+
def main():
177+
logging.basicConfig(format=(sys.argv[0] + ': %(message)s'),
178+
level=logging.INFO)
179+
options, args = _GetOptionsParser().parse_args()
180+
181+
# Make our output pipe.
182+
if options.output_file:
183+
out = open(options.output_file, 'w')
184+
else:
185+
out = sys.stdout
186+
187+
sources = set()
188+
189+
logging.info('Scanning paths...')
190+
for path in options.roots:
191+
for js_path in treescan.ScanTreeForJsFiles(path):
192+
sources.add(_PathSource(js_path))
193+
194+
# Add scripts specified on the command line.
195+
for js_path in args:
196+
sources.add(_PathSource(js_path))
197+
198+
logging.info('%s sources scanned.', len(sources))
199+
200+
# Though deps output doesn't need to query the tree, we still build it
201+
# to validate dependencies.
202+
logging.info('Building dependency tree..')
203+
tree = depstree.DepsTree(sources)
204+
205+
input_namespaces = set()
206+
inputs = options.inputs or []
207+
for input_path in inputs:
208+
js_input = _GetInputByPath(input_path, sources)
209+
if not js_input:
210+
logging.error('No source matched input %s', input_path)
211+
sys.exit(1)
212+
input_namespaces.update(js_input.provides)
213+
214+
input_namespaces.update(options.namespaces)
215+
216+
if not input_namespaces:
217+
logging.error('No namespaces found. At least one namespace must be '
218+
'specified with the --namespace or --input flags.')
219+
sys.exit(2)
220+
221+
# The Closure Library base file must go first.
222+
base = _GetClosureBaseFile(sources)
223+
deps = [base] + tree.GetDependencies(input_namespaces)
224+
225+
output_mode = options.output_mode
226+
if output_mode == 'list':
227+
out.writelines([js_source.GetPath() + '\n' for js_source in deps])
228+
elif output_mode == 'script':
229+
out.writelines([js_source.GetSource() for js_source in deps])
230+
elif output_mode == 'compiled':
231+
232+
# Make sure a .jar is specified.
233+
if not options.compiler_jar:
234+
logging.error('--compiler_jar flag must be specified if --output is '
235+
'"compiled"')
236+
sys.exit(2)
237+
238+
compiled_source = jscompiler.Compile(
239+
options.compiler_jar,
240+
[js_source.GetPath() for js_source in deps],
241+
options.compiler_flags)
242+
243+
if compiled_source is None:
244+
logging.error('JavaScript compilation failed.')
245+
sys.exit(1)
246+
else:
247+
logging.info('JavaScript compilation succeeded.')
248+
out.write(compiled_source)
249+
250+
else:
251+
logging.error('Invalid value for --output flag.')
252+
sys.exit(2)
253+
254+
255+
if __name__ == '__main__':
256+
main()

0 commit comments

Comments
 (0)