Special case global-run message for updated sdk (#4419)

diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart
index f491d69..16e2f17 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -28,6 +28,7 @@
 import 'source/hosted.dart';
 import 'source/path.dart';
 import 'source/root.dart';
+import 'source/sdk.dart';
 import 'system_cache.dart';
 import 'utils.dart';
 
@@ -455,7 +456,16 @@
       recompile: (exectuable) async {
         final root = entrypoint.workspaceRoot;
         final name = exectuable.package;
-        // Resolve it and download its dependencies.
+
+        // When recompiling we re-resolve it and download its dependencies. This
+        // is mainly to protect from the case where the sdk was updated, and
+        // that causes some incompatibilities. (could be the new sdk is outside
+        // some package's environment constraint range, or that the sdk came
+        // with incompatible versions of sdk packages).
+        //
+        // We use --enforce-lockfile semantics, because we want upgrading
+        // globally activated packages to be conscious, and not a part of
+        // running them.
         SolveResult result;
         try {
           result = await log.spinner(
@@ -474,14 +484,44 @@
         result.packages.removeWhere((id) => id.name == 'pub global activate');
 
         final newLockFile = await result.downloadCachedPackages(cache);
+        final report = SolveReport(
+          SolveType.get,
+          entrypoint.workspaceRoot.dir,
+          entrypoint.workspaceRoot.pubspec,
+          entrypoint.workspaceRoot.allOverridesInWorkspace,
+          entrypoint.lockFile,
+          newLockFile,
+          result.availableVersions,
+          cache,
+          dryRun: true,
+          enforceLockfile: true,
+          quiet: false,
+        );
+        await report.show(summary: true);
+
         final sameVersions = entrypoint.lockFile.samePackageIds(newLockFile);
+
         if (!sameVersions) {
-          dataError('''
-The package `$name` as currently activated cannot resolve to the same packages.
+          if (newLockFile.packages.values.any((p) {
+            return p.source is SdkSource &&
+                p.version != entrypoint.lockFile.packages[p.name]?.version;
+          })) {
+            // More specific error message for the case of a version match with
+            // an sdk package.
+            dataError('''
+The current activation of `$name` is not compatible with your current SDK.
 
 Try reactivating the package.
 `$topLevelProgram pub global activate $name`
 ''');
+          } else {
+            dataError('''
+The current activation of `$name` cannot resolve to the same set of dependencies.
+
+Try reactivating the package.
+`$topLevelProgram pub global activate $name`
+''');
+          }
         }
         await recompile(exectuable);
         _refreshBinStubs(entrypoint, executable);
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 dc308c7..01a03d6 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
@@ -48,6 +48,7 @@
     // all output we see the precompilation messages as well.
     expect(pub.stdout, emits('Resolving dependencies...'));
     expect(pub.stdout, emits('Downloading packages...'));
+    expect(pub.stdout, emits(startsWith('No dependencies would change in ')));
     expect(pub.stdout, emits('Building package executable...'));
     expect(pub.stdout, emitsThrough('ok'));
     await pub.shouldExit();
@@ -70,6 +71,102 @@
       'foo',
       '1.0.0',
       deps: {
+        'bar': 'any',
+      },
+      contents: [
+        d.dir('bin', [
+          d.file('foo.dart', 'import "package:bar/bar.dart"; main() => bar();'),
+        ]),
+      ],
+    );
+
+    server.serve(
+      'bar',
+      '1.0.0',
+      contents: [
+        d.dir('lib', [
+          d.file('bar.dart', 'bar() => print("original");'),
+        ]),
+      ],
+    );
+
+    await runPub(
+      args: ['global', 'activate', 'foo'],
+    );
+
+    await runPub(
+      args: ['global', 'run', 'foo'],
+      output: 'original',
+    );
+
+    server.serve(
+      'bar',
+      '1.0.0',
+      contents: [
+        d.dir('lib', [
+          d.file('foo.dart', 'foo() => print("updated");'),
+        ]),
+      ],
+    );
+
+    await runPub(
+      args: ['global', 'run', 'foo'],
+      environment: {
+        'DART_ROOT': p.join(d.sandbox, 'dart'),
+        // Updated sdk version makes the old snapshot obsolete
+        '_PUB_TEST_SDK_VERSION': '3.2.1+4',
+      },
+      output: contains('~ bar 1.0.0 (was 1.0.0)'),
+      error: allOf(
+        contains(
+          'The current activation of `foo` cannot resolve to the same set of '
+          'dependencies.',
+        ),
+        contains(
+          "The existing content-hash from pubspec.lock doesn't match "
+          'contents for:',
+        ),
+        contains('Try reactivating the package'),
+      ),
+      exitCode: DATA,
+    );
+
+    await d.dir('dart', [
+      d.dir('packages', [
+        d.dir('bar', [
+          // Doesn't fulfill constraint, but doesn't satisfy pubspec.lock.
+          d.libPubspec('bar', '2.0.0', deps: {}),
+        ]),
+      ]),
+    ]).create();
+    await runPub(
+      args: ['global', 'run', 'foo'],
+      environment: {
+        'DART_ROOT': p.join(d.sandbox, 'dart'),
+        '_PUB_TEST_SDK_VERSION': '3.2.1+4',
+      },
+      error: allOf(
+        contains(
+          'The existing content-hash from pubspec.lock doesn\'t match '
+          'contents for:',
+        ),
+        contains(
+          'The current activation of `foo` cannot resolve to the same '
+          'set of dependencies.',
+        ),
+        contains('Try reactivating the package'),
+      ),
+      exitCode: DATA,
+    );
+  });
+
+  test('validate resolution before recompilation - updated sdk package',
+      () async {
+    final server = await servePackages();
+    server.serve(
+      'foo',
+      '1.0.0',
+      deps: {
         'bar': {'sdk': 'dart', 'version': '^1.0.0'},
       },
       contents: [
@@ -120,9 +217,12 @@
         'DART_ROOT': p.join(d.sandbox, 'dart'),
         '_PUB_TEST_SDK_VERSION': '3.2.1+4',
       },
+      output: contains('> bar 1.2.0 from sdk dart (was 1.0.0 from sdk dart)'),
       error: allOf(
-        contains('The package `foo` as currently activated cannot resolve to '
-            'the same packages'),
+        contains(
+          'The current activation of `foo` is not compatible with your '
+          'current SDK.',
+        ),
         contains('Try reactivating the package'),
       ),
       exitCode: DATA,