blob: 8dd77f65e19740bcc2d8351605646b4e24d8490d [file] [log] [blame]
// Copyright (c) 2015, 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.
/// A programmatic API for reflecting on Pub's cache directory.
library pub_cache;
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:pub_semver/pub_semver.dart';
import 'package:yaml/yaml.dart' as yaml;
import 'src/impl.dart';
/// A programmatic API for reflecting on Pub's cache directory.
class PubCache {
/// Return the location of Pub's package cache.
static Directory getSystemCacheLocation() {
Map env = Platform.environment;
if (env.containsKey('PUB_CACHE')) {
return new Directory(env['PUB_CACHE']);
} else if (Platform.operatingSystem == 'windows') {
return new Directory(path.join(env['APPDATA'], 'Pub', 'Cache'));
} else {
return new Directory('${env['HOME']}/.pub-cache');
}
}
// The location of the pub cache.
final Directory location;
List<Application> _applications;
List<PackageRef> _packageRefs;
/// Create a pubcache instance. [dir] defaults to the default platform pub
/// cache location.
PubCache([Directory dir]) :
location = dir == null ? getSystemCacheLocation() : dir {
_parse();
}
/// Return the contents of `bin/` - the scripts for the activated applications.
List<File> getBinScripts() {
Directory dir = _getSubDir(location, 'bin');
return dir.existsSync() ? dir.listSync() : [];
}
/// Return applications that have been installed via `pub global activate`.
List<Application> getGlobalApplications() => _applications;
/// Get all the packages and their versions that have been installed into the
/// cache.
List<PackageRef> getPackageRefs() => _packageRefs;
/// Return the list of package names (not versions) that are available in the
/// cache.
List<String> getCachedPackages() =>
new Set.from(getPackageRefs().map((p) => p.name)).toList();
/// Return all available cached versions for a given package.
List<PackageRef> getAllPackageVersions(String packageName) =>
getPackageRefs().where((p) => p.name == packageName).toList();
/// Return the most recent verison of the given package contained in the
/// cache. This method will prefer to return only release verions. If
/// [includePreRelease] is true, then the very latest verision will be
/// returned, include pre-release versions.
PackageRef getLatestVersion(String packageName, {bool includePreRelease: false}) {
List<PackageRef> refs = getAllPackageVersions(packageName);
if (refs.isEmpty) return null;
if (refs.length == 1) return refs.first;
PackageRef latest = refs.first;
if (includePreRelease) {
for (int i = 1; i < refs.length; i++) {
if (refs[i].version > latest.version) latest = refs[i];
}
} else {
List<Version> versions = refs.map((ref) => ref.version).toList();
Version latestVersion = Version.primary(versions);
for (PackageRef ref in refs) {
if (ref.version == latestVersion) latest = ref;
}
}
return latest;
}
void _parse() {
// Read the activated applications.
_applications = [];
Directory globalPackagesDir = _getSubDir(location, 'global_packages');
if (globalPackagesDir.existsSync()) {
_applications = globalPackagesDir
.listSync()
.where((item) => item is Directory)
.map((dir) => new Application._(this, dir))
.toList();
}
// Scan hosted packages - just pub.dartlang.org for now.
_packageRefs = [];
Directory dartlangDir = new Directory(
path.join(location.path, 'hosted', 'pub.dartlang.org'));
if (dartlangDir.existsSync()) {
_packageRefs = dartlangDir.listSync()
.where((dir) => dir is Directory)
.map((dir) => new DirectoryPackageRef('hosted', dir))
.toList();
}
// Scan for git packages (ignore the git/cache directory).
// ace-a1a140cc933e7d44d2955a6d6033308754bb9235
Directory gitDir = new Directory(path.join(location.path, 'git'));
if (gitDir.existsSync()) {
Iterable gitRefs = gitDir.listSync()
.where((dir) => dir is Directory && path.basename(dir.path) != 'cache')
.map((dir) => new GitDirectoryPackageRef(dir));
_packageRefs.addAll(gitRefs);
}
}
Directory _getSubDir(Directory dir, String name) =>
new Directory(path.join(dir.path, name));
}
/// A Dart application; a package with an entry-point, available via `pub global
/// activate`.
class Application {
final PubCache _cache;
final Directory _dir;
List<PackageRef> _packageRefs;
Application._(this._cache, this._dir);
/// The name of the defining package.
String get name => path.basename(_dir.path);
/// The version of the application and of the defining package.
Version get version {
PackageRef ref = getDefiningPackageRef();
return ref == null ? Version.none : ref.version;
}
/// Return the reference to the defining package. This is the package that
/// defines the application.
PackageRef getDefiningPackageRef() {
for (PackageRef ref in getPackageRefs()) {
if (ref.name == name) return ref;
}
return null;
}
/// Return all the package references for the application. This includes the
/// defining package as well as the direct and transitive dependencies.
List<PackageRef> getPackageRefs() {
if (_packageRefs == null) _parsePubspecLock();
return _packageRefs;
}
String toString() => '${name} ${version}';
void _parsePubspecLock() {
File pubspecLock = new File(path.join(_dir.path, 'pubspec.lock'));
Map doc = yaml.loadYaml(pubspecLock.readAsStringSync());
Map packages = doc['packages'];
_packageRefs = packages.keys.map((key) {
Map m = packages[key];
String source = m['source'];
if (source == 'git') {
return new PackageRefImpl.git(key, m['version'], m['description'], (curRef) {
for (PackageRef ref in _cache.getPackageRefs()) {
if (ref == curRef) return ref.resolve();
}
return null;
});
} else if (source == 'hosted') {
return new PackageRefImpl.hosted(key, m['version'], (curRef) {
for (PackageRef ref in _cache.getPackageRefs()) {
if (ref == curRef) return ref.resolve();
}
return null;
});
} else if (source == 'path') {
return new PackageRefImpl.path(key, m['version'], m['description']);
} else {
return new PackageRefImpl(source, key, m['version']);
}
}).toList();
}
}
/// A package reference, including the package name and version. This package
/// reference can be resolved to the actual package on disk.
abstract class PackageRef {
/// The type of the package reference. Valid types include `hosted` and `git`.
String get sourceType;
/// The name of the package.
String get name;
/// The version of the package.
Version get version;
/// Resolve the package reference into the actual package, including the
/// location on disk.
Package resolve();
bool operator ==(other) {
return this.sourceType == other.sourceType
&& this.name == other.name
&& this.version == other.version;
}
String toString() => '${name} ${version}';
}
/// A representation of a package, including the name, version, and location on
/// disk.
class Package {
final Directory location;
final String name;
final Version version;
Package(this.location, this.name, this.version);
String toString() => '${name} ${version}';
}