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

Unified Diff: sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart

Issue 24016002: Support transformers that depend on other transformers. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes. Created 7 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 side-by-side diff with in-line comments
Download patch
Index: sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart
diff --git a/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart b/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart
new file mode 100644
index 0000000000000000000000000000000000000000..240c7c44838fd4e5645e442d22f92c7611b9bea5
--- /dev/null
+++ b/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart
@@ -0,0 +1,217 @@
+// Copyright (c) 2013, 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.load_all_transformers;
+
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+
+import 'load_transformers.dart';
+import 'rewrite_import_transformer.dart';
+import 'server.dart';
+import 'watch_sources.dart';
+import '../utils.dart';
+
+/// Loads all transformers depended on by packages in [graph].
+///
+/// This uses [server] to serve the Dart files from which transformers are
+/// loaded, then adds the transformers to `server.barback`.
+Future loadAllTransformers(BarbackServer server, PackageGraph graph) {
+ // In order to determine in what order we should load transformers, we need to
+ // know which transformers depend on which others. This is different than
+ // normal package dependencies. Let's begin with some terminology:
+ //
+ // * If package A is transformed by package B, we say A has a "transformer
+ // dependency" on B.
+ // * If A imports B we say A has a "package dependency" on B.
+ // * If A needs B's transformers to be loaded in order to load A's
+ // transformers, we say A has an "ordering dependency" on B.
+ //
+ // In particular, an ordering dependency is defined as follows:
+ //
+ // * If A has a transformer dependency on B, A also has an ordering dependency
+ // on B.
+ // * If A has a transitive package dependency on B and B has a transformer
+ // dependency on C, A has an ordering dependency on C.
+ //
+ // The order that transformers are loaded is determined by each package's
+ // ordering dependencies. We treat the packages as a directed acyclic[1] graph
+ // where each package is a node and the ordering dependencies are the edges
+ // (that is, the packages form a partially ordered set). We then load[2]
+ // packages in the topological sort order of this graph.
Bob Nystrom 2013/09/09 23:34:57 Nit but "the" -> "a". There are often multiple val
nweiz 2013/09/10 00:47:44 Done.
+ //
+ // [1] TODO(nweiz): support cycles in some cases.
+ //
+ // [2] We use "loading a package" as a shorthand for loading that package's
+ // transformers.
+
+ // Add a rewrite transformer for each package, so that we can resolve
+ // "package:" imports while loading transformers.
+ var rewrite = new RewriteImportTransformer();
+ for (var package in graph.packages.values) {
+ server.barback.updateTransformers(package.name, [[rewrite]]);
+ }
+
+ var orderingDeps = _computeOrderingDeps(graph);
+ var packageTransformers = _computePackageTransformers(graph);
+
+ var loader = new _TransformerLoader(server, graph);
+
+ // The packages on which no packages have ordering dependencies -- that is,
+ // the packages that don't need to be loaded before any other packages. These
+ // packages will be loaded last, since all of their ordering dependencies need
+ // to be loaded before they're loaded. However, they'll be traversed by
+ // [loadPackage] first.
+ var rootPackages = graph.packages.keys.toSet()
+ .difference(unionAll(orderingDeps.values));
+
+ // The Futures for packages that have been loaded or are being actively loaded
+ // by [loadPackage]. Once one of these Futures is complete, the transformers
+ // for that package will all be available from [loader].
+ var loadingPackages = new Map<String, Future>();
+
+ // A helper function that loads all the transformers that [package] uses, then
+ // all the transformers that [package] defines.
+ Future loadPackage(String package) {
+ if (loadingPackages.containsKey(package)) return loadingPackages[package];
+
+ // First, load each package upon which [package] has an ordering dependency.
+ var future = Future.wait(orderingDeps[package].map(loadPackage)).then((_) {
+ // Go through the transformers used by [package] phase-by-phase. If any
+ // phase uses a transformer defined in [package] itself, that transform
+ // should be loaded after running all previous phases.
+ var transformers = [[rewrite]];
+ return Future.forEach(graph.packages[package].pubspec.transformers,
+ (phase) {
+ return Future.wait(phase.where((id) => id.package == package)
+ .map(loader.load)).then((_) {
+ transformers.add(unionAll(phase.map(
+ (id) => loader.transformersFor(id))));
+ server.barback.updateTransformers(package, transformers);
+ });
+ }).then((_) {
+ // Now that we've applied all the transformers used by [package] via
+ // [Barback.updateTransformers], we load any transformers defined in
+ // [package] but used elsewhere.
+ return Future.wait(packageTransformers[package].map(loader.load));
+ });
+ });
+ loadingPackages[package] = future;
+ return future;
+ }
+
+ return Future.wait(rootPackages.map(loadPackage)).then((_) {
+ /// Reset the transformers for each package to get rid of [rewrite], which
+ /// is no longer needed.
+ for (var package in graph.packages.values) {
+ var phases = package.pubspec.transformers.map((phase) {
+ return unionAll(phase.map((id) => loader.transformersFor(id)));
+ });
+ server.barback.updateTransformers(package.name, phases);
+ }
+ });
+}
+
+/// Computes and returns the graph of ordering dependencies for [graph].
+///
+/// This graph is in the form of a map whose keys are packages and whose values
+/// are those packages' ordering dependencies.
+Map<String, Set<String>> _computeOrderingDeps(PackageGraph graph) {
+ var orderingDeps = new Map<String, Set<String>>();
+ for (var package in graph.packages.values) {
+ // This package's transformer dependencies are also ordering dependencies.
+ var deps = _transformerDeps(graph, package.name);
+ deps.remove(package.name);
+ // The transformer dependencies of this package's transitive package
+ // dependencies are also ordering dependencies for this package.
+ for (var packageDep in graph.transitiveDependencies(package.name)) {
+ deps.addAll(_transformerDeps(graph, packageDep.name));
+ }
+ orderingDeps[package.name] = deps;
+ }
+ // TODO(nweiz): check for cycles in orderingDeps.
+ return orderingDeps;
+}
+
+/// Returns the set of transformer dependencies for [package].
+Set<String> _transformerDeps(PackageGraph graph, String package) =>
+ unionAll(graph.packages[package].pubspec.transformers)
+ .map((id) => id.package).toSet();
+
+/// Returns a map from each package name in [graph] to the asset ids of all
+/// transformers exposed by that package and used by other packages.
+Map<String, Set<AssetId>> _computePackageTransformers(PackageGraph graph) {
+ var packageTransformers = listToMap(graph.packages.values,
+ (package) => package.name, (_) => new Set<AssetId>());
+ for (var package in graph.packages.values) {
+ for (var phase in package.pubspec.transformers) {
+ for (var id in phase) {
+ packageTransformers[id.package].add(id);
+ }
+ }
+ }
+ return packageTransformers;
+}
+
+/// A class that loads transformers defined in specific files.
+class _TransformerLoader {
+ final BarbackServer _server;
+
+ /// The loaded transformers defined in the library identified by each asset
+ /// id.
+ final _transformers = new Map<AssetId, Set<Transformer>>();
+
+ /// The packages that use each transformer id.
+ ///
+ /// Used for error reporting.
+ final _transformerUsers = new Map<AssetId, List<String>>();
+
+ _TransformerLoader(this._server, PackageGraph graph) {
+ for (var package in graph.packages.values) {
+ for (var id in unionAll(package.pubspec.transformers)) {
+ _transformerUsers.putIfAbsent(id, () => <String>[]).add(package.name);
+ }
+ }
+
+ // Ensure that the transformer users are printed in a deterministic order if
+ // an error occurs.
+ for (var list in _transformerUsers.values) {
+ list.sort();
+ }
+ }
+
+ /// Loads the transformer(s) defined in the asset [id].
+ ///
+ /// Once the returned future completes, these transformers can be retrieved
+ /// using [transformersFor]. If [id] doesn't define any transformers, this
+ /// will complete to an error.
+ Future load(AssetId id) {
+ if (_transformers.containsKey(id)) return new Future.value();
+
+ return loadTransformers(_server, id).then((transformers) {
+ if (!transformers.isEmpty) {
+ _transformers[id] = transformers;
+ return;
+ }
+
+ // The path is parsed from a library identifier in the pubspec, so it
+ // should always refer to a library.
+ assert(id.path.startsWith('lib/'));
+ var path = id.path.replaceFirst('lib/', '');
+ throw new ApplicationException(
+ "No transformers were defined in package:${id.package}/$path,\n"
+ "required by ${_transformerUsers[id].join(', ')}.");
+ });
+ }
+
+ /// Returns the set of transformers for [id].
+ ///
+ /// It's an error to call this before [load] is called with [id] and the
+ /// future it returns has completed.
+ Set<Transformers> transformersFor(AssetId id) {
+ assert(_transformers.containsKey(id));
+ return _transformers[id];
+ }
+}
« no previous file with comments | « sdk/lib/_internal/pub/lib/src/barback.dart ('k') | sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698