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

Side by Side Diff: packages/polymer/lib/src/build/runner.dart

Issue 2312183003: Removed Polymer from Observatory deps (Closed)
Patch Set: Created 4 years, 3 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 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /// Definitions used to run the polymer linter and deploy tools without using
6 /// pub serve or pub deploy.
7 library polymer.src.build.runner;
8
9 import 'dart:async';
10 import 'dart:convert';
11 import 'dart:io';
12
13 import 'package:barback/barback.dart';
14 import 'package:path/path.dart' as path;
15 import 'package:stack_trace/stack_trace.dart';
16 import 'package:yaml/yaml.dart';
17
18 /// Collects different parameters needed to configure and run barback.
19 class BarbackOptions {
20 /// Phases of transformers to run for the current package.
21 /// Use packagePhases to specify phases for other packages.
22 final List<List<Transformer>> phases;
23
24 /// Package to treat as the current package in barback.
25 final String currentPackage;
26
27 /// Directory root for the current package.
28 final String packageHome;
29
30 /// Mapping between package names and the path in the file system where
31 /// to find the sources of such package.
32 final Map<String, String> packageDirs;
33
34 /// Whether to run transformers on the test folder.
35 final bool transformTests;
36
37 /// Directory where to generate code, if any.
38 final String outDir;
39
40 /// Disregard files that match these filters when copying in non
41 /// transformed files
42 List<String> fileFilter;
43
44 /// Whether to print error messages using a json-format that tools, such as
45 /// the Dart Editor, can process.
46 final bool machineFormat;
47
48 /// Whether to follow symlinks when listing directories. By default this is
49 /// false because directories have symlinks for the packages directory created
50 /// by pub, but it can be turned on for custom uses of this library.
51 final bool followLinks;
52
53 /// Phases of transformers to apply to packages other than the current
54 /// package, keyed by the package name.
55 final Map<String, List<List<Transformer>>> packagePhases;
56
57 BarbackOptions(this.phases, this.outDir, {currentPackage, String packageHome,
58 packageDirs, this.transformTests: false, this.machineFormat: false,
59 this.followLinks: false, this.packagePhases: const {},
60 this.fileFilter: const []})
61 : currentPackage = (currentPackage != null
62 ? currentPackage
63 : readCurrentPackageFromPubspec()),
64 packageHome = packageHome,
65 packageDirs = (packageDirs != null
66 ? packageDirs
67 : readPackageDirsFromPub(packageHome, currentPackage));
68 }
69
70 /// Creates a barback system as specified by [options] and runs it. Returns a
71 /// future that contains the list of assets generated after barback runs to
72 /// completion.
73 Future<AssetSet> runBarback(BarbackOptions options) {
74 var barback = new Barback(new _PackageProvider(options.packageDirs));
75 _initBarback(barback, options);
76 _attachListeners(barback, options);
77 if (options.outDir == null) return barback.getAllAssets();
78 return _emitAllFiles(barback, options);
79 }
80
81 /// Extract the current package from the pubspec.yaml file.
82 String readCurrentPackageFromPubspec([String dir]) {
83 var pubspec =
84 new File(dir == null ? 'pubspec.yaml' : path.join(dir, 'pubspec.yaml'));
85 if (!pubspec.existsSync()) {
86 print('error: pubspec.yaml file not found, please run this script from '
87 'your package root directory.');
88 return null;
89 }
90 return loadYaml(pubspec.readAsStringSync())['name'];
91 }
92
93 /// Extract a mapping between package names and the path in the file system
94 /// which has the source of the package. This map will contain an entry for the
95 /// current package and everything it depends on (extracted via `pub
96 /// list-package-dirs`).
97 Map<String, String> readPackageDirsFromPub(
98 [String packageHome, String currentPackage]) {
99 var cachedDir = Directory.current;
100 if (packageHome != null) {
101 Directory.current = new Directory(packageHome);
102 } else {
103 packageHome = cachedDir.path;
104 }
105
106 var dartExec = Platform.executable;
107 // If dartExec == dart, then dart and pub are in standard PATH.
108 var sdkDir = dartExec == 'dart' ? '' : path.dirname(dartExec);
109 var pub = path.join(sdkDir, Platform.isWindows ? 'pub.bat' : 'pub');
110 var result = Process.runSync(pub, ['list-package-dirs']);
111 if (result.exitCode != 0) {
112 print("unexpected error invoking 'pub':");
113 print(result.stdout);
114 print(result.stderr);
115 exit(result.exitCode);
116 }
117 var map = JSON.decode(result.stdout)["packages"];
118 map.forEach((k, v) {
119 map[k] = path.absolute(packageHome, path.dirname(v));
120 });
121
122 if (currentPackage == null) {
123 currentPackage = readCurrentPackageFromPubspec(packageHome);
124 }
125 map[currentPackage] = packageHome;
126
127 Directory.current = cachedDir;
128 return map;
129 }
130
131 bool shouldSkip(List<String> filters, String path) {
132 return filters.any((filter) => path.contains(filter));
133 }
134
135 /// Return the relative path of each file under [subDir] in [package].
136 Iterable<String> _listPackageDir(
137 String package, String subDir, BarbackOptions options) {
138 var packageDir = options.packageDirs[package];
139 if (packageDir == null) return const [];
140 var dir = new Directory(path.join(packageDir, subDir));
141 if (!dir.existsSync()) return const [];
142 return dir
143 .listSync(recursive: true, followLinks: options.followLinks)
144 .where((f) => f is File)
145 .where((f) => !shouldSkip(options.fileFilter, f.path))
146 .map((f) => path.relative(f.path, from: packageDir));
147 }
148
149 /// A simple provider that reads files directly from the pub cache.
150 class _PackageProvider implements PackageProvider {
151 Map<String, String> packageDirs;
152 Iterable<String> get packages => packageDirs.keys;
153
154 _PackageProvider(this.packageDirs);
155
156 Future<Asset> getAsset(AssetId id) => new Future.value(new Asset.fromPath(
157 id, path.join(packageDirs[id.package], _toSystemPath(id.path))));
158 }
159
160 /// Convert asset paths to system paths (Assets always use the posix style).
161 String _toSystemPath(String assetPath) {
162 if (path.Style.platform != path.Style.windows) return assetPath;
163 return path.joinAll(path.posix.split(assetPath));
164 }
165
166 /// Tell barback which transformers to use and which assets to process.
167 void _initBarback(Barback barback, BarbackOptions options) {
168 var assets = [];
169 void addAssets(String package, String subDir) {
170 for (var filepath in _listPackageDir(package, subDir, options)) {
171 assets.add(new AssetId(package, filepath));
172 }
173 }
174
175 for (var package in options.packageDirs.keys) {
176 // Notify barback to process anything under 'lib' and 'asset'.
177 addAssets(package, 'lib');
178 addAssets(package, 'asset');
179
180 if (options.packagePhases.containsKey(package)) {
181 barback.updateTransformers(package, options.packagePhases[package]);
182 }
183 }
184 barback.updateTransformers(options.currentPackage, options.phases);
185
186 // In case of the current package, include also 'web'.
187 addAssets(options.currentPackage, 'web');
188 if (options.transformTests) addAssets(options.currentPackage, 'test');
189
190 // Add the sources after the transformers so all transformers are present
191 // when barback starts processing the assets.
192 barback.updateSources(assets);
193 }
194
195 /// Attach error listeners on [barback] so we can report errors.
196 void _attachListeners(Barback barback, BarbackOptions options) {
197 // Listen for errors and results
198 barback.errors.listen((e) {
199 var trace = null;
200 if (e is Error) trace = e.stackTrace;
201 if (trace != null) {
202 print(Trace.format(trace));
203 }
204 print('error running barback: $e');
205 exit(1);
206 });
207
208 barback.results.listen((result) {
209 if (!result.succeeded) {
210 print("build failed with errors: ${result.errors}");
211 exit(1);
212 }
213 });
214
215 barback.log.listen((entry) {
216 if (options.machineFormat) {
217 print(_jsonFormatter(entry));
218 } else {
219 print(_consoleFormatter(entry));
220 }
221 });
222 }
223
224 /// Emits all outputs of [barback] and copies files that we didn't process (like
225 /// dependent package's libraries).
226 Future _emitAllFiles(Barback barback, BarbackOptions options) {
227 return barback.getAllAssets().then((assets) {
228 // Delete existing output folder before we generate anything
229 var dir = new Directory(options.outDir);
230 if (dir.existsSync()) dir.deleteSync(recursive: true);
231 return _emitPackagesDir(options)
232 .then((_) => _emitTransformedFiles(assets, options))
233 .then((_) => _addPackagesSymlinks(assets, options))
234 .then((_) => assets);
235 });
236 }
237
238 Future _emitTransformedFiles(AssetSet assets, BarbackOptions options) {
239 // Copy all the assets we transformed
240 var futures = [];
241 var currentPackage = options.currentPackage;
242 var transformTests = options.transformTests;
243 var outPackages = path.join(options.outDir, 'packages');
244
245 return Future.forEach(assets, (asset) {
246 var id = asset.id;
247 var dir = _firstDir(id.path);
248 if (dir == null) return null;
249
250 var filepath;
251 if (dir == 'lib') {
252 // Put lib files directly under the packages folder (e.g. 'lib/foo.dart'
253 // will be emitted at out/packages/package_name/foo.dart).
254 filepath = path.join(
255 outPackages, id.package, _toSystemPath(id.path.substring(4)));
256 } else if (id.package == currentPackage &&
257 (dir == 'web' || (transformTests && dir == 'test'))) {
258 filepath = path.join(options.outDir, _toSystemPath(id.path));
259 } else {
260 // TODO(sigmund): do something about other assets?
261 return null;
262 }
263
264 return _writeAsset(filepath, asset);
265 });
266 }
267
268 /// Adds a package symlink from each directory under `out/web/foo/` to
269 /// `out/packages`.
270 void _addPackagesSymlinks(AssetSet assets, BarbackOptions options) {
271 var outPackages = path.join(options.outDir, 'packages');
272 var currentPackage = options.currentPackage;
273 for (var asset in assets) {
274 var id = asset.id;
275 if (id.package != currentPackage) continue;
276 var firstDir = _firstDir(id.path);
277 if (firstDir == null) continue;
278
279 if (firstDir == 'web' || (options.transformTests && firstDir == 'test')) {
280 var dir = path.join(options.outDir, path.dirname(_toSystemPath(id.path)));
281 var linkPath = path.join(dir, 'packages');
282 var link = new Link(linkPath);
283 if (!link.existsSync()) {
284 var targetPath = Platform.operatingSystem == 'windows'
285 ? path.normalize(path.absolute(outPackages))
286 : path.normalize(path.relative(outPackages, from: dir));
287 link.createSync(targetPath);
288 }
289 }
290 }
291 }
292
293 /// Emits a 'packages' directory directly under `out/packages` with the contents
294 /// of every file that was not transformed by barback.
295 Future _emitPackagesDir(BarbackOptions options) {
296 var outPackages = path.join(options.outDir, 'packages');
297 _ensureDir(outPackages);
298
299 // Copy all the files we didn't process
300 var dirs = options.packageDirs;
301 return Future.forEach(dirs.keys, (package) {
302 return Future.forEach(_listPackageDir(package, 'lib', options), (relpath) {
303 var inpath = path.join(dirs[package], relpath);
304 var outpath = path.join(outPackages, package, relpath.substring(4));
305 return _copyFile(inpath, outpath);
306 });
307 });
308 }
309
310 /// Ensure [dirpath] exists.
311 void _ensureDir(String dirpath) {
312 new Directory(dirpath).createSync(recursive: true);
313 }
314
315 /// Returns the first directory name on a url-style path, or null if there are
316 /// no slashes.
317 String _firstDir(String url) {
318 var firstSlash = url.indexOf('/');
319 if (firstSlash == -1) return null;
320 return url.substring(0, firstSlash);
321 }
322
323 /// Copy a file from [inpath] to [outpath].
324 Future _copyFile(String inpath, String outpath) {
325 _ensureDir(path.dirname(outpath));
326 return new File(inpath).openRead().pipe(new File(outpath).openWrite());
327 }
328
329 /// Write contents of an [asset] into a file at [filepath].
330 Future _writeAsset(String filepath, Asset asset) {
331 _ensureDir(path.dirname(filepath));
332 return asset.read().pipe(new File(filepath).openWrite());
333 }
334
335 String _kindFromEntry(LogEntry entry) {
336 var level = entry.level;
337 return level == LogLevel.ERROR
338 ? 'error'
339 : (level == LogLevel.WARNING ? 'warning' : 'info');
340 }
341
342 /// Formatter that generates messages using a format that can be parsed
343 /// by tools, such as the Dart Editor, for reporting error messages.
344 String _jsonFormatter(LogEntry entry) {
345 var kind = _kindFromEntry(entry);
346 var span = entry.span;
347 return JSON.encode((span == null)
348 ? [{'method': kind, 'params': {'message': entry.message}}]
349 : [
350 {
351 'method': kind,
352 'params': {
353 'file': span.sourceUrl.toString(),
354 'message': entry.message,
355 'line': span.start.line + 1,
356 'charStart': span.start.offset,
357 'charEnd': span.end.offset,
358 }
359 }
360 ]);
361 }
362
363 /// Formatter that generates messages that are easy to read on the console (used
364 /// by default).
365 String _consoleFormatter(LogEntry entry) {
366 var kind = _kindFromEntry(entry);
367 var useColors = stdioType(stdout) == StdioType.TERMINAL;
368 var levelColor = (kind == 'error') ? _RED_COLOR : _MAGENTA_COLOR;
369 var output = new StringBuffer();
370 if (useColors) output.write(levelColor);
371 output
372 ..write(kind)
373 ..write(' ');
374 if (useColors) output.write(_NO_COLOR);
375 if (entry.span == null) {
376 output.write(entry.message);
377 } else {
378 output.write(entry.span.message(entry.message,
379 color: useColors ? levelColor : null));
380 }
381 return output.toString();
382 }
383
384 const String _RED_COLOR = '\u001b[31m';
385 const String _MAGENTA_COLOR = '\u001b[35m';
386 const String _NO_COLOR = '\u001b[0m';
OLDNEW
« no previous file with comments | « packages/polymer/lib/src/build/polymer_smoke_generator.dart ('k') | packages/polymer/lib/src/build/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698