| // Copyright (c) 2012, 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 package; |
| |
| import 'io.dart'; |
| import 'pubspec.dart'; |
| import 'source.dart'; |
| import 'source_registry.dart'; |
| import 'version.dart'; |
| |
| /** |
| * A named, versioned, unit of code and resource reuse. |
| */ |
| class Package { |
| /** |
| * Loads the package whose root directory is [packageDir]. [name] is the |
| * expected name of that package (e.g. the name given in the dependency), or |
| * null if the package being loaded is the entrypoint package. |
| */ |
| static Future<Package> load(String name, String packageDir, |
| SourceRegistry sources) { |
| var pubspecPath = join(packageDir, 'pubspec.yaml'); |
| |
| return fileExists(pubspecPath).chain((exists) { |
| if (!exists) throw new PubspecNotFoundException(name); |
| return readTextFile(pubspecPath); |
| }).transform((contents) { |
| try { |
| var pubspec = new Pubspec.parse(contents, sources); |
| |
| if (pubspec.name == null) throw new PubspecHasNoNameException(name); |
| if (name != null && pubspec.name != name) { |
| throw new PubspecNameMismatchException(name, pubspec.name); |
| } |
| return new Package._(packageDir, pubspec); |
| } on FormatException catch (ex) { |
| throw 'Could not parse $pubspecPath:\n${ex.message}'; |
| } |
| }); |
| } |
| |
| /** |
| * The path to the directory containing the package. |
| */ |
| final String dir; |
| |
| /** |
| * The name of the package. |
| */ |
| String get name { |
| if (pubspec.name != null) return pubspec.name; |
| if (dir != null) return basename(dir); |
| return null; |
| } |
| |
| /** |
| * The package's version. |
| */ |
| Version get version => pubspec.version; |
| |
| /** |
| * The parsed pubspec associated with this package. |
| */ |
| final Pubspec pubspec; |
| |
| /** |
| * The ids of the packages that this package depends on. This is what is |
| * specified in the pubspec when this package depends on another. |
| */ |
| Collection<PackageRef> get dependencies => pubspec.dependencies; |
| |
| /** |
| * Constructs a package with the given pubspec. The package will have no |
| * directory associated with it. |
| */ |
| Package.inMemory(this.pubspec) |
| : dir = null; |
| |
| /** |
| * Constructs a package. This should not be called directly. Instead, acquire |
| * packages from [load()]. |
| */ |
| Package._(this.dir, this.pubspec); |
| |
| /** |
| * Returns a debug string for the package. |
| */ |
| String toString() => '$name $version ($dir)'; |
| } |
| |
| /** |
| * An unambiguous resolved reference to a package. A package ID contains enough |
| * information to correctly install the package. |
| * |
| * Note that it's possible for multiple distinct package IDs to point to |
| * different directories that happen to contain identical packages. For example, |
| * the same package may be available from multiple sources. As far as Pub is |
| * concerned, those packages are different. |
| */ |
| class PackageId implements Comparable { |
| /// The name of the package being identified. |
| final String name; |
| |
| /** |
| * The [Source] used to look up this package given its [description]. |
| */ |
| final Source source; |
| |
| /** |
| * The package's version. |
| */ |
| final Version version; |
| |
| /** |
| * The metadata used by the package's [source] to identify and locate it. It |
| * contains whatever [Source]-specific data it needs to be able to install |
| * the package. For example, the description of a git sourced package might |
| * by the URL "git://github.com/dart/uilib.git". |
| */ |
| final description; |
| |
| PackageId(this.name, this.source, this.version, this.description); |
| |
| int get hashCode => name.hashCode ^ |
| source.name.hashCode ^ |
| version.hashCode; |
| |
| bool operator ==(other) { |
| if (other is! PackageId) return false; |
| // TODO(rnystrom): We're assuming here the name/version/source tuple is |
| // enough to uniquely identify the package and that we don't need to delve |
| // into the description. |
| return other.name == name && |
| other.source.name == source.name && |
| other.version == version; |
| } |
| |
| String toString() { |
| if (source.isDefault) return "$name $version"; |
| return "$name $version from $source"; |
| } |
| |
| int compareTo(Comparable other) { |
| if (other is! PackageId) throw new ArgumentError(other); |
| |
| var sourceComp = source.name.compareTo(other.source.name); |
| if (sourceComp != 0) return sourceComp; |
| |
| var nameComp = name.compareTo(other.name); |
| if (nameComp != 0) return nameComp; |
| |
| return version.compareTo(other.version); |
| } |
| |
| /** |
| * Returns the pubspec for this package. |
| */ |
| Future<Pubspec> describe() => source.describe(this); |
| |
| /** |
| * Returns a future that completes to the resovled [PackageId] for this id. |
| */ |
| Future<PackageId> get resolved => source.resolveId(this); |
| } |
| |
| /** |
| * A reference to a package. Unlike a [PackageId], a PackageRef may not |
| * unambiguously refer to a single package. It may describe a range of allowed |
| * packages. |
| */ |
| class PackageRef { |
| /// The name of the package being identified. |
| final String name; |
| |
| /** |
| * The [Source] used to look up the package. |
| */ |
| final Source source; |
| |
| /** |
| * The allowed package versions. |
| */ |
| final VersionConstraint constraint; |
| |
| /** |
| * The metadata used to identify the package being referenced. The |
| * interpretation of this will vary based on the [source]. |
| */ |
| final description; |
| |
| PackageRef(this.name, this.source, this.constraint, this.description); |
| |
| String toString() => "$name $constraint from $source ($description)"; |
| |
| /** |
| * Returns a [PackageId] generated from this [PackageRef] with the given |
| * concrete version. |
| */ |
| PackageId atVersion(Version version) => |
| new PackageId(name, source, version, description); |
| } |
| |
| class PubspecNotFoundException implements Exception { |
| final String name; |
| |
| PubspecNotFoundException(this.name); |
| |
| String toString() => 'Package "$name" doesn\'t have a pubspec.yaml file.'; |
| } |
| |
| class PubspecHasNoNameException implements Exception { |
| final String name; |
| |
| PubspecHasNoNameException(this.name); |
| |
| String toString() => 'Package "$name"\'s pubspec.yaml file is missing the ' |
| 'required "name" field (e.g. "name: $name").'; |
| } |
| |
| class PubspecNameMismatchException implements Exception { |
| final String expectedName; |
| final String actualName; |
| |
| PubspecNameMismatchException(this.expectedName, this.actualName); |
| |
| String toString() => 'The name you specified for your dependency, ' |
| '"$expectedName", doesn\'t match the name "$actualName" in its pubspec.'; |
| } |