mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 08:07:11 +00:00
Add support for specifying SDK version constraints in pubspecs.
These constraints don't currently do anything, but these patch gets them being parsed by pub. Review URL: https://codereview.chromium.org//12038038 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@17477 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
b1bdb27a12
commit
edf7ca2927
7 changed files with 167 additions and 27 deletions
|
@ -22,11 +22,14 @@ class Pubspec {
|
|||
/// The packages this package depends on.
|
||||
final List<PackageRef> dependencies;
|
||||
|
||||
/// The environment-related metadata.
|
||||
final PubspecEnvironment environment;
|
||||
|
||||
/// All pubspec fields. This includes the fields from which other properties
|
||||
/// are derived.
|
||||
final Map<String, Object> fields;
|
||||
|
||||
Pubspec(this.name, this.version, this.dependencies,
|
||||
Pubspec(this.name, this.version, this.dependencies, this.environment,
|
||||
[Map<String, Object> fields])
|
||||
: this.fields = fields == null ? {} : fields;
|
||||
|
||||
|
@ -34,9 +37,10 @@ class Pubspec {
|
|||
: name = null,
|
||||
version = Version.none,
|
||||
dependencies = <PackageRef>[],
|
||||
environment = new PubspecEnvironment(),
|
||||
fields = {};
|
||||
|
||||
/// Whether or not the pubspec has no contents.
|
||||
/// Whether or not the pubspec has no contents.
|
||||
bool get isEmpty =>
|
||||
name == null && version == Version.none && dependencies.isEmpty;
|
||||
|
||||
|
@ -70,6 +74,26 @@ class Pubspec {
|
|||
var dependencies = _parseDependencies(sources,
|
||||
parsedPubspec['dependencies']);
|
||||
|
||||
var environmentYaml = parsedPubspec['environment'];
|
||||
var sdkConstraint = VersionConstraint.any;
|
||||
if (environmentYaml != null) {
|
||||
if (environmentYaml is! Map) {
|
||||
throw new FormatException(
|
||||
'The pubspec "environment" field should be a map, but was '
|
||||
'"$environmentYaml".');
|
||||
}
|
||||
|
||||
var sdkYaml = environmentYaml['sdk'];
|
||||
if (sdkYaml is! String) {
|
||||
throw new FormatException(
|
||||
'The "sdk" field of "environment" should be a string, but was '
|
||||
'"$sdkYaml".');
|
||||
}
|
||||
|
||||
sdkConstraint = new VersionConstraint.parse(sdkYaml);
|
||||
}
|
||||
var environment = new PubspecEnvironment(sdkConstraint);
|
||||
|
||||
// Even though the pub app itself doesn't use these fields, we validate
|
||||
// them here so that users find errors early before they try to upload to
|
||||
// the server:
|
||||
|
@ -123,7 +147,7 @@ class Pubspec {
|
|||
}
|
||||
}
|
||||
|
||||
return new Pubspec(name, version, dependencies, parsedPubspec);
|
||||
return new Pubspec(name, version, dependencies, environment, parsedPubspec);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,3 +205,14 @@ List<PackageRef> _parseDependencies(SourceRegistry sources, yaml) {
|
|||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
/// The environment-related metadata in the pubspec. Corresponds to the data
|
||||
/// under the "environment:" key in the pubspec.
|
||||
class PubspecEnvironment {
|
||||
/// The version constraint specifying which SDK versions this package works
|
||||
/// with.
|
||||
final VersionConstraint sdkVersion;
|
||||
|
||||
PubspecEnvironment([VersionConstraint sdk])
|
||||
: sdkVersion = sdk != null ? sdk : VersionConstraint.any;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@ class SdkSource extends Source {
|
|||
return Package.load(id.name, packageDir, systemCache.sources);
|
||||
}).then((package) {
|
||||
// Ignore the pubspec's version, and use the SDK's.
|
||||
return new Pubspec(id.name, sdk.version, package.pubspec.dependencies);
|
||||
return new Pubspec(id.name, sdk.version, package.pubspec.dependencies,
|
||||
package.pubspec.environment);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,9 @@ class Version implements Comparable, VersionConstraint {
|
|||
if (other is VersionRange) return other.intersect(this);
|
||||
|
||||
// Intersecting two versions only works if they are the same.
|
||||
if (other is Version) return this == other ? this : const _EmptyVersion();
|
||||
if (other is Version) {
|
||||
return this == other ? this : VersionConstraint.empty;
|
||||
}
|
||||
|
||||
throw new ArgumentError(
|
||||
'Unknown VersionConstraint type $other.');
|
||||
|
@ -206,8 +208,11 @@ class Version implements Comparable, VersionConstraint {
|
|||
/// version that is "2.0.0" or greater. Version objects themselves implement
|
||||
/// this to match a specific version.
|
||||
abstract class VersionConstraint {
|
||||
/// A [VersionConstraint] that allows all versions.
|
||||
static VersionConstraint any = new VersionRange();
|
||||
|
||||
/// A [VersionConstraint] that allows no versions: i.e. the empty set.
|
||||
factory VersionConstraint.empty() => const _EmptyVersion();
|
||||
static VersionConstraint empty = const _EmptyVersion();
|
||||
|
||||
/// Parses a version constraint. This string is a space-separated series of
|
||||
/// version parts. Each part can be one of:
|
||||
|
@ -333,7 +338,9 @@ class VersionRange implements VersionConstraint {
|
|||
if (other.isEmpty) return other;
|
||||
|
||||
// A range and a Version just yields the version if it's in the range.
|
||||
if (other is Version) return allows(other) ? other : const _EmptyVersion();
|
||||
if (other is Version) {
|
||||
return allows(other) ? other : VersionConstraint.empty;
|
||||
}
|
||||
|
||||
if (other is VersionRange) {
|
||||
// Intersect the two ranges.
|
||||
|
@ -373,13 +380,13 @@ class VersionRange implements VersionConstraint {
|
|||
if (intersectIncludeMin && intersectIncludeMax) return intersectMin;
|
||||
|
||||
// Otherwise, no versions.
|
||||
return const _EmptyVersion();
|
||||
return VersionConstraint.empty;
|
||||
}
|
||||
|
||||
if (intersectMin != null && intersectMax != null &&
|
||||
intersectMin > intersectMax) {
|
||||
// Non-overlapping ranges, so empty.
|
||||
return const _EmptyVersion();
|
||||
return VersionConstraint.empty;
|
||||
}
|
||||
|
||||
// If we got here, there is an actual range.
|
||||
|
|
|
@ -17,6 +17,7 @@ const _NONE = '\u001b[0m';
|
|||
/// Pretty Unicode characters!
|
||||
const _CHECKBOX = '\u2713';
|
||||
const _BALLOT_X = '\u2717';
|
||||
const _LAMBDA = '\u03bb';
|
||||
|
||||
/// A custom unittest configuration for running the pub tests from the
|
||||
/// command-line and generating human-friendly output.
|
||||
|
@ -70,14 +71,10 @@ class CommandLineConfiguration extends Configuration {
|
|||
if (stackTrace == null || stackTrace == '') return;
|
||||
|
||||
// Parse out each stack entry.
|
||||
var regexp = new RegExp(r'#\d+\s+(.*) \(file:///([^)]+)\)');
|
||||
var stack = [];
|
||||
for (var line in stackTrace.split('\n')) {
|
||||
if (line.trim() == '') continue;
|
||||
|
||||
var match = regexp.firstMatch(line);
|
||||
if (match == null) throw "Couldn't clean up stack trace line '$line'.";
|
||||
stack.add(new Pair(match[2], match[1]));
|
||||
stack.add(new _StackFrame(line));
|
||||
}
|
||||
|
||||
if (stack.length == 0) return;
|
||||
|
@ -86,10 +83,12 @@ class CommandLineConfiguration extends Configuration {
|
|||
var common = 0;
|
||||
while (true) {
|
||||
var matching = true;
|
||||
// TODO(bob): Handle empty stack.
|
||||
var c = stack[0].first[common];
|
||||
for (var pair in stack) {
|
||||
if (pair.first.length <= common || pair.first[common] != c) {
|
||||
var c;
|
||||
for (var frame in stack) {
|
||||
if (frame.isCore) continue;
|
||||
if (c == null) c = frame.library[common];
|
||||
|
||||
if (frame.library.length <= common || frame.library[common] != c) {
|
||||
matching = false;
|
||||
break;
|
||||
}
|
||||
|
@ -101,19 +100,18 @@ class CommandLineConfiguration extends Configuration {
|
|||
|
||||
// Remove them.
|
||||
if (common > 0) {
|
||||
for (var pair in stack) {
|
||||
pair.first = pair.first.substring(common);
|
||||
for (var frame in stack) {
|
||||
if (frame.isCore) continue;
|
||||
frame.library = frame.library.substring(common);
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out the longest path so we know how much to pad.
|
||||
int longest = stack.mappedBy((pair) => pair.first.length).max();
|
||||
int longest = stack.mappedBy((frame) => frame.location.length).max();
|
||||
|
||||
// Print out the stack trace nicely formatted.
|
||||
for (var pair in stack) {
|
||||
var path = pair.first;
|
||||
path = path.replaceFirst(':', ' ');
|
||||
print(' ${_padLeft(path, longest)} ${pair.last}');
|
||||
for (var frame in stack) {
|
||||
print(' ${_padLeft(frame.location, longest)} ${frame.member}');
|
||||
}
|
||||
|
||||
print('');
|
||||
|
@ -137,3 +135,44 @@ class CommandLineConfiguration extends Configuration {
|
|||
return Strings.join(str.split("\n").mappedBy((line) => " $line"), "\n");
|
||||
}
|
||||
}
|
||||
|
||||
class _StackFrame {
|
||||
static final fileRegExp = new RegExp(
|
||||
r'#\d+\s+(.*) \((file:///.+):(\d+):(\d+)\)');
|
||||
static final coreRegExp = new RegExp(r'#\d+\s+(.*) \((.+):(\d+):(\d+)\)');
|
||||
|
||||
/// If `true`, then this stack frame is for a library built into Dart and
|
||||
/// not a regular file path.
|
||||
final bool isCore;
|
||||
|
||||
/// The path to the library or the library name if a core library.
|
||||
String library;
|
||||
|
||||
/// The line number.
|
||||
final String line;
|
||||
|
||||
/// The column number.
|
||||
final String column;
|
||||
|
||||
/// The member where the error occurred.
|
||||
final String member;
|
||||
|
||||
/// A formatted description of the code location.
|
||||
String get location => '$library $line:$column';
|
||||
|
||||
_StackFrame._(this.isCore, this.library, this.line, this.column, this.member);
|
||||
|
||||
factory _StackFrame(String text) {
|
||||
var match = fileRegExp.firstMatch(text);
|
||||
var isCore = false;
|
||||
|
||||
if (match == null) {
|
||||
match = coreRegExp.firstMatch(text);
|
||||
if (match == null) throw "Couldn't parse stack trace line '$text'.";
|
||||
isCore = true;
|
||||
}
|
||||
|
||||
var member = match[1].replaceAll("<anonymous closure>", _LAMBDA);
|
||||
return new _StackFrame._(isCore, match[2], match[3], match[4], member);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
library pubspec_test;
|
||||
|
||||
import '../../../pkg/unittest/lib/unittest.dart';
|
||||
import 'test_pub.dart';
|
||||
import '../../pub/pubspec.dart';
|
||||
import '../../pub/source.dart';
|
||||
import '../../pub/source_registry.dart';
|
||||
|
@ -112,6 +113,49 @@ dependencies:
|
|||
expect(pubspec.version, equals(Version.none));
|
||||
expect(pubspec.dependencies, isEmpty);
|
||||
});
|
||||
|
||||
group("environment", () {
|
||||
test("defaults to any SDK constraint if environment is omitted", () {
|
||||
var pubspec = new Pubspec.parse('', sources);
|
||||
expect(pubspec.environment.sdkVersion, equals(VersionConstraint.any));
|
||||
});
|
||||
|
||||
test("allows an empty environment map", () {
|
||||
var pubspec = new Pubspec.parse('''
|
||||
environment:
|
||||
''', sources);
|
||||
expect(pubspec.environment.sdkVersion, equals(VersionConstraint.any));
|
||||
});
|
||||
|
||||
test("throws if the environment value isn't a map", () {
|
||||
expectFormatError('''
|
||||
environment: []
|
||||
''');
|
||||
});
|
||||
|
||||
test("allows a version constraint for the sdk", () {
|
||||
var pubspec = new Pubspec.parse('''
|
||||
environment:
|
||||
sdk: ">=1.2.3 <2.3.4"
|
||||
''', sources);
|
||||
expect(pubspec.environment.sdkVersion,
|
||||
equals(new VersionConstraint.parse(">=1.2.3 <2.3.4")));
|
||||
});
|
||||
|
||||
test("throws if the sdk isn't a string", () {
|
||||
expectFormatError('''
|
||||
environment:
|
||||
sdk: []
|
||||
''');
|
||||
});
|
||||
|
||||
test("throws if the sdk isn't a valid version constraint", () {
|
||||
expectFormatError('''
|
||||
environment:
|
||||
sdk: "oopies"
|
||||
''');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -495,7 +495,8 @@ class MockSource extends Source {
|
|||
});
|
||||
|
||||
var pubspec = new Pubspec(
|
||||
description, new Version.parse(version), dependencies);
|
||||
description, new Version.parse(version), dependencies,
|
||||
new PubspecEnvironment());
|
||||
return new Package.inMemory(pubspec);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
library version_test;
|
||||
|
||||
import '../../../pkg/unittest/lib/unittest.dart';
|
||||
import 'test_pub.dart';
|
||||
import '../../pub/utils.dart';
|
||||
import '../../pub/version.dart';
|
||||
|
||||
|
@ -302,8 +303,20 @@ main() {
|
|||
});
|
||||
|
||||
group('VersionConstraint', () {
|
||||
test('any', () {
|
||||
expect(VersionConstraint.any.isAny, isTrue);
|
||||
expect(VersionConstraint.any, allows([
|
||||
new Version.parse('0.0.0-blah'),
|
||||
new Version.parse('1.2.3'),
|
||||
new Version.parse('12345.678.90')]));
|
||||
});
|
||||
|
||||
test('empty', () {
|
||||
expect(new VersionConstraint.empty().isEmpty, isTrue);
|
||||
expect(VersionConstraint.empty.isEmpty, isTrue);
|
||||
expect(VersionConstraint.empty, doesNotAllow([
|
||||
new Version.parse('0.0.0-blah'),
|
||||
new Version.parse('1.2.3'),
|
||||
new Version.parse('12345.678.90')]));
|
||||
});
|
||||
|
||||
group('parse()', () {
|
||||
|
|
Loading…
Reference in a new issue