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 |
index c6205bdec1094692c91a84ee46d58c52bc956475..2f9414b6c8029eea2781815a26d8ceaa8ed68b23 100644 |
--- a/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart |
+++ b/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart |
@@ -12,6 +12,7 @@ import 'load_transformers.dart'; |
import 'rewrite_import_transformer.dart'; |
import 'server.dart'; |
import 'watch_sources.dart'; |
+import '../barback.dart'; |
import '../utils.dart'; |
/// Loads all transformers depended on by packages in [graph]. |
@@ -120,18 +121,27 @@ Future loadAllTransformers(BarbackServer server, PackageGraph graph) { |
/// 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) { |
+ // Iterate through the packages in a deterministic order so that if there are |
+ // multiple cycles we choose which to print consistently. |
+ var packages = ordered(graph.packages.values.map((package) => package.name)); |
+ for (var package in packages) { |
// This package's transformer dependencies are also ordering dependencies. |
- var deps = _transformerDeps(graph, package.name); |
- deps.remove(package.name); |
+ var deps = _transformerDeps(graph, package); |
+ deps.remove(package); |
// 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)); |
+ var transitivePackageDeps = graph.transitiveDependencies(package) |
+ .map((package) => package.name); |
+ for (var packageDep in ordered(transitivePackageDeps)) { |
+ var transformerDeps = _transformerDeps(graph, packageDep); |
+ if (transformerDeps.contains(package)) { |
+ throw _cycleError(graph, package, packageDep); |
+ } |
+ deps.addAll(transformerDeps); |
} |
- orderingDeps[package.name] = deps; |
+ orderingDeps[package] = deps; |
} |
- // TODO(nweiz): check for cycles in orderingDeps. |
+ |
return orderingDeps; |
} |
@@ -140,6 +150,33 @@ Set<String> _transformerDeps(PackageGraph graph, String package) => |
unionAll(graph.packages[package].pubspec.transformers) |
.map((id) => id.package).toSet(); |
+/// Returns an [ApplicationException] describing an ordering dependency cycle |
+/// detected in [graph]. |
+/// |
+/// [dependee] and [depender] should be the names of two packages known to be in |
+/// the cycle. In addition, [depender] should have a transformer dependency on |
+/// [dependee]. |
+ApplicationException _cycleError(PackageGraph graph, String dependee, |
+ String depender) { |
+ assert(_transformerDeps(graph, depender).contains(dependee)); |
+ |
+ var simpleGraph = mapMapValues(graph.packages, |
+ (_, package) => package.dependencies.map((dep) => dep.name).toList()); |
+ var path = shortestPath(simpleGraph, dependee, depender); |
+ path.add(dependee); |
+ return new ApplicationException("Transformer cycle detected:\n" + |
+ pairs(path).map((pair) { |
+ var transformers = unionAll(graph.packages[pair.first].pubspec.transformers) |
+ .where((id) => id.package == pair.last) |
+ .map(idToLibraryIdentifier).toList(); |
+ if (transformers.isEmpty) { |
+ return " ${pair.first} depends on ${pair.last}"; |
+ } else { |
+ return " ${pair.first} is transformed by ${toSentence(transformers)}"; |
+ } |
+ }).join("\n")); |
+} |
+ |
/// 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) { |
@@ -166,20 +203,15 @@ class _TransformerLoader { |
/// The packages that use each transformer id. |
/// |
/// Used for error reporting. |
- final _transformerUsers = new Map<AssetId, List<String>>(); |
+ final _transformerUsers = new Map<AssetId, Set<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); |
+ _transformerUsers.putIfAbsent(id, () => new Set<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]. |
@@ -196,13 +228,9 @@ class _TransformerLoader { |
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(', ')}."); |
+ "No transformers were defined in ${idToPackageUri(id)},\n" |
+ "required by ${ordered(_transformerUsers[id]).join(', ')}."); |
}); |
} |