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

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

Powered by Google App Engine
This is Rietveld 408576698