diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart index ad219daeb7..a59ecd660a 100644 --- a/lib/src/command/add.dart +++ b/lib/src/command/add.dart @@ -208,7 +208,7 @@ Specify multiple sdk packages with descriptors.'''); solveResult = await resolveVersions( SolveType.upgrade, cache, - Package.inMemory(resolutionPubspec), + Package(resolutionPubspec, entrypoint.rootDir), ); } on GitException { final name = updates.first.ref.name; diff --git a/lib/src/command/dependency_services.dart b/lib/src/command/dependency_services.dart index 124cae186e..451643fabf 100644 --- a/lib/src/command/dependency_services.dart +++ b/lib/src/command/dependency_services.dart @@ -69,13 +69,13 @@ class DependencyServicesReportCommand extends PubCommand { final breakingPubspec = stripVersionBounds(compatiblePubspec); final compatiblePackagesResult = await _tryResolve( - compatiblePubspec, + Package(compatiblePubspec, entrypoint.rootDir), cache, additionalConstraints: additionalConstraints, ); final breakingPackagesResult = await _tryResolve( - breakingPubspec, + Package(breakingPubspec, entrypoint.rootDir), cache, additionalConstraints: additionalConstraints, ); @@ -110,8 +110,10 @@ class DependencyServicesReportCommand extends PubCommand { dependencySet[package.name] = package .toRef() .withConstraint(stripUpperBound(package.toRange().constraint)); - final singleBreakingPackagesResult = - await _tryResolve(singleBreakingPubspec, cache); + final singleBreakingPackagesResult = await _tryResolve( + Package(singleBreakingPubspec, entrypoint.rootDir), + cache, + ); singleBreakingVersion = singleBreakingPackagesResult ?.firstWhereOrNull((element) => element.name == package.name); } @@ -126,7 +128,7 @@ class DependencyServicesReportCommand extends PubCommand { ); final smallestUpgradeResult = await _tryResolve( - atLeastCurrentPubspec, + Package(atLeastCurrentPubspec, entrypoint.rootDir), cache, solveType: SolveType.downgrade, additionalConstraints: additionalConstraints, @@ -214,7 +216,8 @@ class DependencyServicesListCommand extends PubCommand { final currentPackages = fileExists(entrypoint.lockFilePath) ? entrypoint.lockFile.packages.values.toList() - : (await _tryResolve(pubspec, cache) ?? []); + : (await _tryResolve(Package(pubspec, entrypoint.rootDir), cache) ?? + []); final dependencies = []; final result = {'dependencies': dependencies}; @@ -418,12 +421,13 @@ class DependencyServicesApplyCommand extends PubCommand { final solveResult = await resolveVersions( SolveType.get, cache, - Package.inMemory( + Package( Pubspec.parse( updatedPubspec, cache.sources, location: toUri(entrypoint.pubspecPath), ), + entrypoint.rootDir, ), lockFile: updatedLockfile, ); @@ -635,10 +639,10 @@ bool _lockFileHasContentHashes(dynamic lockfile) { return false; } -/// Try to solve [pubspec] return [PackageId]s in the resolution or `null` if no +/// Try to solve [package] return [PackageId]s in the resolution or `null` if no /// resolution was found. Future?> _tryResolve( - Pubspec pubspec, + Package package, SystemCache cache, { SolveType solveType = SolveType.upgrade, Iterable? additionalConstraints, @@ -646,7 +650,7 @@ Future?> _tryResolve( final solveResult = await tryResolveVersions( solveType, cache, - Package.inMemory(pubspec), + package, additionalConstraints: additionalConstraints, ); @@ -686,7 +690,7 @@ Future> _computeCurrentPackages( if (fileExists(entrypoint.lockFilePath)) { currentPackages = Map.from(entrypoint.lockFile.packages); } else { - final resolution = await _tryResolve(entrypoint.root.pubspec, cache) ?? + final resolution = await _tryResolve(entrypoint.root, cache) ?? (throw DataException('Failed to resolve pubspec')); currentPackages = Map.fromIterable( resolution, @@ -730,7 +734,7 @@ Future> _computeUpgradeSet( ? SolveType.downgrade : SolveType.get, cache, - Package.inMemory(pubspec), + Package(pubspec, entrypoint.rootDir), lockFile: lockFile, additionalConstraints: additionalConstraints, ); diff --git a/lib/src/command/outdated.dart b/lib/src/command/outdated.dart index d60e49aa50..f76a6e8ee1 100644 --- a/lib/src/command/outdated.dart +++ b/lib/src/command/outdated.dart @@ -145,7 +145,7 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); 'Resolving', () async { final upgradablePackagesResult = await _tryResolve( - upgradablePubspec, + Package(upgradablePubspec, entrypoint.rootDir), cache, lockFile: entrypoint.lockFile, ); @@ -153,7 +153,7 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); upgradablePackages = upgradablePackagesResult ?? []; final resolvablePackagesResult = await _tryResolve( - resolvablePubspec, + Package(resolvablePubspec, entrypoint.rootDir), cache, lockFile: entrypoint.lockFile, ); @@ -407,14 +407,14 @@ Consider using the Dart 2.19 sdk to migrate to null safety.'''); /// Try to solve [pubspec] return [PackageId]s in the resolution or `null` if no /// resolution was found. Future?> _tryResolve( - Pubspec pubspec, + Package package, SystemCache cache, { LockFile? lockFile, }) async { final solveResult = await tryResolveVersions( SolveType.upgrade, cache, - Package.inMemory(pubspec), + package, lockFile: lockFile, ); diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart index f202a356f6..7a32bd699c 100644 --- a/lib/src/command/upgrade.dart +++ b/lib/src/command/upgrade.dart @@ -278,7 +278,7 @@ be direct 'dependencies' or 'dev_dependencies', following packages are not: return await resolveVersions( SolveType.upgrade, cache, - Package.inMemory(resolvablePubspec), + Package(resolvablePubspec, entrypoint.rootDir), ); }, condition: _shouldShowSpinner, @@ -328,7 +328,10 @@ be direct 'dependencies' or 'dev_dependencies', following packages are not: final solveResult = await resolveVersions( SolveType.upgrade, cache, - Package.inMemory(_updatedPubspec(newPubspecText, entrypoint)), + Package( + _updatedPubspec(newPubspecText, entrypoint), + entrypoint.rootDir, + ), ); changes = tighten( entrypoint.root.pubspec, diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart index 3220371d05..fb599ef02d 100644 --- a/lib/src/entrypoint.dart +++ b/lib/src/entrypoint.dart @@ -235,7 +235,7 @@ class Entrypoint { this.rootDir, this.cache, { Pubspec? pubspec, - }) : _root = pubspec == null ? null : Package.inMemory(pubspec), + }) : _root = pubspec == null ? null : Package(pubspec, rootDir), globalDir = null { if (p.isWithin(cache.rootDir, rootDir)) { fail('Cannot operate on packages inside the cache.'); @@ -251,7 +251,7 @@ class Entrypoint { _example, _packageGraph, cache, - Package.inMemory(pubspec), + Package(pubspec, rootDir), globalDir, ); } diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart index d216133854..83b48313b9 100644 --- a/lib/src/global_packages.dart +++ b/lib/src/global_packages.dart @@ -212,12 +212,13 @@ class GlobalPackages { final originalLockFile = _describeActive(name, cache); // Create a dummy package with just [dep] so we can do resolution on it. - var root = Package.inMemory( + var root = Package( Pubspec( 'pub global activate', dependencies: [dep], sources: cache.sources, ), + _packageDir(name), ); // Resolve it and download its dependencies. diff --git a/lib/src/package.dart b/lib/src/package.dart index 8058dc3bb3..7a67e7ec2e 100644 --- a/lib/src/package.dart +++ b/lib/src/package.dart @@ -17,7 +17,8 @@ import 'pubspec.dart'; import 'system_cache.dart'; import 'utils.dart'; -/// A named, versioned, unit of code and resource reuse. +/// A Package is a [Pubspec] and a directory where it belongs that can be used +/// for version solving or as a node in a package graph. class Package { /// Compares [a] and [b] orders them by name then version number. /// @@ -31,24 +32,8 @@ class Package { return a.version.compareTo(b.version); } - final String? _dir; - /// The path to the directory containing the package. - /// - /// It is an error to access this on an in-memory package. - String get dir { - if (isInMemory) { - throw UnsupportedError( - 'Package directory cannot be used for an in-memory package', - ); - } - - return _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; + final String dir; /// The name of the package. String get name => pubspec.name; @@ -99,7 +84,7 @@ class Package { late final bool inGitRepo = computeInGitRepoCache(); bool computeInGitRepoCache() { - if (isInMemory || !git.isInstalled) { + if (!git.isInstalled) { return false; } else { // If the entire package directory is ignored, don't consider it part of a @@ -134,21 +119,14 @@ class Package { expectedName: name, allowOverridesFile: withPubspecOverrides, ); - return Package._(dir, pubspec); + return Package(pubspec, dir); } - Package._( - this._dir, - this.pubspec, - ); - - /// Constructs a package with the given pubspec. + /// Creates a package with [pubspec] associated with [dir]. /// - /// The package will have no directory associated with it. - Package.inMemory(this.pubspec) : _dir = null; - - /// Creates a package with [pubspec] located at [dir]. - Package(this.pubspec, String this._dir); + /// For temporary resolution attempts [pubspec] does not have to correspond + /// to the one at disk. + Package(this.pubspec, this.dir); /// Given a relative path within this package, returns its absolute path. /// @@ -164,20 +142,12 @@ class Package { String? part6, String? part7, ]) { - if (isInMemory) { - throw StateError("Package $name is in-memory and doesn't have paths " - 'on disk.'); - } return p.join(dir, part1, part2, part3, part4, part5, part6, part7); } /// Given an absolute path within this package (such as that returned by /// [path] or [listFiles]), returns it relative to the package root. String relative(String path) { - if (isInMemory) { - throw StateError("Package $name is in-memory and doesn't have paths " - 'on disk.'); - } return p.relative(path, from: dir); } @@ -209,9 +179,6 @@ class Package { /// /// To convert them to paths relative to the package root, use [p.relative]. List listFiles({String? beneath, bool recursive = true}) { - // An in-memory package has no files. - if (isInMemory) return []; - var packageDir = dir; var root = git.repoRoot(packageDir) ?? packageDir; beneath = p diff --git a/lib/src/package_name.dart b/lib/src/package_name.dart index f3aff59b24..dcc7712479 100644 --- a/lib/src/package_name.dart +++ b/lib/src/package_name.dart @@ -27,7 +27,7 @@ class PackageRef { /// Creates a reference to the given root package. static PackageRef root(Package package) => - PackageRef(package.name, RootDescription(package)); + PackageRef(package.name, RootDescription(package.dir)); @override String toString([PackageDetail? detail]) { @@ -88,7 +88,7 @@ class PackageId { static PackageId root(Package package) => PackageId( package.name, package.version, - ResolvedRootDescription(RootDescription(package)), + ResolvedRootDescription(RootDescription(package.dir)), ); @override diff --git a/lib/src/solver/package_lister.dart b/lib/src/solver/package_lister.dart index bce663b4c4..f233aad03c 100644 --- a/lib/src/solver/package_lister.dart +++ b/lib/src/solver/package_lister.dart @@ -27,6 +27,9 @@ class PackageLister { /// The package that is being listed. final PackageRef _ref; + /// Only used when _ref is root. + final Pubspec? _rootPubspec; + /// The version of this package in the lockfile. /// /// This is `null` if this package isn't locked or if the current version @@ -110,7 +113,8 @@ class PackageLister { this._allowedRetractedVersion, { bool downgrade = false, this.sdkOverrides = const {}, - }) : _isDowngrade = downgrade; + }) : _isDowngrade = downgrade, + _rootPubspec = null; /// Creates a package lister for the root [package]. PackageLister.root( @@ -127,7 +131,8 @@ class PackageLister { Set.unmodifiable(package.dependencyOverrides.keys), _isDowngrade = false, _allowedRetractedVersion = null, - sdkOverrides = sdkOverrides ?? {}; + sdkOverrides = sdkOverrides ?? {}, + _rootPubspec = package.pubspec; /// Returns the number of versions of this package that match [constraint]. Future countVersions(VersionConstraint constraint) async { @@ -195,33 +200,36 @@ class PackageLister { /// previous call to [incompatibilitiesFor]. Future> incompatibilitiesFor(PackageId id) async { if (_knownInvalidVersions.allows(id.version)) return const []; - Pubspec pubspec; - try { - pubspec = await withDependencyType( - _dependencyType, - () => _systemCache.describe(id), - ); - } on SourceSpanApplicationException catch (error) { - // The lockfile for the pubspec couldn't be parsed, - log.fine('Failed to parse pubspec for $id:\n$error'); - _knownInvalidVersions = _knownInvalidVersions.union(id.version); - return [ - Incompatibility( - [Term(id.toRange(), true)], - NoVersionsIncompatibilityCause(), - ), - ]; - } on PackageNotFoundException { - // We can only get here if the lockfile refers to a specific package - // version that doesn't exist (probably because it was yanked). - _knownInvalidVersions = _knownInvalidVersions.union(id.version); - return [ - Incompatibility( - [Term(id.toRange(), true)], - NoVersionsIncompatibilityCause(), - ), - ]; + if (id.isRoot) { + pubspec = _rootPubspec!; + } else { + try { + pubspec = await withDependencyType( + _dependencyType, + () => _systemCache.describe(id), + ); + } on SourceSpanApplicationException catch (error) { + // The lockfile for the pubspec couldn't be parsed, + log.fine('Failed to parse pubspec for $id:\n$error'); + _knownInvalidVersions = _knownInvalidVersions.union(id.version); + return [ + Incompatibility( + [Term(id.toRange(), true)], + NoVersionsIncompatibilityCause(), + ), + ]; + } on PackageNotFoundException { + // We can only get here if the lockfile refers to a specific package + // version that doesn't exist (probably because it was yanked). + _knownInvalidVersions = _knownInvalidVersions.union(id.version); + return [ + Incompatibility( + [Term(id.toRange(), true)], + NoVersionsIncompatibilityCause(), + ), + ]; + } } if (_cachedVersions == null && diff --git a/lib/src/solver/solve_suggestions.dart b/lib/src/solver/solve_suggestions.dart index 2431b75256..0ffcc0045e 100644 --- a/lib/src/solver/solve_suggestions.dart +++ b/lib/src/solver/solve_suggestions.dart @@ -9,7 +9,6 @@ import '../flutter_releases.dart'; import '../io.dart'; import '../package.dart'; import '../package_name.dart'; -import '../pubspec.dart'; import '../pubspec_utils.dart'; import '../solver.dart'; import '../source/hosted.dart'; @@ -149,7 +148,7 @@ class _ResolutionContext { await inferBestFlutterRelease({cause.sdk.identifier: constraint}); if (bestRelease == null) return null; final result = await _tryResolve( - entrypoint.root.pubspec, + entrypoint.root, sdkOverrides: { 'dart': bestRelease.dartVersion, 'flutter': bestRelease.flutterVersion, @@ -185,7 +184,8 @@ class _ResolutionContext { stripLowerBound: true, ); - final result = await _tryResolve(relaxedPubspec); + final result = + await _tryResolve(Package(relaxedPubspec, entrypoint.rootDir)); if (result == null) { return null; } @@ -223,7 +223,8 @@ class _ResolutionContext { final relaxedPubspec = stripVersionBounds(originalPubspec, stripLowerBound: stripLowerBound); - final result = await _tryResolve(relaxedPubspec); + final result = + await _tryResolve(Package(relaxedPubspec, entrypoint.rootDir)); if (result == null) { return null; } @@ -259,14 +260,14 @@ class _ResolutionContext { /// Attempt resolving Future _tryResolve( - Pubspec pubspec, { + Package package, { Map sdkOverrides = const {}, }) async { try { return await resolveVersions( type, cache, - Package.inMemory(pubspec), + package, sdkOverrides: sdkOverrides, lockFile: entrypoint.lockFile, unlock: unlock, diff --git a/lib/src/source/root.dart b/lib/src/source/root.dart index c91c726e09..0936e11c8a 100644 --- a/lib/src/source/root.dart +++ b/lib/src/source/root.dart @@ -5,12 +5,21 @@ import 'package:pub_semver/pub_semver.dart'; import '../language_version.dart'; -import '../package.dart'; import '../package_name.dart'; import '../pubspec.dart'; import '../source.dart'; import '../system_cache.dart'; +/// A root package is the root of a package dependency graph that seeds a +/// version resolution. +/// +/// There is no explicit way to depend on a root package. +/// +/// A root package is the only package for which dev_dependencies are taken into +/// account. +/// +/// A root package is the only package for which dependency_overrides are taken +/// into account. class RootSource extends Source { static final RootSource instance = RootSource._(); @@ -24,11 +33,7 @@ class RootSource extends Source { PackageId id, SystemCache cache, ) async { - final description = id.description.description; - if (description is! RootDescription) { - throw ArgumentError('Wrong source'); - } - return description.package.pubspec; + throw UnsupportedError('Cannot describe the root'); } @override @@ -37,11 +42,7 @@ class RootSource extends Source { Duration? maxAge, SystemCache cache, ) async { - final description = ref.description; - if (description is! RootDescription) { - throw ArgumentError('Wrong source'); - } - return [PackageId.root(description.package)]; + throw UnsupportedError('Trying to get versions of the root package'); } @override @@ -76,6 +77,9 @@ class RootSource extends Source { } class ResolvedRootDescription extends ResolvedDescription { + @override + RootDescription get description => super.description as RootDescription; + ResolvedRootDescription(RootDescription super.description); @override @@ -92,9 +96,9 @@ class ResolvedRootDescription extends ResolvedDescription { } class RootDescription extends Description { - final Package package; + final String path; - RootDescription(this.package); + RootDescription(this.path); @override String format() { throw UnsupportedError('Trying to format a root package description.'); @@ -113,7 +117,7 @@ class RootDescription extends Description { @override bool operator ==(Object other) => - other is RootDescription && other.package == package; + other is RootDescription && other.path == path; @override int get hashCode => 'root'.hashCode;