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

Unified Diff: sdk/lib/_internal/pub_generated/lib/src/barback/asset_environment.dart

Issue 657673002: Regenerate pub sources. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 2 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 side-by-side diff with in-line comments
Download patch
Index: sdk/lib/_internal/pub_generated/lib/src/barback/asset_environment.dart
diff --git a/sdk/lib/_internal/pub_generated/lib/src/barback/asset_environment.dart b/sdk/lib/_internal/pub_generated/lib/src/barback/asset_environment.dart
index 894a1aff4cfe1a2e9f6453c2ff0afa056405d53b..27c25932f2b922e2ab8f8bd0c49148f1e5684041 100644
--- a/sdk/lib/_internal/pub_generated/lib/src/barback/asset_environment.dart
+++ b/sdk/lib/_internal/pub_generated/lib/src/barback/asset_environment.dart
@@ -1,9 +1,16 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library pub.barback.asset_environment;
+
import 'dart:async';
import 'dart:io';
+
import 'package:barback/barback.dart';
import 'package:path/path.dart' as path;
import 'package:watcher/watcher.dart';
+
import '../cached_package.dart';
import '../entrypoint.dart';
import '../exceptions.dart';
@@ -21,7 +28,40 @@ import 'dart2js_transformer.dart';
import 'load_all_transformers.dart';
import 'pub_package_provider.dart';
import 'source_directory.dart';
+
+/// The entire "visible" state of the assets of a package and all of its
+/// dependencies, taking into account the user's configuration when running pub.
+///
+/// Where [PackageGraph] just describes the entrypoint's dependencies as
+/// specified by pubspecs, this includes "transient" information like the mode
+/// that the user is running pub in, or which directories they want to
+/// transform.
class AssetEnvironment {
+ /// Creates a new build environment for working with the assets used by
+ /// [entrypoint] and its dependencies.
+ ///
+ /// HTTP servers that serve directories from this environment will be bound
+ /// to [hostname] and have ports based on [basePort]. If omitted, they
+ /// default to "localhost" and "0" (use ephemeral ports), respectively.
+ ///
+ /// Loads all used transformers using [mode] (including dart2js if
+ /// [useDart2JS] is true).
+ ///
+ /// This will only add the root package's "lib" directory to the environment.
+ /// Other directories can be added to the environment using [serveDirectory].
+ ///
+ /// If [watcherType] is not [WatcherType.NONE] (the default), watches source
+ /// assets for modification.
+ ///
+ /// If [packages] is passed, only those packages' assets are loaded and
+ /// served.
+ ///
+ /// If [entrypoints] is passed, only transformers necessary to run those
+ /// entrypoints are loaded. Each entrypoint is expected to refer to a Dart
+ /// library.
+ ///
+ /// Returns a [Future] that completes to the environment once the inputs,
+ /// transformers, and server are loaded and ready.
static Future<AssetEnvironment> create(Entrypoint entrypoint,
BarbackMode mode, {WatcherType watcherType, String hostname, int basePort,
Iterable<String> packages, Iterable<AssetId> entrypoints, bool useDart2JS:
@@ -29,22 +69,29 @@ class AssetEnvironment {
if (watcherType == null) watcherType = WatcherType.NONE;
if (hostname == null) hostname = "localhost";
if (basePort == null) basePort = 0;
+
return entrypoint.loadPackageGraph().then((graph) {
log.fine("Loaded package graph.");
graph = _adjustPackageGraph(graph, mode, packages);
var barback = new Barback(new PubPackageProvider(graph));
barback.log.listen(_log);
+
var environment =
new AssetEnvironment._(graph, barback, mode, watcherType, hostname, basePort);
+
return environment._load(
entrypoints: entrypoints,
useDart2JS: useDart2JS).then((_) => environment);
});
}
+
+ /// Return a version of [graph] that's restricted to [packages] (if passed)
+ /// and loads cached packages (if [mode] is [BarbackMode.DEBUG]).
static PackageGraph _adjustPackageGraph(PackageGraph graph, BarbackMode mode,
Iterable<String> packages) {
if (mode != BarbackMode.DEBUG && packages == null) return graph;
packages = (packages == null ? graph.packages.keys : packages).toSet();
+
return new PackageGraph(
graph.entrypoint,
graph.lockFile,
@@ -56,31 +103,104 @@ class AssetEnvironment {
return new CachedPackage(package, cache);
}));
}
+
+ /// The server for the Web Socket API and admin interface.
AdminServer _adminServer;
+
+ /// The public directories in the root package that are included in the asset
+ /// environment, keyed by their root directory.
final _directories = new Map<String, SourceDirectory>();
+
+ /// The [Barback] instance used to process assets in this environment.
final Barback barback;
+
+ /// The root package being built.
Package get rootPackage => graph.entrypoint.root;
+
+ /// The graph of packages whose assets and transformers are loaded in this
+ /// environment.
+ ///
+ /// This isn't necessarily identical to the graph that's passed in to the
+ /// environment. It may expose fewer packages if some packages' assets don't
+ /// need to be loaded, and it may expose some [CachedPackage]s.
final PackageGraph graph;
+
+ /// The mode to run the transformers in.
final BarbackMode mode;
+
+ /// The [Transformer]s that should be appended by default to the root
+ /// package's transformer cascade. Will be empty if there are none.
final _builtInTransformers = <Transformer>[];
+
+ /// How source files should be watched.
final WatcherType _watcherType;
+
+ /// The hostname that servers are bound to.
final String _hostname;
+
+ /// The starting number for ports that servers will be bound to.
+ ///
+ /// Servers will be bound to ports starting at this number and then
+ /// incrementing from there. However, if this is zero, then ephemeral port
+ /// numbers will be selected for each server.
final int _basePort;
+
+ /// The modified source assets that have not been sent to barback yet.
+ ///
+ /// The build environment can be paused (by calling [pauseUpdates]) and
+ /// resumed ([resumeUpdates]). While paused, all source asset updates that
+ /// come from watching or adding new directories are not sent to barback.
+ /// When resumed, all pending source updates are sent to barback.
+ ///
+ /// This lets pub serve and pub build create an environment and bind several
+ /// servers before barback starts building and producing results
+ /// asynchronously.
+ ///
+ /// If this is `null`, then the environment is "live" and all updates will
+ /// go to barback immediately.
Set<AssetId> _modifiedSources;
+
AssetEnvironment._(this.graph, this.barback, this.mode, this._watcherType,
this._hostname, this._basePort);
+
+ /// Gets the built-in [Transformer]s that should be added to [package].
+ ///
+ /// Returns `null` if there are none.
Iterable<Transformer> getBuiltInTransformers(Package package) {
+ // Built-in transformers only apply to the root package.
if (package.name != rootPackage.name) return null;
+
+ // The built-in transformers are for dart2js and forwarding assets around
+ // dart2js.
if (_builtInTransformers.isEmpty) return null;
+
return _builtInTransformers;
}
+
+ /// Starts up the admin server on an appropriate port and returns it.
+ ///
+ /// This may only be called once on the build environment.
Future<AdminServer> startAdminServer([int port]) {
+ // Can only start once.
assert(_adminServer == null);
+
+ // The admin server is bound to one before the base port by default, unless
+ // it's ephemeral in which case the admin port is too.
if (port == null) port = _basePort == 0 ? 0 : _basePort - 1;
+
return AdminServer.bind(this, _hostname, port).then((server) => _adminServer =
server);
}
+
+ /// Binds a new port to serve assets from within [rootDirectory] in the
+ /// entrypoint package.
+ ///
+ /// Adds and watches the sources within that directory. Returns a [Future]
+ /// that completes to the bound server.
+ ///
+ /// If [rootDirectory] is already being served, returns that existing server.
Future<BarbackServer> serveDirectory(String rootDirectory) {
+ // See if there is already a server bound to the directory.
var directory = _directories[rootDirectory];
if (directory != null) {
return directory.server.then((server) {
@@ -88,15 +208,21 @@ class AssetEnvironment {
return server;
});
}
+
+ // See if the new directory overlaps any existing servers.
var overlapping = _directories.keys.where(
(directory) =>
path.isWithin(directory, rootDirectory) ||
path.isWithin(rootDirectory, directory)).toList();
+
if (overlapping.isNotEmpty) {
return new Future.error(
new OverlappingSourceDirectoryException(overlapping));
}
+
var port = _basePort;
+
+ // If not using an ephemeral port, find the lowest-numbered available one.
if (port != 0) {
var boundPorts =
_directories.values.map((directory) => directory.port).toSet();
@@ -104,9 +230,11 @@ class AssetEnvironment {
port++;
}
}
+
var sourceDirectory =
new SourceDirectory(this, rootDirectory, _hostname, port);
_directories[rootDirectory] = sourceDirectory;
+
return _provideDirectorySources(
rootPackage,
rootDirectory).then((subscription) {
@@ -114,6 +242,15 @@ class AssetEnvironment {
return sourceDirectory.serve();
});
}
+
+ /// Binds a new port to serve assets from within the "bin" directory of
+ /// [package].
+ ///
+ /// Adds the sources within that directory and then binds a server to it.
+ /// Unlike [serveDirectory], this works with packages that are not the
+ /// entrypoint.
+ ///
+ /// Returns a [Future] that completes to the bound server.
Future<BarbackServer> servePackageBinDirectory(String package) {
return _provideDirectorySources(
graph.packages[package],
@@ -121,6 +258,14 @@ class AssetEnvironment {
(_) =>
BarbackServer.bind(this, _hostname, 0, package: package, rootDirectory: "bin"));
}
+
+ /// Precompiles all of [packageName]'s executables to snapshots in
+ /// [directory].
+ ///
+ /// If [executableIds] is passed, only those executables are precompiled.
+ ///
+ /// Returns a map from executable name to path for the snapshots that were
+ /// successfully precompiled.
Future<Map<String, String>> precompileExecutables(String packageName,
String directory, {Iterable<AssetId> executableIds}) {
final completer0 = new Completer();
@@ -133,15 +278,15 @@ class AssetEnvironment {
servePackageBinDirectory(packageName).then((x0) {
try {
var server = x0;
- join2(x1) {
- completer0.complete(null);
+ join2() {
+ completer0.complete();
}
- finally0(cont0, v0) {
+ finally0(cont0) {
server.close();
- cont0(v0);
+ cont0();
}
- catch0(e1) {
- finally0(join2, null);
+ catch0(e1, s1) {
+ finally0(() => completer0.completeError(e1, s1));
}
try {
var precompiled = {};
@@ -160,7 +305,7 @@ class AssetEnvironment {
try {
var result = x0;
join0() {
- completer0.complete(null);
+ completer0.complete();
}
if (result.success) {
log.message(
@@ -169,43 +314,38 @@ class AssetEnvironment {
snapshotPath;
join0();
} else {
- completer0.completeError(
- new ApplicationException(
- log.yellow("Failed to precompile ${_formatExecutable(id)}:\n") +
- result.stderr.join('\n')));
+ throw new ApplicationException(
+ log.yellow("Failed to precompile ${_formatExecutable(id)}:\n") +
+ result.stderr.join('\n'));
+ join0();
}
- } catch (e0) {
- completer0.completeError(e0);
+ } catch (e0, s0) {
+ completer0.completeError(e0, s0);
}
- }, onError: (e1) {
- completer0.completeError(e1);
- });
- } catch (e2) {
- completer0.completeError(e2);
+ }, onError: completer0.completeError);
+ } catch (e, s) {
+ completer0.completeError(e, s);
}
});
return completer0.future;
- }))).then((x2) {
+ }))).then((x1) {
try {
- x2;
- finally0((v1) {
- completer0.complete(v1);
- }, precompiled);
- } catch (e2) {
- catch0(e2);
+ x1;
+ final v0 = precompiled;
+ finally0(() {
+ completer0.complete(v0);
+ });
+ } catch (e2, s2) {
+ catch0(e2, s2);
}
- }, onError: (e3) {
- catch0(e3);
- });
- } catch (e4) {
- catch0(e4);
+ }, onError: catch0);
+ } catch (e3, s3) {
+ catch0(e3, s3);
}
- } catch (e0) {
- completer0.completeError(e0);
+ } catch (e4, s4) {
+ completer0.completeError(e4, s4);
}
- }, onError: (e5) {
- completer0.completeError(e5);
- });
+ }, onError: completer0.completeError);
}
if (executableIds.isEmpty) {
completer0.complete([]);
@@ -219,18 +359,30 @@ class AssetEnvironment {
} else {
join0();
}
- } catch (e6) {
- completer0.completeError(e6);
+ } catch (e, s) {
+ completer0.completeError(e, s);
}
});
return completer0.future;
}
+
+ /// Returns the executable name for [id].
+ ///
+ /// [id] is assumed to be an executable in a bin directory. The return value
+ /// is intended for log output and may contain formatting.
String _formatExecutable(AssetId id) =>
log.bold("${id.package}:${path.basenameWithoutExtension(id.path)}");
+
+ /// Stops the server bound to [rootDirectory].
+ ///
+ /// Also removes any source files within that directory from barback. Returns
+ /// the URL of the unbound server, of `null` if [rootDirectory] was not
+ /// bound to a server.
Future<Uri> unserveDirectory(String rootDirectory) {
log.fine("Unserving $rootDirectory.");
var directory = _directories.remove(rootDirectory);
if (directory == null) return new Future.value();
+
return directory.server.then((server) {
var url = server.url;
return directory.close().then((_) {
@@ -239,10 +391,19 @@ class AssetEnvironment {
});
});
}
+
+ /// Gets the source directory that contains [assetPath] within the entrypoint
+ /// package.
+ ///
+ /// If [assetPath] is not contained within a source directory, this throws
+ /// an exception.
String getSourceDirectoryContaining(String assetPath) =>
_directories.values.firstWhere(
(dir) => path.isWithin(dir.directory, assetPath)).directory;
+
+ /// Return all URLs serving [assetPath] in this environment.
Future<List<Uri>> getUrlsForAssetPath(String assetPath) {
+ // Check the three (mutually-exclusive) places the path could be pointing.
return _lookUpPathInServerRoot(assetPath).then((urls) {
if (urls.isNotEmpty) return urls;
return _lookUpPathInPackagesDirectory(assetPath);
@@ -251,7 +412,12 @@ class AssetEnvironment {
return _lookUpPathInDependency(assetPath);
});
}
+
+ /// Look up [assetPath] in the root directories of servers running in the
+ /// entrypoint package.
Future<List<Uri>> _lookUpPathInServerRoot(String assetPath) {
+ // Find all of the servers whose root directories contain the asset and
+ // generate appropriate URLs for each.
return Future.wait(
_directories.values.where(
(dir) => path.isWithin(dir.directory, assetPath)).map((dir) {
@@ -260,6 +426,8 @@ class AssetEnvironment {
(server) => server.url.resolveUri(path.toUri(relativePath)));
}));
}
+
+ /// Look up [assetPath] in the "packages" directory in the entrypoint package.
Future<List<Uri>> _lookUpPathInPackagesDirectory(String assetPath) {
var components = path.split(path.relative(assetPath));
if (components.first != "packages") return new Future.value([]);
@@ -269,11 +437,15 @@ class AssetEnvironment {
(server) => server.url.resolveUri(path.toUri(assetPath)));
}));
}
+
+ /// Look up [assetPath] in the "lib" or "asset" directory of a dependency
+ /// package.
Future<List<Uri>> _lookUpPathInDependency(String assetPath) {
for (var packageName in graph.packages.keys) {
var package = graph.packages[packageName];
var libDir = package.path('lib');
var assetDir = package.path('asset');
+
var uri;
if (path.isWithin(libDir, assetPath)) {
uri = path.toUri(
@@ -284,12 +456,19 @@ class AssetEnvironment {
} else {
continue;
}
+
return Future.wait(_directories.values.map((dir) {
return dir.server.then((server) => server.url.resolveUri(uri));
}));
}
+
return new Future.value([]);
}
+
+ /// Given a URL to an asset served by this environment, returns the ID of the
+ /// asset that would be accessed by that URL.
+ ///
+ /// If no server can serve [url], completes to `null`.
Future<AssetId> getAssetIdForUrl(Uri url) {
return Future.wait(
_directories.values.map((dir) => dir.server)).then((servers) {
@@ -302,28 +481,58 @@ class AssetEnvironment {
return server.urlToId(url);
});
}
+
+ /// Determines if [sourcePath] is contained within any of the directories in
+ /// the root package that are visible to this build environment.
bool containsPath(String sourcePath) {
var directories = ["lib"];
directories.addAll(_directories.keys);
return directories.any((dir) => path.isWithin(dir, sourcePath));
}
+
+ /// Pauses sending source asset updates to barback.
void pauseUpdates() {
+ // Cannot pause while already paused.
assert(_modifiedSources == null);
+
_modifiedSources = new Set<AssetId>();
}
+
+ /// Sends any pending source updates to barback and begins the asynchronous
+ /// build process.
void resumeUpdates() {
+ // Cannot resume while not paused.
assert(_modifiedSources != null);
+
barback.updateSources(_modifiedSources);
_modifiedSources = null;
}
+
+ /// Loads the assets and transformers for this environment.
+ ///
+ /// This transforms and serves all library and asset files in all packages in
+ /// the environment's package graph. It loads any transformer plugins defined
+ /// in packages in [graph] and re-runs them as necessary when any input files
+ /// change.
+ ///
+ /// If [useDart2JS] is `true`, then the [Dart2JSTransformer] is implicitly
+ /// added to end of the root package's transformer phases.
+ ///
+ /// If [entrypoints] is passed, only transformers necessary to run those
+ /// entrypoints will be loaded.
+ ///
+ /// Returns a [Future] that completes once all inputs and transformers are
+ /// loaded.
Future _load({Iterable<AssetId> entrypoints, bool useDart2JS}) {
return log.progress("Initializing barback", () {
final completer0 = new Completer();
scheduleMicrotask(() {
try {
- var containsDart2JS = graph.entrypoint.root.pubspec.transformers.any(
- ((transformers) =>
- transformers.any((config) => config.id.package == '\$dart2js')));
+ var containsDart2JS =
+ graph.entrypoint.root.pubspec.transformers.any(((transformers) {
+ return transformers.any(
+ (config) => config.id.package == '\$dart2js');
+ }));
join0() {
BarbackServer.bind(this, _hostname, 0).then((x0) {
try {
@@ -369,21 +578,19 @@ class AssetEnvironment {
try {
x0;
transformerServer.close();
- completer0.complete(null);
- } catch (e0) {
- completer0.completeError(e0);
+ completer0.complete();
+ } catch (e0, s0) {
+ completer0.completeError(e0, s0);
}
- }, onError: (e1) {
- completer0.completeError(e1);
- });
- } catch (e2) {
- completer0.completeError(e2);
+ }, onError: completer0.completeError);
+ } catch (e, s) {
+ completer0.completeError(e, s);
}
});
return completer0.future;
}), fine: true));
- } catch (e0) {
- completer0.completeError(e0);
+ } catch (e, s) {
+ completer0.completeError(e, s);
}
});
return completer0.future;
@@ -391,25 +598,19 @@ class AssetEnvironment {
[errorStream, barback.results, transformerServer.results]).then((x2) {
try {
x2;
- completer0.complete(null);
- } catch (e2) {
- completer0.completeError(e2);
+ completer0.complete();
+ } catch (e0, s0) {
+ completer0.completeError(e0, s0);
}
- }, onError: (e3) {
- completer0.completeError(e3);
- });
- } catch (e1) {
- completer0.completeError(e1);
+ }, onError: completer0.completeError);
+ } catch (e1, s1) {
+ completer0.completeError(e1, s1);
}
- }, onError: (e4) {
- completer0.completeError(e4);
- });
- } catch (e0) {
- completer0.completeError(e0);
+ }, onError: completer0.completeError);
+ } catch (e2, s2) {
+ completer0.completeError(e2, s2);
}
- }, onError: (e5) {
- completer0.completeError(e5);
- });
+ }, onError: completer0.completeError);
}
if (!containsDart2JS && useDart2JS) {
_builtInTransformers.addAll(
@@ -418,13 +619,17 @@ class AssetEnvironment {
} else {
join0();
}
- } catch (e6) {
- completer0.completeError(e6);
+ } catch (e, s) {
+ completer0.completeError(e, s);
}
});
return completer0.future;
}, fine: true);
}
+
+ /// Provides the public source assets in the environment to barback.
+ ///
+ /// If [watcherType] is not [WatcherType.NONE], enables watching on them.
Future _provideSources() {
final completer0 = new Completer();
scheduleMicrotask(() {
@@ -437,51 +642,64 @@ class AssetEnvironment {
_provideDirectorySources(package, "lib").then((x0) {
try {
x0;
- completer0.complete(null);
- } catch (e0) {
- completer0.completeError(e0);
+ completer0.complete();
+ } catch (e0, s0) {
+ completer0.completeError(e0, s0);
}
- }, onError: (e1) {
- completer0.completeError(e1);
- });
+ }, onError: completer0.completeError);
}
if (graph.isPackageStatic(package.name)) {
completer0.complete(null);
} else {
join0();
}
- } catch (e2) {
- completer0.completeError(e2);
+ } catch (e, s) {
+ completer0.completeError(e, s);
}
});
return completer0.future;
}))).then((x0) {
try {
x0;
- completer0.complete(null);
- } catch (e0) {
- completer0.completeError(e0);
+ completer0.complete();
+ } catch (e0, s0) {
+ completer0.completeError(e0, s0);
}
- }, onError: (e1) {
- completer0.completeError(e1);
- });
- } catch (e2) {
- completer0.completeError(e2);
+ }, onError: completer0.completeError);
+ } catch (e, s) {
+ completer0.completeError(e, s);
}
});
return completer0.future;
}
+
+ /// Provides all of the source assets within [dir] in [package] to barback.
+ ///
+ /// If [watcherType] is not [WatcherType.NONE], enables watching on them.
+ /// Returns the subscription to the watcher, or `null` if none was created.
Future<StreamSubscription<WatchEvent>>
_provideDirectorySources(Package package, String dir) {
log.fine("Providing sources for ${package.name}|$dir.");
+ // TODO(rnystrom): Handle overlapping directories. If two served
+ // directories overlap like so:
+ //
+ // $ pub serve example example/subdir
+ //
+ // Then the sources of the subdirectory will be updated and watched twice.
+ // See: #17454
if (_watcherType == WatcherType.NONE) {
_updateDirectorySources(package, dir);
return new Future.value();
}
+
+ // Watch the directory before listing is so we don't miss files that
+ // are added between the initial list and registering the watcher.
return _watchDirectorySources(package, dir).then((_) {
_updateDirectorySources(package, dir);
});
}
+
+ /// Updates barback with all of the files in [dir] inside [package].
void _updateDirectorySources(Package package, String dir) {
var ids = _listDirectorySources(package, dir);
if (_modifiedSources == null) {
@@ -490,6 +708,8 @@ class AssetEnvironment {
_modifiedSources.addAll(ids);
}
}
+
+ /// Removes all of the files in [dir] in the root package from barback.
void _removeDirectorySources(String dir) {
var ids = _listDirectorySources(rootPackage, dir);
if (_modifiedSources == null) {
@@ -498,32 +718,69 @@ class AssetEnvironment {
_modifiedSources.removeAll(ids);
}
}
+
+ /// Lists all of the source assets in [dir] inside [package].
+ ///
+ /// For large packages, listing the contents is a performance bottleneck, so
+ /// this is optimized for our needs in here instead of using the more general
+ /// but slower [listDir].
Iterable<AssetId> _listDirectorySources(Package package, String dir) {
+ // This is used in some performance-sensitive paths and can list many, many
+ // files. As such, it leans more havily towards optimization as opposed to
+ // readability than most code in pub. In particular, it avoids using the
+ // path package, since re-parsing a path is very expensive relative to
+ // string operations.
return package.listFiles(beneath: dir).map((file) {
+ // From profiling, path.relative here is just as fast as a raw substring
+ // and is correct in the case where package.dir has a trailing slash.
var relative = package.relative(file);
+
if (Platform.operatingSystem == 'windows') {
relative = relative.replaceAll("\\", "/");
}
+
var uri = new Uri(pathSegments: relative.split("/"));
return new AssetId(package.name, uri.toString());
});
}
+
+ /// Adds a file watcher for [dir] within [package], if the directory exists
+ /// and the package needs watching.
Future<StreamSubscription<WatchEvent>> _watchDirectorySources(Package package,
String dir) {
+ // If this package comes from a cached source, its contents won't change so
+ // we don't need to monitor it. `packageId` will be null for the
+ // application package, since that's not locked.
var packageId = graph.lockFile.packages[package.name];
if (packageId != null &&
graph.entrypoint.cache.sources[packageId.source] is CachedSource) {
return new Future.value();
}
+
var subdirectory = package.path(dir);
if (!dirExists(subdirectory)) return new Future.value();
+
+ // TODO(nweiz): close this watcher when [barback] is closed.
var watcher = _watcherType.create(subdirectory);
var subscription = watcher.events.listen((event) {
+ // Don't watch files symlinked into these directories.
+ // TODO(rnystrom): If pub gets rid of symlinks, remove this.
var parts = path.split(event.path);
if (parts.contains("packages")) return;
+
+ // Skip files that were (most likely) compiled from nearby ".dart"
+ // files. These are created by the Editor's "Run as JavaScript"
+ // command and are written directly into the package's directory.
+ // When pub's dart2js transformer then tries to create the same file
+ // name, we get a build error. To avoid that, just don't consider
+ // that file to be a source.
+ // TODO(rnystrom): Remove these when the Editor no longer generates
+ // .js files and users have had enough time that they no longer have
+ // these files laying around. See #15859.
if (event.path.endsWith(".dart.js")) return;
if (event.path.endsWith(".dart.js.map")) return;
if (event.path.endsWith(".dart.precompiled.js")) return;
+
var idPath = package.relative(event.path);
var id = new AssetId(package.name, path.toUri(idPath).toString());
if (event.type == ChangeType.REMOVE) {
@@ -538,17 +795,28 @@ class AssetEnvironment {
barback.updateSources([id]);
}
});
+
return watcher.ready.then((_) => subscription);
}
+
+ /// Returns the result of [futureCallback] unless any stream in [streams]
+ /// emits an error before it's done.
+ ///
+ /// If a stream does emit an error, that error is thrown instead.
+ /// [futureCallback] is a callback rather than a plain future to ensure that
+ /// [streams] are listened to before any code that might cause an error starts
+ /// running.
Future _withStreamErrors(Future futureCallback(), List<Stream> streams) {
var completer = new Completer.sync();
var subscriptions = streams.map(
(stream) => stream.listen((_) {}, onError: completer.completeError)).toList();
+
new Future.sync(futureCallback).then((_) {
if (!completer.isCompleted) completer.complete();
}).catchError((error, stackTrace) {
if (!completer.isCompleted) completer.completeError(error, stackTrace);
});
+
return completer.future.whenComplete(() {
for (var subscription in subscriptions) {
subscription.cancel();
@@ -556,68 +824,116 @@ class AssetEnvironment {
});
}
}
+
+/// Log [entry] using Pub's logging infrastructure.
+///
+/// Since both [LogEntry] objects and the message itself often redundantly
+/// show the same context like the file where an error occurred, this tries
+/// to avoid showing redundant data in the entry.
void _log(LogEntry entry) {
messageMentions(text) =>
entry.message.toLowerCase().contains(text.toLowerCase());
+
messageMentionsAsset(id) =>
messageMentions(id.toString()) ||
messageMentions(path.fromUri(entry.assetId.path));
+
var prefixParts = [];
+
+ // Show the level (unless the message mentions it).
if (!messageMentions(entry.level.name)) {
prefixParts.add("${entry.level} from");
}
+
+ // Show the transformer.
prefixParts.add(entry.transform.transformer);
+
+ // Mention the primary input of the transform unless the message seems to.
if (!messageMentionsAsset(entry.transform.primaryId)) {
prefixParts.add("on ${entry.transform.primaryId}");
}
+
+ // If the relevant asset isn't the primary input, mention it unless the
+ // message already does.
if (entry.assetId != entry.transform.primaryId &&
!messageMentionsAsset(entry.assetId)) {
prefixParts.add("with input ${entry.assetId}");
}
+
var prefix = "[${prefixParts.join(' ')}]:";
var message = entry.message;
if (entry.span != null) {
message = entry.span.message(entry.message);
}
+
switch (entry.level) {
case LogLevel.ERROR:
log.error("${log.red(prefix)}\n$message");
break;
+
case LogLevel.WARNING:
log.warning("${log.yellow(prefix)}\n$message");
break;
+
case LogLevel.INFO:
log.message("${log.cyan(prefix)}\n$message");
break;
+
case LogLevel.FINE:
log.fine("${log.gray(prefix)}\n$message");
break;
}
}
+
+/// Exception thrown when trying to serve a new directory that overlaps one or
+/// more directories already being served.
class OverlappingSourceDirectoryException implements Exception {
+ /// The relative paths of the directories that overlap the one that could not
+ /// be served.
final List<String> overlappingDirectories;
+
OverlappingSourceDirectoryException(this.overlappingDirectories);
}
+
+/// An enum describing different modes of constructing a [DirectoryWatcher].
abstract class WatcherType {
+ /// A watcher that automatically chooses its type based on the operating
+ /// system.
static const AUTO = const _AutoWatcherType();
+
+ /// A watcher that always polls the filesystem for changes.
static const POLLING = const _PollingWatcherType();
+
+ /// No directory watcher at all.
static const NONE = const _NoneWatcherType();
+
+ /// Creates a new DirectoryWatcher.
DirectoryWatcher create(String directory);
+
String toString();
}
+
class _AutoWatcherType implements WatcherType {
const _AutoWatcherType();
+
DirectoryWatcher create(String directory) => new DirectoryWatcher(directory);
+
String toString() => "auto";
}
+
class _PollingWatcherType implements WatcherType {
const _PollingWatcherType();
+
DirectoryWatcher create(String directory) =>
new PollingDirectoryWatcher(directory);
+
String toString() => "polling";
}
+
class _NoneWatcherType implements WatcherType {
const _NoneWatcherType();
+
DirectoryWatcher create(String directory) => null;
+
String toString() => "none";
}

Powered by Google App Engine
This is Rietveld 408576698