Don't re-precompile if current global installed was same version (#2810)
diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart
index 43cf996..bde09a2 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -85,8 +85,6 @@
Future<void> activateGit(String repo, List<String> executables,
{Map<String, FeatureDependency> features, bool overwriteBinStubs}) async {
var name = await cache.git.getPackageNameFromRepo(repo);
- // Call this just to log what the current active package is, if any.
- _describeActive(name);
// TODO(nweiz): Add some special handling for git repos that contain path
// dependencies. Their executables shouldn't be cached, and there should
@@ -119,7 +117,6 @@
{Map<String, FeatureDependency> features,
bool overwriteBinStubs,
String url}) async {
- _describeActive(name);
await _installInCache(
cache.hosted.source
.refFor(name, url: url)
@@ -146,8 +143,14 @@
await entrypoint.acquireDependencies(SolveType.GET);
var name = entrypoint.root.name;
- // Call this just to log what the current active package is, if any.
- _describeActive(name);
+ try {
+ var originalLockFile =
+ LockFile.load(_getLockFilePath(name), cache.sources);
+ // Call this just to log what the current active package is, if any.
+ _describeActive(originalLockFile, name);
+ } on IOException {
+ // Couldn't read the lock file. It probably doesn't exist.
+ }
// Write a lockfile that points to the local package.
var fullPath = canonicalize(entrypoint.root.dir);
@@ -168,6 +171,16 @@
/// Installs the package [dep] and its dependencies into the system cache.
Future<void> _installInCache(PackageRange dep, List<String> executables,
{bool overwriteBinStubs}) async {
+ LockFile originalLockFile;
+ try {
+ originalLockFile =
+ LockFile.load(_getLockFilePath(dep.name), cache.sources);
+ // Call this just to log what the current active package is, if any.
+ _describeActive(originalLockFile, dep.name);
+ } on IOException {
+ // Couldn't read the lock file. It probably doesn't exist.
+ }
+
// Create a dummy package with just [dep] so we can do resolution on it.
var root = Package.inMemory(Pubspec('pub global activate',
dependencies: [dep], sources: cache.sources));
@@ -190,7 +203,17 @@
rethrow;
}
- result.showReport(SolveType.GET);
+ final sameVersions = originalLockFile != null &&
+ originalLockFile.samePackageIds(result.lockFile);
+
+ if (sameVersions) {
+ log.message('''
+The package ${dep.name} is already activated at newest available version.
+To recompile executables, first run `global decativate ${dep.name}`.
+''');
+ } else {
+ result.showReport(SolveType.GET);
+ }
// Make sure all of the dependencies are locally installed.
await Future.wait(result.packages.map((id) {
@@ -215,10 +238,13 @@
final entrypoint = Entrypoint.global(
Package(result.pubspecs[dep.name],
cache.source(dep.source).getDirectory(id)),
- result.lockFile,
+ lockFile,
cache,
solveResult: result);
- await entrypoint.precompileExecutables();
+ if (!sameVersions) {
+ // Only precompile binaries if we have a new resolution.
+ await entrypoint.precompileExecutables();
+ }
_updateBinStubs(
entrypoint,
@@ -255,27 +281,21 @@
}
/// Shows the user the currently active package with [name], if any.
- void _describeActive(String name) {
- try {
- var lockFile = LockFile.load(_getLockFilePath(name), cache.sources);
- var id = lockFile.packages[name];
+ void _describeActive(LockFile lockFile, String name) {
+ var id = lockFile.packages[name];
- var source = id.source;
- if (source is GitSource) {
- var url = source.urlFromDescription(id.description);
- log.message('Package ${log.bold(name)} is currently active from Git '
- 'repository "$url".');
- } else if (source is PathSource) {
- var path = source.pathFromDescription(id.description);
- log.message('Package ${log.bold(name)} is currently active at path '
- '"$path".');
- } else {
- log.message('Package ${log.bold(name)} is currently active at version '
- '${log.bold(id.version)}.');
- }
- } on IOException {
- // If we couldn't read the lock file, it's not activated.
- return;
+ var source = id.source;
+ if (source is GitSource) {
+ var url = source.urlFromDescription(id.description);
+ log.message('Package ${log.bold(name)} is currently active from Git '
+ 'repository "$url".');
+ } else if (source is PathSource) {
+ var path = source.pathFromDescription(id.description);
+ log.message('Package ${log.bold(name)} is currently active at path '
+ '"$path".');
+ } else {
+ log.message('Package ${log.bold(name)} is currently active at version '
+ '${log.bold(id.version)}.');
}
}
diff --git a/lib/src/lock_file.dart b/lib/src/lock_file.dart
index 795dfc4..8616778 100644
--- a/lib/src/lock_file.dart
+++ b/lib/src/lock_file.dart
@@ -338,4 +338,17 @@
}
return 'transitive';
}
+
+ /// `true` if [other] has the same packages as `this` in the same versions
+ /// from the same sources.
+ bool samePackageIds(LockFile other) {
+ if (packages.length != other.packages.length) {
+ return false;
+ }
+ for (final id in packages.values) {
+ final otherId = other.packages[id.name];
+ if (id != otherId) return false;
+ }
+ return true;
+ }
}
diff --git a/test/global/activate/activate_hosted_twice_test.dart b/test/global/activate/activate_hosted_twice_test.dart
new file mode 100644
index 0000000..a90e52c
--- /dev/null
+++ b/test/global/activate/activate_hosted_twice_test.dart
@@ -0,0 +1,68 @@
+// 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.
+
+import 'package:test/test.dart';
+
+import '../../descriptor.dart' as d;
+import '../../test_pub.dart';
+
+void main() {
+ test('activating a hosted package twice will not precompile', () async {
+ await servePackages((builder) => builder
+ ..serve('foo', '1.0.0', deps: {
+ 'bar': 'any'
+ }, contents: [
+ d.dir('bin', [
+ d.file('foo.dart', r'''
+import 'package:bar/bar.dart';
+main(args) => print('bar $version');''')
+ ])
+ ])
+ ..serve('bar', '1.0.0', contents: [
+ d.dir('lib', [d.file('bar.dart', 'final version = "1.0.0";')])
+ ]));
+
+ await runPub(args: ['global', 'activate', 'foo'], output: '''
+Resolving dependencies...
++ bar 1.0.0
++ foo 1.0.0
+Downloading foo 1.0.0...
+Downloading bar 1.0.0...
+Precompiling executables...
+Precompiled foo:foo.
+Activated foo 1.0.0.''');
+
+ await runPub(args: ['global', 'activate', 'foo'], output: '''
+Package foo is currently active at version 1.0.0.
+Resolving dependencies...
+The package foo is already activated at newest available version.
+To recompile executables, first run `global decativate foo`.
+Activated foo 1.0.0.''');
+
+ var pub = await pubRun(global: true, args: ['foo']);
+ expect(pub.stdout, emits('bar 1.0.0'));
+ await pub.shouldExit();
+
+ await runPub(args: ['global', 'activate', 'foo']);
+
+ globalPackageServer
+ .add((builder) => builder.serve('bar', '2.0.0', contents: [
+ d.dir('lib', [d.file('bar.dart', 'final version = "2.0.0";')])
+ ]));
+
+ await runPub(args: ['global', 'activate', 'foo'], output: '''
+Package foo is currently active at version 1.0.0.
+Resolving dependencies...
++ bar 2.0.0
++ foo 1.0.0
+Downloading bar 2.0.0...
+Precompiling executables...
+Precompiled foo:foo.
+Activated foo 1.0.0.''');
+
+ var pub2 = await pubRun(global: true, args: ['foo']);
+ expect(pub2.stdout, emits('bar 2.0.0'));
+ await pub2.shouldExit();
+ });
+}