Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/tools/jsbundler.py

Issue 299703003: Build ChromeVox using gyp. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lkgr
Patch Set: Fix build Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
2
3 # Copyright 2014 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 '''Produces various output formats from a set of JavaScript files with
8 closure style require/provide calls.
9
10 Scans one or more directory trees for JavaScript files. Then, from a
11 given list of top-level files, sorts all required input files topologically.
12 The top-level files are appended to the sorted list in the order specified
13 on the command line. If no root directories are specified, the source
14 files are assumed to be ordered already and no dependency analysis is
15 performed. The resulting file list can then be output in various ways:
16
17 - list: a plain list of files, one per line.
18
19 - html: html <script> tags with src attributes containing paths.
20
21 - bundle: a cocatenation of all the files, separated by newlines.
dmazzoni 2014/05/21 17:42:17 cocatenation -> concatenation
22
23 - compressed_bundle: A bundle where non-significant whitespace, including
24 comments, has been stripped.
25 '''
26
27
28 import optparse
29 import os
30 import shutil
31 import sys
32
33 _SCRIPT_DIR = os.path.realpath(os.path.dirname(__file__))
34 _CHROME_SOURCE = os.path.realpath(
35 os.path.join(_SCRIPT_DIR, *[os.path.pardir] * 6))
36 sys.path.insert(0, os.path.join(
37 _CHROME_SOURCE, 'third_party/WebKit/Source/build/scripts'))
38 sys.path.insert(0, os.path.join(
39 _CHROME_SOURCE, ('chrome/third_party/chromevox/third_party/' +
40 'closure-library/closure/bin/build')))
41 import depstree
42 import rjsmin
43 import source
44 import treescan
45
46
47 def Die(message):
48 '''Prints an error message and exit the program.'''
49 print >>sys.stderr, message
50 sys.exit(1)
51
52
53 class SourceWithPaths(source.Source):
54 '''A source.Source object with its relative input and output paths'''
55
56 def __init__(self, content, in_path, out_path):
57 super(SourceWithPaths, self).__init__(content)
58 self._in_path = in_path
59 self._out_path = out_path
60
61 def GetInPath(self):
62 return self._in_path
63
64 def GetOutPath(self):
65 return self._out_path
66
67
68 class Bundle():
69 '''An ordered list of sources without duplicates.'''
70
71 def __init__(self):
72 self._added_paths = set()
73 self._added_sources = []
74
75 def Add(self, sources):
76 '''Appends one or more source objects the list if it doesn't already
77 exist.
78
79 Args:
80 sources: A SourceWithPath or an iterable of such objects.
81 '''
82 if isinstance(sources, SourceWithPaths):
83 sources = [sources]
84 for source in sources:
85 path = source.GetInPath()
86 if path not in self._added_paths:
87 self._added_paths.add(path)
88 self._added_sources.append(source)
89
90 def GetOutPaths(self):
91 return (source.GetOutPath() for source in self._added_sources)
92
93 def GetSources(self):
94 return self._added_sources
95
96 def GetUncompressedSource(self):
97 return '\n'.join((s.GetSource() for s in self._added_sources))
98
99 def GetCompressedSource(self):
100 return rjsmin.jsmin(self.GetUncompressedSource())
101
102
103 class PathRewriter():
104 '''A list of simple path rewrite rules to map relative input paths to
105 relative output paths.
106 '''
107
108 def __init__(self, specs):
109 '''Args:
110 specs: A list of mappings, each consisting of the input prefix and
111 the corresponding output prefix separated by colons.
112 '''
113 self._prefix_map = []
114 for spec in specs:
115 parts = spec.split(':')
116 if len(parts) != 2:
117 Die('Invalid prefix rewrite spec %s' % spec)
118 if not parts[0].endswith('/'):
119 parts[0] += '/'
120 self._prefix_map.append(parts)
121
122 def RewritePath(self, in_path):
123 '''Rewrites an input path according to the list of rules.
124
125 Args:
126 in_path, str: The input path to rewrite.
127 Returns:
128 str: The corresponding output path.
129 '''
130 for in_prefix, out_prefix in self._prefix_map:
131 if in_path.startswith(in_prefix):
132 return os.path.join(out_prefix, in_path[len(in_prefix):])
133 return in_path
134
135
136 def ReadSources(options, args):
137 '''Reads all source specified on the command line, including sources
138 included by --root options.
139 '''
140
141 def EnsureSourceLoaded(in_path, sources, path_rewriter):
142 if in_path not in sources:
143 out_path = path_rewriter.RewritePath(in_path)
144 sources[in_path] = SourceWithPaths(source.GetFileContents(in_path),
145 in_path, out_path)
146
147 # Only read the actual source file if we will do a dependency analysis or
148 # if we'll need it for the output.
149 need_source_text = (len(options.roots) > 0 or
150 options.mode in ('bundle', 'compressed_bundle'))
151 path_rewriter = PathRewriter(options.prefix_map)
152 sources = {}
153 for root in options.roots:
154 for name in treescan.ScanTreeForJsFiles(root):
155 EnsureSourceLoaded(name, sources, path_rewriter)
156 for path in args:
157 if need_source_text:
158 EnsureSourceLoaded(path, sources, path_rewriter)
159 else:
160 # Just add an empty representation of the source.
161 sources[path] = SourceWithPaths(
162 '', path, path_rewriter.RewritePath(path))
163 return sources
164
165
166 def CalcDeps(bundle, sources, top_level):
167 '''Calculates dependencies for a set of top-level files.
168
169 Args:
170 bundle: Bundle to add the sources to.
171 sources, dict: Mapping from input path to SourceWithPaths objects.
172 top_level, list: List of top-level input paths to calculate dependencies
173 for.
174 '''
175 def GetBase(sources):
176 for source in sources.itervalues():
177 if (os.path.basename(source.GetInPath()) == 'base.js' and
178 'goog' in source.provides):
179 return source
180 Die('goog.base not provided by any file')
181
182 providers = [s for s in sources.itervalues() if len(s.provides) > 0]
183 deps = depstree.DepsTree(providers)
184 namespaces = []
185 for path in top_level:
186 namespaces.extend(sources[path].requires)
187 # base.js is an implicit dependency that always goes first.
188 bundle.Add(GetBase(sources))
189 bundle.Add(deps.GetDependencies(namespaces))
190
191
192 def CopyFiles(sources, dest_dir):
193 '''Copies a list of sources to a destination directory.'''
dmazzoni 2014/05/21 17:42:17 I think this function should be called HardLinkOrC
194
195 def CopyOrLink(src, dst):
196 if not os.path.exists(os.path.dirname(dst)):
197 os.makedirs(os.path.dirname(dst))
198 if os.path.exists(dst):
199 # Avoid clobbering the inode if source and destination refer to the
200 # same file already.
201 if os.path.samefile(src, dst):
202 return
203 os.unlink(dst)
204 try:
205 os.link(src, dst)
206 except:
207 shutil.copy(src, dst)
208
209 for source in sources:
210 CopyOrLink(source.GetInPath(),
211 os.path.join(dest_dir, source.GetOutPath()))
212
213
214 def WriteOutput(bundle, format, out_file, dest_dir):
215 '''Writes output in the specified format.
216
217 Args:
218 bundle: The ordered bundle iwth all sources already added.
219 format: Output format, one of list, html, bundle, compressed_bundle.
220 out_file: File object to receive the output.
221 dest_dir: Prepended to each path mentioned in the output, if aplicable.
dmazzoni 2014/05/21 17:42:17 aplicable -> applicable
222 '''
223 if format == 'list':
224 paths = bundle.GetOutPaths()
225 if dest_dir:
226 paths = (os.path.join(dest_dir, p) for p in paths)
227 paths = (os.path.normpath(p) for p in paths)
228 out_file.write('\n'.join(paths))
229 elif format == 'html':
230 HTML_TEMPLATE = '<script src=\'%s\'>'
231 script_lines = (HTML_TEMPLATE % p for p in bundle.GetOutPaths())
232 out_file.write('\n'.join(script_lines))
233 elif format == 'bundle':
234 out_file.write(bundle.GetUncompressedSource())
235 elif format == 'compressed_bundle':
236 out_file.write(bundle.GetCompressedSource())
237 out_file.write('\n')
238
239
240 def CreateOptionParser():
241 parser = optparse.OptionParser(description=__doc__)
242 parser.usage = '%prog [options] <top_level_file>...'
243 parser.add_option('-d', '--dest_dir', action='store', metavar='DIR',
244 help=('Destination directory. Used when translating ' +
245 'input paths to output paths and when copying '
246 'files.'))
247 parser.add_option('-o', '--output_file', action='store', metavar='FILE',
248 help=('File to output result to for modes that output '
249 'a single file.'))
250 parser.add_option('-r', '--root', dest='roots', action='append', default=[],
251 metavar='ROOT',
252 help='Roots of directory trees to scan for sources.')
253 parser.add_option('-w', '--rewrite_prefix', action='append', default=[],
254 dest='prefix_map', metavar='SPEC',
255 help=('Two path prefixes, separated by colons ' +
256 'specifying that a file whose (relative) path ' +
257 'name starts with the first prefix should have ' +
258 'that prefix replaced by the second prefix to ' +
259 'form a path relative to the output directory.'))
260 parser.add_option('-m', '--mode', type='choice', action='store',
261 choices=['list', 'html', 'bundle',
262 'compressed_bundle', 'copy'],
263 default='list', metavar='MODE',
264 help=("Otput mode. One of 'list', 'html', 'bundle', " +
265 "'compressed_bundle' or 'copy'."))
266 return parser
267
268
269 def main():
270 options, args = CreateOptionParser().parse_args()
271 if len(args) < 1:
272 Die('At least one top-level source file must be specified.')
273 sources = ReadSources(options, args)
274 bundle = Bundle()
275 if len(options.roots) > 0:
276 CalcDeps(bundle, sources, args)
277 bundle.Add((sources[name] for name in args))
278 if options.mode == 'copy':
279 if options.dest_dir is None:
280 Die('Must specify --dest_dir when copying.')
281 CopyFiles(bundle.GetSources(), options.dest_dir)
282 else:
283 if options.output_file:
284 out_file = open(options.output_file, 'w')
285 else:
286 out_file = sys.stdout
287 try:
288 WriteOutput(bundle, options.mode, out_file, options.dest_dir)
289 finally:
290 if options.output_file:
291 out_file.close()
292
293
294 if __name__ == '__main__':
295 main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698