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

Side by Side Diff: sdk/lib/_internal/pub/lib/src/barback/build_environment.dart

Issue 141113011: Support directories other than "web" in pub build. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 11 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
nweiz 2014/01/28 03:21:51 2014. Same for other new files
Bob Nystrom 2014/01/28 23:02:04 Done.
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 library pub.barback.build_environment;
6
7 import 'dart:async';
8
9 import 'package:barback/barback.dart';
10 import 'package:path/path.dart' as path;
11 import 'package:stack_trace/stack_trace.dart';
12 import 'package:watcher/watcher.dart';
13
14 import '../entrypoint.dart';
15 import '../io.dart';
16 import '../log.dart' as log;
17 import '../package.dart';
18 import '../package_graph.dart';
19 import '../utils.dart';
20 import 'dart_forwarding_transformer.dart';
21 import 'dart2js_transformer.dart';
22 import 'load_all_transformers.dart';
23 import 'pub_package_provider.dart';
24 import 'server.dart';
25
26 /// The entire "visible" state of the assets of a package and all of its
27 /// dependencies, taking into account the user's configuration when running pub.
28 ///
29 /// Where [PackageGraph] just describes the entrypoint's dependencies as
30 /// specified by pubspecs, this includes "transient" information like the mode
31 /// that the user is running pub in, or which directories they want to build.
32 class BuildEnvironment {
nweiz 2014/01/28 03:21:51 This class confuses me, since the only public stuf
Bob Nystrom 2014/01/28 23:02:04 It's exposing more stuff now.
33 /// Creates a new build environment for working with the assets used by
34 /// [entrypoint] and its dependencies.
35 ///
36 /// Spawns an HTTP server on [hostname] and [port]. Loads all used
37 /// transformers using [mode] (including dart2js if [useDart2JS] is true).
38 ///
39 /// Includes [buildDirectories] in the root package.
nweiz 2014/01/28 03:21:51 " in addition to `lib/` and `asset/`."
Bob Nystrom 2014/01/28 23:02:04 Done.
40 ///
41 /// If [watcherType] is not [WatcherType.NONE], watches source assets for
42 /// modification.
43 ///
44 /// Returns a [Future] that completes to the environment once the inputs,
45 /// transformers, and server are loaded and ready.
46 static Future<BuildEnvironment> create(Entrypoint entrypoint,
47 String hostname, int port, BarbackMode mode, WatcherType watcherType,
48 Set<String> buildDirectories,
49 {bool useDart2JS: true}) {
50 return entrypoint.loadPackageGraph().then((graph) {
51 var dart2JSTransformer;
52 if (useDart2JS) {
53 dart2JSTransformer = new Dart2JSTransformer(graph, mode);
54 }
55
56 var builtInTransformers;
57 if (useDart2JS) {
58 builtInTransformers = [
59 dart2JSTransformer,
60 new DartForwardingTransformer(mode)
61 ];
62 }
63
64 // If the entrypoint package manually configures the dart2js transformer,
65 // remove it from the built-in transformer list.
66 //
67 // TODO(nweiz): if/when we support more built-in transformers, make this
68 // more general.
69 var containsDart2Js = graph.entrypoint.root.pubspec.transformers
70 .any((transformers) => transformers
71 .any((id) => id.package == '\$dart2js'));
72
73 if (containsDart2Js) {
74 builtInTransformers = builtInTransformers.where(
75 (transformer) => transformer is! Dart2JSTransformer);
76 }
nweiz 2014/01/28 03:21:51 It might be cleaner if we do this above and just r
Bob Nystrom 2014/01/28 23:02:04 Done. This brought to my attention that we weren't
77
78 var barback = new Barback(new PubPackageProvider(graph));
79 barback.log.listen(_log);
80
81 return BarbackServer.bind(hostname, port, barback,
82 graph.entrypoint.root.name).then((server) {
83
84 var environment = new BuildEnvironment._(graph, server, mode,
85 watcherType, buildDirectories, dart2JSTransformer);
86
87 return environment._load(barback, builtInTransformers)
88 .then((server) => environment);
nweiz 2014/01/28 03:21:51 "server" => "_"
Bob Nystrom 2014/01/28 23:02:04 Done.
89 });
90 });
91 }
92
93 /// The server serving this environment's assets.
94 final BarbackServer server;
95
96 /// The [Dart2JSTransformer], or `null` if it's not enabled.
97 final Dart2JSTransformer dart2JSTransformer;
98
99 /// The underlying [PackageGraph] being built.
100 final PackageGraph _graph;
101
102 /// The mode to run the transformers in.
103 final BarbackMode _mode;
104
105 /// How source files should be watched.
106 final WatcherType _watcherType;
107
108 /// The set of top-level directories in the entrypoint package that should be
109 /// built.
110 final Set<String> _buildDirectories;
111
112 BuildEnvironment._(this._graph, this.server, this._mode, this._watcherType,
113 this._buildDirectories, this.dart2JSTransformer);
114
115 /// Creates a [BarbackServer] for this environment.
116 ///
117 /// This transforms and serves all library and asset files in all packages in
118 /// the environment's package graph. It loads any transformer plugins defined
119 /// in packages in [graph] and re-runs them as necessary when any input files
120 /// change.
121 ///
122 /// Returns a [Future] that completes once all inputs and transformers are
123 /// loaded.
124 Future _load(Barback barback, Iterable<Transformer> transformers) {
125 return _provideSources(barback).then((_) {
126 var completer = new Completer();
127
128 // If any errors get emitted either by barback or by the server,
129 // including non-programmatic barback errors, they should take down the
130 // whole program.
131 var subscriptions = [
132 server.barback.errors.listen((error) {
133 if (error is TransformerException) error = error.error;
134 if (!completer.isCompleted) {
135 completer.completeError(error, new Chain.current());
136 }
137 }),
138 server.barback.results.listen((_) {}, onError: (error, stackTrace) {
139 if (completer.isCompleted) return;
140 completer.completeError(error, stackTrace);
141 }),
142 server.results.listen((_) {}, onError: (error, stackTrace) {
143 if (completer.isCompleted) return;
144 completer.completeError(error, stackTrace);
145 })
146 ];
147
148 loadAllTransformers(server, _graph, _mode, transformers).then((_) {
149 if (!completer.isCompleted) completer.complete(server);
nweiz 2014/01/28 03:21:51 "server" is unused, so this should probably just b
Bob Nystrom 2014/01/28 23:02:04 Done.
150 }).catchError((error, stackTrace) {
151 if (!completer.isCompleted) {
152 completer.completeError(error, stackTrace);
153 }
154 });
155
156 return completer.future.whenComplete(() {
157 for (var subscription in subscriptions) {
158 subscription.cancel();
159 }
160 });
161 });
162 }
163
164 /// Provides all of the source assets in the environment to barback.
165 ///
166 /// If [watcherType] is not [WatcherType.NONE], enables watching on them.
167 Future _provideSources(Barback barback) {
168 if (_watcherType != WatcherType.NONE) {
169 return _watchSources(barback);
170 }
171
172 return syncFuture(() {
173 _loadSources(barback);
174 });
175 }
176
177 /// Provides all of the source assets in the environment to barback.
178 void _loadSources(Barback barback) {
179 for (var package in _graph.packages.values) {
180 barback.updateSources(_listAssets(_graph.entrypoint, package));
181 }
182 }
183
184 /// Adds all of the source assets in this environment to barback and then
185 /// watches the public directories for changes.
186 ///
187 /// Returns a Future that completes when the sources are loaded and the
188 /// watchers are active.
189 Future _watchSources(Barback barback) {
190 return Future.wait(_graph.packages.values.map((package) {
191 // If this package comes from a cached source, its contents won't change
192 // so we don't need to monitor it. `packageId` will be null for the
193 // application package, since that's not locked.
194 var packageId = _graph.lockFile.packages[package.name];
195 if (packageId != null &&
196 _graph.entrypoint.cache.sources[packageId.source].shouldCache) {
197 barback.updateSources(_listAssets(_graph.entrypoint, package));
198 return new Future.value();
199 }
200
201 // Watch the visible package directories for changes.
202 return Future.wait(_getPublicDirectories(_graph.entrypoint, package)
203 .map((name) {
204 var subdirectory = path.join(package.dir, name);
205 if (!dirExists(subdirectory)) return new Future.value();
206
207 // TODO(nweiz): close these watchers when [barback] is closed.
208 var watcher = _watcherType.create(subdirectory);
209 watcher.events.listen((event) {
210 // Don't watch files symlinked into these directories.
211 // TODO(rnystrom): If pub gets rid of symlinks, remove this.
212 var parts = path.split(event.path);
213 if (parts.contains("packages") || parts.contains("assets")) return;
214
215 // Skip ".js" files that were (most likely) compiled from nearby
216 // ".dart" files. These are created by the Editor's "Run as
217 // JavaScript" command and are written directly into the package's
218 // directory. When pub's dart2js transformer then tries to create the
219 // same file name, we get a build error. To avoid that, just don't
220 // consider that file to be a source.
221 // TODO(rnystrom): Remove this when the Editor no longer generates
222 // .js files. See #15859.
223 if (event.path.endsWith(".dart.js")) return;
224
225 var id = new AssetId(package.name,
226 path.relative(event.path, from: package.dir));
227 if (event.type == ChangeType.REMOVE) {
228 barback.removeSources([id]);
229 } else {
230 barback.updateSources([id]);
231 }
232 });
233 return watcher.ready;
234 })).then((_) {
235 barback.updateSources(_listAssets(_graph.entrypoint, package));
236 });
237 }));
238 }
239
240 /// Lists all of the visible files in [package].
241 ///
242 /// This is the recursive contents of the "asset" and "lib" directories (if
243 /// present). If [package] is the entrypoint package, it also includes the
244 /// contents of "web".
245 List<AssetId> _listAssets(Entrypoint entrypoint, Package package) {
246 var files = <AssetId>[];
247
248 for (var dirPath in _getPublicDirectories(entrypoint, package)) {
249 var dir = path.join(package.dir, dirPath);
250 if (!dirExists(dir)) continue;
251 for (var entry in listDir(dir, recursive: true)) {
252 // Ignore "packages" symlinks if there.
253 if (path.split(entry).contains("packages")) continue;
254
255 // Skip directories.
256 if (!fileExists(entry)) continue;
257
258 // Skip ".js" files that were (most likely) compiled from nearby ".dart"
259 // files. These are created by the Editor's "Run as JavaScript" command
260 // and are written directly into the package's directory. When pub's
261 // dart2js transformer then tries to create the same file name, we get
262 // a build error. To avoid that, just don't consider that file to be a
263 // source.
264 // TODO(rnystrom): Remove this when the Editor no longer generates .js
265 // files. See #15859.
266 if (entry.endsWith(".dart.js")) continue;
267
268 var id = new AssetId(package.name,
269 path.relative(entry, from: package.dir));
270 files.add(id);
271 }
272 }
273
274 return files;
275 }
276
277 /// Gets the names of the top-level directories in [package] whose contents
278 /// should be provided as source assets.
279 Iterable<String> _getPublicDirectories(Entrypoint entrypoint,
280 Package package) {
281 var directories = ["asset", "lib"];
282
283 if (package.name == entrypoint.root.name) {
284 directories.addAll(_buildDirectories);
285 }
286
287 return directories;
288 }
289 }
290
291 /// Log [entry] using Pub's logging infrastructure.
292 ///
293 /// Since both [LogEntry] objects and the message itself often redundantly
294 /// show the same context like the file where an error occurred, this tries
295 /// to avoid showing redundant data in the entry.
296 void _log(LogEntry entry) {
297 messageMentions(String text) {
298 return entry.message.toLowerCase().contains(text.toLowerCase());
299 }
300
301 var prefixParts = [];
302
303 // Show the level (unless the message mentions it).
304 if (!messageMentions(entry.level.name)) {
305 prefixParts.add("${entry.level} from");
306 }
307
308 // Show the transformer.
309 prefixParts.add(entry.transform.transformer);
310
311 // Mention the primary input of the transform unless the message seems to.
312 if (!messageMentions(entry.transform.primaryId.path)) {
313 prefixParts.add("on ${entry.transform.primaryId}");
314 }
315
316 // If the relevant asset isn't the primary input, mention it unless the
317 // message already does.
318 if (entry.assetId != entry.transform.primaryId &&
319 !messageMentions(entry.assetId.path)) {
320 prefixParts.add("with input ${entry.assetId}");
321 }
322
323 var prefix = "[${prefixParts.join(' ')}]:";
324 var message = entry.message;
325 if (entry.span != null) {
326 message = entry.span.getLocationMessage(entry.message);
327 }
328
329 switch (entry.level) {
330 case LogLevel.ERROR:
331 log.error("${log.red(prefix)}\n$message");
332 break;
333
334 case LogLevel.WARNING:
335 log.warning("${log.yellow(prefix)}\n$message");
336 break;
337
338 case LogLevel.INFO:
339 log.message("${log.cyan(prefix)}\n$message");
340 break;
341 }
342 }
343
344 /// An enum describing different modes of constructing a [DirectoryWatcher].
345 abstract class WatcherType {
346 /// A watcher that automatically chooses its type based on the operating
347 /// system.
348 static const AUTO = const _AutoWatcherType();
349
350 /// A watcher that always polls the filesystem for changes.
351 static const POLLING = const _PollingWatcherType();
352
353 /// No directory watcher at all.
354 static const NONE = const _NoneWatcherType();
355
356 /// Creates a new DirectoryWatcher.
357 DirectoryWatcher create(String directory);
358
359 String toString();
360 }
361
362 class _AutoWatcherType implements WatcherType {
363 const _AutoWatcherType();
364
365 DirectoryWatcher create(String directory) =>
366 new DirectoryWatcher(directory);
367
368 String toString() => "auto";
369 }
370
371 class _PollingWatcherType implements WatcherType {
372 const _PollingWatcherType();
373
374 DirectoryWatcher create(String directory) =>
375 new PollingDirectoryWatcher(directory);
376
377 String toString() => "polling";
378 }
379
380 class _NoneWatcherType implements WatcherType {
381 const _NoneWatcherType();
382
383 DirectoryWatcher create(String directory) => null;
384
385 String toString() => "none";
386 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698