Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(293)

Unified Diff: sdk/lib/_internal/pub_generated/lib/src/global_packages.dart

Issue 745153002: Make pub's binstubs resilient to changes in snapshot format. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 6eb66cd3bd9712aaed8f7e95af6ff16eabc616c1..8073e64264e17538cf7dee5a39d8f3482be5a340 100644
--- a/sdk/lib/_internal/pub_generated/lib/src/global_packages.dart
+++ b/sdk/lib/_internal/pub_generated/lib/src/global_packages.dart
@@ -13,6 +13,7 @@ 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';
@@ -27,10 +28,6 @@ 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/
@@ -523,29 +520,35 @@ class GlobalPackages {
String _getLockFilePath(String name) =>
p.join(_directory, name, "pubspec.lock");
- /// Shows to the user formatted list of globally activated packages.
+ /// Shows the user a 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));
- } else {
- return loadPackageId(p.join(entry, 'pubspec.lock'), p.basename(entry));
- }
- }).toList();
-
- packages
+ 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') {
@@ -559,6 +562,218 @@ class GlobalPackages {
}
}
+ /// 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;
+ }
+ 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}...");
+ find(id.name).then((x0) {
+ trampoline0 = () {
+ trampoline0 = null;
+ try {
+ var entrypoint = x0;
+ entrypoint.loadPackageGraph().then((x1) {
+ trampoline0 = () {
+ trampoline0 = null;
+ try {
+ var graph = x1;
+ _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;
+ }
+ 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
@@ -576,10 +791,15 @@ class GlobalPackages {
/// 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.
+ /// 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 overwriteBinStubs, Map<String, String> snapshots, bool suggestIfNotOnPath:
+ true}) {
if (snapshots == null) snapshots = const {};
// Remove any previously activated binstubs for this package, in case the
@@ -667,7 +887,9 @@ class GlobalPackages {
}
}
- if (installed.isNotEmpty) _suggestIfNotOnPath(installed);
+ if (suggestIfNotOnPath && installed.isNotEmpty) {
+ _suggestIfNotOnPath(installed.first);
+ }
}
/// Creates a binstub named [executable] that runs [script] from [package].
@@ -691,12 +913,11 @@ class GlobalPackages {
var previousPackage;
if (fileExists(binStubPath)) {
var contents = readTextFile(binStubPath);
- var match = _binStubPackagePattern.firstMatch(contents);
- if (match != null) {
- previousPackage = match[1];
- if (!overwrite) return previousPackage;
- } else {
+ previousPackage = _binStubProperty(contents, "Package");
+ if (previousPackage == null) {
log.fine("Could not parse binstub $binStubPath:\n$contents");
+ } else if (!overwrite) {
+ return previousPackage;
}
}
@@ -722,6 +943,20 @@ 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 = """
@@ -733,6 +968,21 @@ $invocation %*
# 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.
@@ -761,13 +1011,13 @@ $invocation "\$@"
for (var file in listDir(_binStubDir, includeDirs: false)) {
var contents = readTextFile(file);
- var match = _binStubPackagePattern.firstMatch(contents);
- if (match == null) {
+ var binStubPackage = _binStubProperty(contents, "Package");
+ if (binStubPackage == null) {
log.fine("Could not parse binstub $file:\n$contents");
continue;
}
- if (match[1] == package) {
+ if (binStubPackage == package) {
log.fine("Deleting old binstub $file");
deleteEntry(file);
}
@@ -776,11 +1026,14 @@ $invocation "\$@"
/// 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) {
+ ///
+ /// [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.first + ".bat"]);
+ var result = runProcessSync("where", [r"\q", installed + ".bat"]);
if (result.exitCode == 0) return;
log.warning(
@@ -791,7 +1044,7 @@ $invocation "\$@"
'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]);
+ var result = runProcessSync("which", [installed]);
if (result.exitCode == 0) return;
var binDir = _binStubDir;
@@ -808,4 +1061,12 @@ $invocation "\$@"
" ${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];
+ }
}
« no previous file with comments | « sdk/lib/_internal/pub_generated/lib/src/entrypoint.dart ('k') | sdk/lib/_internal/pub_generated/lib/src/io.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698