| Index: sdk/lib/_internal/pub_generated/lib/src/global_packages.dart
|
| diff --git a/sdk/lib/_internal/pub_generated/lib/src/global_packages.dart b/sdk/lib/_internal/pub_generated/lib/src/global_packages.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3dfed37924599d5a6add3a9cb3c45609e762a92b
|
| --- /dev/null
|
| +++ b/sdk/lib/_internal/pub_generated/lib/src/global_packages.dart
|
| @@ -0,0 +1,1109 @@
|
| +// Copyright (c) 2014, 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 pub.global_packages;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:io';
|
| +
|
| +import 'package:path/path.dart' as p;
|
| +import 'package:barback/barback.dart';
|
| +import 'package:pub_semver/pub_semver.dart';
|
| +
|
| +import 'barback/asset_environment.dart';
|
| +import 'entrypoint.dart';
|
| +import 'exceptions.dart';
|
| +import 'executable.dart' as exe;
|
| +import 'io.dart';
|
| +import 'lock_file.dart';
|
| +import 'log.dart' as log;
|
| +import 'package.dart';
|
| +import 'pubspec.dart';
|
| +import 'sdk.dart' as sdk;
|
| +import 'solver/version_solver.dart';
|
| +import 'source/cached.dart';
|
| +import 'source/git.dart';
|
| +import 'source/path.dart';
|
| +import 'system_cache.dart';
|
| +import 'utils.dart';
|
| +
|
| +/// Maintains the set of packages that have been globally activated.
|
| +///
|
| +/// These have been hand-chosen by the user to make their executables in bin/
|
| +/// available to the entire system. This lets them access them even when the
|
| +/// current working directory is not inside another entrypoint package.
|
| +///
|
| +/// Only one version of a given package name can be globally activated at a
|
| +/// time. Activating a different version of a package will deactivate the
|
| +/// previous one.
|
| +///
|
| +/// This handles packages from uncached and cached sources a little differently.
|
| +/// For a cached source, the package is physically in the user's pub cache and
|
| +/// we don't want to mess with it by putting a lockfile in there. Instead, when
|
| +/// we activate the package, we create a full lockfile and put it in the
|
| +/// "global_packages" directory. It's named "<package>.lock". Unlike a normal
|
| +/// lockfile, it also contains an entry for the root package itself, so that we
|
| +/// know the version and description that was activated.
|
| +///
|
| +/// Uncached packages (i.e. "path" packages) are somewhere else on the user's
|
| +/// local file system and can have a lockfile directly in place. (And, in fact,
|
| +/// we want to ensure we honor the user's lockfile there.) To activate it, we
|
| +/// just need to know where that package directory is. For that, we create a
|
| +/// lockfile that *only* contains the root package's [PackageId] -- basically
|
| +/// just the path to the directory where the real lockfile lives.
|
| +class GlobalPackages {
|
| + /// The [SystemCache] containing the global packages.
|
| + final SystemCache cache;
|
| +
|
| + /// The directory where the lockfiles for activated packages are stored.
|
| + String get _directory => p.join(cache.rootDir, "global_packages");
|
| +
|
| + /// The directory where binstubs for global package executables are stored.
|
| + String get _binStubDir => p.join(cache.rootDir, "bin");
|
| +
|
| + /// Creates a new global package registry backed by the given directory on
|
| + /// the user's file system.
|
| + ///
|
| + /// The directory may not physically exist yet. If not, this will create it
|
| + /// when needed.
|
| + GlobalPackages(this.cache);
|
| +
|
| + /// Caches the package located in the Git repository [repo] and makes it the
|
| + /// active global version.
|
| + ///
|
| + /// [executables] is the names of the executables that should have binstubs.
|
| + /// If `null`, all executables in the package will get binstubs. If empty, no
|
| + /// binstubs will be created.
|
| + ///
|
| + /// if [overwriteBinStubs] is `true`, any binstubs that collide with
|
| + /// existing binstubs in other packages will be overwritten by this one's.
|
| + /// Otherwise, the previous ones will be preserved.
|
| + Future activateGit(String repo, List<String> executables,
|
| + {bool overwriteBinStubs}) {
|
| + final completer0 = new Completer();
|
| + scheduleMicrotask(() {
|
| + try {
|
| + var source = cache.sources["git"] as GitSource;
|
| + new Future.value(source.getPackageNameFromRepo(repo)).then((x0) {
|
| + try {
|
| + var name = x0;
|
| + _describeActive(name);
|
| + new Future.value(
|
| + _installInCache(
|
| + new PackageDep(name, "git", VersionConstraint.any, repo),
|
| + executables,
|
| + overwriteBinStubs: overwriteBinStubs)).then((x1) {
|
| + try {
|
| + x1;
|
| + completer0.complete();
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + } catch (e1, s1) {
|
| + completer0.completeError(e1, s1);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + } catch (e, s) {
|
| + completer0.completeError(e, s);
|
| + }
|
| + });
|
| + return completer0.future;
|
| + }
|
| +
|
| + /// Finds the latest version of the hosted package with [name] that matches
|
| + /// [constraint] and makes it the active global version.
|
| + ///
|
| + /// [executables] is the names of the executables that should have binstubs.
|
| + /// If `null`, all executables in the package will get binstubs. If empty, no
|
| + /// binstubs will be created.
|
| + ///
|
| + /// if [overwriteBinStubs] is `true`, any binstubs that collide with
|
| + /// existing binstubs in other packages will be overwritten by this one's.
|
| + /// Otherwise, the previous ones will be preserved.
|
| + Future activateHosted(String name, VersionConstraint constraint,
|
| + List<String> executables, {bool overwriteBinStubs}) {
|
| + final completer0 = new Completer();
|
| + scheduleMicrotask(() {
|
| + try {
|
| + _describeActive(name);
|
| + new Future.value(
|
| + _installInCache(
|
| + new PackageDep(name, "hosted", constraint, name),
|
| + executables,
|
| + overwriteBinStubs: overwriteBinStubs)).then((x0) {
|
| + try {
|
| + x0;
|
| + completer0.complete();
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + } catch (e, s) {
|
| + completer0.completeError(e, s);
|
| + }
|
| + });
|
| + return completer0.future;
|
| + }
|
| +
|
| + /// Makes the local package at [path] globally active.
|
| + ///
|
| + /// [executables] is the names of the executables that should have binstubs.
|
| + /// If `null`, all executables in the package will get binstubs. If empty, no
|
| + /// binstubs will be created.
|
| + ///
|
| + /// if [overwriteBinStubs] is `true`, any binstubs that collide with
|
| + /// existing binstubs in other packages will be overwritten by this one's.
|
| + /// Otherwise, the previous ones will be preserved.
|
| + Future activatePath(String path, List<String> executables,
|
| + {bool overwriteBinStubs}) {
|
| + final completer0 = new Completer();
|
| + scheduleMicrotask(() {
|
| + try {
|
| + var entrypoint = new Entrypoint(path, cache);
|
| + new Future.value(entrypoint.ensureLockFileIsUpToDate()).then((x0) {
|
| + try {
|
| + x0;
|
| + var name = entrypoint.root.name;
|
| + _describeActive(name);
|
| + var fullPath = canonicalize(entrypoint.root.dir);
|
| + var id = new PackageId(
|
| + name,
|
| + "path",
|
| + entrypoint.root.version,
|
| + PathSource.describePath(fullPath));
|
| + _writeLockFile(name, new LockFile([id]));
|
| + var binDir = p.join(_directory, name, 'bin');
|
| + join0() {
|
| + _updateBinStubs(
|
| + entrypoint.root,
|
| + executables,
|
| + overwriteBinStubs: overwriteBinStubs);
|
| + completer0.complete();
|
| + }
|
| + if (dirExists(binDir)) {
|
| + deleteEntry(binDir);
|
| + join0();
|
| + } else {
|
| + join0();
|
| + }
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + } catch (e, s) {
|
| + completer0.completeError(e, s);
|
| + }
|
| + });
|
| + return completer0.future;
|
| + }
|
| +
|
| + /// Installs the package [dep] and its dependencies into the system cache.
|
| + Future _installInCache(PackageDep dep, List<String> executables,
|
| + {bool overwriteBinStubs}) {
|
| + final completer0 = new Completer();
|
| + scheduleMicrotask(() {
|
| + try {
|
| + var source = cache.sources[dep.source];
|
| + var root = new Package.inMemory(
|
| + new Pubspec(
|
| + "pub global activate",
|
| + dependencies: [dep],
|
| + sources: cache.sources));
|
| + new Future.value(
|
| + resolveVersions(SolveType.GET, cache.sources, root)).then((x0) {
|
| + try {
|
| + var result = x0;
|
| + join0() {
|
| + result.showReport(SolveType.GET);
|
| + new Future.value(
|
| + Future.wait(result.packages.map(_cacheDependency))).then((x1) {
|
| + try {
|
| + var ids = x1;
|
| + var lockFile = new LockFile(ids);
|
| + new Future.value(
|
| + new Entrypoint.inMemory(
|
| + root,
|
| + lockFile,
|
| + cache).loadPackageGraph(result)).then((x2) {
|
| + try {
|
| + var graph = x2;
|
| + new Future.value(
|
| + _precompileExecutables(graph.entrypoint, dep.name)).then((x3) {
|
| + try {
|
| + var snapshots = x3;
|
| + _writeLockFile(dep.name, lockFile);
|
| + _updateBinStubs(
|
| + graph.packages[dep.name],
|
| + executables,
|
| + overwriteBinStubs: overwriteBinStubs,
|
| + snapshots: snapshots);
|
| + completer0.complete();
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + } catch (e1, s1) {
|
| + completer0.completeError(e1, s1);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + } catch (e2, s2) {
|
| + completer0.completeError(e2, s2);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + }
|
| + if (!result.succeeded) {
|
| + join1() {
|
| + join2() {
|
| + throw result.error;
|
| + join0();
|
| + }
|
| + if (result.error is NoVersionException) {
|
| + dataError(result.error.message);
|
| + join2();
|
| + } else {
|
| + join2();
|
| + }
|
| + }
|
| + if (result.error.package != dep.name) {
|
| + throw result.error;
|
| + join1();
|
| + } else {
|
| + join1();
|
| + }
|
| + } else {
|
| + join0();
|
| + }
|
| + } catch (e3, s3) {
|
| + completer0.completeError(e3, s3);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + } catch (e, s) {
|
| + completer0.completeError(e, s);
|
| + }
|
| + });
|
| + return completer0.future;
|
| + }
|
| +
|
| + /// Precompiles the executables for [package] and saves them in the global
|
| + /// cache.
|
| + ///
|
| + /// Returns a map from executable name to path for the snapshots that were
|
| + /// successfully precompiled.
|
| + Future<Map<String, String>> _precompileExecutables(Entrypoint entrypoint,
|
| + String package) {
|
| + return log.progress("Precompiling executables", () {
|
| + final completer0 = new Completer();
|
| + scheduleMicrotask(() {
|
| + try {
|
| + var binDir = p.join(_directory, package, 'bin');
|
| + cleanDir(binDir);
|
| + new Future.value(entrypoint.loadPackageGraph()).then((x0) {
|
| + try {
|
| + var graph = x0;
|
| + new Future.value(
|
| + AssetEnvironment.create(
|
| + entrypoint,
|
| + BarbackMode.RELEASE,
|
| + entrypoints: graph.packages[package].executableIds,
|
| + useDart2JS: false)).then((x1) {
|
| + try {
|
| + var environment = x1;
|
| + environment.barback.errors.listen(((error) {
|
| + log.error(log.red("Build error:\n$error"));
|
| + }));
|
| + completer0.complete(
|
| + environment.precompileExecutables(package, binDir));
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + } catch (e1, s1) {
|
| + completer0.completeError(e1, s1);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + } catch (e, s) {
|
| + completer0.completeError(e, s);
|
| + }
|
| + });
|
| + return completer0.future;
|
| + });
|
| + }
|
| +
|
| + /// Downloads [id] into the system cache if it's a cached package.
|
| + ///
|
| + /// Returns the resolved [PackageId] for [id].
|
| + Future<PackageId> _cacheDependency(PackageId id) {
|
| + final completer0 = new Completer();
|
| + scheduleMicrotask(() {
|
| + try {
|
| + var source = cache.sources[id.source];
|
| + join0() {
|
| + completer0.complete(source.resolveId(id));
|
| + }
|
| + if (!id.isRoot && source is CachedSource) {
|
| + new Future.value(source.downloadToSystemCache(id)).then((x0) {
|
| + try {
|
| + x0;
|
| + join0();
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + } else {
|
| + join0();
|
| + }
|
| + } catch (e, s) {
|
| + completer0.completeError(e, s);
|
| + }
|
| + });
|
| + return completer0.future;
|
| + }
|
| +
|
| + /// Finishes activating package [package] by saving [lockFile] in the cache.
|
| + void _writeLockFile(String package, LockFile lockFile) {
|
| + ensureDir(p.join(_directory, package));
|
| +
|
| + // TODO(nweiz): This cleans up Dart 1.6's old lockfile location. Remove it
|
| + // when Dart 1.6 is old enough that we don't think anyone will have these
|
| + // lockfiles anymore (issue 20703).
|
| + var oldPath = p.join(_directory, "$package.lock");
|
| + if (fileExists(oldPath)) deleteEntry(oldPath);
|
| +
|
| + writeTextFile(
|
| + _getLockFilePath(package),
|
| + lockFile.serialize(cache.rootDir, cache.sources));
|
| +
|
| + var id = lockFile.packages[package];
|
| + log.message('Activated ${_formatPackage(id)}.');
|
| + }
|
| +
|
| + /// Shows the user the currently active package with [name], if any.
|
| + void _describeActive(String name) {
|
| + try {
|
| + var lockFile = new LockFile.load(_getLockFilePath(name), cache.sources);
|
| + var id = lockFile.packages[name];
|
| +
|
| + if (id.source == 'git') {
|
| + var url = GitSource.urlFromDescription(id.description);
|
| + log.message(
|
| + 'Package ${log.bold(name)} is currently active from Git '
|
| + 'repository "${url}".');
|
| + } else if (id.source == 'path') {
|
| + var path = PathSource.pathFromDescription(id.description);
|
| + log.message(
|
| + 'Package ${log.bold(name)} is currently active at path ' '"$path".');
|
| + } else {
|
| + log.message(
|
| + 'Package ${log.bold(name)} is currently active at version '
|
| + '${log.bold(id.version)}.');
|
| + }
|
| + } on IOException catch (error) {
|
| + // If we couldn't read the lock file, it's not activated.
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + /// Deactivates a previously-activated package named [name].
|
| + ///
|
| + /// Returns `false` if no package with [name] was currently active.
|
| + bool deactivate(String name) {
|
| + var dir = p.join(_directory, name);
|
| + if (!dirExists(dir)) return false;
|
| +
|
| + _deleteBinStubs(name);
|
| +
|
| + var lockFile = new LockFile.load(_getLockFilePath(name), cache.sources);
|
| + var id = lockFile.packages[name];
|
| + log.message('Deactivated package ${_formatPackage(id)}.');
|
| +
|
| + deleteEntry(dir);
|
| +
|
| + return true;
|
| + }
|
| +
|
| + /// Finds the active package with [name].
|
| + ///
|
| + /// Returns an [Entrypoint] loaded with the active package if found.
|
| + Future<Entrypoint> find(String name) {
|
| + final completer0 = new Completer();
|
| + scheduleMicrotask(() {
|
| + try {
|
| + var lockFilePath = _getLockFilePath(name);
|
| + var lockFile;
|
| + join0() {
|
| + var id = lockFile.packages[name];
|
| + lockFile.packages.remove(name);
|
| + var source = cache.sources[id.source];
|
| + join1() {
|
| + assert(id.source == "path");
|
| + completer0.complete(
|
| + new Entrypoint(PathSource.pathFromDescription(id.description), cache));
|
| + }
|
| + if (source is CachedSource) {
|
| + new Future.value(
|
| + cache.sources[id.source].getDirectory(id)).then((x0) {
|
| + try {
|
| + var dir = x0;
|
| + var package = new Package.load(name, dir, cache.sources);
|
| + completer0.complete(
|
| + new Entrypoint.inMemory(package, lockFile, cache));
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| + }
|
| + }, onError: completer0.completeError);
|
| + } else {
|
| + join1();
|
| + }
|
| + }
|
| + catch0(error, s1) {
|
| + try {
|
| + if (error is IOException) {
|
| + var oldLockFilePath = p.join(_directory, '${name}.lock');
|
| + join2() {
|
| + ensureDir(p.dirname(lockFilePath));
|
| + new File(oldLockFilePath).renameSync(lockFilePath);
|
| + join0();
|
| + }
|
| + catch1(error, s2) {
|
| + try {
|
| + if (error is IOException) {
|
| + dataError("No active package ${log.bold(name)}.");
|
| + join2();
|
| + } else {
|
| + throw error;
|
| + }
|
| + } catch (error, s2) {
|
| + completer0.completeError(error, s2);
|
| + }
|
| + }
|
| + try {
|
| + lockFile = new LockFile.load(oldLockFilePath, cache.sources);
|
| + join2();
|
| + } catch (e1, s3) {
|
| + catch1(e1, s3);
|
| + }
|
| + } else {
|
| + throw error;
|
| + }
|
| + } catch (error, s1) {
|
| + completer0.completeError(error, s1);
|
| + }
|
| + }
|
| + try {
|
| + lockFile = new LockFile.load(lockFilePath, cache.sources);
|
| + join0();
|
| + } catch (e2, s4) {
|
| + catch0(e2, s4);
|
| + }
|
| + } catch (e, s) {
|
| + completer0.completeError(e, s);
|
| + }
|
| + });
|
| + return completer0.future;
|
| + }
|
| +
|
| + /// Runs [package]'s [executable] with [args].
|
| + ///
|
| + /// If [executable] is available in its precompiled form, that will be
|
| + /// recompiled if the SDK has been upgraded since it was first compiled and
|
| + /// then run. Otherwise, it will be run from source.
|
| + ///
|
| + /// If [mode] is passed, it's used as the barback mode; it defaults to
|
| + /// [BarbackMode.RELEASE].
|
| + ///
|
| + /// Returns the exit code from the executable.
|
| + Future<int> runExecutable(String package, String executable,
|
| + Iterable<String> args, {BarbackMode mode}) {
|
| + if (mode == null) mode = BarbackMode.RELEASE;
|
| +
|
| + var binDir = p.join(_directory, package, 'bin');
|
| + if (mode != BarbackMode.RELEASE ||
|
| + !fileExists(p.join(binDir, '$executable.dart.snapshot'))) {
|
| + return find(package).then((entrypoint) {
|
| + return exe.runExecutable(
|
| + entrypoint,
|
| + package,
|
| + executable,
|
| + args,
|
| + mode: mode,
|
| + isGlobal: true);
|
| + });
|
| + }
|
| +
|
| + // Unless the user overrides the verbosity, we want to filter out the
|
| + // normal pub output shown while loading the environment.
|
| + if (log.verbosity == log.Verbosity.NORMAL) {
|
| + log.verbosity = log.Verbosity.WARNING;
|
| + }
|
| +
|
| + var snapshotPath = p.join(binDir, '$executable.dart.snapshot');
|
| + return exe.runSnapshot(snapshotPath, args, recompile: () {
|
| + log.fine(
|
| + "$package:$executable is out of date and needs to be " "recompiled.");
|
| + return find(
|
| + package).then(
|
| + (entrypoint) =>
|
| + entrypoint.loadPackageGraph()).then(
|
| + (graph) => _precompileExecutables(graph.entrypoint, package));
|
| + });
|
| + }
|
| +
|
| + /// Gets the path to the lock file for an activated cached package with
|
| + /// [name].
|
| + String _getLockFilePath(String name) =>
|
| + p.join(_directory, name, "pubspec.lock");
|
| +
|
| + /// Shows the user a formatted list of globally activated packages.
|
| + void listActivePackages() {
|
| + if (!dirExists(_directory)) return;
|
| +
|
| + listDir(_directory).map(_loadPackageId).toList()
|
| + ..sort((id1, id2) => id1.name.compareTo(id2.name))
|
| + ..forEach((id) => log.message(_formatPackage(id)));
|
| + }
|
| +
|
| + /// Returns the [PackageId] for the globally-activated package at [path].
|
| + ///
|
| + /// [path] should be a path within [_directory]. It can either be an old-style
|
| + /// path to a single lockfile or a new-style path to a directory containing a
|
| + /// lockfile.
|
| + PackageId _loadPackageId(String path) {
|
| + var name = p.basenameWithoutExtension(path);
|
| + if (!fileExists(path)) path = p.join(path, 'pubspec.lock');
|
| +
|
| + var id =
|
| + new LockFile.load(p.join(_directory, path), cache.sources).packages[name];
|
| +
|
| + if (id == null) {
|
| + throw new FormatException(
|
| + "Pubspec for activated package $name didn't " "contain an entry for itself.");
|
| + }
|
| +
|
| + return id;
|
| + }
|
| +
|
| + /// Returns formatted string representing the package [id].
|
| + String _formatPackage(PackageId id) {
|
| + if (id.source == 'git') {
|
| + var url = GitSource.urlFromDescription(id.description);
|
| + return '${log.bold(id.name)} ${id.version} from Git repository "$url"';
|
| + } else if (id.source == 'path') {
|
| + var path = PathSource.pathFromDescription(id.description);
|
| + return '${log.bold(id.name)} ${id.version} at path "$path"';
|
| + } else {
|
| + return '${log.bold(id.name)} ${id.version}';
|
| + }
|
| + }
|
| +
|
| + /// Repairs any corrupted globally-activated packages and their binstubs.
|
| + ///
|
| + /// Returns a pair of two [int]s. The first indicates how many packages were
|
| + /// successfully re-activated; the second indicates how many failed.
|
| + Future<Pair<int, int>> repairActivatedPackages() {
|
| + final completer0 = new Completer();
|
| + scheduleMicrotask(() {
|
| + try {
|
| + var executables = {};
|
| + join0() {
|
| + var successes = 0;
|
| + var failures = 0;
|
| + join1() {
|
| + join2() {
|
| + completer0.complete(new Pair(successes, failures));
|
| + }
|
| + if (executables.isNotEmpty) {
|
| + var packages = pluralize("package", executables.length);
|
| + var message =
|
| + new StringBuffer("Binstubs exist for non-activated " "packages:\n");
|
| + executables.forEach(((package, executableNames) {
|
| + executableNames.forEach(
|
| + (executable) => deleteEntry(p.join(_binStubDir, executable)));
|
| + message.writeln(
|
| + " From ${log.bold(package)}: " "${toSentence(executableNames)}");
|
| + }));
|
| + log.error(message);
|
| + join2();
|
| + } else {
|
| + join2();
|
| + }
|
| + }
|
| + if (dirExists(_directory)) {
|
| + var it0 = listDir(_directory).iterator;
|
| + break0() {
|
| + join1();
|
| + }
|
| + var trampoline0;
|
| + continue0() {
|
| + trampoline0 = null;
|
| + if (it0.moveNext()) {
|
| + var entry = it0.current;
|
| + var id;
|
| + join3() {
|
| + trampoline0 = continue0;
|
| + do trampoline0(); while (trampoline0 != null);
|
| + }
|
| + catch0(error, stackTrace) {
|
| + try {
|
| + var message =
|
| + "Failed to reactivate " "${log.bold(p.basenameWithoutExtension(entry))}";
|
| + join4() {
|
| + log.error(message, error, stackTrace);
|
| + failures++;
|
| + tryDeleteEntry(entry);
|
| + join3();
|
| + }
|
| + if (id != null) {
|
| + message += " ${id.version}";
|
| + join5() {
|
| + join4();
|
| + }
|
| + if (id.source != "hosted") {
|
| + message += " from ${id.source}";
|
| + join5();
|
| + } else {
|
| + join5();
|
| + }
|
| + } else {
|
| + join4();
|
| + }
|
| + } catch (error, stackTrace) {
|
| + completer0.completeError(error, stackTrace);
|
| + }
|
| + }
|
| + try {
|
| + id = _loadPackageId(entry);
|
| + log.message(
|
| + "Reactivating ${log.bold(id.name)} ${id.version}...");
|
| + new Future.value(find(id.name)).then((x0) {
|
| + trampoline0 = () {
|
| + trampoline0 = null;
|
| + try {
|
| + var entrypoint = x0;
|
| + new Future.value(
|
| + entrypoint.loadPackageGraph()).then((x1) {
|
| + trampoline0 = () {
|
| + trampoline0 = null;
|
| + try {
|
| + var graph = x1;
|
| + new Future.value(
|
| + _precompileExecutables(entrypoint, id.name)).then((x2) {
|
| + trampoline0 = () {
|
| + trampoline0 = null;
|
| + try {
|
| + var snapshots = x2;
|
| + var packageExecutables =
|
| + executables.remove(id.name);
|
| + join6() {
|
| + _updateBinStubs(
|
| + graph.packages[id.name],
|
| + packageExecutables,
|
| + overwriteBinStubs: true,
|
| + snapshots: snapshots,
|
| + suggestIfNotOnPath: false);
|
| + successes++;
|
| + join3();
|
| + }
|
| + if (packageExecutables == null) {
|
| + packageExecutables = [];
|
| + join6();
|
| + } else {
|
| + join6();
|
| + }
|
| + } catch (e0, s0) {
|
| + catch0(e0, s0);
|
| + }
|
| + };
|
| + do trampoline0(); while (trampoline0 != null);
|
| + }, onError: catch0);
|
| + } catch (e1, s1) {
|
| + catch0(e1, s1);
|
| + }
|
| + };
|
| + do trampoline0(); while (trampoline0 != null);
|
| + }, onError: catch0);
|
| + } catch (e2, s2) {
|
| + catch0(e2, s2);
|
| + }
|
| + };
|
| + do trampoline0(); while (trampoline0 != null);
|
| + }, onError: catch0);
|
| + } catch (e3, s3) {
|
| + catch0(e3, s3);
|
| + }
|
| + } else {
|
| + break0();
|
| + }
|
| + }
|
| + trampoline0 = continue0;
|
| + do trampoline0(); while (trampoline0 != null);
|
| + } else {
|
| + join1();
|
| + }
|
| + }
|
| + if (dirExists(_binStubDir)) {
|
| + var it1 = listDir(_binStubDir).iterator;
|
| + break1() {
|
| + join0();
|
| + }
|
| + var trampoline1;
|
| + continue1() {
|
| + trampoline1 = null;
|
| + if (it1.moveNext()) {
|
| + var entry = it1.current;
|
| + join7() {
|
| + trampoline1 = continue1;
|
| + do trampoline1(); while (trampoline1 != null);
|
| + }
|
| + catch1(error, stackTrace) {
|
| + try {
|
| + log.error(
|
| + "Error reading binstub for " "\"${p.basenameWithoutExtension(entry)}\"",
|
| + error,
|
| + stackTrace);
|
| + tryDeleteEntry(entry);
|
| + join7();
|
| + } catch (error, stackTrace) {
|
| + completer0.completeError(error, stackTrace);
|
| + }
|
| + }
|
| + try {
|
| + var binstub = readTextFile(entry);
|
| + var package = _binStubProperty(binstub, "Package");
|
| + join8() {
|
| + var executable = _binStubProperty(binstub, "Executable");
|
| + join9() {
|
| + executables.putIfAbsent(package, (() {
|
| + return [];
|
| + })).add(executable);
|
| + join7();
|
| + }
|
| + if (executable == null) {
|
| + throw new ApplicationException("No 'Executable' property.");
|
| + join9();
|
| + } else {
|
| + join9();
|
| + }
|
| + }
|
| + if (package == null) {
|
| + throw new ApplicationException("No 'Package' property.");
|
| + join8();
|
| + } else {
|
| + join8();
|
| + }
|
| + } catch (e4, s4) {
|
| + catch1(e4, s4);
|
| + }
|
| + } else {
|
| + break1();
|
| + }
|
| + }
|
| + trampoline1 = continue1;
|
| + do trampoline1(); while (trampoline1 != null);
|
| + } else {
|
| + join0();
|
| + }
|
| + } catch (e, s) {
|
| + completer0.completeError(e, s);
|
| + }
|
| + });
|
| + return completer0.future;
|
| + }
|
| +
|
| + /// Updates the binstubs for [package].
|
| + ///
|
| + /// A binstub is a little shell script in `PUB_CACHE/bin` that runs an
|
| + /// executable from a globally activated package. This removes any old
|
| + /// binstubs from the previously activated version of the package and
|
| + /// (optionally) creates new ones for the executables listed in the package's
|
| + /// pubspec.
|
| + ///
|
| + /// [executables] is the names of the executables that should have binstubs.
|
| + /// If `null`, all executables in the package will get binstubs. If empty, no
|
| + /// binstubs will be created.
|
| + ///
|
| + /// If [overwriteBinStubs] is `true`, any binstubs that collide with
|
| + /// existing binstubs in other packages will be overwritten by this one's.
|
| + /// Otherwise, the previous ones will be preserved.
|
| + ///
|
| + /// If [snapshots] is given, it is a map of the names of executables whose
|
| + /// snapshots were precompiled to the paths of those snapshots. Binstubs for
|
| + /// those will run the snapshot directly and skip pub entirely.
|
| + ///
|
| +
|
| + /// If [suggestIfNotOnPath] is `true` (the default), this will warn the user if
|
| + /// the bin directory isn't on their path.
|
| + void _updateBinStubs(Package package, List<String> executables,
|
| + {bool overwriteBinStubs, Map<String, String> snapshots, bool suggestIfNotOnPath:
|
| + true}) {
|
| + if (snapshots == null) snapshots = const {};
|
| +
|
| + // Remove any previously activated binstubs for this package, in case the
|
| + // list of executables has changed.
|
| + _deleteBinStubs(package.name);
|
| +
|
| + if ((executables != null && executables.isEmpty) ||
|
| + package.pubspec.executables.isEmpty) {
|
| + return;
|
| + }
|
| +
|
| + ensureDir(_binStubDir);
|
| +
|
| + var installed = [];
|
| + var collided = {};
|
| + var allExecutables = ordered(package.pubspec.executables.keys);
|
| + for (var executable in allExecutables) {
|
| + if (executables != null && !executables.contains(executable)) continue;
|
| +
|
| + var script = package.pubspec.executables[executable];
|
| +
|
| + var previousPackage = _createBinStub(
|
| + package,
|
| + executable,
|
| + script,
|
| + overwrite: overwriteBinStubs,
|
| + snapshot: snapshots[script]);
|
| + if (previousPackage != null) {
|
| + collided[executable] = previousPackage;
|
| +
|
| + if (!overwriteBinStubs) continue;
|
| + }
|
| +
|
| + installed.add(executable);
|
| + }
|
| +
|
| + if (installed.isNotEmpty) {
|
| + var names = namedSequence("executable", installed.map(log.bold));
|
| + log.message("Installed $names.");
|
| + }
|
| +
|
| + // Show errors for any collisions.
|
| + if (collided.isNotEmpty) {
|
| + for (var command in ordered(collided.keys)) {
|
| + if (overwriteBinStubs) {
|
| + log.warning(
|
| + "Replaced ${log.bold(command)} previously installed from "
|
| + "${log.bold(collided[command])}.");
|
| + } else {
|
| + log.warning(
|
| + "Executable ${log.bold(command)} was already installed "
|
| + "from ${log.bold(collided[command])}.");
|
| + }
|
| + }
|
| +
|
| + if (!overwriteBinStubs) {
|
| + log.warning(
|
| + "Deactivate the other package(s) or activate "
|
| + "${log.bold(package.name)} using --overwrite.");
|
| + }
|
| + }
|
| +
|
| + // Show errors for any unknown executables.
|
| + if (executables != null) {
|
| + var unknown = ordered(
|
| + executables.where((exe) => !package.pubspec.executables.keys.contains(exe)));
|
| + if (unknown.isNotEmpty) {
|
| + dataError("Unknown ${namedSequence('executable', unknown)}.");
|
| + }
|
| + }
|
| +
|
| + // Show errors for any missing scripts.
|
| + // TODO(rnystrom): This can print false positives since a script may be
|
| + // produced by a transformer. Do something better.
|
| + var binFiles = package.listFiles(
|
| + beneath: "bin",
|
| + recursive: false).map((path) => package.relative(path)).toList();
|
| + for (var executable in installed) {
|
| + var script = package.pubspec.executables[executable];
|
| + var scriptPath = p.join("bin", "$script.dart");
|
| + if (!binFiles.contains(scriptPath)) {
|
| + log.warning(
|
| + 'Warning: Executable "$executable" runs "$scriptPath", '
|
| + 'which was not found in ${log.bold(package.name)}.');
|
| + }
|
| + }
|
| +
|
| + if (suggestIfNotOnPath && installed.isNotEmpty) {
|
| + _suggestIfNotOnPath(installed.first);
|
| + }
|
| + }
|
| +
|
| + /// Creates a binstub named [executable] that runs [script] from [package].
|
| + ///
|
| + /// If [overwrite] is `true`, this will replace an existing binstub with that
|
| + /// name for another package.
|
| + ///
|
| + /// If [snapshot] is non-null, it is a path to a snapshot file. The binstub
|
| + /// will invoke that directly. Otherwise, it will run `pub global run`.
|
| + ///
|
| + /// If a collision occurs, returns the name of the package that owns the
|
| + /// existing binstub. Otherwise returns `null`.
|
| + String _createBinStub(Package package, String executable, String script,
|
| + {bool overwrite, String snapshot}) {
|
| + var binStubPath = p.join(_binStubDir, executable);
|
| +
|
| + if (Platform.operatingSystem == "windows") binStubPath += ".bat";
|
| +
|
| + // See if the binstub already exists. If so, it's for another package
|
| + // since we already deleted all of this package's binstubs.
|
| + var previousPackage;
|
| + if (fileExists(binStubPath)) {
|
| + var contents = readTextFile(binStubPath);
|
| + previousPackage = _binStubProperty(contents, "Package");
|
| + if (previousPackage == null) {
|
| + log.fine("Could not parse binstub $binStubPath:\n$contents");
|
| + } else if (!overwrite) {
|
| + return previousPackage;
|
| + }
|
| + }
|
| +
|
| + // If the script was precompiled to a snapshot, just invoke that directly
|
| + // and skip pub global run entirely.
|
| + var invocation;
|
| + if (snapshot != null) {
|
| + // We expect absolute paths from the precompiler since relative ones
|
| + // won't be relative to the right directory when the user runs this.
|
| + assert(p.isAbsolute(snapshot));
|
| + invocation = 'dart "$snapshot"';
|
| + } else {
|
| + invocation = "pub global run ${package.name}:$script";
|
| + }
|
| +
|
| + if (Platform.operatingSystem == "windows") {
|
| + var batch = """
|
| +@echo off
|
| +rem This file was created by pub v${sdk.version}.
|
| +rem Package: ${package.name}
|
| +rem Version: ${package.version}
|
| +rem Executable: ${executable}
|
| +rem Script: ${script}
|
| +$invocation %*
|
| +""";
|
| +
|
| + if (snapshot != null) {
|
| + batch += """
|
| +
|
| +rem The VM exits with code 255 if the snapshot version is out-of-date.
|
| +rem If it is, we need to delete it and run "pub global" manually.
|
| +if not errorlevel 255 (
|
| + exit /b %errorlevel%
|
| +)
|
| +
|
| +pub global run ${package.name}:$script %*
|
| +""";
|
| + }
|
| +
|
| + writeTextFile(binStubPath, batch);
|
| + } else {
|
| + var bash = """
|
| +#!/usr/bin/env sh
|
| +# This file was created by pub v${sdk.version}.
|
| +# Package: ${package.name}
|
| +# Version: ${package.version}
|
| +# Executable: ${executable}
|
| +# Script: ${script}
|
| +$invocation "\$@"
|
| +""";
|
| +
|
| + if (snapshot != null) {
|
| + bash += """
|
| +
|
| +# The VM exits with code 255 if the snapshot version is out-of-date.
|
| +# If it is, we need to delete it and run "pub global" manually.
|
| +exit_code=\$?
|
| +if [ \$exit_code != 255 ]; then
|
| + exit \$exit_code
|
| +fi
|
| +
|
| +pub global run ${package.name}:$script "\$@"
|
| +""";
|
| + }
|
| +
|
| + writeTextFile(binStubPath, bash);
|
| +
|
| + // Make it executable.
|
| + var result = Process.runSync('chmod', ['+x', binStubPath]);
|
| + if (result.exitCode != 0) {
|
| + // Couldn't make it executable so don't leave it laying around.
|
| + try {
|
| + deleteEntry(binStubPath);
|
| + } on IOException catch (err) {
|
| + // Do nothing. We're going to fail below anyway.
|
| + log.fine("Could not delete binstub:\n$err");
|
| + }
|
| +
|
| + fail(
|
| + 'Could not make "$binStubPath" executable (exit code '
|
| + '${result.exitCode}):\n${result.stderr}');
|
| + }
|
| + }
|
| +
|
| + return previousPackage;
|
| + }
|
| +
|
| + /// Deletes all existing binstubs for [package].
|
| + void _deleteBinStubs(String package) {
|
| + if (!dirExists(_binStubDir)) return;
|
| +
|
| + for (var file in listDir(_binStubDir, includeDirs: false)) {
|
| + var contents = readTextFile(file);
|
| + var binStubPackage = _binStubProperty(contents, "Package");
|
| + if (binStubPackage == null) {
|
| + log.fine("Could not parse binstub $file:\n$contents");
|
| + continue;
|
| + }
|
| +
|
| + if (binStubPackage == package) {
|
| + log.fine("Deleting old binstub $file");
|
| + deleteEntry(file);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /// Checks to see if the binstubs are on the user's PATH and, if not, suggests
|
| + /// that the user add the directory to their PATH.
|
| + ///
|
| + /// [installed] should be the name of an installed executable that can be used
|
| + /// to test whether accessing it on the path works.
|
| + void _suggestIfNotOnPath(String installed) {
|
| + if (Platform.operatingSystem == "windows") {
|
| + // See if the shell can find one of the binstubs.
|
| + // "\q" means return exit code 0 if found or 1 if not.
|
| + var result = runProcessSync("where", [r"\q", installed + ".bat"]);
|
| + if (result.exitCode == 0) return;
|
| +
|
| + log.warning(
|
| + "${log.yellow('Warning:')} Pub installs executables into "
|
| + "${log.bold(_binStubDir)}, which is not on your path.\n"
|
| + "You can fix that by adding that directory to your system's "
|
| + '"Path" environment variable.\n'
|
| + 'A web search for "configure windows path" will show you how.');
|
| + } else {
|
| + // See if the shell can find one of the binstubs.
|
| + var result = runProcessSync("which", [installed]);
|
| + if (result.exitCode == 0) return;
|
| +
|
| + var binDir = _binStubDir;
|
| + if (binDir.startsWith(Platform.environment['HOME'])) {
|
| + binDir =
|
| + p.join("~", p.relative(binDir, from: Platform.environment['HOME']));
|
| + }
|
| +
|
| + log.warning(
|
| + "${log.yellow('Warning:')} Pub installs executables into "
|
| + "${log.bold(binDir)}, which is not on your path.\n"
|
| + "You can fix that by adding this to your shell's config file "
|
| + "(.bashrc, .bash_profile, etc.):\n" "\n"
|
| + " ${log.bold('export PATH="\$PATH":"$binDir"')}\n" "\n");
|
| + }
|
| + }
|
| +
|
| + /// Returns the value of the property named [name] in the bin stub script
|
| + /// [source].
|
| + String _binStubProperty(String source, String name) {
|
| + var pattern = new RegExp(quoteRegExp(name) + r": ([a-zA-Z0-9_-]+)");
|
| + var match = pattern.firstMatch(source);
|
| + return match == null ? null : match[1];
|
| + }
|
| +}
|
|
|