Refresh binstubs after recompile in global run (#2610)
diff --git a/.travis.yml b/.travis.yml
index a763fcf..74955f8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,7 @@
env:
global:
- SPLIT=`which gsplit || which split`
- - _PUB_TEST_SNAPSHOT="`pwd`/.dart_tool//pub.dart.snapshot.dart2"
+ - _PUB_TEST_SNAPSHOT="`pwd`/.dart_tool/pub.dart.snapshot.dart2"
dart_task:
- test: --preset travis `$SPLIT -n l/1/7 .dart_tool/test_files`
@@ -42,6 +42,7 @@
before_script:
- dart --snapshot="$_PUB_TEST_SNAPSHOT" bin/pub.dart
- find test -name "*_test\\.dart" | sort > .dart_tool/test_files
+ - dart --disable-analytics
# Only building these branches means that we don't run two builds for each pull
# request.
diff --git a/lib/src/command/global_run.dart b/lib/src/command/global_run.dart
index 0d9bb94..087598e 100644
--- a/lib/src/command/global_run.dart
+++ b/lib/src/command/global_run.dart
@@ -66,7 +66,7 @@
final vmArgs = vmArgsFromArgResults(argResults);
final globalEntrypoint = await globals.find(package);
- final exitCode = await runExecutable(globalEntrypoint,
+ final exitCode = await globals.runExecutable(globalEntrypoint,
Executable.adaptProgramName(package, executable), args,
vmArgs: vmArgs,
enableAsserts: argResults['enable-asserts'] || argResults['checked'],
diff --git a/lib/src/executable.dart b/lib/src/executable.dart
index b647c9c..62ec5e8 100644
--- a/lib/src/executable.dart
+++ b/lib/src/executable.dart
@@ -201,10 +201,11 @@
/// An executable in a package
class Executable {
String package;
- // The relative path to the executable inside the root of [package].
+
+ /// The relative path to the executable inside the root of [package].
String relativePath;
- // Adapts the program-name following conventions of dart run
+ /// Adapts the program-name following conventions of dart run
Executable.adaptProgramName(this.package, String program)
: relativePath = _adaptProgramToPath(program);
diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart
index d6b96f5..257f7bf 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -11,7 +11,7 @@
import 'entrypoint.dart';
import 'exceptions.dart';
-import 'executable.dart';
+import 'executable.dart' as exec;
import 'http.dart' as http;
import 'io.dart';
import 'lock_file.dart';
@@ -371,16 +371,17 @@
///
/// Returns the exit code from the executable.
Future<int> runExecutable(
- Entrypoint entrypoint, Executable executable, Iterable<String> args,
+ Entrypoint entrypoint, exec.Executable executable, Iterable<String> args,
{bool enableAsserts = false,
String packagesFile,
- Future<void> Function(Executable) recompile,
+ Future<void> Function(exec.Executable) recompile,
List<String> vmArgs = const []}) async {
- return await runExecutable(entrypoint, executable, args,
+ return await exec.runExecutable(entrypoint, executable, args,
enableAsserts: enableAsserts,
- packagesFile: packagesFile,
- recompile: recompile,
- vmArgs: vmArgs);
+ packagesFile: packagesFile, recompile: (exectuable) async {
+ await recompile(exectuable);
+ _refreshBinStubs(entrypoint, executable);
+ }, vmArgs: vmArgs);
}
/// Gets the path to the lock file for an activated cached package with
@@ -524,6 +525,33 @@
return Pair(successes, failures);
}
+ /// Rewrites all binstubs that refer to [executable] of [entrypoint].
+ ///
+ /// This is meant to be called after a recompile due to eg. outdated
+ /// snapshots.
+ void _refreshBinStubs(Entrypoint entrypoint, exec.Executable executable) {
+ if (!dirExists(_binStubDir)) return;
+ for (var file in listDir(_binStubDir, includeDirs: false)) {
+ var contents = readTextFile(file);
+ var binStubPackage = _binStubProperty(contents, 'Package');
+ var binStubScript = _binStubProperty(contents, 'Script');
+ if (binStubPackage == null || binStubScript == null) {
+ log.fine('Could not parse binstub $file:\n$contents');
+ continue;
+ }
+ if (binStubPackage == entrypoint.root.name &&
+ binStubScript ==
+ p.basenameWithoutExtension(executable.relativePath)) {
+ log.fine('Replacing old binstub $file');
+ deleteEntry(file);
+ _createBinStub(
+ entrypoint.root, p.basenameWithoutExtension(file), binStubScript,
+ overwrite: true,
+ snapshot: entrypoint.snapshotPathOfExecutable(executable));
+ }
+ }
+ }
+
/// Updates the binstubs for [package].
///
/// A binstub is a little shell script in `PUB_CACHE/bin` that runs an
@@ -570,7 +598,7 @@
script,
overwrite: overwriteBinStubs,
snapshot: entrypoint.snapshotPathOfExecutable(
- Executable.adaptProgramName(package.name, script),
+ exec.Executable.adaptProgramName(package.name, script),
),
);
if (previousPackage != null) {
diff --git a/test/global/binstubs/outdated_binstub_runs_pub_global_test.dart b/test/global/binstubs/outdated_binstub_runs_pub_global_test.dart
index aa2a618..16e4f0e 100644
--- a/test/global/binstubs/outdated_binstub_runs_pub_global_test.dart
+++ b/test/global/binstubs/outdated_binstub_runs_pub_global_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 'dart:io';
+
import 'package:path/path.dart' as p;
import 'package:test_process/test_process.dart';
import 'package:test/test.dart';
@@ -10,22 +12,78 @@
import '../../test_pub.dart';
import 'utils.dart';
+/// The contents of the binstub for [executable], or `null` if it doesn't exist.
+String binStub(String executable) {
+ final f = File(p.join(d.sandbox, cachePath, 'bin', binStubName(executable)));
+ if (f.existsSync()) {
+ return f.readAsStringSync();
+ }
+ return null;
+}
+
void main() {
- test("an outdated binstub runs 'pub global run'", () async {
+ test("an outdated binstub runs 'pub global run', which replaces old binstub",
+ () async {
await servePackages((builder) {
builder.serve('foo', '1.0.0', pubspec: {
- 'executables': {'foo-script': 'script'}
+ 'executables': {
+ 'foo-script': 'script',
+ 'foo-script2': 'script',
+ 'foo-script-not-installed': 'script',
+ 'foo-another-script': 'another-script',
+ 'foo-another-script-not-installed': 'another-script'
+ }
}, contents: [
- d.dir('bin', [d.file('script.dart', "main(args) => print('ok');")])
+ d.dir('bin', [
+ d.file('script.dart', r"main(args) => print('ok $args');"),
+ d.file('another-script.dart',
+ r"main(args) => print('not so good $args');")
+ ])
]);
});
- await runPub(args: ['global', 'activate', 'foo']);
+ await runPub(args: [
+ 'global',
+ 'activate',
+ 'foo',
+ '--executable',
+ 'foo-script',
+ '--executable',
+ 'foo-script2',
+ '--executable',
+ 'foo-another-script',
+ ], environment: {
+ '_PUB_TEST_SDK_VERSION': '0.0.1'
+ });
+ expect(binStub('foo-script'), contains('script.dart-0.0.1.snapshot'));
+
+ expect(binStub('foo-script2'), contains('script.dart-0.0.1.snapshot'));
+
+ expect(
+ binStub('foo-script-not-installed'),
+ null,
+ );
+
+ expect(
+ binStub('foo-another-script'),
+ contains('another-script.dart-0.0.1.snapshot'),
+ );
+
+ expect(
+ binStub('foo-another-script-not-installed'),
+ null,
+ );
+
+ // Replace the created snapshot with one that really doesn't work with the
+ // current dart.
await d.dir(cachePath, [
d.dir('global_packages', [
d.dir('foo', [
- d.dir('bin', [d.outOfDateSnapshot('script.dart.snapshot')])
+ d.dir(
+ 'bin',
+ [d.outOfDateSnapshot('script.dart-0.0.1.snapshot')],
+ )
])
])
]).create();
@@ -35,7 +93,43 @@
['arg1', 'arg2'],
environment: getEnvironment());
- expect(process.stdout, emitsThrough('ok'));
+ expect(
+ await process.stdout.rest.toList(),
+ allOf([
+ contains('Precompiled foo:script.'),
+ contains('ok [arg1, arg2]')
+ ]));
+
+ expect(
+ binStub('foo-script'),
+ contains('script.dart-0.1.2+3.snapshot'),
+ );
+
+ expect(
+ binStub('foo-script2'),
+ contains('script.dart-0.1.2+3.snapshot'),
+ );
+
+ expect(
+ binStub('foo-script-not-installed'),
+ null,
+ reason: 'global run recompile should not install new binstubs',
+ );
+
+ expect(
+ binStub('foo-another-script'),
+ contains('another-script.dart-0.0.1.snapshot'),
+ reason:
+ 'global run recompile should not refresh binstubs for other scripts',
+ );
+
+ expect(
+ binStub('foo-another-script-not-installed'),
+ null,
+ reason:
+ 'global run recompile should not install binstubs for other scripts',
+ );
+
await process.shouldExit();
});
}
diff --git a/test/global/binstubs/utils.dart b/test/global/binstubs/utils.dart
index e7a185f..752762f 100644
--- a/test/global/binstubs/utils.dart
+++ b/test/global/binstubs/utils.dart
@@ -11,20 +11,15 @@
/// The buildbots do not have the Dart SDK (containing "dart" and "pub") on
/// their PATH, so we need to spawn the binstub process with a PATH that
/// explicitly includes it.
+///
+/// The `pub`/`pub.bat` command on the PATH will be the one in tool/test-bin not
+/// the one from the sdk.
Map getEnvironment() {
- // TODO(rnystrom): This doesn't do the right thing when running pub's tests
- // from pub's own repo instead of from within the Dart SDK repo. This always
- // sets up the PATH to point to the directory where the Dart VM was run from,
- // which will be unrelated to the path where pub itself is located when
- // running from pub's repo.
- //
- // However, pub's repo doesn't actually have the shell scripts required to
- // run "pub". Those live in the Dart SDK repo. One fix would be to make shell
- // scripts in pub's repo that can act like those scripts but invoke pub from
- // source from the pub repo.
- var binDir = p.dirname(Platform.executable);
+ var binDir = p.dirname(Platform.resolvedExecutable);
var separator = Platform.isWindows ? ';' : ':';
- var path = "${Platform.environment["PATH"]}$separator$binDir";
+ var pubBin = p.absolute('tool', 'test-bin');
+ var path =
+ "$pubBin$separator${Platform.environment["PATH"]}$separator$binDir";
var environment = getPubTestEnvironment();
environment['PATH'] = path;
diff --git a/tool/test-bin/pub b/tool/test-bin/pub
new file mode 100755
index 0000000..c8b55a7
--- /dev/null
+++ b/tool/test-bin/pub
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+# Copyright (c) 2020, 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.
+
+# Runs bin/pub.dart with dart from PATH (or the snapshot if present).
+
+if [ -n "$_PUB_TEST_SNAPSHOT" ]; then
+ dart $_PUB_TEST_SNAPSHOT $*
+else
+ dart `dirname "$0"`/../../bin/pub.dart $*
+fi
diff --git a/tool/test-bin/pub.bat b/tool/test-bin/pub.bat
new file mode 100644
index 0000000..3254535
--- /dev/null
+++ b/tool/test-bin/pub.bat
@@ -0,0 +1,12 @@
+@echo off
+rem Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+rem for details. All rights reserved. Use of this source code is governed by a
+rem BSD-style license that can be found in the LICENSE file.
+
+rem Runs bin/pub.dart with dart from PATH (or the snapshot if present).
+
+if "%_PUB_TEST_SNAPSHOT%"=="" (
+ dart %~p0\..\..\..\bin\pub.dart %*
+) else (
+ dart %_PUB_TEST_SNAPSHOT% %*
+)