| Index: sdk/lib/_internal/pub/lib/src/validator/dependency.dart
|
| diff --git a/sdk/lib/_internal/pub/lib/src/validator/dependency.dart b/sdk/lib/_internal/pub/lib/src/validator/dependency.dart
|
| index 19f571d1f19d53e3c93f4e1c572fec057ce3bc0c..51c3e36aef8741d2362304a32dc70c908ef76203 100644
|
| --- a/sdk/lib/_internal/pub/lib/src/validator/dependency.dart
|
| +++ b/sdk/lib/_internal/pub/lib/src/validator/dependency.dart
|
| @@ -11,20 +11,39 @@ import 'package:pub_semver/pub_semver.dart';
|
| import '../entrypoint.dart';
|
| import '../log.dart' as log;
|
| import '../package.dart';
|
| +import '../utils.dart';
|
| import '../validator.dart';
|
|
|
| +/// The range of all pub versions that don't support `^` version constraints.
|
| +final _preCaretPubVersions = new VersionConstraint.parse("<1.8.0-dev.3.0");
|
| +
|
| +// TODO(nweiz): replace this with "^1.8.0" for the 1.8 release.
|
| +/// The range of all pub versions that do support `^` version constraints.
|
| +///
|
| +/// This is intersected with the user's SDK constraint to provide a suggested
|
| +/// constraint.
|
| +final _postCaretPubVersions = new VersionConstraint.parse("^1.8.0-dev.3.0");
|
| +
|
| /// A validator that validates a package's dependencies.
|
| class DependencyValidator extends Validator {
|
| + /// Whether the SDK constraint guarantees that `^` version constraints are
|
| + /// safe.
|
| + bool get _caretAllowed => entrypoint.root.pubspec.environment.sdkVersion
|
| + .intersect(_preCaretPubVersions).isEmpty;
|
| +
|
| DependencyValidator(Entrypoint entrypoint)
|
| : super(entrypoint);
|
|
|
| - Future validate() {
|
| - return Future.forEach(entrypoint.root.pubspec.dependencies, (dependency) {
|
| - if (dependency.source != "hosted") {
|
| - return _warnAboutSource(dependency);
|
| - }
|
| + Future validate() async {
|
| + var caretDeps = [];
|
|
|
| - if (dependency.constraint.isAny) {
|
| + // TODO(nweiz): Replace this with a real for/in loop when we update
|
| + // async_await.
|
| + await Future.forEach(entrypoint.root.pubspec.dependencies,
|
| + (dependency) async {
|
| + if (dependency.source != "hosted") {
|
| + await _warnAboutSource(dependency);
|
| + } else if (dependency.constraint.isAny) {
|
| _warnAboutNoConstraint(dependency);
|
| } else if (dependency.constraint is Version) {
|
| _warnAboutSingleVersionConstraint(dependency);
|
| @@ -34,10 +53,16 @@ class DependencyValidator extends Validator {
|
| } else if (dependency.constraint.max == null) {
|
| _warnAboutNoConstraintUpperBound(dependency);
|
| }
|
| - }
|
|
|
| - return new Future.value();
|
| + if (dependency.constraint.toString().startsWith("^")) {
|
| + caretDeps.add(dependency);
|
| + }
|
| + }
|
| });
|
| +
|
| + if (caretDeps.isNotEmpty && !_caretAllowed) {
|
| + _errorAboutCaretConstraints(caretDeps);
|
| + }
|
| }
|
|
|
| /// Warn that dependencies should use the hosted source.
|
| @@ -128,27 +153,63 @@ class DependencyValidator extends Validator {
|
|
|
| /// Warn that dependencies should have upper bounds on their constraints.
|
| void _warnAboutNoConstraintUpperBound(PackageDep dep) {
|
| + var constraint;
|
| + if ((dep.constraint as VersionRange).includeMin) {
|
| + constraint = _constraintForVersion(dep.constraint.min);
|
| + } else {
|
| + constraint = '"${dep.constraint} '
|
| + '<${(dep.constraint as VersionRange).min.nextBreaking}"';
|
| + }
|
| +
|
| warnings.add(
|
| 'Your dependency on "${dep.name}" should have an upper bound. For '
|
| 'example:\n'
|
| '\n'
|
| 'dependencies:\n'
|
| - ' ${dep.name}: "${dep.constraint} '
|
| - '${_upperBoundForVersion((dep.constraint as VersionRange).min)}"\n'
|
| + ' ${dep.name}: $constraint\n'
|
| '\n'
|
| 'Without an upper bound, you\'re promising to support '
|
| '${log.bold("all")} future versions of ${dep.name}.');
|
| }
|
|
|
| + /// Emits an error for any version constraints that use `^` without an
|
| + /// appropriate SDK constraint.
|
| + void _errorAboutCaretConstraints(List<PackageDeps> caretDeps) {
|
| + var newSdkConstraint = entrypoint.root.pubspec.environment.sdkVersion
|
| + .intersect(_postCaretPubVersions);
|
| +
|
| + if (newSdkConstraint.isEmpty) newSdkConstraint = _postCaretPubVersions;
|
| +
|
| + var buffer = new StringBuffer(
|
| + "Older versions of pub don't support ^ version constraints.\n"
|
| + "Make sure your SDK constraint excludes those old versions:\n"
|
| + "\n"
|
| + "environment:\n"
|
| + " sdk: \"$newSdkConstraint\"\n"
|
| + "\n");
|
| +
|
| + if (caretDeps.length == 1) {
|
| + buffer.writeln("Or use a fully-expanded constraint:");
|
| + } else {
|
| + buffer.writeln("Or use fully-expanded constraints:");
|
| + }
|
| +
|
| + buffer.writeln();
|
| + buffer.writeln("dependencies:");
|
| +
|
| + caretDeps.forEach((dep) {
|
| + VersionRange constraint = dep.constraint;
|
| + buffer.writeln(
|
| + " ${dep.name}: \">=${constraint.min} <${constraint.max}\"");
|
| + });
|
| +
|
| + errors.add(buffer.toString().trim());
|
| + }
|
| +
|
| /// Returns the suggested version constraint for a dependency that was tested
|
| /// against [version].
|
| - String _constraintForVersion(Version version) =>
|
| - '">=$version ${_upperBoundForVersion(version)}"';
|
| -
|
| - /// Returns the suggested upper bound for a dependency that was tested against
|
| - /// [version].
|
| - String _upperBoundForVersion(Version version) {
|
| - if (version.major != 0) return '<${version.major + 1}.0.0';
|
| - return '<${version.major}.${version.minor + 1}.0';
|
| + String _constraintForVersion(Version version) {
|
| + if (_caretAllowed) return "^$version";
|
| + return '">=$version <${version.nextBreaking}"';
|
| }
|
| }
|
|
|