diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart index 693c9a2c1..7c8da54bc 100644 --- a/lib/src/entrypoint.dart +++ b/lib/src/entrypoint.dart @@ -566,7 +566,6 @@ Try running `$topLevelProgram pub get` to create `$lockFilePath`.'''); // We have to download files also with --dry-run to ensure we know the // archive hashes for downloaded files. final newLockFile = await result.downloadCachedPackages(cache); - final report = SolveReport( type, workspaceRoot.presentationDir, @@ -582,7 +581,7 @@ Try running `$topLevelProgram pub get` to create `$lockFilePath`.'''); ); await report.show(summary: true); - if (enforceLockfile && !_lockfilesMatch(lockFile, newLockFile)) { + if (enforceLockfile && !lockFile.samePackageIds(newLockFile)) { dataError(''' Unable to satisfy `${workspaceRoot.pubspecPath}` using `$lockFilePath`$suffix. @@ -1333,25 +1332,6 @@ See https://dart.dev/go/sdk-constraint bool get _summaryOnlyEnvironment => (Platform.environment['PUB_SUMMARY_ONLY'] ?? '0') != '0'; - /// Returns true if the packages in [newLockFile] and [previousLockFile] are - /// all the same, meaning: - /// * same set of package-names - /// * for each package - /// * same version number - /// * same resolved description (same content-hash, git hash, path) - bool _lockfilesMatch(LockFile previousLockFile, LockFile newLockFile) { - if (previousLockFile.packages.length != newLockFile.packages.length) { - return false; - } - for (final package in newLockFile.packages.values) { - final oldPackage = previousLockFile.packages[package.name]; - if (oldPackage == null) return false; // Package added to resolution. - if (oldPackage.version != package.version) return false; - if (oldPackage.description != package.description) return false; - } - return true; - } - /// Remove any `pubspec.lock` or `.dart_tool/package_config.json` files in /// workspace packages that are not the root package. /// diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart index f61768590..f491d69b8 100644 --- a/lib/src/global_packages.dart +++ b/lib/src/global_packages.dart @@ -130,6 +130,12 @@ class GlobalPackages { 'pub global activate', dependencies: [dep], sources: cache.sources, + sdkConstraints: { + 'dart': SdkConstraint.interpretDartSdkConstraint( + VersionConstraint.parse('>=2.12.0'), + defaultUpperBoundConstraint: null, + ), + }, ), dir, [], @@ -447,6 +453,36 @@ try: args, enableAsserts: enableAsserts, recompile: (exectuable) async { + final root = entrypoint.workspaceRoot; + final name = exectuable.package; + // Resolve it and download its dependencies. + SolveResult result; + try { + result = await log.spinner( + 'Resolving dependencies', + () => resolveVersions(SolveType.get, cache, root), + ); + } on SolveFailure catch (e) { + log.error(e.message); + fail('''The package `$name` as currently activated cannot resolve. + +Try reactivating the package. +`$topLevelProgram pub global activate $name` +'''); + } + // We want the entrypoint to be rooted at 'dep' not the dummy-package. + result.packages.removeWhere((id) => id.name == 'pub global activate'); + + final newLockFile = await result.downloadCachedPackages(cache); + final sameVersions = entrypoint.lockFile.samePackageIds(newLockFile); + if (!sameVersions) { + dataError(''' +The package `$name` as currently activated cannot resolve to the same packages. + +Try reactivating the package. +`$topLevelProgram pub global activate $name` +'''); + } await recompile(exectuable); _refreshBinStubs(entrypoint, executable); }, diff --git a/lib/src/lock_file.dart b/lib/src/lock_file.dart index d2a018706..11fcbeec7 100644 --- a/lib/src/lock_file.dart +++ b/lib/src/lock_file.dart @@ -415,15 +415,21 @@ ${yamlToString(data)} return _transitive; } - /// `true` if [other] has the same packages as `this` in the same versions - /// from the same sources. + /// Returns true if the packages in `this` and [other] are + /// all the same, meaning: + /// * same set of package-names + /// * for each package + /// * same version number + /// * same resolved description (same content-hash, git hash, path) bool samePackageIds(LockFile other) { - if (packages.length != other.packages.length) { + if (other.packages.length != packages.length) { return false; } - for (final id in packages.values) { - final otherId = other.packages[id.name]; - if (id != otherId) return false; + for (final package in packages.values) { + final oldPackage = other.packages[package.name]; + if (oldPackage == null) return false; // Package added to resolution. + if (oldPackage.version != package.version) return false; + if (oldPackage.description != package.description) return false; } return true; } diff --git a/pubspec.lock b/pubspec.lock index e20fe4db8..85bff242b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: f6dbf021f4b214d85c79822912c5fcd142a2c4869f01222ad371bc51f9f1c356 + sha256: c57b02f47e021c9d7ced6d2e28824b315e0fd585578274bc4c2a5db0626f154a url: "https://pub.dev" source: hosted - version: "74.0.0" + version: "75.0.0" _macros: dependency: transitive description: dart @@ -18,10 +18,10 @@ packages: dependency: "direct main" description: name: analyzer - sha256: f7e8caf82f2d3190881d81012606effdf8a38e6c1ab9e30947149733065f817c + sha256: ef226c581b7cd875f734125b1b9928df3db08cc85ff87ce7d9be89a677aaee23 url: "https://pub.dev" source: hosted - version: "6.9.0" + version: "6.10.0" args: dependency: "direct main" description: 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 f16d55a5b..baf5e7437 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 @@ -3,7 +3,9 @@ // BSD-style license that can be found in the LICENSE file. import 'package:path/path.dart' as p; +import 'package:pub/src/exit_codes.dart'; import 'package:pub/src/io.dart'; +import 'package:pub/src/sdk/sdk_package_config.dart'; import 'package:test/test.dart'; import '../../descriptor.dart' as d; @@ -59,4 +61,91 @@ void main() { ]), ]).validate(); }); + + test('validate resolution before recompilation', () async { + final server = await servePackages(); + server.serve( + 'foo', + '1.0.0', + deps: { + 'bar': {'sdk': 'dart', 'version': '^1.0.0'}, + }, + contents: [ + d.dir('bin', [ + d.file('foo.dart', 'main() => print("foo");'), + ]), + ], + ); + + await d.dir('dart', [ + d.dir('packages', [ + d.dir('bar', [ + d.libPubspec('bar', '1.0.0', deps: {}), + ]), + ]), + d.sdkPackagesConfig( + SdkPackageConfig( + 'dart', + {'bar': SdkPackage('bar', 'packages/bar')}, + 1, + ), + ), + ]).create(); + + await runPub( + args: ['global', 'activate', 'foo'], + environment: {'DART_ROOT': p.join(d.sandbox, 'dart')}, + ); + + await runPub( + args: ['global', 'run', 'foo'], + environment: {'DART_ROOT': p.join(d.sandbox, 'dart')}, + output: 'foo', + ); + + await d.dir('dart', [ + d.dir('packages', [ + d.dir('bar', [ + // Within constraint, but doesn't satisfy pubspec.lock. + d.libPubspec('bar', '1.2.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 package `foo` as currently activated cannot resolve to ' + 'the same packages'), + 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('Because every version of foo depends on bar ^1.0.0 from sdk'), + contains('The package `foo` as currently activated cannot resolve.'), + contains('Try reactivating the package'), + ), + exitCode: 1, + ); + }); }