--enable-experiments for `pub run` (#2493)
* Fix global run
* Normalize executable paths on windows (use \)
* Document lack of snapshotting when experiments are enabled
diff --git a/lib/src/command/global_run.dart b/lib/src/command/global_run.dart
index 221d05c..82d6783 100644
--- a/lib/src/command/global_run.dart
+++ b/lib/src/command/global_run.dart
@@ -7,6 +7,7 @@
import 'package:path/path.dart' as p;
import '../command.dart';
+import '../executable.dart';
import '../io.dart';
import '../log.dart' as log;
import '../utils.dart';
@@ -27,6 +28,10 @@
GlobalRunCommand() {
argParser.addFlag('enable-asserts', help: 'Enable assert statements.');
argParser.addFlag('checked', abbr: 'c', hide: true);
+ argParser.addMultiOption('enable-experiment',
+ help: 'Runs the executable in a VM with the given experiments enabled. '
+ '(Will disable snapshotting, resulting in slower startup)',
+ valueHelp: 'experiment');
argParser.addOption('mode', help: 'Deprecated option', hide: true);
}
@@ -57,8 +62,14 @@
log.warning('The --mode flag is deprecated and has no effect.');
}
- var exitCode = await globals.runExecutable(package, executable, args,
- enableAsserts: argResults['enable-asserts'] || argResults['checked']);
+ final experiments = argResults['enable-experiment'] as List;
+ final vmArgs = vmArgFromExperiments(experiments);
+ final globalEntrypoint = await globals.find(package);
+ final exitCode = await runExecutable(globalEntrypoint,
+ Executable.adaptProgramName(package, executable), args,
+ vmArgs: vmArgs,
+ enableAsserts: argResults['enable-asserts'] || argResults['checked'],
+ recompile: globalEntrypoint.precompileExecutable);
await flushThenExit(exitCode);
}
}
diff --git a/lib/src/command/run.dart b/lib/src/command/run.dart
index 9e66cec..931e8ec 100644
--- a/lib/src/command/run.dart
+++ b/lib/src/command/run.dart
@@ -28,6 +28,11 @@
RunCommand() {
argParser.addFlag('enable-asserts', help: 'Enable assert statements.');
argParser.addFlag('checked', abbr: 'c', hide: true);
+ argParser.addMultiOption('enable-experiment',
+ help:
+ 'Runs the executable in a VM with the given experiments enabled.\n'
+ '(Will disable snapshotting, resulting in slower startup)',
+ valueHelp: 'experiment');
argParser.addOption('mode', help: 'Deprecated option', hide: true);
// mode exposed for `dartdev run` to use as subprocess.
argParser.addFlag('dart-dev-run', hide: true);
@@ -67,28 +72,14 @@
log.warning('The --mode flag is deprecated and has no effect.');
}
- // The user may pass in an executable without an extension, but the file
- // to actually execute will always have one.
- if (p.extension(executable) != '.dart') executable += '.dart';
+ final experiments = argResults['enable-experiment'] as List;
+ final vmArgs = vmArgFromExperiments(experiments);
- var snapshotPath = p.join(
- entrypoint.cachePath, 'bin', package, '$executable.snapshot.dart2');
-
- // Don't ever compile snapshots for mutable packages, since their code may
- // change later on.
- var useSnapshot = fileExists(snapshotPath) ||
- (package != entrypoint.root.name &&
- !entrypoint.packageGraph.isPackageMutable(package));
-
- var exitCode = await runExecutable(entrypoint, package, executable, args,
+ var exitCode = await runExecutable(
+ entrypoint, Executable.adaptProgramName(package, executable), args,
enableAsserts: argResults['enable-asserts'] || argResults['checked'],
- snapshotPath: useSnapshot ? snapshotPath : null, recompile: () {
- final pkg = entrypoint.packageGraph.packages[package];
- // The recompile function will only be called when [package] exists.
- assert(pkg != null);
- final executablePath = pkg.path(p.join('bin', executable));
- return entrypoint.precompileExecutable(package, executablePath);
- });
+ recompile: entrypoint.precompileExecutable,
+ vmArgs: vmArgs);
await flushThenExit(exitCode);
}
@@ -101,7 +92,8 @@
///
/// Runs `bin/<command>.dart` from package `<package>`. If `<package>` is not
/// mutable (local root package or path-dependency) a source snapshot will be
- /// cached in `.dart_tool/pub/bin/<package>/<command>.dart.snapshot.dart2`.
+ /// cached in
+ /// `.dart_tool/pub/bin/<package>/<command>.dart-<sdkVersion>.snapshot`.
Future _runFromDartDev() async {
var package = entrypoint.root.name;
var command = package;
@@ -126,41 +118,13 @@
args = argResults.rest.skip(1).toList();
}
- String snapshotPath(String command) => p.join(
- entrypoint.cachePath,
- 'bin',
- package,
- '$command.dart.snapshot.dart2',
- );
-
- // If snapshot exists, we strive to avoid using [entrypoint.packageGraph]
- // because this will load additional files. Instead we just run with the
- // snapshot. Note. that `pub get|upgrade` will purge snapshots.
- var snapshotExists = fileExists(snapshotPath(command));
-
- // Don't ever compile snapshots for mutable packages, since their code may
- // change later on. Don't check if this the case if a snapshot already
- // exists.
- var useSnapshot = snapshotExists ||
- (package != entrypoint.root.name &&
- !entrypoint.packageGraph.isPackageMutable(package));
+ final experiments = argResults['enable-experiment'] as List;
+ final vmArgs = vmArgFromExperiments(experiments);
return await flushThenExit(await runExecutable(
- entrypoint,
- package,
- '$command.dart',
- args,
- enableAsserts: argResults['enable-asserts'] || argResults['checked'],
- snapshotPath: useSnapshot ? snapshotPath(command) : null,
- recompile: () {
- final pkg = entrypoint.packageGraph.packages[package];
- // The recompile function will only be called when [package] exists.
- assert(pkg != null);
- return entrypoint.precompileExecutable(
- package,
- pkg.path('bin', '$command.dart'),
- );
- },
- ));
+ entrypoint, Executable(package, 'bin/$command.dart'), args,
+ vmArgs: vmArgs,
+ enableAsserts: argResults['enable-asserts'] || argResults['checked'],
+ recompile: entrypoint.precompileExecutable));
}
}
diff --git a/lib/src/dart.dart b/lib/src/dart.dart
index c6e1418..93a06d0 100644
--- a/lib/src/dart.dart
+++ b/lib/src/dart.dart
@@ -34,7 +34,7 @@
});
}
-/// Snapshots the Dart executable at [executableUrl] to a snapshot at
+/// Snapshots the Dart executable at [executablePath] to a snapshot at
/// [snapshotPath].
///
/// If [packagesFile] is passed, it's used to resolve `package:` URIs in the
@@ -43,11 +43,15 @@
///
/// If [name] is passed, it is used to describe the executable in logs and error
/// messages.
-Future snapshot(Uri executableUrl, String snapshotPath,
- {Uri packagesFile, String name}) async {
- name = log.bold(name ?? executableUrl.toString());
+Future snapshot(
+ String executablePath,
+ String snapshotPath, {
+ Uri packagesFile,
+ String name,
+}) async {
+ name = log.bold(name ?? executablePath.toString());
- var args = ['--snapshot=$snapshotPath', executableUrl.toString()];
+ var args = ['--snapshot=$snapshotPath', p.toUri(executablePath).toString()];
if (packagesFile != null) {
// Resolve [packagesFile] in case it's relative to work around sdk#33177.
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index 6e545fd..9a29ff7 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -15,6 +15,7 @@
import 'dart.dart' as dart;
import 'exceptions.dart';
+import 'executable.dart';
import 'http.dart' as http;
import 'io.dart';
import 'lock_file.dart';
@@ -75,10 +76,6 @@
/// the network.
final SystemCache cache;
- /// Whether this entrypoint is in memory only, as opposed to representing a
- /// real directory on disk.
- final bool _inMemory;
-
/// Whether this entrypoint exists within the package cache.
bool get isCached => root.dir != null && p.isWithin(cache.rootDir, root.dir);
@@ -122,31 +119,50 @@
PackageGraph _packageGraph;
+ /// Where the lock file and package configurations are to be found.
+ ///
+ /// Global packages (except those from path source)
+ /// store these in the global cache.
+ String get _configRoot =>
+ isCached ? p.join(cache.rootDir, 'global_packages', root.name) : root.dir;
+
/// The path to the entrypoint's "packages" directory.
String get packagesPath => root.path('packages');
/// The path to the entrypoint's ".packages" file.
- String get packagesFile => root.path('.packages');
+ String get packagesFile => p.join(_configRoot, '.packages');
/// The path to the entrypoint's ".dart_tool/package_config.json" file.
String get packageConfigFile =>
- root.path('.dart_tool', 'package_config.json');
+ p.join(_configRoot, '.dart_tool', 'package_config.json');
/// The path to the entrypoint package's pubspec.
String get pubspecPath => root.path('pubspec.yaml');
/// The path to the entrypoint package's lockfile.
- String get lockFilePath => root.path('pubspec.lock');
+ String get lockFilePath => p.join(_configRoot, 'pubspec.lock');
/// The path to the entrypoint package's `.dart_tool/pub` cache directory.
///
/// If the old-style `.pub` directory is being used, this returns that
/// instead.
+ ///
+ /// For globally activated packages from path, this is not the same as
+ /// [configRoot], because the snapshots should be stored in the global cache,
+ /// but the configuration is stored at the package itself.
String get cachePath {
- var newPath = root.path('.dart_tool/pub');
- var oldPath = root.path('.pub');
- if (!dirExists(newPath) && dirExists(oldPath)) return oldPath;
- return newPath;
+ if (isGlobal) {
+ return p.join(
+ cache.rootDir,
+ 'global_packages',
+ root.name,
+ );
+ } else {
+ var newPath = root.path('.dart_tool/pub');
+ var oldPath = root.path('.pub');
+ if (!dirExists(newPath) && dirExists(oldPath)) return oldPath;
+ return newPath;
+ }
}
/// The path to the directory containing dependency executable snapshots.
@@ -155,27 +171,21 @@
/// Loads the entrypoint for the package at the current directory.
Entrypoint.current(this.cache)
: root = Package.load(null, '.', cache.sources, isRootPackage: true),
- _inMemory = false,
isGlobal = false;
/// Loads the entrypoint from a package at [rootDir].
Entrypoint(String rootDir, this.cache)
: root = Package.load(null, rootDir, cache.sources, isRootPackage: true),
- _inMemory = false,
- isGlobal = true;
+ isGlobal = false;
/// Creates an entrypoint given package and lockfile objects.
- Entrypoint.inMemory(this.root, this._lockFile, this.cache)
- : _inMemory = true,
- isGlobal = true;
-
- /// Creates an entrypoint given a package and a [solveResult], from which the
- /// package graph and lockfile will be computed.
- Entrypoint.fromSolveResult(this.root, this.cache, SolveResult solveResult)
- : _inMemory = true,
- isGlobal = true {
- _packageGraph = PackageGraph.fromSolveResult(this, solveResult);
- _lockFile = _packageGraph.lockFile;
+ /// If a SolveResult is already created it can be passes as an optimization.
+ Entrypoint.global(this.root, this._lockFile, this.cache,
+ {SolveResult solveResult})
+ : isGlobal = true {
+ if (solveResult != null) {
+ _packageGraph = PackageGraph.fromSolveResult(this, solveResult);
+ }
}
/// Writes .packages and .dart_tool/package_config.json
@@ -274,59 +284,98 @@
}
}
- /// Precompiles all executables from dependencies that don't transitively
- /// depend on [this] or on a path dependency.
- Future precompileExecutables({Iterable<String> changed}) async {
- migrateCache();
- _deleteExecutableSnapshots(changed: changed);
-
- var executables = mapMap<String, PackageRange, String, List<String>>(
- root.immediateDependencies,
- value: (name, _) => _executablesForPackage(name));
-
- for (var package in executables.keys.toList()) {
- if (executables[package].isEmpty) executables.remove(package);
+ /// All executables that should be snapshotted from this entrypoint.
+ ///
+ /// This is all executables in direct dependencies.
+ /// that don't transitively depend on [this] or on a mutable dependency.
+ ///
+ /// Except globally activated packages they should precompile executables from
+ /// the package itself if they are immutable.
+ List<Executable> get precompiledExecutables {
+ if (isGlobal) {
+ if (isCached) {
+ return root.executablePaths
+ .map((path) => Executable(root.name, path))
+ .toList();
+ } else {
+ return <Executable>[];
+ }
}
+ final r = root.immediateDependencies.keys.expand((packageName) {
+ if (packageGraph.isPackageMutable(packageName)) {
+ return <Executable>[];
+ }
+ final package = packageGraph.packages[packageName];
+ return package.executablePaths
+ .map((path) => Executable(packageName, path));
+ }).toList();
+ return r;
+ }
+
+ /// Precompiles all [precompiledExecutables].
+ Future<void> precompileExecutables({Iterable<String> changed}) async {
+ migrateCache();
+
+ final executables = precompiledExecutables;
if (executables.isEmpty) return;
await log.progress('Precompiling executables', () async {
- ensureDir(_snapshotPath);
-
- // Make sure there's a trailing newline so our version file matches the
- // SDK's.
- writeTextFile(p.join(_snapshotPath, 'sdk-version'), '${sdk.version}\n');
-
- await _precompileExecutables(executables);
+ if (isGlobal) {
+ /// Global snapshots might linger in the cache if we don't remove old
+ /// snapshots when it is re-activated.
+ cleanDir(_snapshotPath);
+ } else {
+ ensureDir(_snapshotPath);
+ }
+ return waitAndPrintErrors(executables.map((executable) {
+ var dir = p.dirname(snapshotPathOfExecutable(executable));
+ cleanDir(dir);
+ return waitAndPrintErrors(executables.map(_precompileExecutable));
+ }));
});
}
- //// Precompiles [executables] to snapshots from the filesystem.
- Future<void> _precompileExecutables(Map<String, List<String>> executables) {
- return waitAndPrintErrors(executables.keys.map((package) {
- var dir = p.join(_snapshotPath, package);
- cleanDir(dir);
- return waitAndPrintErrors(executables[package].map((path) =>
- _precompileExecutable(
- package, p.join(packageGraph.packages[package].dir, path))));
- }));
- }
-
/// Precompiles executable .dart file at [path] to a snapshot.
- Future<void> precompileExecutable(String package, String path) async {
+ Future<void> precompileExecutable(Executable executable) async {
return await log.progress('Precompiling executable', () async {
- var dir = p.join(_snapshotPath, package);
- ensureDir(dir);
- return waitAndPrintErrors([_precompileExecutable(package, path)]);
+ ensureDir(p.dirname(snapshotPathOfExecutable(executable)));
+ return waitAndPrintErrors([_precompileExecutable(executable)]);
});
}
- Future<void> _precompileExecutable(String package, String path) async {
- var dir = p.join(_snapshotPath, package);
+ Future<void> _precompileExecutable(Executable executable) async {
+ final package = executable.package;
await dart.snapshot(
- p.toUri(path), p.join(dir, p.basename(path) + '.snapshot.dart2'),
+ resolveExecutable(executable), snapshotPathOfExecutable(executable),
packagesFile: p.toUri(packagesFile),
- name: '$package:${p.basenameWithoutExtension(path)}');
+ name:
+ '$package:${p.basenameWithoutExtension(executable.relativePath)}');
+ }
+
+ /// The location of the snapshot of the dart program at [path] in [package]
+ /// will be stored here.
+ ///
+ /// We use the sdk version to make sure we don't run snapshots from a
+ /// different sdk.
+ ///
+ /// [path] must be relative.
+ String snapshotPathOfExecutable(Executable executable) {
+ assert(p.isRelative(executable.relativePath));
+ final versionSuffix = sdk.version;
+ return isGlobal
+ ? p.join(_snapshotPath,
+ '${p.basename(executable.relativePath)}-$versionSuffix.snapshot')
+ : p.join(_snapshotPath, executable.package,
+ '${p.basename(executable.relativePath)}-$versionSuffix.snapshot');
+ }
+
+ /// The absolute path of [executable] resolved relative to [this].
+ String resolveExecutable(Executable executable) {
+ return p.join(
+ packageGraph.packages[executable.package].dir,
+ executable.relativePath,
+ );
}
/// Deletes outdated cached executable snapshots.
@@ -368,34 +417,6 @@
}
}
- /// Returns the list of all paths within [packageName] that should be
- /// precompiled.
- List<String> _executablesForPackage(String packageName) {
- var package = packageGraph.packages[packageName];
- var binDir = package.path('bin');
- if (!dirExists(binDir)) return [];
- if (packageGraph.isPackageMutable(packageName)) return [];
-
- var executables = package.executablePaths;
-
- // If any executables don't exist, recompile all executables.
- //
- // Normally, [_deleteExecutableSnapshots] will ensure that all the outdated
- // executable directories will be deleted, any checking for any non-existent
- // executable will save us a few IO operations over checking each one. If
- // some executables do exist and some do not, the directory is corrupted and
- // it's good to start from scratch anyway.
- var executablesExist = executables.every((executable) {
- var snapshotPath = p.join(_snapshotPath, packageName,
- '${p.basename(executable)}.snapshot.dart2');
- return fileExists(snapshotPath);
- });
- if (!executablesExist) return executables;
-
- // Otherwise, we don't need to recompile.
- return [];
- }
-
/// Makes sure the package at [id] is locally available.
///
/// This automatically downloads the package to the system-wide cache as well
@@ -414,7 +435,7 @@
/// `.dart_tool/package_config.json` file doesn't exist or if it's out-of-date
/// relative to the lockfile or the pubspec.
void assertUpToDate() {
- if (_inMemory) return;
+ if (isCached) return;
if (!entryExists(lockFilePath)) {
dataError('No pubspec.lock file found, please run "pub get" first.');
@@ -761,7 +782,10 @@
/// If the entrypoint uses the old-style `.pub` cache directory, migrates it
/// to the new-style `.dart_tool/pub` directory.
void migrateCache() {
- var oldPath = root.path('.pub');
+ // Cached packages don't have these.
+ if (isCached) return;
+
+ var oldPath = p.join(_configRoot, '.pub');
if (!dirExists(oldPath)) return;
var newPath = root.path('.dart_tool/pub');
diff --git a/lib/src/executable.dart b/lib/src/executable.dart
index 720691c..04e5d78 100644
--- a/lib/src/executable.dart
+++ b/lib/src/executable.dart
@@ -7,6 +7,7 @@
import 'dart:isolate';
import 'package:path/path.dart' as p;
+import 'package:pedantic/pedantic.dart';
import 'entrypoint.dart';
import 'exit_codes.dart' as exit_codes;
@@ -15,6 +16,14 @@
import 'log.dart' as log;
import 'utils.dart';
+/// Take a list of experiments to enable and turn them into a list with a single
+/// argument to pass to the VM enabling the same experiments.
+List<String> vmArgFromExperiments(List<String> experiments) {
+ return [
+ if (experiments.isNotEmpty) "--enable-experiment=${experiments.join(',')}"
+ ];
+}
+
/// Runs [executable] from [package] reachable from [entrypoint].
///
/// The [executable] is a relative path to a Dart file within [package], which
@@ -27,24 +36,22 @@
/// If [packagesFile] is passed, it's used as the package config file path for
/// the executable. Otherwise, `entrypoint.packagesFile` is used.
///
-/// If [snapshotPath] is passed, this will run the executable from that snapshot
-/// if it exists. If [recompile] is passed, it's called if the snapshot is
-/// out-of-date or nonexistent. It's expected to regenerate a snapshot at
-/// [snapshotPath], after which the snapshot will be re-run. It's ignored if
-/// [snapshotPath] isn't passed.
+/// If the executable is in an immutable package and we pass no [vmArgs], it
+/// run from snapshot (and precompiled if the snapshot doesn't already exist).
///
/// Returns the exit code of the spawned app.
-Future<int> runExecutable(Entrypoint entrypoint, String package,
- String executable, Iterable<String> args,
+Future<int> runExecutable(
+ Entrypoint entrypoint, Executable executable, Iterable<String> args,
{bool enableAsserts = false,
String packagesFile,
- String snapshotPath,
- Future<void> Function() recompile}) async {
+ Future<void> Function(Executable) recompile,
+ List<String> vmArgs = const []}) async {
+ final package = executable.package;
packagesFile ??= entrypoint.packagesFile;
// Make sure the package is an immediate dependency of the entrypoint or the
// entrypoint itself.
- if (entrypoint.root.name != package &&
+ if (entrypoint.root.name != executable.package &&
!entrypoint.root.immediateDependencies.containsKey(package)) {
if (entrypoint.packageGraph.packages.containsKey(package)) {
dataError('Package "$package" is not an immediate dependency.\n'
@@ -57,130 +64,155 @@
entrypoint.migrateCache();
- // Unless the user overrides the verbosity, we want to filter out the
- // normal pub output that may be shown when recompiling snapshots if we are
- // not attached to a terminal. This is to not pollute stdout when the output
- // of `pub run` is piped somewhere.
- return await log.warningsOnlyUnlessTerminal(() async {
- // Uncached packages are run from source.
- if (snapshotPath != null) {
- // Since we don't access the package graph, this doesn't happen
- // automatically.
- entrypoint.assertUpToDate();
+ var snapshotPath = entrypoint.snapshotPathOfExecutable(executable);
- var result = await _runOrCompileSnapshot(snapshotPath, args,
- packagesFile: packagesFile,
- enableAsserts: enableAsserts,
- recompile: recompile);
- if (result != null) return result;
+ // Don't compile snapshots for mutable packages, since their code may
+ // change later on.
+ //
+ // Also we don't snapshot if we have non-default arguments to the VM, as
+ // these would be inconsistent if another set of settings are given in a
+ // later invocation.
+ var useSnapshot =
+ !entrypoint.packageGraph.isPackageMutable(package) && vmArgs.isEmpty;
+
+ var executablePath = entrypoint.resolveExecutable(executable);
+ if (!fileExists(executablePath)) {
+ var message =
+ 'Could not find ${log.bold(p.normalize(executable.relativePath))}';
+ if (entrypoint.isGlobal || package != entrypoint.root.name) {
+ message += ' in package ${log.bold(package)}';
}
+ log.error('$message.');
+ return exit_codes.NO_INPUT;
+ }
- // If the command has a path separator, then it's a path relative to the
- // root of the package. Otherwise, it's implicitly understood to be in
- // "bin".
- if (p.split(executable).length == 1) executable = p.join('bin', executable);
+ if (useSnapshot) {
+ // Since we don't access the package graph, this doesn't happen
+ // automatically.
+ entrypoint.assertUpToDate();
- var executablePath = await _executablePath(entrypoint, package, executable);
-
+ if (!fileExists(snapshotPath)) {
+ await recompile(executable);
+ }
+ executablePath = snapshotPath;
+ } else {
if (executablePath == null) {
- var message = 'Could not find ${log.bold(executable)}';
+ var message =
+ 'Could not find ${log.bold(p.normalize(executable.relativePath))}';
if (entrypoint.isGlobal || package != entrypoint.root.name) {
message += ' in package ${log.bold(package)}';
}
log.error('$message.');
return exit_codes.NO_INPUT;
}
-
- // We use an absolute path here not because the VM insists but because it's
- // helpful for the subprocess to be able to spawn Dart with
- // Platform.executableArguments and have that work regardless of the working
- // directory.
- var packageConfig = p.toUri(p.absolute(packagesFile));
-
- await isolate.runUri(p.toUri(executablePath), args.toList(), null,
- enableAsserts: enableAsserts,
- automaticPackageResolution: packageConfig == null,
- packageConfig: packageConfig);
- return exitCode;
- });
-}
-
-/// Returns the full path the VM should use to load the executable at [path].
-///
-/// [path] must be relative to the root of [package]. If [path] doesn't exist,
-/// returns `null`. If the executable is global and doesn't already have a
-/// `.packages` file one will be created.
-Future<String> _executablePath(
- Entrypoint entrypoint, String package, String path) async {
- assert(p.isRelative(path));
-
- var fullPath = entrypoint.packageGraph.packages[package].path(path);
- if (!fileExists(fullPath)) return null;
- return p.absolute(fullPath);
-}
-
-/// Like [_runSnapshot], but runs [recompile] if [path] doesn't exist yet.
-///
-/// Returns `null` if [path] doesn't exist and isn't generated by [recompile].
-Future<int> _runOrCompileSnapshot(String path, Iterable<String> args,
- {Future<void> Function() recompile,
- String packagesFile,
- bool enableAsserts = false}) async {
- if (!fileExists(path)) {
- if (recompile == null) return null;
- await recompile();
- if (!fileExists(path)) return null;
}
- return await _runSnapshot(path, args,
- recompile: recompile,
- packagesFile: packagesFile,
- enableAsserts: enableAsserts);
-}
+ // We use an absolute path here not because the VM insists but because it's
+ // helpful for the subprocess to be able to spawn Dart with
+ // Platform.executableArguments and have that work regardless of the working
+ // directory.
+ var packageConfig = p.absolute(packagesFile);
-/// Runs the snapshot at [path] with [args] and hooks its stdout, stderr, and
-/// sdtin to this process's.
-///
-/// If [recompile] is passed, it's called if the snapshot is out-of-date. It's
-/// expected to regenerate a snapshot at [path], after which the snapshot will
-/// be re-run.
-///
-/// If [enableAsserts] is set, runs the snapshot with assertions enabled.
-///
-/// Returns the snapshot's exit code.
-///
-/// This doesn't do any validation of the snapshot's SDK version.
-Future<int> _runSnapshot(String path, Iterable<String> args,
- {Future<void> Function() recompile,
- String packagesFile,
- bool enableAsserts = false}) async {
- Uri packageConfig;
- if (packagesFile != null) {
- // We use an absolute path here not because the VM insists but because it's
- // helpful for the subprocess to be able to spawn Dart with
- // Platform.executableArguments and have that work regardless of the working
- // directory.
- packageConfig = p.toUri(p.absolute(packagesFile));
- }
-
- var url = p.toUri(p.absolute(path));
- var argList = args.toList();
try {
- await isolate.runUri(url, argList, null,
- enableAsserts: enableAsserts,
- automaticPackageResolution: packageConfig == null,
- packageConfig: packageConfig);
+ return await _runDartProgram(executablePath, args, packageConfig,
+ enableAsserts: enableAsserts, vmArgs: vmArgs);
} on IsolateSpawnException catch (error) {
- if (recompile == null) rethrow;
- if (!error.message.contains('Invalid kernel binary format version')) {
+ if (!useSnapshot ||
+ !error.message.contains('Invalid kernel binary format version')) {
rethrow;
}
log.fine('Precompiled executable is out of date.');
- await recompile();
- await isolate.runUri(url, argList, null,
- enableAsserts: enableAsserts, packageConfig: packageConfig);
+ await recompile(executable);
+ return _runDartProgram(executablePath, args, packageConfig,
+ enableAsserts: enableAsserts, vmArgs: vmArgs);
}
+}
- return exitCode;
+/// Runs the dart program (can be a snapshot) at [path] with [args] and hooks
+/// its stdout, stderr, and sdtin to this process's.
+///
+/// [packageConfig] is the path to the ".dart_tool/package_config.json" file.
+///
+/// If [enableAsserts] is set, runs the program with assertions enabled.
+///
+/// Passes [vmArgs] to the vm.
+///
+/// Returns the programs's exit code.
+Future<int> _runDartProgram(
+ String path, List<String> args, String packageConfig,
+ {bool enableAsserts, List<String> vmArgs}) async {
+ path = p.absolute(path);
+ packageConfig = p.absolute(packageConfig);
+
+ // We use Isolate.spawnUri when there are no extra vm-options.
+ // That provides better signal handling, and possibly faster startup.
+ if (vmArgs.isEmpty) {
+ var argList = args.toList();
+ await isolate.runUri(p.toUri(path), argList, null,
+ enableAsserts: enableAsserts,
+ automaticPackageResolution: packageConfig == null,
+ packageConfig: p.toUri(packageConfig));
+ return exitCode;
+ } else {
+ // By ignoring sigint, only the child process will get it when
+ // they are sent to the current process group. That is what happens when
+ // you send signals from the terminal.
+ //
+ // This allows the child to not be orphaned if it sets up handlers for these
+ // signals.
+ //
+ // We do not drain sighub because it is generally a bad idea to have
+ // non-default handling for it.
+ //
+ // We do not drain sigterm and sigusr1/sigusr2 because it does not seem to
+ // work well in manual tests.
+ //
+ // We do not drain sigquit because dart doesn't support listening to it.
+ // https://github.com/dart-lang/sdk/issues/41961 .
+ //
+ // TODO(sigurdm) To handle signals better we would ideally have `exec`
+ // semantics without `fork` for starting the subprocess.
+ // https://github.com/dart-lang/sdk/issues/41966.
+ unawaited(ProcessSignal.sigint.watch().drain());
+
+ final process = await Process.start(
+ Platform.resolvedExecutable,
+ [
+ '--packages=$packageConfig',
+ ...vmArgs,
+ if (enableAsserts) '--enable-asserts',
+ p.toUri(path).toString(),
+ ...args,
+ ],
+ mode: ProcessStartMode.inheritStdio,
+ );
+
+ return process.exitCode;
+ }
+}
+
+/// An executable in a package
+class Executable {
+ String package;
+ // The relative path to the executable inside the root of [package].
+ String relativePath;
+
+ // Adapts the program-name following conventions of dart run
+ Executable.adaptProgramName(this.package, String program)
+ : relativePath = _adaptProgramToPath(program);
+
+ Executable(this.package, this.relativePath);
+
+ static String _adaptProgramToPath(String program) {
+ // If the command has a path separator, then it's a path relative to the
+ // root of the package. Otherwise, it's implicitly understood to be in
+ // "bin".
+ if (p.split(program).length == 1) program = p.join('bin', program);
+
+ // The user may pass in an executable without an extension, but the file
+ // to actually execute will always have one.
+ if (p.extension(program) != '.dart') program += '.dart';
+ return program;
+ }
}
diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart
index 2cb0b64..f052b60 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -5,13 +5,13 @@
import 'dart:async';
import 'dart:io';
+import 'package:meta/meta.dart';
import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';
-import 'dart.dart' as dart;
import 'entrypoint.dart';
import 'exceptions.dart';
-import 'executable.dart' as exe;
+import 'executable.dart';
import 'http.dart' as http;
import 'io.dart';
import 'lock_file.dart';
@@ -104,8 +104,6 @@
/// Finds the latest version of the hosted package with [name] that matches
/// [constraint] and makes it the active global version.
///
- /// The [features] map controls which features of the package to activate.
- ///
/// [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.
@@ -162,7 +160,7 @@
var binDir = p.join(_directory, name, 'bin');
if (dirExists(binDir)) deleteEntry(binDir);
- _updateBinStubs(entrypoint.root, executables,
+ _updateBinStubs(entrypoint, entrypoint.root, executables,
overwriteBinStubs: overwriteBinStubs);
log.message('Activated ${_formatPackage(id)}.');
}
@@ -206,69 +204,41 @@
var lockFile = result.lockFile;
_writeLockFile(dep.name, lockFile);
+ await _writePackageConfigFiles(dep.name, lockFile);
+
+ // We want the entrypoint to be rooted at 'dep' not the dummy-package.
+ result.packages.removeWhere((id) => id.name == 'pub global activate');
+
+ var id = lockFile.packages[dep.name];
+ // Load the package graph from [result] so we don't need to re-parse all
+ // the pubspecs.
+ final entrypoint = Entrypoint.global(
+ Package(result.pubspecs[dep.name],
+ cache.source(dep.source).getDirectory(id)),
+ result.lockFile,
+ cache,
+ solveResult: result);
+ await entrypoint.precompileExecutables();
+
+ _updateBinStubs(
+ entrypoint,
+ cache.load(entrypoint.lockFile.packages[dep.name]),
+ executables,
+ overwriteBinStubs: overwriteBinStubs,
+ );
+
+ log.message('Activated ${_formatPackage(id)}.');
+ }
+
+ Future<void> _writePackageConfigFiles(
+ String package, LockFile lockFile) async {
// TODO(sigurdm): Use [Entrypoint.writePackagesFiles] instead.
- final packagesFilePath = _getPackagesFilePath(dep.name);
- final packageConfigFilePath = _getPackageConfigFilePath(dep.name);
+ final packagesFilePath = _getPackagesFilePath(package);
+ final packageConfigFilePath = _getPackageConfigFilePath(package);
writeTextFile(packagesFilePath, lockFile.packagesFile(cache));
ensureDir(p.dirname(packageConfigFilePath));
writeTextFile(
packageConfigFilePath, await lockFile.packageConfigFile(cache));
-
- // Load the package graph from [result] so we don't need to re-parse all
- // the pubspecs.
- var entrypoint = Entrypoint.fromSolveResult(root, cache, result);
- var snapshots = await _precompileExecutables(entrypoint, dep.name);
-
- _updateBinStubs(entrypoint.packageGraph.packages[dep.name], executables,
- overwriteBinStubs: overwriteBinStubs, snapshots: snapshots);
-
- var id = lockFile.packages[dep.name];
- log.message('Activated ${_formatPackage(id)}.');
- }
-
- /// Precompiles the executables for [packageName] 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 packageName) {
- return log.progress('Precompiling executables', () async {
- var binDir = p.join(_directory, packageName, 'bin');
- cleanDir(binDir);
-
- final packagesFilePath = _getPackagesFilePath(packageName);
- final packageConfigFilePath = _getPackageConfigFilePath(packageName);
- if (!fileExists(packagesFilePath) || !fileExists(packageConfigFilePath)) {
- // TODO(sigurdm): Use [entrypoint.writePackagesFiles] instead.
- // The `.packages` file may not already exist if the global executable
- // has a 1.6-style lock file instead.
- // Similarly, the `.dart_tool/package_config.json` may not exist if the
- // global executable was activated before 2.6
- writeTextFile(
- packagesFilePath, entrypoint.lockFile.packagesFile(cache));
- ensureDir(p.dirname(packageConfigFilePath));
- writeTextFile(
- packageConfigFilePath,
- await entrypoint.lockFile.packageConfigFile(cache),
- );
- }
-
- // Try to avoid starting up an asset server to precompile packages if
- // possible. This is faster and produces better error messages.
- var package = entrypoint.packageGraph.packages[packageName];
- var precompiled = <String, String>{};
- await waitAndPrintErrors(package.executablePaths.map((path) async {
- var url = p.toUri(p.join(package.dir, path));
- var basename = p.basename(path);
- var snapshotPath = p.join(binDir, '$basename.snapshot.dart2');
- await dart.snapshot(url, snapshotPath,
- packagesFile: p.toUri(_getPackagesFilePath(package.name)),
- name: '${package.name}:${p.basenameWithoutExtension(path)}');
- precompiled[p.withoutExtension(basename)] = snapshotPath;
- }));
- return precompiled;
- });
}
/// Finishes activating package [package] by saving [lockFile] in the cache.
@@ -330,7 +300,7 @@
/// Finds the active package with [name].
///
/// Returns an [Entrypoint] loaded with the active package if found.
- Entrypoint find(String name) {
+ Future<Entrypoint> find(String name) async {
var lockFilePath = _getLockFilePath(name);
LockFile lockFile;
try {
@@ -350,6 +320,8 @@
// Move the old lockfile to its new location.
ensureDir(p.dirname(lockFilePath));
File(oldLockFilePath).renameSync(lockFilePath);
+ // Just make sure these files are created as well.
+ await _writePackageConfigFiles(name, lockFile);
}
// Remove the package itself from the lockfile. We put it in there so we
@@ -363,7 +335,7 @@
if (source is CachedSource) {
// For cached sources, the package itself is in the cache and the
// lockfile is the one we just loaded.
- entrypoint = Entrypoint.inMemory(cache.load(id), lockFile, cache);
+ entrypoint = Entrypoint.global(cache.load(id), lockFile, cache);
} else {
// For uncached sources (i.e. path), the ID just points to the real
// directory for the package.
@@ -399,20 +371,16 @@
///
/// Returns the exit code from the executable.
Future<int> runExecutable(
- String package, String executable, Iterable<String> args,
- {bool enableAsserts = false}) {
- var entrypoint = find(package);
- return exe.runExecutable(
- entrypoint, package, p.join('bin', '$executable.dart'), args,
+ Entrypoint entrypoint, Executable executable, Iterable<String> args,
+ {bool enableAsserts = false,
+ String packagesFile,
+ Future<void> Function(Executable) recompile,
+ List<String> vmArgs = const []}) async {
+ return await runExecutable(entrypoint, executable, args,
enableAsserts: enableAsserts,
- packagesFile:
- entrypoint.isCached ? _getPackagesFilePath(package) : null,
- // Don't use snapshots for executables activated from paths.
- snapshotPath: entrypoint.isCached
- ? p.join(
- _directory, package, 'bin', '$executable.dart.snapshot.dart2')
- : null,
- recompile: () => _precompileExecutables(entrypoint, package));
+ packagesFile: packagesFile,
+ recompile: recompile,
+ vmArgs: vmArgs);
}
/// Gets the path to the lock file for an activated cached package with
@@ -515,14 +483,13 @@
id = _loadPackageId(entry);
log.message('Reactivating ${log.bold(id.name)} ${id.version}...');
- var entrypoint = find(id.name);
- var snapshots = await _precompileExecutables(entrypoint, id.name);
+ var entrypoint = await find(id.name);
+
+ await _writePackageConfigFiles(id.name, entrypoint.lockFile);
+ await entrypoint.precompileExecutables();
var packageExecutables = executables.remove(id.name) ?? [];
- _updateBinStubs(
- entrypoint.packageGraph.packages[id.name], packageExecutables,
- overwriteBinStubs: true,
- snapshots: snapshots,
- suggestIfNotOnPath: false);
+ _updateBinStubs(entrypoint, cache.load(id), packageExecutables,
+ overwriteBinStubs: true, suggestIfNotOnPath: false);
successes.add(id.name);
} catch (error, stackTrace) {
var message = 'Failed to reactivate '
@@ -573,18 +540,11 @@
/// 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}) {
- snapshots ??= const {};
-
+ void _updateBinStubs(
+ Entrypoint entrypoint, Package package, List<String> executables,
+ {bool overwriteBinStubs, bool suggestIfNotOnPath = true}) {
// Remove any previously activated binstubs for this package, in case the
// list of executables has changed.
_deleteBinStubs(package.name);
@@ -604,8 +564,15 @@
var script = package.pubspec.executables[executable];
- var previousPackage = _createBinStub(package, executable, script,
- overwrite: overwriteBinStubs, snapshot: snapshots[script]);
+ var previousPackage = _createBinStub(
+ package,
+ executable,
+ script,
+ overwrite: overwriteBinStubs,
+ snapshot: entrypoint.snapshotPathOfExecutable(
+ Executable.adaptProgramName(package.name, script),
+ ),
+ );
if (previousPackage != null) {
collided[executable] = previousPackage;
@@ -652,7 +619,7 @@
// produced by a transformer. Do something better.
var binFiles = package
.listFiles(beneath: 'bin', recursive: false)
- .map((path) => package.relative(path))
+ .map(package.relative)
.toList();
for (var executable in installed) {
var script = package.pubspec.executables[executable];
@@ -673,13 +640,19 @@
/// 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`.
+ /// [snapshot] is a path to a snapshot file. If that snapshot exists 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}) {
+ String _createBinStub(
+ Package package,
+ String executable,
+ String script, {
+ @required bool overwrite,
+ @required String snapshot,
+ }) {
var binStubPath = p.join(_binStubDir, executable);
if (Platform.isWindows) binStubPath += '.bat';
@@ -696,19 +669,29 @@
}
}
- // If the script was precompiled to a snapshot, just invoke that directly
- // and skip pub global run entirely.
+ // If the script was precompiled to a snapshot, just try to invoke that
+ // directly and skip pub global run entirely.
String 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.isWindows) {
+ if (snapshot != null && fileExists(snapshot)) {
+ // 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 = '''
+if exist "$snapshot" (
+ dart "$snapshot" %*
+ rem The VM exits with code 253 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 253 (
+ exit /b %errorlevel%
+ )
+ pub global run ${package.name}:$script %*
+) else (
+ pub global run ${package.name}:$script %*
+)''';
+ } else {
+ invocation = 'pub global run ${package.name}:$script %*';
+ }
var batch = '''
@echo off
rem This file was created by pub v${sdk.version}.
@@ -716,24 +699,31 @@
rem Version: ${package.version}
rem Executable: $executable
rem Script: $script
-$invocation %*
+$invocation
''';
-
- if (snapshot != null) {
- batch += '''
-
-rem The VM exits with code 253 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 253 (
- exit /b %errorlevel%
-)
-
-pub global run ${package.name}:$script %*
-''';
- }
-
writeTextFile(binStubPath, batch);
} else {
+ if (snapshot != null && fileExists(snapshot)) {
+ // 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 = '''
+if [ -f $snapshot ]; then
+ dart "$snapshot" "\$@"
+ # The VM exits with code 253 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 != 253 ]; then
+ exit \$exit_code
+ fi
+ pub global run ${package.name}:$script "\$@"
+else
+ pub global run ${package.name}:$script "\$@"
+fi
+''';
+ } else {
+ invocation = 'pub global run ${package.name}:$script "\$@"';
+ }
var bash = '''
#!/usr/bin/env sh
# This file was created by pub v${sdk.version}.
@@ -741,23 +731,9 @@
# Version: ${package.version}
# Executable: $executable
# Script: $script
-$invocation "\$@"
+$invocation
''';
- if (snapshot != null) {
- bash += '''
-
-# The VM exits with code 253 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 != 253 ]; then
- exit \$exit_code
-fi
-
-pub global run ${package.name}:$script "\$@"
-''';
- }
-
// Write this as the system encoding since the system is going to execute
// it and it might contain non-ASCII characters in the pathnames.
writeTextFile(binStubPath, bash, encoding: const SystemEncoding());
diff --git a/lib/src/package.dart b/lib/src/package.dart
index 4af87ca..48e48b4 100644
--- a/lib/src/package.dart
+++ b/lib/src/package.dart
@@ -34,6 +34,10 @@
/// The path to the directory containing the package.
final String dir;
+ /// An in-memory package can be created for doing a resolution without having
+ /// a package on disk. Paths should not be resolved for these.
+ bool get _isInMemory => dir == null;
+
/// The name of the package.
String get name {
if (pubspec.name != null) return pubspec.name;
@@ -156,7 +160,7 @@
String part5,
String part6,
String part7]) {
- if (dir == null) {
+ if (_isInMemory) {
throw StateError("Package $name is in-memory and doesn't have paths "
'on disk.');
}
diff --git a/lib/src/package_graph.dart b/lib/src/package_graph.dart
index 31cfdbf..66086d0 100644
--- a/lib/src/package_graph.dart
+++ b/lib/src/package_graph.dart
@@ -75,6 +75,19 @@
return _transitiveDependencies[package];
}
+ bool _isPackageCached(String package) {
+ // The root package is not included in the lock file, so we instead ask
+ // the entrypoint.
+ // TODO(sigurdm): there should be a way to get the id of any package
+ // including the root.
+ if (package == entrypoint.root.name) {
+ return entrypoint.isCached;
+ } else {
+ var id = lockFile.packages[package];
+ return entrypoint.cache.source(id.source) is CachedSource;
+ }
+ }
+
/// Returns whether [package] is mutable.
///
/// A package is considered to be mutable if it or any of its dependencies
@@ -82,19 +95,9 @@
/// without modifying the pub cache. Information generated from mutable
/// packages is generally not safe to cache, since it may change frequently.
bool isPackageMutable(String package) {
- var id = lockFile.packages[package];
- if (id == null) return true;
+ if (!_isPackageCached(package)) return true;
- if (entrypoint.cache.source(id.source) is! CachedSource) return true;
-
- return transitiveDependencies(package).any((dep) {
- var depId = lockFile.packages[dep.name];
-
- // The entrypoint package doesn't have a lockfile entry. It's always
- // mutable.
- if (depId == null) return true;
-
- return entrypoint.cache.source(depId.source) is! CachedSource;
- });
+ return transitiveDependencies(package)
+ .any((dep) => !_isPackageCached(dep.name));
}
}
diff --git a/lib/src/rate_limited_scheduler.dart b/lib/src/rate_limited_scheduler.dart
index 2c76412..9d8a1f6 100644
--- a/lib/src/rate_limited_scheduler.dart
+++ b/lib/src/rate_limited_scheduler.dart
@@ -5,6 +5,7 @@
import 'dart:async';
import 'dart:collection';
+import 'package:meta/meta.dart';
import 'package:pool/pool.dart';
import 'package:pedantic/pedantic.dart';
@@ -59,7 +60,7 @@
final Set<J> _started = {};
RateLimitedScheduler(Future<V> Function(J) runJob,
- {maxConcurrentOperations = 10})
+ {@required int maxConcurrentOperations})
: _runJob = runJob,
_pool = Pool(maxConcurrentOperations);
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart
index 09c8f9b..d8c3ef7 100644
--- a/lib/src/source/hosted.dart
+++ b/lib/src/source/hosted.dart
@@ -169,7 +169,10 @@
RateLimitedScheduler<PackageRef, Map<PackageId, _VersionInfo>> _scheduler;
BoundHostedSource(this.source, this.systemCache) {
- _scheduler = RateLimitedScheduler(_fetchVersions);
+ _scheduler = RateLimitedScheduler(
+ _fetchVersions,
+ maxConcurrentOperations: 10,
+ );
}
Future<Map<PackageId, _VersionInfo>> _fetchVersions(PackageRef ref) async {
diff --git a/test/cache/repair/updates_binstubs_test.dart b/test/cache/repair/updates_binstubs_test.dart
index 20ad027..d877357 100644
--- a/test/cache/repair/updates_binstubs_test.dart
+++ b/test/cache/repair/updates_binstubs_test.dart
@@ -47,8 +47,8 @@
// The broken versions should have been replaced.
await d.dir(cachePath, [
d.dir('bin', [
- // 253 is the VM's exit code upon seeing an out-of-date snapshot.
- d.file(binStubName('foo-script'), contains('253'))
+ d.file(binStubName('foo-script'),
+ contains('This file was created by pub v0.1.2+3'))
])
]).validate();
});
diff --git a/test/global/activate/cached_package_test.dart b/test/global/activate/cached_package_test.dart
index 324e158..76c5753 100644
--- a/test/global/activate/cached_package_test.dart
+++ b/test/global/activate/cached_package_test.dart
@@ -10,7 +10,9 @@
void main() {
test('can activate an already cached package', () async {
await servePackages((builder) {
- builder.serve('foo', '1.0.0');
+ builder.serve('foo', '1.0.0', contents: [
+ d.dir('bin', [d.file('foo.dart', 'main() => print("hi"); ')])
+ ]);
});
await runPub(args: ['cache', 'add', 'foo']);
@@ -19,6 +21,7 @@
Resolving dependencies...
+ foo 1.0.0
Precompiling executables...
+ Precompiled foo:foo.
Activated foo 1.0.0.''');
// Should be in global package cache.
diff --git a/test/global/activate/different_version_test.dart b/test/global/activate/different_version_test.dart
index 037f18d..b0284f0 100644
--- a/test/global/activate/different_version_test.dart
+++ b/test/global/activate/different_version_test.dart
@@ -4,6 +4,7 @@
import 'package:test/test.dart';
+import '../../descriptor.dart' as d;
import '../../test_pub.dart';
void main() {
@@ -11,8 +12,12 @@
"discards the previous active version if it doesn't match the "
'constraint', () async {
await servePackages((builder) {
- builder.serve('foo', '1.0.0');
- builder.serve('foo', '2.0.0');
+ builder.serve('foo', '1.0.0', contents: [
+ d.dir('bin', [d.file('foo.dart', 'main() => print("hi"); ')])
+ ]);
+ builder.serve('foo', '2.0.0', contents: [
+ d.dir('bin', [d.file('foo.dart', 'main() => print("hi2"); ')])
+ ]);
});
// Activate 1.0.0.
@@ -25,6 +30,7 @@
+ foo 2.0.0
Downloading foo 2.0.0...
Precompiling executables...
+ Precompiled foo:foo.
Activated foo 2.0.0.''');
});
}
diff --git a/test/global/activate/ignores_active_version_test.dart b/test/global/activate/ignores_active_version_test.dart
index 9d57d58..7e996a4 100644
--- a/test/global/activate/ignores_active_version_test.dart
+++ b/test/global/activate/ignores_active_version_test.dart
@@ -4,13 +4,19 @@
import 'package:test/test.dart';
+import '../../descriptor.dart' as d;
import '../../test_pub.dart';
void main() {
test('ignores previously activated version', () async {
await servePackages((builder) {
- builder.serve('foo', '1.2.3');
- builder.serve('foo', '1.3.0');
+ builder.serve(
+ 'foo',
+ '1.2.3',
+ );
+ builder.serve('foo', '1.3.0', contents: [
+ d.dir('bin', [d.file('foo.dart', 'main() => print("hi"); ')])
+ ]);
});
// Activate 1.2.3.
@@ -23,6 +29,7 @@
+ foo 1.3.0
Downloading foo 1.3.0...
Precompiling executables...
+ Precompiled foo:foo.
Activated foo 1.3.0.''');
});
}
diff --git a/test/global/activate/outdated_binstub_test.dart b/test/global/activate/outdated_binstub_test.dart
index d8bc249..5a5a0e6 100644
--- a/test/global/activate/outdated_binstub_test.dart
+++ b/test/global/activate/outdated_binstub_test.dart
@@ -38,8 +38,9 @@
await d.dir(cachePath, [
d.dir('bin', [
- // 253 is the VM's exit code upon seeing an out-of-date snapshot.
- d.file(binStubName('foo-script'), contains('253'))
+ // The new binstub should contain an if
+ d.file(binStubName('foo-script'),
+ contains('This file was created by pub v0.1.2+3.'))
])
]).validate();
});
diff --git a/test/global/activate/reactivating_git_upgrades_test.dart b/test/global/activate/reactivating_git_upgrades_test.dart
index 4ba3e89..7b6bb5a 100644
--- a/test/global/activate/reactivating_git_upgrades_test.dart
+++ b/test/global/activate/reactivating_git_upgrades_test.dart
@@ -11,7 +11,10 @@
test('ignores previously activated git commit', () async {
ensureGit();
- await d.git('foo.git', [d.libPubspec('foo', '1.0.0')]).create();
+ await d.git('foo.git', [
+ d.libPubspec('foo', '1.0.0'),
+ d.dir('bin', [d.file('foo.dart', 'main() => print("hi"); ')])
+ ]).create();
await runPub(
args: ['global', 'activate', '-sgit', '../foo.git'],
@@ -20,6 +23,7 @@
'+ foo 1.0.0 from git ../foo.git at '),
// Specific revision number goes here.
endsWith('Precompiling executables...\n'
+ 'Precompiled foo:foo.\n'
'Activated foo 1.0.0 from Git repository "../foo.git".')));
await d.git('foo.git', [d.libPubspec('foo', '1.0.1')]).commit();
@@ -34,6 +38,7 @@
'+ foo 1.0.1 from git ../foo.git at '),
// Specific revision number goes here.
endsWith('Precompiling executables...\n'
+ 'Precompiled foo:foo.\n'
'Activated foo 1.0.1 from Git repository "../foo.git".')));
});
}
diff --git a/test/global/activate/snapshots_git_executables_test.dart b/test/global/activate/snapshots_git_executables_test.dart
index e57c19a..c7d33d8 100644
--- a/test/global/activate/snapshots_git_executables_test.dart
+++ b/test/global/activate/snapshots_git_executables_test.dart
@@ -33,9 +33,10 @@
d.dir('foo', [
d.file('pubspec.lock', contains('1.0.0')),
d.dir('bin', [
- d.file('hello.dart.snapshot.dart2', contains('hello!')),
- d.file('goodbye.dart.snapshot.dart2', contains('goodbye!')),
- d.nothing('shell.sh.snapshot.dart2'),
+ d.file('hello.dart-$versionSuffix.snapshot', contains('hello!')),
+ d.file(
+ 'goodbye.dart-$versionSuffix.snapshot', contains('goodbye!')),
+ d.nothing('shell.sh-$versionSuffix.snapshot'),
d.nothing('subdir')
])
])
diff --git a/test/global/activate/snapshots_hosted_executables_test.dart b/test/global/activate/snapshots_hosted_executables_test.dart
index 5f7761f..1147762 100644
--- a/test/global/activate/snapshots_hosted_executables_test.dart
+++ b/test/global/activate/snapshots_hosted_executables_test.dart
@@ -32,9 +32,10 @@
d.dir('foo', [
d.file('pubspec.lock', contains('1.0.0')),
d.dir('bin', [
- d.file('hello.dart.snapshot.dart2', contains('hello!')),
- d.file('goodbye.dart.snapshot.dart2', contains('goodbye!')),
- d.nothing('shell.sh.snapshot.dart2'),
+ d.file('hello.dart-$versionSuffix.snapshot', contains('hello!')),
+ d.file(
+ 'goodbye.dart-$versionSuffix.snapshot', contains('goodbye!')),
+ d.nothing('shell.sh-$versionSuffix.snapshot'),
d.nothing('subdir')
])
])
diff --git a/test/global/activate/uncached_package_test.dart b/test/global/activate/uncached_package_test.dart
index 3fa546c..6b67fdd 100644
--- a/test/global/activate/uncached_package_test.dart
+++ b/test/global/activate/uncached_package_test.dart
@@ -10,9 +10,15 @@
void main() {
test('installs and activates the best version of a package', () async {
await servePackages((builder) {
- builder.serve('foo', '1.0.0');
- builder.serve('foo', '1.2.3');
- builder.serve('foo', '2.0.0-wildly.unstable');
+ builder.serve('foo', '1.0.0', contents: [
+ d.dir('bin', [d.file('foo.dart', 'main() => print("hi"); ')])
+ ]);
+ builder.serve('foo', '1.2.3', contents: [
+ d.dir('bin', [d.file('foo.dart', 'main() => print("hi 1.2.3"); ')])
+ ]);
+ builder.serve('foo', '2.0.0-wildly.unstable', contents: [
+ d.dir('bin', [d.file('foo.dart', 'main() => print("hi unstable"); ')])
+ ]);
});
await runPub(args: ['global', 'activate', 'foo'], output: '''
@@ -20,6 +26,7 @@
+ foo 1.2.3 (2.0.0-wildly.unstable available)
Downloading foo 1.2.3...
Precompiling executables...
+ Precompiled foo:foo.
Activated foo 1.2.3.''');
// Should be in global package cache.
diff --git a/test/global/binstubs/binstub_runs_precompiled_snapshot_test.dart b/test/global/binstubs/binstub_runs_precompiled_snapshot_test.dart
index 0fa7282..3f8ef2e 100644
--- a/test/global/binstubs/binstub_runs_precompiled_snapshot_test.dart
+++ b/test/global/binstubs/binstub_runs_precompiled_snapshot_test.dart
@@ -20,8 +20,10 @@
await runPub(args: ['global', 'activate', 'foo']);
await d.dir(cachePath, [
- d.dir('bin',
- [d.file(binStubName('foo-script'), contains('script.dart.snapshot'))])
+ d.dir('bin', [
+ d.file(binStubName('foo-script'),
+ contains('script.dart-$versionSuffix.snapshot'))
+ ])
]).validate();
});
}
diff --git a/test/global/binstubs/outdated_snapshot_test.dart b/test/global/binstubs/outdated_snapshot_test.dart
index 2dd4be5..093c8c0 100644
--- a/test/global/binstubs/outdated_snapshot_test.dart
+++ b/test/global/binstubs/outdated_snapshot_test.dart
@@ -28,28 +28,34 @@
await d.dir(cachePath, [
d.dir('global_packages', [
d.dir('foo', [
- d.dir('bin', [d.outOfDateSnapshot('script.dart.snapshot.dart2')])
+ d.dir('bin',
+ [d.outOfDateSnapshot('script.dart-$versionSuffix.snapshot-1')])
])
])
]).create();
+ deleteEntry(p.join(d.dir(cachePath).io.path, 'global_packages', 'foo',
+ 'bin', 'script.dart-$versionSuffix.snapshot'));
+
var process = await TestProcess.start(
p.join(d.sandbox, cachePath, 'bin', binStubName('foo-script')),
['arg1', 'arg2'],
environment: getEnvironment());
- expect(process.stderr,
- emits(contains('Invalid kernel binary format version.')));
+ // We don't get `Precompiling executable...` because we are running through
+ // the binstub.
expect(process.stdout, emitsThrough('ok [arg1, arg2]'));
await process.shouldExit();
- await d.dir(cachePath, [
- d.dir('global_packages/foo/bin', [
- d.file(
- 'script.dart.snapshot.dart2',
- isNot(equals(
- readBinaryFile(testAssetPath('out-of-date.snapshot.dart2')))))
- ])
- ]).validate();
+ // TODO(sigurdm): This is hard to test because the binstub invokes the wrong
+ // pub.
+ // await d.dir(cachePath, [
+ // d.dir('global_packages/foo/bin', [
+ // d.file(
+ // 'script.dart-$versionSuffix.snapshot',
+ // isNot(equals(
+ // readBinaryFile(testAssetPath('out-of-date-$versionSuffix.snapshot')))))
+ // ])
+ // ]).validate();
});
}
diff --git a/test/global/deactivate/deactivate_and_reactivate_package_test.dart b/test/global/deactivate/deactivate_and_reactivate_package_test.dart
index dc7481c..cc7f597 100644
--- a/test/global/deactivate/deactivate_and_reactivate_package_test.dart
+++ b/test/global/deactivate/deactivate_and_reactivate_package_test.dart
@@ -25,7 +25,6 @@
Resolving dependencies...
+ foo 2.0.0
Downloading foo 2.0.0...
- Precompiling executables...
Activated foo 2.0.0.''');
});
}
diff --git a/test/global/run/errors_if_outside_bin_test.dart b/test/global/run/errors_if_outside_bin_test.dart
index 52b7d29..2078ae8 100644
--- a/test/global/run/errors_if_outside_bin_test.dart
+++ b/test/global/run/errors_if_outside_bin_test.dart
@@ -22,8 +22,12 @@
Cannot run an executable in a subdirectory of a global package.
Usage: pub global run <package>:<executable> [args...]
--h, --help Print this usage information.
- --[no-]enable-asserts Enable assert statements.
+-h, --help Print this usage information.
+ --[no-]enable-asserts Enable assert statements.
+ --enable-experiment=<experiment> Runs the executable in a VM with the
+ given experiments enabled. (Will disable
+ snapshotting, resulting in slower
+ startup)
Run "pub help" to see global options.
''', exitCode: exit_codes.USAGE);
diff --git a/test/global/run/missing_executable_arg_test.dart b/test/global/run/missing_executable_arg_test.dart
index 90b8db4..47f2420 100644
--- a/test/global/run/missing_executable_arg_test.dart
+++ b/test/global/run/missing_executable_arg_test.dart
@@ -11,13 +11,17 @@
void main() {
test('fails if no executable was given', () {
return runPub(args: ['global', 'run'], error: '''
- Must specify an executable to run.
+Must specify an executable to run.
- Usage: pub global run <package>:<executable> [args...]
- -h, --help Print this usage information.
- --[no-]enable-asserts Enable assert statements.
+Usage: pub global run <package>:<executable> [args...]
+-h, --help Print this usage information.
+ --[no-]enable-asserts Enable assert statements.
+ --enable-experiment=<experiment> Runs the executable in a VM with the
+ given experiments enabled. (Will disable
+ snapshotting, resulting in slower
+ startup)
- Run "pub help" to see global options.
- ''', exitCode: exit_codes.USAGE);
+Run "pub help" to see global options.
+''', exitCode: exit_codes.USAGE);
});
}
diff --git a/test/global/run/recompiles_if_snapshot_is_out_of_date_test.dart b/test/global/run/recompiles_if_snapshot_is_out_of_date_test.dart
index e27c30a..6f16599 100644
--- a/test/global/run/recompiles_if_snapshot_is_out_of_date_test.dart
+++ b/test/global/run/recompiles_if_snapshot_is_out_of_date_test.dart
@@ -2,6 +2,8 @@
// 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.
+import 'package:path/path.dart' as p;
+import 'package:pub/src/io.dart';
import 'package:test/test.dart';
import '../../descriptor.dart' as d;
@@ -20,22 +22,27 @@
await d.dir(cachePath, [
d.dir('global_packages', [
d.dir('foo', [
- d.dir('bin', [d.outOfDateSnapshot('script.dart.snapshot.dart2')])
+ d.dir('bin', [
+ d.outOfDateSnapshot('script.dart-$versionSuffix.snapshot-1'),
+ ])
])
])
]).create();
+ deleteEntry(p.join(d.dir(cachePath).io.path, 'global_packages', 'foo',
+ 'bin', 'script.dart-$versionSuffix.snapshot'));
var pub = await pubRun(global: true, args: ['foo:script']);
// In the real world this would just print "hello!", but since we collect
// all output we see the precompilation messages as well.
- expect(pub.stdout, emits('Precompiling executables...'));
+ expect(pub.stdout, emits('Precompiling executable...'));
expect(pub.stdout, emitsThrough('ok'));
await pub.shouldExit();
await d.dir(cachePath, [
d.dir('global_packages', [
d.dir('foo', [
- d.dir('bin', [d.file('script.dart.snapshot.dart2', contains('ok'))])
+ d.dir('bin',
+ [d.file('script.dart-$versionSuffix.snapshot', contains('ok'))])
])
])
]).validate();
diff --git a/test/run/dartdev/errors_if_path_in_dependency_test.dart b/test/run/dartdev/errors_if_path_in_dependency_test.dart
index a9d9d91..7081a04 100644
--- a/test/run/dartdev/errors_if_path_in_dependency_test.dart
+++ b/test/run/dartdev/errors_if_path_in_dependency_test.dart
@@ -25,8 +25,12 @@
Cannot run an executable in a subdirectory of a dependency.
Usage: pub run <executable> [args...]
--h, --help Print this usage information.
- --[no-]enable-asserts Enable assert statements.
+-h, --help Print this usage information.
+ --[no-]enable-asserts Enable assert statements.
+ --enable-experiment=<experiment> Runs the executable in a VM with the
+ given experiments enabled.
+ (Will disable snapshotting, resulting in
+ slower startup)
Run "pub help" to see global options.
See https://dart.dev/tools/pub/cmd/pub-run for detailed documentation.
diff --git a/test/run/enable_experiments_test.dart b/test/run/enable_experiments_test.dart
new file mode 100644
index 0000000..d241b9d
--- /dev/null
+++ b/test/run/enable_experiments_test.dart
@@ -0,0 +1,31 @@
+// 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.
+
+import 'dart:io';
+
+import 'package:test/test.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+
+void main() {
+ test('Succeeds running experimental code.', () async {
+ await d.dir(appPath, [
+ d.appPubspec(),
+ d.dir('bin', [
+ d.file('script.dart', '''
+ main() {
+ int? a = int.tryParse('123');
+ }
+''')
+ ])
+ ]).create();
+ await pubGet();
+ await runPub(
+ args: ['run', '--enable-experiment=non-nullable', 'bin/script.dart']);
+ },
+ skip: Platform.version.contains('2.9')
+ ? false
+ : 'experiement non-nullable only available for test on sdk 2.9');
+}
diff --git a/test/run/errors_if_no_executable_is_given_test.dart b/test/run/errors_if_no_executable_is_given_test.dart
index 666b608..66201bd 100644
--- a/test/run/errors_if_no_executable_is_given_test.dart
+++ b/test/run/errors_if_no_executable_is_given_test.dart
@@ -17,8 +17,12 @@
Must specify an executable to run.
Usage: pub run <executable> [args...]
--h, --help Print this usage information.
- --[no-]enable-asserts Enable assert statements.
+-h, --help Print this usage information.
+ --[no-]enable-asserts Enable assert statements.
+ --enable-experiment=<experiment> Runs the executable in a VM with the
+ given experiments enabled.
+ (Will disable snapshotting, resulting in
+ slower startup)
Run "pub help" to see global options.
See https://dart.dev/tools/pub/cmd/pub-run for detailed documentation.
diff --git a/test/run/errors_if_path_in_dependency_test.dart b/test/run/errors_if_path_in_dependency_test.dart
index c2213d2..6b109a1 100644
--- a/test/run/errors_if_path_in_dependency_test.dart
+++ b/test/run/errors_if_path_in_dependency_test.dart
@@ -25,8 +25,12 @@
Cannot run an executable in a subdirectory of a dependency.
Usage: pub run <executable> [args...]
--h, --help Print this usage information.
- --[no-]enable-asserts Enable assert statements.
+-h, --help Print this usage information.
+ --[no-]enable-asserts Enable assert statements.
+ --enable-experiment=<experiment> Runs the executable in a VM with the
+ given experiments enabled.
+ (Will disable snapshotting, resulting in
+ slower startup)
Run "pub help" to see global options.
See https://dart.dev/tools/pub/cmd/pub-run for detailed documentation.
diff --git a/test/run/forwards_signal_posix_test.dart b/test/run/forwards_signal_posix_test.dart
index d8a7d05..a1c2f03 100644
--- a/test/run/forwards_signal_posix_test.dart
+++ b/test/run/forwards_signal_posix_test.dart
@@ -4,6 +4,10 @@
// Windows doesn't support sending signals.
@TestOn('!windows')
+// TODO(sigurdm): Test this when vm-args are provided.
+// This test doesn't work when we subprocess instead of an isolate
+// in `pub run`. Now signals only work as expected when sent to the process
+// group. And this seems hard to emulate in a test.
import 'dart:io';
import 'package:test/test.dart';
diff --git a/test/snapshot_test.dart b/test/snapshot_test.dart
index c4ffb8d..e70448c 100644
--- a/test/snapshot_test.dart
+++ b/test/snapshot_test.dart
@@ -33,11 +33,10 @@
]));
await d.dir(p.join(appPath, '.dart_tool', 'pub', 'bin'), [
- d.file('sdk-version', '0.1.2+3\n'),
d.dir('foo', [
- d.file('hello.dart.snapshot.dart2', contains('hello!')),
- d.file('goodbye.dart.snapshot.dart2', contains('goodbye!')),
- d.nothing('shell.sh.snapshot.dart2'),
+ d.file('hello.dart-$versionSuffix.snapshot', contains('hello!')),
+ d.file('goodbye.dart-$versionSuffix.snapshot', contains('goodbye!')),
+ d.nothing('shell.sh-$versionSuffix.snapshot'),
d.nothing('subdir')
])
]).validate();
@@ -75,11 +74,10 @@
]));
await d.dir(p.join(appPath, '.dart_tool', 'pub', 'bin'), [
- d.file('sdk-version', '0.1.2+3\n'),
d.dir('foo', [
- d.file('hello.dart.snapshot.dart2', contains('hello!')),
- d.file('goodbye.dart.snapshot.dart2', contains('goodbye!')),
- d.nothing('shell.sh.snapshot.dart2'),
+ d.file('hello.dart-$versionSuffix.snapshot', contains('hello!')),
+ d.file('goodbye.dart-$versionSuffix.snapshot', contains('goodbye!')),
+ d.nothing('shell.sh-$versionSuffix.snapshot'),
d.nothing('subdir')
])
]).validate();
@@ -108,7 +106,7 @@
args: ['--precompile'], output: contains('Precompiled foo:hello.'));
await d.dir(p.join(appPath, '.dart_tool', 'pub', 'bin', 'foo'), [
- d.file('hello.dart.snapshot.dart2', contains('hello!'))
+ d.file('hello.dart-$versionSuffix.snapshot', contains('hello!'))
]).validate();
globalPackageServer.add((builder) {
@@ -122,7 +120,7 @@
args: ['--precompile'], output: contains('Precompiled foo:hello.'));
await d.dir(p.join(appPath, '.dart_tool', 'pub', 'bin', 'foo'), [
- d.file('hello.dart.snapshot.dart2', contains('hello 2!'))
+ d.file('hello.dart-$versionSuffix.snapshot', contains('hello 2!'))
]).validate();
var process = await pubRun(args: ['foo:hello']);
@@ -154,7 +152,7 @@
args: ['--precompile'], output: contains('Precompiled foo:hello.'));
await d.dir(p.join(appPath, '.dart_tool', 'pub', 'bin', 'foo'), [
- d.file('hello.dart.snapshot.dart2', contains('hello!'))
+ d.file('hello.dart-$versionSuffix.snapshot', contains('hello!'))
]).validate();
globalPackageServer.add((builder) {
@@ -167,7 +165,7 @@
args: ['--precompile'], output: contains('Precompiled foo:hello.'));
await d.dir(p.join(appPath, '.dart_tool', 'pub', 'bin', 'foo'), [
- d.file('hello.dart.snapshot.dart2', contains('hello 2!'))
+ d.file('hello.dart-$versionSuffix.snapshot', contains('hello 2!'))
]).validate();
var process = await pubRun(args: ['foo:hello']);
@@ -192,7 +190,7 @@
args: ['--precompile'], output: contains('Precompiled foo:hello.'));
await d.dir(p.join(appPath, '.dart_tool', 'pub', 'bin', 'foo'), [
- d.file('hello.dart.snapshot.dart2', contains('Hello!'))
+ d.file('hello.dart-$versionSuffix.snapshot', contains('Hello!'))
]).validate();
await d.git('foo.git', [
@@ -204,7 +202,7 @@
args: ['--precompile'], output: contains('Precompiled foo:hello.'));
await d.dir(p.join(appPath, '.dart_tool', 'pub', 'bin', 'foo'), [
- d.file('hello.dart.snapshot.dart2', contains('Goodbye!'))
+ d.file('hello.dart-$versionSuffix.snapshot', contains('Goodbye!'))
]).validate();
var process = await pubRun(args: ['foo:hello']);
@@ -222,11 +220,11 @@
await d.appDir({'foo': '5.6.7'}).create();
- await pubGet(
- args: ['--precompile'], output: contains('Precompiled foo:hello.'));
+ await pubGet(args: ['--no-precompile']);
await d.dir(p.join(appPath, '.dart_tool', 'pub', 'bin'), [
- d.dir('foo', [d.outOfDateSnapshot('hello.dart.snapshot.dart2')])
+ d.dir('foo',
+ [d.outOfDateSnapshot('hello.dart-$versionSuffix.snapshot')])
]).create();
var process = await pubRun(args: ['foo:hello']);
@@ -238,9 +236,9 @@
await process.shouldExit();
await d.dir(p.join(appPath, '.dart_tool', 'pub', 'bin'), [
- d.file('sdk-version', '0.1.2+3\n'),
- d.dir(
- 'foo', [d.file('hello.dart.snapshot.dart2', contains('hello!'))])
+ d.dir('foo', [
+ d.file('hello.dart-$versionSuffix.snapshot', contains('hello!'))
+ ])
]).validate();
});
});
diff --git a/test/test_pub.dart b/test/test_pub.dart
index 669fc74..bbf77fb 100644
--- a/test/test_pub.dart
+++ b/test/test_pub.dart
@@ -31,6 +31,7 @@
import 'package:pub/src/io.dart';
import 'package:pub/src/lock_file.dart';
import 'package:pub/src/log.dart' as log;
+import 'package:pub/src/sdk.dart';
import 'package:pub/src/source_registry.dart';
import 'package:pub/src/system_cache.dart';
import 'package:pub/src/utils.dart';
@@ -76,6 +77,9 @@
.readAsLinesSync()
.firstWhere((l) => l.startsWith('$packageName:'));
+/// The suffix appended to a precompiled snapshot.
+final versionSuffix = testVersion ?? sdk.version;
+
/// Enum identifying a pub command that can be run with a well-defined success
/// output.
class RunCommand {
@@ -324,6 +328,8 @@
/// sandbox.
String _pathInSandbox(String relPath) => p.join(d.sandbox, relPath);
+String testVersion = '0.1.2+3';
+
/// Gets the environment variables used to run pub in a test context.
Map<String, String> getPubTestEnvironment([String tokenEndpoint]) {
var environment = {
@@ -332,7 +338,7 @@
'PUB_ENVIRONMENT': 'test-environment',
// Ensure a known SDK version is set for the tests that rely on that.
- '_PUB_TEST_SDK_VERSION': '0.1.2+3'
+ '_PUB_TEST_SDK_VERSION': testVersion
};
if (tokenEndpoint != null) {
diff --git a/tool/test.sh b/tool/test.sh
index da934ee..7dff3b5 100755
--- a/tool/test.sh
+++ b/tool/test.sh
@@ -13,7 +13,7 @@
ROOT="$DIR/.."
# PATH to a snapshot file.
-PUB_SNAPSHOT_FILE=`tempfile -p 'pub.' -s '.dart.snapshot.dart2'`;
+PUB_SNAPSHOT_FILE=`mktemp -t pub.XXXXXXX.dart.snapshot.dart2`
# Always remove the snapshot
function cleanup {
@@ -32,4 +32,4 @@
# Run tests
echo 'Running tests'
export _PUB_TEST_SNAPSHOT="$PUB_SNAPSHOT_FILE"
-pub run test --reporter expanded "$@"
+pub run test "$@"