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

Unified 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, 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 side-by-side diff with in-line comments
Download patch
Index: sdk/lib/_internal/pub/lib/src/barback/build_environment.dart
diff --git a/sdk/lib/_internal/pub/lib/src/barback/build_environment.dart b/sdk/lib/_internal/pub/lib/src/barback/build_environment.dart
new file mode 100644
index 0000000000000000000000000000000000000000..9e1467b071a00811e382b5d860bd081a349ee377
--- /dev/null
+++ b/sdk/lib/_internal/pub/lib/src/barback/build_environment.dart
@@ -0,0 +1,395 @@
+// 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.build_environment;
+
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+import 'package:path/path.dart' as path;
+import 'package:stack_trace/stack_trace.dart';
+import 'package:watcher/watcher.dart';
+
+import '../entrypoint.dart';
+import '../io.dart';
+import '../log.dart' as log;
+import '../package.dart';
+import '../package_graph.dart';
+import '../utils.dart';
+import 'dart_forwarding_transformer.dart';
+import 'dart2js_transformer.dart';
+import 'load_all_transformers.dart';
+import 'pub_package_provider.dart';
+import 'server.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 build.
+class BuildEnvironment {
+ /// Creates a new build environment for working with the assets used by
+ /// [entrypoint] and its dependencies.
+ ///
+ /// Spawns an HTTP server on [hostname] and [port]. Loads all used
+ /// transformers using [mode] (including dart2js if [useDart2JS] is true).
+ ///
+ /// Includes [buildDirectories] in the root package, as well as "lib" and
+ /// "asset".
+ ///
+ /// If [watcherType] is not [WatcherType.NONE], watches source assets for
+ /// modification.
+ ///
+ /// Returns a [Future] that completes to the environment once the inputs,
+ /// transformers, and server are loaded and ready.
+ static Future<BuildEnvironment> create(Entrypoint entrypoint,
+ String hostname, int port, BarbackMode mode, WatcherType watcherType,
+ Set<String> buildDirectories,
+ {bool useDart2JS: true}) {
+ return entrypoint.loadPackageGraph().then((graph) {
+ var barback = new Barback(new PubPackageProvider(graph));
+ barback.log.listen(_log);
+
+ return BarbackServer.bind(hostname, port, barback,
+ graph.entrypoint.root.name).then((server) {
+ var environment = new BuildEnvironment._(graph, server, mode,
+ watcherType, buildDirectories);
+
+ // If the entrypoint package manually configures the dart2js
+ // transformer, don't include it in the built-in transformer list.
+ //
+ // TODO(nweiz): if/when we support more built-in transformers, make
+ // this more general.
+ var containsDart2JS = graph.entrypoint.root.pubspec.transformers
+ .any((transformers) => transformers
+ .any((id) => id.package == '\$dart2js'));
+
+ if (!containsDart2JS && useDart2JS) {
+ environment._builtInTransformers.addAll([
+ new Dart2JSTransformer(environment, mode),
+ new DartForwardingTransformer(mode)
+ ]);
+ }
+
+ return environment._load(barback).then((_) => environment);
+ });
+ });
+ }
+
+ /// The server serving this environment's assets.
+ final BarbackServer server;
+
+ /// The [Barback] instance used to process assets in this environment.
+ Barback get barback => server.barback;
+
+ /// The root package being built.
+ Package get rootPackage => graph.entrypoint.root;
+
+ /// The underlying [PackageGraph] being built.
+ 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 set of top-level directories in the entrypoint package that should be
+ /// built.
+ final Set<String> _buildDirectories;
+
+ BuildEnvironment._(this.graph, this.server, this.mode, this._watcherType,
+ this._buildDirectories);
+
+ /// 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;
+ }
+
+ /// Creates a [BarbackServer] 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.
+ ///
+ /// Returns a [Future] that completes once all inputs and transformers are
+ /// loaded.
+ Future _load(Barback barback) {
+ return _provideSources(barback).then((_) {
+ var completer = new Completer();
+
+ // If any errors get emitted either by barback or by the server,
+ // including non-programmatic barback errors, they should take down the
+ // whole program.
+ var subscriptions = [
+ server.barback.errors.listen((error) {
+ if (error is TransformerException) error = error.error;
+ if (!completer.isCompleted) {
+ completer.completeError(error, new Chain.current());
+ }
+ }),
+ server.barback.results.listen((_) {}, onError: (error, stackTrace) {
+ if (completer.isCompleted) return;
+ completer.completeError(error, stackTrace);
+ }),
+ server.results.listen((_) {}, onError: (error, stackTrace) {
+ if (completer.isCompleted) return;
+ completer.completeError(error, stackTrace);
+ })
+ ];
+
+ loadAllTransformers(this).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();
+ }
+ });
+ });
+ }
+
+ /// Provides all of the source assets in the environment to barback.
+ ///
+ /// If [watcherType] is not [WatcherType.NONE], enables watching on them.
+ Future _provideSources(Barback barback) {
+ if (_watcherType != WatcherType.NONE) {
+ return _watchSources(barback);
+ }
+
+ return syncFuture(() {
+ _loadSources(barback);
+ });
+ }
+
+ /// Provides all of the source assets in the environment to barback.
+ void _loadSources(Barback barback) {
+ for (var package in graph.packages.values) {
+ barback.updateSources(_listAssets(graph.entrypoint, package));
+ }
+ }
+
+ /// Adds all of the source assets in this environment to barback and then
+ /// watches the public directories for changes.
+ ///
+ /// Returns a Future that completes when the sources are loaded and the
+ /// watchers are active.
+ Future _watchSources(Barback barback) {
+ return Future.wait(graph.packages.values.map((package) {
+ // 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].shouldCache) {
+ barback.updateSources(_listAssets(graph.entrypoint, package));
+ return new Future.value();
+ }
+
+ // Watch the visible package directories for changes.
+ return Future.wait(_getPublicDirectories(graph.entrypoint, package)
+ .map((name) {
+ var subdirectory = path.join(package.dir, name);
+ if (!dirExists(subdirectory)) return new Future.value();
+
+ // TODO(nweiz): close these watchers when [barback] is closed.
+ var watcher = _watcherType.create(subdirectory);
+ 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") || parts.contains("assets")) return;
+
+ // Skip ".js" 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 this when the Editor no longer generates
+ // .js files. See #15859.
+ if (event.path.endsWith(".dart.js")) return;
+
+ var id = new AssetId(package.name,
+ path.relative(event.path, from: package.dir));
+ if (event.type == ChangeType.REMOVE) {
+ barback.removeSources([id]);
+ } else {
+ barback.updateSources([id]);
+ }
+ });
+ return watcher.ready;
+ })).then((_) {
+ barback.updateSources(_listAssets(graph.entrypoint, package));
+ });
+ }));
+ }
+
+ /// Lists all of the visible files in [package].
+ ///
+ /// This is the recursive contents of the "asset" and "lib" directories (if
+ /// present). If [package] is the entrypoint package, it also includes the
+ /// contents of "web".
+ List<AssetId> _listAssets(Entrypoint entrypoint, Package package) {
+ var files = <AssetId>[];
+
+ for (var dirPath in _getPublicDirectories(entrypoint, package)) {
+ var dir = path.join(package.dir, dirPath);
+ if (!dirExists(dir)) continue;
+ for (var entry in listDir(dir, recursive: true)) {
+ // Ignore "packages" symlinks if there.
+ if (path.split(entry).contains("packages")) continue;
+
+ // Skip directories.
+ if (!fileExists(entry)) continue;
+
+ // Skip ".js" 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 this when the Editor no longer generates .js
+ // files. See #15859.
+ if (entry.endsWith(".dart.js")) continue;
+
+ var id = new AssetId(package.name,
+ path.relative(entry, from: package.dir));
+ files.add(id);
+ }
+ }
+
+ return files;
+ }
+
+ /// Gets the names of the top-level directories in [package] whose contents
+ /// should be provided as source assets.
+ Iterable<String> _getPublicDirectories(Entrypoint entrypoint,
+ Package package) {
+ var directories = ["asset", "lib"];
+
+ if (package.name == entrypoint.root.name) {
+ directories.addAll(_buildDirectories);
+ }
+
+ return directories;
+ }
+}
+
+/// 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(String text) {
+ return entry.message.toLowerCase().contains(text.toLowerCase());
+ }
+
+ 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 (!messageMentions(entry.transform.primaryId.path)) {
+ 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 &&
+ !messageMentions(entry.assetId.path)) {
+ prefixParts.add("with input ${entry.assetId}");
+ }
+
+ var prefix = "[${prefixParts.join(' ')}]:";
+ var message = entry.message;
+ if (entry.span != null) {
+ message = entry.span.getLocationMessage(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;
+ }
+}
+
+/// 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";
+}
« 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