| Index: utils/pub/version.dart
|
| diff --git a/utils/pub/version.dart b/utils/pub/version.dart
|
| index 840dd4c164182b1087aa5c9d8921cf7072818735..62669e4b6376a4953863efb9e447e9efe85e074c 100644
|
| --- a/utils/pub/version.dart
|
| +++ b/utils/pub/version.dart
|
| @@ -11,18 +11,25 @@ import 'dart:math';
|
|
|
| import 'utils.dart';
|
|
|
| +
|
| +/// Regex that matches a version number at the beginning of a string.
|
| +final _START_VERSION = new RegExp(
|
| + r'^' // Start at beginning.
|
| + r'(\d+).(\d+).(\d+)' // Version number.
|
| + r'(-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?' // Pre-release.
|
| + r'(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?'); // Build.
|
| +
|
| +/// Like [_START_VERSION] but matches the entire string.
|
| +final _COMPLETE_VERSION = new RegExp("${_START_VERSION.pattern}\$");
|
| +
|
| +/// Parses a comparison operator ("<", ">", "<=", or ">=") at the beginning of
|
| +/// a string.
|
| +final _START_COMPARISON = new RegExp(r"^[<>]=?");
|
| +
|
| /// A parsed semantic version number.
|
| class Version implements Comparable<Version>, VersionConstraint {
|
| /// No released version: i.e. "0.0.0".
|
| static Version get none => new Version(0, 0, 0);
|
| -
|
| - static final _PARSE_REGEX = new RegExp(
|
| - r'^' // Start at beginning.
|
| - r'(\d+).(\d+).(\d+)' // Version number.
|
| - r'(-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?' // Pre-release.
|
| - r'(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?' // Build.
|
| - r'$'); // Consume entire string.
|
| -
|
| /// The major version number: "1" in "1.2.3".
|
| final int major;
|
|
|
| @@ -51,7 +58,7 @@ class Version implements Comparable<Version>, VersionConstraint {
|
|
|
| /// Creates a new [Version] by parsing [text].
|
| factory Version.parse(String text) {
|
| - final match = _PARSE_REGEX.firstMatch(text);
|
| + final match = _COMPLETE_VERSION.firstMatch(text);
|
| if (match == null) {
|
| throw new FormatException('Could not parse "$text".');
|
| }
|
| @@ -146,9 +153,9 @@ class Version implements Comparable<Version>, VersionConstraint {
|
|
|
| String toString() {
|
| var buffer = new StringBuffer();
|
| - buffer.add('$major.$minor.$patch');
|
| - if (preRelease != null) buffer.add('-$preRelease');
|
| - if (build != null) buffer.add('+$build');
|
| + buffer.write('$major.$minor.$patch');
|
| + if (preRelease != null) buffer.write('-$preRelease');
|
| + if (build != null) buffer.write('+$build');
|
| return buffer.toString();
|
| }
|
|
|
| @@ -214,28 +221,92 @@ abstract class VersionConstraint {
|
| /// A [VersionConstraint] that allows no versions: i.e. the empty set.
|
| static VersionConstraint empty = const _EmptyVersion();
|
|
|
| - /// Parses a version constraint. This string is a space-separated series of
|
| + /// Parses a version constraint. This string is either "any" or a series of
|
| /// version parts. Each part can be one of:
|
| ///
|
| /// * A version string like `1.2.3`. In other words, anything that can be
|
| /// parsed by [Version.parse()].
|
| /// * A comparison operator (`<`, `>`, `<=`, or `>=`) followed by a version
|
| - /// string. There cannot be a space between the operator and the version.
|
| + /// string.
|
| + ///
|
| + /// Whitespace is ignored.
|
| ///
|
| /// Examples:
|
| ///
|
| + /// any
|
| /// 1.2.3-alpha
|
| /// <=5.1.4
|
| - /// >2.0.4 <=2.4.6
|
| + /// >2.0.4 <= 2.4.6
|
| factory VersionConstraint.parse(String text) {
|
| - if (text.trim() == '') {
|
| - throw new FormatException('Cannot parse an empty string.');
|
| - }
|
| + // Handle the "any" constraint.
|
| + if (text.trim() == "any") return new VersionRange();
|
|
|
| - // Split it into space-separated parts.
|
| + var originalText = text;
|
| var constraints = <VersionConstraint>[];
|
| - for (var part in text.split(' ')) {
|
| - constraints.add(_parseSingleConstraint(part));
|
| +
|
| + void skipWhitespace() {
|
| + text = text.trim();
|
| + }
|
| +
|
| + // Try to parse and consume a version number.
|
| + Version matchVersion() {
|
| + var version = _START_VERSION.firstMatch(text);
|
| + if (version == null) return null;
|
| +
|
| + text = text.substring(version.end);
|
| + return new Version.parse(version[0]);
|
| + }
|
| +
|
| + // Try to parse and consume a comparison operator followed by a version.
|
| + VersionConstraint matchComparison() {
|
| + var comparison = _START_COMPARISON.firstMatch(text);
|
| + if (comparison == null) return null;
|
| +
|
| + var op = comparison[0];
|
| + text = text.substring(comparison.end);
|
| + skipWhitespace();
|
| +
|
| + var version = matchVersion();
|
| + if (version == null) {
|
| + throw new FormatException('Expected version number after "$op" in '
|
| + '"$originalText", got "$text".');
|
| + }
|
| +
|
| + switch (op) {
|
| + case '<=':
|
| + return new VersionRange(max: version, includeMax: true);
|
| + case '<':
|
| + return new VersionRange(max: version, includeMax: false);
|
| + case '>=':
|
| + return new VersionRange(min: version, includeMin: true);
|
| + case '>':
|
| + return new VersionRange(min: version, includeMin: false);
|
| + }
|
| + }
|
| +
|
| + while (true) {
|
| + skipWhitespace();
|
| + if (text.isEmpty) break;
|
| +
|
| + var version = matchVersion();
|
| + if (version != null) {
|
| + constraints.add(version);
|
| + continue;
|
| + }
|
| +
|
| + var comparison = matchComparison();
|
| + if (comparison != null) {
|
| + constraints.add(comparison);
|
| + continue;
|
| + }
|
| +
|
| + // If we got here, we couldn't parse the remaining string.
|
| + throw new FormatException('Could not parse version "$originalText". '
|
| + 'Unknown text at "$text".');
|
| + }
|
| +
|
| + if (constraints.isEmpty) {
|
| + throw new FormatException('Cannot parse an empty string.');
|
| }
|
|
|
| return new VersionConstraint.intersection(constraints);
|
| @@ -266,32 +337,6 @@ abstract class VersionConstraint {
|
| /// Creates a new [VersionConstraint] that only allows [Version]s allowed by
|
| /// both this and [other].
|
| VersionConstraint intersect(VersionConstraint other);
|
| -
|
| - static VersionConstraint _parseSingleConstraint(String text) {
|
| - if (text == 'any') {
|
| - return new VersionRange();
|
| - }
|
| -
|
| - // TODO(rnystrom): Consider other syntaxes for version constraints. This
|
| - // one is whitespace sensitive (you can't do "< 1.2.3") and "<" is
|
| - // unfortunately meaningful in YAML, requiring it to be quoted in a
|
| - // pubspec.
|
| - // See if it's a comparison operator followed by a version, like ">1.2.3".
|
| - var match = new RegExp(r"^([<>]=?)?(.*)$").firstMatch(text);
|
| - if (match != null) {
|
| - var comparison = match[1];
|
| - var version = new Version.parse(match[2]);
|
| - switch (match[1]) {
|
| - case '<=': return new VersionRange(max: version, includeMax: true);
|
| - case '<': return new VersionRange(max: version, includeMax: false);
|
| - case '>=': return new VersionRange(min: version, includeMin: true);
|
| - case '>': return new VersionRange(min: version, includeMin: false);
|
| - }
|
| - }
|
| -
|
| - // Otherwise, it must be an explicit version.
|
| - return new Version.parse(text);
|
| - }
|
| }
|
|
|
| /// Constrains versions to a fall within a given range. If there is a minimum,
|
| @@ -402,17 +447,17 @@ class VersionRange implements VersionConstraint {
|
| var buffer = new StringBuffer();
|
|
|
| if (min != null) {
|
| - buffer.add(includeMin ? '>=' : '>');
|
| - buffer.add(min);
|
| + buffer.write(includeMin ? '>=' : '>');
|
| + buffer.write(min);
|
| }
|
|
|
| if (max != null) {
|
| - if (min != null) buffer.add(' ');
|
| - buffer.add(includeMax ? '<=' : '<');
|
| - buffer.add(max);
|
| + if (min != null) buffer.write(' ');
|
| + buffer.write(includeMax ? '<=' : '<');
|
| + buffer.write(max);
|
| }
|
|
|
| - if (min == null && max == null) buffer.add('any');
|
| + if (min == null && max == null) buffer.write('any');
|
| return buffer.toString();
|
| }
|
| }
|
|
|