| 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
|
| index 65b22690603633f60e6a43501397fd2b875c19c2..6eb66cd3bd9712aaed8f7e95af6ff16eabc616c1 100644
|
| --- a/sdk/lib/_internal/pub_generated/lib/src/global_packages.dart
|
| +++ b/sdk/lib/_internal/pub_generated/lib/src/global_packages.dart
|
| @@ -1,9 +1,16 @@
|
| +// 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 'executable.dart' as exe;
|
| @@ -19,12 +26,62 @@ import 'source/git.dart';
|
| import 'source/path.dart';
|
| import 'system_cache.dart';
|
| import 'utils.dart';
|
| +
|
| +/// Matches the package name that a binstub was created for inside the contents
|
| +/// of the shell script.
|
| final _binStubPackagePattern = new RegExp(r"Package: ([a-zA-Z0-9_-]+)");
|
| +
|
| +/// 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();
|
| @@ -41,25 +98,32 @@ class GlobalPackages {
|
| overwriteBinStubs: overwriteBinStubs).then((x1) {
|
| try {
|
| x1;
|
| - completer0.complete(null);
|
| - } catch (e1) {
|
| - completer0.completeError(e1);
|
| + completer0.complete();
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| }
|
| - }, onError: (e2) {
|
| - completer0.completeError(e2);
|
| - });
|
| - } catch (e0) {
|
| - completer0.completeError(e0);
|
| + }, onError: completer0.completeError);
|
| + } catch (e1, s1) {
|
| + completer0.completeError(e1, s1);
|
| }
|
| - }, onError: (e3) {
|
| - completer0.completeError(e3);
|
| - });
|
| - } catch (e4) {
|
| - completer0.completeError(e4);
|
| + }, 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();
|
| @@ -72,19 +136,27 @@ class GlobalPackages {
|
| overwriteBinStubs: overwriteBinStubs).then((x0) {
|
| try {
|
| x0;
|
| - completer0.complete(null);
|
| - } catch (e0) {
|
| - completer0.completeError(e0);
|
| + completer0.complete();
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| }
|
| - }, onError: (e1) {
|
| - completer0.completeError(e1);
|
| - });
|
| - } catch (e2) {
|
| - completer0.completeError(e2);
|
| + }, 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();
|
| @@ -109,7 +181,7 @@ class GlobalPackages {
|
| entrypoint.root,
|
| executables,
|
| overwriteBinStubs: overwriteBinStubs);
|
| - completer0.complete(null);
|
| + completer0.complete();
|
| }
|
| if (dirExists(binDir)) {
|
| deleteEntry(binDir);
|
| @@ -117,18 +189,18 @@ class GlobalPackages {
|
| } else {
|
| join0();
|
| }
|
| - } catch (e0) {
|
| - completer0.completeError(e0);
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| }
|
| - }, onError: (e1) {
|
| - completer0.completeError(e1);
|
| - });
|
| - } catch (e2) {
|
| - completer0.completeError(e2);
|
| + }, 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();
|
| @@ -166,30 +238,25 @@ class GlobalPackages {
|
| executables,
|
| overwriteBinStubs: overwriteBinStubs,
|
| snapshots: snapshots);
|
| - completer0.complete(null);
|
| - } catch (e3) {
|
| - completer0.completeError(e3);
|
| + completer0.complete();
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| }
|
| - }, onError: (e4) {
|
| - completer0.completeError(e4);
|
| - });
|
| - } catch (e2) {
|
| - completer0.completeError(e2);
|
| + }, onError: completer0.completeError);
|
| + } catch (e1, s1) {
|
| + completer0.completeError(e1, s1);
|
| }
|
| - }, onError: (e5) {
|
| - completer0.completeError(e5);
|
| - });
|
| - } catch (e1) {
|
| - completer0.completeError(e1);
|
| + }, onError: completer0.completeError);
|
| + } catch (e2, s2) {
|
| + completer0.completeError(e2, s2);
|
| }
|
| - }, onError: (e6) {
|
| - completer0.completeError(e6);
|
| - });
|
| + }, onError: completer0.completeError);
|
| }
|
| if (!result.succeeded) {
|
| join1() {
|
| join2() {
|
| - completer0.completeError(result.error);
|
| + throw result.error;
|
| + join0();
|
| }
|
| if (result.error is NoVersionException) {
|
| dataError(result.error.message);
|
| @@ -199,25 +266,30 @@ class GlobalPackages {
|
| }
|
| }
|
| if (result.error.package != dep.name) {
|
| - completer0.completeError(result.error);
|
| + throw result.error;
|
| + join1();
|
| } else {
|
| join1();
|
| }
|
| } else {
|
| join0();
|
| }
|
| - } catch (e0) {
|
| - completer0.completeError(e0);
|
| + } catch (e3, s3) {
|
| + completer0.completeError(e3, s3);
|
| }
|
| - }, onError: (e7) {
|
| - completer0.completeError(e7);
|
| - });
|
| - } catch (e8) {
|
| - completer0.completeError(e8);
|
| + }, 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", () {
|
| @@ -241,25 +313,25 @@ class GlobalPackages {
|
| }));
|
| completer0.complete(
|
| environment.precompileExecutables(package, binDir));
|
| - } catch (e1) {
|
| - completer0.completeError(e1);
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| }
|
| - }, onError: (e2) {
|
| - completer0.completeError(e2);
|
| - });
|
| - } catch (e0) {
|
| - completer0.completeError(e0);
|
| + }, onError: completer0.completeError);
|
| + } catch (e1, s1) {
|
| + completer0.completeError(e1, s1);
|
| }
|
| - }, onError: (e3) {
|
| - completer0.completeError(e3);
|
| - });
|
| - } catch (e4) {
|
| - completer0.completeError(e4);
|
| + }, 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(() {
|
| @@ -273,35 +345,44 @@ class GlobalPackages {
|
| try {
|
| x0;
|
| join0();
|
| - } catch (e0) {
|
| - completer0.completeError(e0);
|
| + } catch (e0, s0) {
|
| + completer0.completeError(e0, s0);
|
| }
|
| - }, onError: (e1) {
|
| - completer0.completeError(e1);
|
| - });
|
| + }, onError: completer0.completeError);
|
| } else {
|
| join0();
|
| }
|
| - } catch (e2) {
|
| - completer0.completeError(e2);
|
| + } 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(
|
| @@ -317,20 +398,35 @@ class GlobalPackages {
|
| '${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) {
|
| + // TODO(rnystrom): Use async/await here when on __ catch is supported.
|
| + // See: https://github.com/dart-lang/async_await/issues/27
|
| return new Future.sync(() {
|
| var lockFilePath = _getLockFilePath(name);
|
| var lockFile;
|
| @@ -339,31 +435,57 @@ class GlobalPackages {
|
| } on IOException catch (error) {
|
| var oldLockFilePath = p.join(_directory, '$name.lock');
|
| try {
|
| + // TODO(nweiz): This looks for 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).
|
| lockFile = new LockFile.load(oldLockFilePath, cache.sources);
|
| } on IOException catch (error) {
|
| + // If we couldn't read the lock file, it's not activated.
|
| dataError("No active package ${log.bold(name)}.");
|
| }
|
| +
|
| + // Move the old lockfile to its new location.
|
| ensureDir(p.dirname(lockFilePath));
|
| new File(oldLockFilePath).renameSync(lockFilePath);
|
| }
|
| +
|
| + // Load the package from the cache.
|
| var id = lockFile.packages[name];
|
| lockFile.packages.remove(name);
|
| +
|
| var source = cache.sources[id.source];
|
| if (source is CachedSource) {
|
| + // For cached sources, the package itself is in the cache and the
|
| + // lockfile is the one we just loaded.
|
| return cache.sources[id.source].getDirectory(
|
| id).then((dir) => new Package.load(name, dir, cache.sources)).then((package) {
|
| return new Entrypoint.inMemory(package, lockFile, cache);
|
| });
|
| }
|
| +
|
| + // For uncached sources (i.e. path), the ID just points to the real
|
| + // directory for the package.
|
| assert(id.source == "path");
|
| return new Entrypoint(
|
| PathSource.pathFromDescription(id.description),
|
| cache);
|
| });
|
| }
|
| +
|
| + /// 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'))) {
|
| @@ -377,9 +499,13 @@ class GlobalPackages {
|
| 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(
|
| @@ -391,14 +517,22 @@ class GlobalPackages {
|
| (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 to the user formatted list of globally activated packages.
|
| void listActivePackages() {
|
| if (!dirExists(_directory)) return;
|
| +
|
| + // Loads lock [file] and returns [PackageId] of the activated package.
|
| loadPackageId(file, name) {
|
| var lockFile = new LockFile.load(p.join(_directory, file), cache.sources);
|
| return lockFile.packages[name];
|
| }
|
| +
|
| var packages = listDir(_directory).map((entry) {
|
| if (fileExists(entry)) {
|
| return loadPackageId(entry, p.basenameWithoutExtension(entry));
|
| @@ -406,10 +540,13 @@ class GlobalPackages {
|
| return loadPackageId(p.join(entry, 'pubspec.lock'), p.basename(entry));
|
| }
|
| }).toList();
|
| +
|
| packages
|
| ..sort((id1, id2) => id1.name.compareTo(id2.name))
|
| ..forEach((id) => log.message(_formatPackage(id)));
|
| }
|
| +
|
| + /// Returns formatted string representing the package [id].
|
| String _formatPackage(PackageId id) {
|
| if (id.source == 'git') {
|
| var url = GitSource.urlFromDescription(id.description);
|
| @@ -421,21 +558,49 @@ class GlobalPackages {
|
| return '${log.bold(id.name)} ${id.version}';
|
| }
|
| }
|
| +
|
| + /// 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 that were precompiled to their paths. Binstubs for those will
|
| + /// run the snapshot directly and skip pub entirely.
|
| void _updateBinStubs(Package package, List<String> executables,
|
| {bool overwriteBinStubs, Map<String, String> snapshots}) {
|
| 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,
|
| @@ -444,14 +609,19 @@ class GlobalPackages {
|
| 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) {
|
| @@ -464,12 +634,15 @@ class GlobalPackages {
|
| "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)));
|
| @@ -477,6 +650,10 @@ class GlobalPackages {
|
| 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();
|
| @@ -489,12 +666,28 @@ class GlobalPackages {
|
| 'which was not found in ${log.bold(package.name)}.');
|
| }
|
| }
|
| +
|
| if (installed.isNotEmpty) _suggestIfNotOnPath(installed);
|
| }
|
| +
|
| + /// 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);
|
| @@ -506,13 +699,19 @@ class GlobalPackages {
|
| log.fine("Could not parse binstub $binStubPath:\n$contents");
|
| }
|
| }
|
| +
|
| + // 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
|
| @@ -535,22 +734,31 @@ $invocation %*
|
| $invocation "\$@"
|
| """;
|
| 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 match = _binStubPackagePattern.firstMatch(contents);
|
| @@ -558,16 +766,23 @@ $invocation "\$@"
|
| log.fine("Could not parse binstub $file:\n$contents");
|
| continue;
|
| }
|
| +
|
| if (match[1] == 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.
|
| void _suggestIfNotOnPath(List<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.first + ".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"
|
| @@ -575,13 +790,16 @@ $invocation "\$@"
|
| '"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.first]);
|
| 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"
|
|
|