Lazy loading of pubspec.yaml from entrypoint (#3835)
diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart index c6e7c84..eeae0c7 100644 --- a/lib/src/command/add.dart +++ b/lib/src/command/add.dart
@@ -11,7 +11,6 @@ import '../command.dart'; import '../command_runner.dart'; -import '../entrypoint.dart'; import '../exceptions.dart'; import '../git.dart'; import '../io.dart'; @@ -184,10 +183,13 @@ final updates = argResults.rest.map((p) => _parsePackage(p, argResults)).toList(); - var updatedPubSpec = entrypoint.root.pubspec; + /// Compute a pubspec that will depend on all the given packages, but the + /// actual constraint will only be determined after a resolution decides the + /// best version. + var resolutionPubspec = entrypoint.root.pubspec; for (final update in updates) { /// Perform version resolution in-memory. - updatedPubSpec = await _addPackageToPubspec(updatedPubSpec, update); + resolutionPubspec = await _addPackageToPubspec(resolutionPubspec, update); } late SolveResult solveResult; @@ -201,7 +203,7 @@ solveResult = await resolveVersions( SolveType.upgrade, cache, - Package.inMemory(updatedPubSpec), + Package.inMemory(resolutionPubspec), ); } on GitException { final name = updates.first.ref.name; @@ -224,7 +226,7 @@ /// Assert that [resultPackage] is within the original user's expectations. final constraint = update.constraint; if (constraint != null && !constraint.allows(resultPackage.version)) { - final dependencyOverrides = updatedPubSpec.dependencyOverrides; + final dependencyOverrides = resolutionPubspec.dependencyOverrides; if (dependencyOverrides.isNotEmpty) { dataError('"$name" resolved to "${resultPackage.version}" which ' 'does not satisfy constraint "$constraint". This could be ' @@ -232,50 +234,42 @@ } } } - if (argResults.isDryRun) { - /// Even if it is a dry run, run `acquireDependencies` so that the user - /// gets a report on the other packages that might change version due - /// to this new dependency. - final newRoot = Package.inMemory(updatedPubSpec); - - await Entrypoint.inMemory( - newRoot, - cache, - solveResult: solveResult, - lockFile: entrypoint.lockFile, - ).acquireDependencies( - SolveType.get, - dryRun: true, - precompile: argResults.shouldPrecompile, - analytics: analytics, - ); - } else { + final newPubspecText = _updatePubspec(solveResult.packages, updates); + if (!argResults.isDryRun) { /// Update the `pubspec.yaml` before calling [acquireDependencies] to /// ensure that the modification timestamp on `pubspec.lock` and /// `.dart_tool/package_config.json` is newer than `pubspec.yaml`, /// ensuring that [entrypoint.assertUptoDate] will pass. - _updatePubspec( - solveResult.packages, - updates, - ); + writeTextFile(entrypoint.pubspecPath, newPubspecText); + } - /// Create a new [Entrypoint] since we have to reprocess the updated - /// pubspec file. - final updatedEntrypoint = Entrypoint(directory, cache); - await updatedEntrypoint.acquireDependencies( + /// Even if it is a dry run, run `acquireDependencies` so that the user + /// gets a report on the other packages that might change version due + /// to this new dependency. + await entrypoint + .withPubspec( + Pubspec.parse( + newPubspecText, + cache.sources, + location: Uri.parse(entrypoint.pubspecPath), + ), + ) + .acquireDependencies( + SolveType.get, + dryRun: argResults.isDryRun, + precompile: !argResults.isDryRun && argResults.shouldPrecompile, + analytics: argResults.isDryRun ? null : analytics, + ); + + if (!argResults.isDryRun && + argResults.example && + entrypoint.example != null) { + await entrypoint.example!.acquireDependencies( SolveType.get, precompile: argResults.shouldPrecompile, + summaryOnly: true, analytics: analytics, ); - - if (argResults.example && entrypoint.example != null) { - await entrypoint.example!.acquireDependencies( - SolveType.get, - precompile: argResults.shouldPrecompile, - summaryOnly: true, - analytics: analytics, - ); - } } if (isOffline) { @@ -656,8 +650,8 @@ ); } - /// Writes the changes to the pubspec file. - void _updatePubspec( + /// Calculates the updates to the pubspec file. + String _updatePubspec( List<PackageId> resultPackages, List<_ParseResult> updates, ) { @@ -683,7 +677,7 @@ } else { pubspecInformation = { ref.source.name: ref.description.serializeForPubspec( - containingDir: entrypoint.root.dir, + containingDir: entrypoint.rootDir, languageVersion: entrypoint.root.pubspec.languageVersion, ), if (description is HostedDescription || constraint != null) @@ -730,8 +724,7 @@ } } - /// Windows line endings are already handled by [yamlEditor] - writeTextFile(entrypoint.pubspecPath, yamlEditor.toString()); + return yamlEditor.toString(); } }
diff --git a/lib/src/command/deps.dart b/lib/src/command/deps.dart index fff66ab..b34e33e 100644 --- a/lib/src/command/deps.dart +++ b/lib/src/command/deps.dart
@@ -10,6 +10,7 @@ import '../command_runner.dart'; import '../log.dart' as log; import '../package.dart'; +import '../pubspec.dart'; import '../sdk.dart'; import '../utils.dart'; @@ -101,7 +102,7 @@ : currentPackage.dependencies) .keys .toList(); - final dependencyType = entrypoint.root.dependencyType(current); + final dependencyType = entrypoint.root.pubspec.dependencyType(current); final kind = currentPackage == entrypoint.root ? 'root' : (dependencyType == DependencyType.direct
diff --git a/lib/src/command/lish.dart b/lib/src/command/lish.dart index b5330c9..89db747 100644 --- a/lib/src/command/lish.dart +++ b/lib/src/command/lish.dart
@@ -260,11 +260,11 @@ var package = entrypoint.root; log.message( 'Publishing ${package.name} ${package.version} to $host:\n' - '${tree.fromFiles(files, baseDir: entrypoint.root.dir, showFileSizes: true)}', + '${tree.fromFiles(files, baseDir: entrypoint.rootDir, showFileSizes: true)}', ); var packageBytesFuture = - createTarGz(files, baseDir: entrypoint.root.dir).toBytes(); + createTarGz(files, baseDir: entrypoint.rootDir).toBytes(); // Validate the package. var isValid = await _validate(
diff --git a/lib/src/command/list_package_dirs.dart b/lib/src/command/list_package_dirs.dart new file mode 100644 index 0000000..b572a9e --- /dev/null +++ b/lib/src/command/list_package_dirs.dart
@@ -0,0 +1,79 @@ +// Copyright (c) 2013, 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:path/path.dart' as p; + +import '../command.dart'; +import '../command_runner.dart'; +import '../io.dart'; +import '../log.dart' as log; +import '../package_name.dart'; +import '../utils.dart'; + +/// Handles the `list-package-dirs` pub command. +class ListPackageDirsCommand extends PubCommand { + @override + String get name => 'list-package-dirs'; + @override + String get description => 'Print local paths to dependencies.'; + @override + String get argumentsDescription => ''; + @override + bool get takesArguments => false; + @override + bool get hidden => true; + + ListPackageDirsCommand() { + argParser.addOption( + 'format', + help: 'How output should be displayed.', + allowed: ['json'], + ); + argParser.addOption( + 'directory', + abbr: 'C', + help: 'Run this in the directory <dir>.', + valueHelp: 'dir', + ); + } + + @override + Future<void> runProtected() async { + log.json.enabled = true; + entrypoint.assertUpToDate(); + if (!fileExists(entrypoint.lockFilePath)) { + dataError( + 'Package "myapp" has no lockfile. Please run "$topLevelProgram pub get" first.', + ); + } + + var output = {}; + + // Include the local paths to all locked packages. + var packages = mapMap( + entrypoint.lockFile.packages, + value: (String name, PackageId package) { + var packageDir = cache.getDirectory(package); + // Normalize paths and make them absolute for backwards compatibility + // with the protocol used by the analyzer. + return p.normalize(p.absolute(p.join(packageDir, 'lib'))); + }, + ); + + // Include the self link. + packages[entrypoint.root.name] = + p.normalize(p.absolute(entrypoint.root.path('lib'))); + + output['packages'] = packages; + + // Include the file(s) which when modified will affect the results. For pub, + // that's just the pubspec and lockfile. + output['input_files'] = [ + p.normalize(p.absolute(entrypoint.lockFilePath)), + p.normalize(p.absolute(entrypoint.pubspecPath)) + ]; + + log.json.message(output); + } +}
diff --git a/lib/src/command/remove.dart b/lib/src/command/remove.dart index cf65241..c993cd0 100644 --- a/lib/src/command/remove.dart +++ b/lib/src/command/remove.dart
@@ -6,10 +6,8 @@ import 'package:yaml_edit/yaml_edit.dart'; import '../command.dart'; -import '../entrypoint.dart'; import '../io.dart'; import '../log.dart' as log; -import '../package.dart'; import '../pubspec.dart'; import '../solver.dart'; @@ -84,40 +82,28 @@ return _PackageRemoval(name, removeFromOverride: isOverride); }); - if (isDryRun) { - final rootPubspec = entrypoint.root.pubspec; - final newPubspec = _removePackagesFromPubspec(rootPubspec, targets); - final newRoot = Package.inMemory(newPubspec); - - await Entrypoint.inMemory(newRoot, cache, lockFile: entrypoint.lockFile) - .acquireDependencies( - SolveType.get, - precompile: argResults['precompile'], - dryRun: true, - analytics: null, - ); - } else { + if (!isDryRun) { /// Update the pubspec. _writeRemovalToPubspec(targets); + } + final rootPubspec = entrypoint.root.pubspec; + final newPubspec = _removePackagesFromPubspec(rootPubspec, targets); - /// Create a new [Entrypoint] since we have to reprocess the updated - /// pubspec file. - final updatedEntrypoint = Entrypoint(directory, cache); - await updatedEntrypoint.acquireDependencies( + await entrypoint.withPubspec(newPubspec).acquireDependencies( + SolveType.get, + precompile: !isDryRun && argResults['precompile'], + dryRun: isDryRun, + analytics: isDryRun ? null : analytics, + ); + + var example = entrypoint.example; + if (!isDryRun && argResults['example'] && example != null) { + await example.acquireDependencies( SolveType.get, precompile: argResults['precompile'], + summaryOnly: true, analytics: analytics, ); - - var example = entrypoint.example; - if (argResults['example'] && example != null) { - await example.acquireDependencies( - SolveType.get, - precompile: argResults['precompile'], - summaryOnly: true, - analytics: analytics, - ); - } } }
diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart index 17db3e7..7efa66e 100644 --- a/lib/src/command/upgrade.dart +++ b/lib/src/command/upgrade.dart
@@ -113,7 +113,7 @@ if (_upgradeMajorVersions) { if (argResults['example'] && entrypoint.example != null) { log.warning( - 'Running `upgrade --major-versions` only in `${entrypoint.root.dir}`. Run `$topLevelProgram pub upgrade --major-versions --directory example/` separately.', + 'Running `upgrade --major-versions` only in `${entrypoint.rootDir}`. Run `$topLevelProgram pub upgrade --major-versions --directory example/` separately.', ); } await _runUpgradeMajorVersions(); @@ -240,36 +240,27 @@ final solveType = argResults.rest.isEmpty ? SolveType.upgrade : SolveType.get; - if (_dryRun) { - // Even if it is a dry run, run `acquireDependencies` so that the user - // gets a report on changes. - await Entrypoint.inMemory( - Package.inMemory( - Pubspec.parse(newPubspecText, cache.sources), - ), - cache, - lockFile: entrypoint.lockFile, - solveResult: solveResult, - ).acquireDependencies( - solveType, - dryRun: true, - precompile: _precompile, - analytics: null, // No analytics for dry-run - ); - } else { + if (!_dryRun) { if (changes.isNotEmpty) { writeTextFile(entrypoint.pubspecPath, newPubspecText); } - // TODO: Allow Entrypoint to be created with in-memory pubspec, so that - // we can show the changes when not in --dry-run mode. For now we only show - // the changes made to pubspec.yaml in dry-run mode. - await Entrypoint(directory, cache).acquireDependencies( - solveType, - precompile: _precompile, - analytics: analytics, - ); } + await entrypoint + .withPubspec( + Pubspec.parse( + newPubspecText, + cache.sources, + location: Uri.parse(entrypoint.pubspecPath), + ), + ) + .acquireDependencies( + solveType, + dryRun: _dryRun, + precompile: !_dryRun && _precompile, + analytics: _dryRun ? null : analytics, // No analytics for dry-run + ); + _outputChangeSummary(changes); // If any of the packages to upgrade are dependency overrides, then we
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart index 1b98da6..9a01f9b 100644 --- a/lib/src/entrypoint.dart +++ b/lib/src/entrypoint.dart
@@ -73,10 +73,24 @@ /// contains a reusable library may not be the entrypoint when used by an app, /// but may be the entrypoint when you're running its tests. class Entrypoint { + /// The directory where the package is stored. + /// + /// For global packages this is inside the pub cache. + /// + /// Except for packages globally activated from path. + final String rootDir; + + Package? _root; + /// The root package this entrypoint is associated with. /// /// For a global package, this is the activated package. - final Package root; + Package get root => _root ??= Package.load( + null, + rootDir, + cache.sources, + withPubspecOverrides: true, + ); /// For a global package, this is the directory that the package is installed /// in. Non-global packages have null. @@ -87,7 +101,7 @@ final SystemCache cache; /// Whether this entrypoint exists within the package cache. - bool get isCached => !root.isInMemory && p.isWithin(cache.rootDir, root.dir); + bool get isCached => p.isWithin(cache.rootDir, rootDir); /// Whether this is an entrypoint for a globally-activated package. // final bool isGlobal; @@ -175,7 +189,7 @@ /// /// Global packages (except those from path source) /// store these in the global cache. - String? get _configRoot => isCached ? globalDir : root.dir; + String? get _configRoot => isCached ? globalDir : rootDir; /// The path to the entrypoint's ".packages" file. /// @@ -191,15 +205,11 @@ ); /// The path to the entrypoint package's pubspec. - String get pubspecPath => p.normalize(root.path('pubspec.yaml')); - - /// Whether the entrypoint package contains a `pubspec_overrides.yaml` file. - bool get hasPubspecOverrides => - !root.isInMemory && fileExists(pubspecOverridesPath); + String get pubspecPath => p.normalize(p.join(rootDir, 'pubspec.yaml')); /// The path to the entrypoint package's pubspec overrides file. String get pubspecOverridesPath => - p.normalize(root.path('pubspec_overrides.yaml')); + p.normalize(p.join(rootDir, 'pubspec_overrides.yaml')); /// The path to the entrypoint package's lockfile. String get lockFilePath => p.normalize(p.join(_configRoot!, 'pubspec.lock')); @@ -209,7 +219,7 @@ /// For globally activated packages from path, this is not the same as /// [configRoot], because the snapshots should be stored in the global cache, /// but the configuration is stored at the package itself. - String get cachePath => globalDir ?? root.path('.dart_tool/pub'); + String get cachePath => globalDir ?? p.join(rootDir, '.dart_tool/pub'); /// The path to the directory containing dependency executable snapshots. String get _snapshotPath => p.join(cachePath, 'bin'); @@ -218,44 +228,51 @@ /// builds. String get _incrementalDillsPath => p.join(cachePath, 'incremental'); - /// Loads the entrypoint from a package at [rootDir]. + Entrypoint._( + this.rootDir, + this._lockFile, + this._example, + this._packageGraph, + this.cache, + this._root, + this.globalDir, + ); + + /// An entrypoint representing a package at [rootDir]. Entrypoint( - String rootDir, + this.rootDir, this.cache, { - bool withPubspecOverrides = true, - }) : root = Package.load( - null, - rootDir, - cache.sources, - withPubspecOverrides: withPubspecOverrides, - ), + Pubspec? pubspec, + }) : _root = pubspec == null ? null : Package.inMemory(pubspec), globalDir = null { if (p.isWithin(cache.rootDir, rootDir)) { fail('Cannot operate on packages inside the cache.'); } } - Entrypoint.inMemory( - this.root, - this.cache, { - required LockFile? lockFile, - SolveResult? solveResult, - }) : _lockFile = lockFile, - globalDir = null { - if (solveResult != null) { - _packageGraph = PackageGraph.fromSolveResult(this, solveResult); - } + /// Creates an entrypoint at the same location, that will use [pubspec] for + /// resolution. + Entrypoint withPubspec(Pubspec pubspec) { + return Entrypoint._( + rootDir, + _lockFile, + _example, + _packageGraph, + cache, + Package.inMemory(pubspec), + globalDir, + ); } /// Creates an entrypoint given package and lockfile objects. /// If a SolveResult is already created it can be passed as an optimization. Entrypoint.global( this.globalDir, - this.root, + Package this._root, this._lockFile, this.cache, { SolveResult? solveResult, - }) { + }) : rootDir = _root.dir { if (solveResult != null) { _packageGraph = PackageGraph.fromSolveResult(this, solveResult); } @@ -285,7 +302,7 @@ entrypoint: entrypointName, entrypointSdkConstraint: root.pubspec.sdkConstraints[sdk.identifier]?.effectiveConstraint, - relativeFrom: isGlobal ? null : root.dir, + relativeFrom: isGlobal ? null : rootDir, ), ); } @@ -325,8 +342,9 @@ bool summaryOnly = false, bool enforceLockfile = false, }) async { + root; // This will throw early if pubspec.yaml could not be found. summaryOnly = summaryOnly || _summaryOnlyEnvironment; - final suffix = root.isInMemory || root.dir == '.' ? '' : ' in ${root.dir}'; + final suffix = rootDir == '.' ? '' : ' in $rootDir'; if (enforceLockfile && !fileExists(lockFilePath)) { throw ApplicationException(''' @@ -354,7 +372,8 @@ final report = SolveReport( type, - root, + rootDir, + root.pubspec, lockFile, newLockFile, result.availableVersions, @@ -597,13 +616,22 @@ /// the resolution automatically. void assertUpToDate({bool checkForSdkUpdate = false}) { if (isCached) return; - - if (!entryExists(lockFilePath)) { + final pubspecStat = tryStatFile(pubspecPath); + if (pubspecStat == null) { + throw FileException( + 'Could not find a file named "pubspec.yaml" in ' + '"${canonicalize(rootDir)}".', + pubspecPath, + ); + } + final lockFileStat = tryStatFile(lockFilePath); + if (lockFileStat == null) { dataError( 'No $lockFilePath file found, please run "$topLevelProgram pub get" first.', ); } - if (!entryExists(packageConfigPath)) { + final packageConfigStat = tryStatFile(packageConfigPath); + if (packageConfigStat == null) { dataError( 'No $packageConfigPath file found, please run "$topLevelProgram pub get".\n' '\n' @@ -617,17 +645,15 @@ var lockFileText = readTextFile(lockFilePath); var hasPathDependencies = lockFileText.contains('\n source: path\n'); - var pubspecModified = File(pubspecPath).lastModifiedSync(); - var lockFileModified = File(lockFilePath).lastModifiedSync(); + var lockFileModified = lockFileStat.modified; - var pubspecChanged = lockFileModified.isBefore(pubspecModified); + var pubspecChanged = lockFileModified.isBefore(pubspecStat.modified); var pubspecOverridesChanged = false; - if (hasPubspecOverrides) { - var pubspecOverridesModified = - File(pubspecOverridesPath).lastModifiedSync(); + final pubspecOverridesStat = tryStatFile(pubspecOverridesPath); + if (pubspecOverridesStat != null) { pubspecOverridesChanged = - lockFileModified.isBefore(pubspecOverridesModified); + lockFileModified.isBefore(pubspecOverridesStat.modified); } var touchedLockFile = false; @@ -650,8 +676,7 @@ } } - var packageConfigModified = File(packageConfigPath).lastModifiedSync(); - if (packageConfigModified.isBefore(lockFileModified) || + if (packageConfigStat.modified.isBefore(lockFileModified) || hasPathDependencies) { // If `package_config.json` is older than `pubspec.lock` or we have // path dependencies, then we check that `package_config.json` is a @@ -666,9 +691,9 @@ } } - for (var match in _sdkConstraint.allMatches(lockFileText)) { - var identifier = match[1] == 'sdk' ? 'dart' : match[1]!.trim(); - var sdk = sdks[identifier]!; + for (final match in _sdkConstraint.allMatches(lockFileText)) { + final identifier = match[1] == 'sdk' ? 'dart' : match[1]!.trim(); + final sdk = sdks[identifier]!; // Don't complain if there's an SDK constraint for an unavailable SDK. For // example, the Flutter SDK being unavailable just means that we aren't @@ -676,7 +701,7 @@ // able to `pub run` non-Flutter tools even in a Flutter app. if (!sdk.isAvailable) continue; - var parsedConstraint = VersionConstraint.parse(match[2]!); + final parsedConstraint = VersionConstraint.parse(match[2]!); if (!parsedConstraint.allows(sdk.version!)) { dataError('${sdk.name} ${sdk.version} is incompatible with your ' "dependencies' SDK constraints. Please run \"$topLevelProgram pub get\" again."); @@ -792,7 +817,7 @@ final source = lockFileId.source; final lockFilePackagePath = root.path( - cache.getDirectory(lockFileId, relativeFrom: root.dir), + cache.getDirectory(lockFileId, relativeFrom: rootDir), ); // Make sure that the packagePath agrees with the lock file about the
diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart index 1d35144..eaa068c 100644 --- a/lib/src/global_packages.dart +++ b/lib/src/global_packages.dart
@@ -178,7 +178,7 @@ _describeActive(name, cache); // Write a lockfile that points to the local package. - var fullPath = canonicalize(entrypoint.root.dir); + var fullPath = canonicalize(entrypoint.rootDir); var id = cache.path.idFor( name, entrypoint.root.version, @@ -261,7 +261,8 @@ if (!silent) { await SolveReport( SolveType.get, - root, + null, + root.pubspec, originalLockFile ?? LockFile.empty(), lockFile, result.availableVersions, @@ -574,7 +575,7 @@ ); } else { await activatePath( - entrypoint.root.dir, + entrypoint.rootDir, packageExecutables, overwriteBinStubs: true, analytics: null,
diff --git a/lib/src/http.dart b/lib/src/http.dart index 00f5a7f..ab302fd 100644 --- a/lib/src/http.dart +++ b/lib/src/http.dart
@@ -13,7 +13,7 @@ import 'command.dart'; import 'log.dart' as log; -import 'package.dart'; +import 'pubspec.dart'; import 'sdk.dart'; import 'source/hosted.dart'; import 'utils.dart';
diff --git a/lib/src/io.dart b/lib/src/io.dart index 005f6c5..8f510f6 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart
@@ -81,6 +81,20 @@ /// points to a file. bool fileExists(String file) => File(file).existsSync(); +/// Stats [path], assuming it or the entry it is a link to is a file. +/// +/// Returns `null` if it is not a file (eg. a directory or not existing). +FileStat? tryStatFile(String path) { + var stat = File(path).statSync(); + if (stat.type == FileSystemEntityType.link) { + stat = File(File(path).resolveSymbolicLinksSync()).statSync(); + } + if (stat.type == FileSystemEntityType.file) { + return stat; + } + return null; +} + /// Returns the canonical path for [pathString]. /// /// This is the normalized, absolute path, with symlinks resolved. As in
diff --git a/lib/src/package.dart b/lib/src/package.dart index 6aeeb78..9853418 100644 --- a/lib/src/package.dart +++ b/lib/src/package.dart
@@ -181,17 +181,6 @@ return p.relative(path, from: dir); } - /// Returns the type of dependency from this package onto [name]. - DependencyType dependencyType(String? name) { - if (pubspec.fields['dependencies']?.containsKey(name) ?? false) { - return DependencyType.direct; - } else if (pubspec.fields['dev_dependencies']?.containsKey(name) ?? false) { - return DependencyType.dev; - } else { - return DependencyType.none; - } - } - static final _basicIgnoreRules = [ '.*', // Don't include dot-files. '!.htaccess', // Include .htaccess anyways. @@ -329,22 +318,3 @@ ).map(resolve).toList(); } } - -/// The type of dependency from one package to another. -class DependencyType { - /// A dependency declared in `dependencies`. - static const direct = DependencyType._('direct'); - - /// A dependency declared in `dev_dependencies`. - static const dev = DependencyType._('dev'); - - /// No dependency exists. - static const none = DependencyType._('none'); - - final String _name; - - const DependencyType._(this._name); - - @override - String toString() => _name; -}
diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart index a1f84c1..5f27abc 100644 --- a/lib/src/pubspec.dart +++ b/lib/src/pubspec.dart
@@ -433,6 +433,27 @@ collectError(() => sdkConstraints); return errors; } + + /// Returns the type of dependency from this package onto [name]. + DependencyType dependencyType(String? name) { + if (dependencies.containsKey(name)) { + return DependencyType.direct; + } else if (devDependencies.containsKey(name)) { + return DependencyType.dev; + } else { + return DependencyType.none; + } + } +} + +/// The type of dependency from one package to another. +enum DependencyType { + direct, + dev, + none; + + @override + String toString() => name; } /// Parses the dependency field named [field], and returns the corresponding
diff --git a/lib/src/solver/report.dart b/lib/src/solver/report.dart index 1f57423..bbc4381 100644 --- a/lib/src/solver/report.dart +++ b/lib/src/solver/report.dart
@@ -8,7 +8,6 @@ import '../command_runner.dart'; import '../lock_file.dart'; import '../log.dart' as log; -import '../package.dart'; import '../package_name.dart'; import '../pubspec.dart'; import '../source/hosted.dart'; @@ -25,7 +24,9 @@ /// It's a report builder. class SolveReport { final SolveType _type; - final Package _root; + // The report will contain "in [_location]" if given. + final String? _location; + final Pubspec _rootPubspec; final LockFile _previousLockFile; final LockFile _newLockFile; final SystemCache _cache; @@ -46,7 +47,8 @@ SolveReport( this._type, - this._root, + this._location, + this._rootPubspec, this._previousLockFile, this._newLockFile, this._availableVersions, @@ -141,7 +143,7 @@ // Count how many dependencies actually changed. var dependencies = _newLockFile.packages.keys.toSet(); dependencies.addAll(_previousLockFile.packages.keys); - dependencies.remove(_root.name); + dependencies.remove(_rootPubspec.name); var numChanged = dependencies.where((name) { var oldId = _previousLockFile.packages[name]; @@ -156,8 +158,8 @@ }).length; var suffix = ''; - if (!_root.isInMemory) { - final dir = _root.dir; + final dir = _location; + if (dir != null) { if (dir != '.') { suffix = ' in $dir'; } @@ -218,7 +220,7 @@ final output = StringBuffer(); // Show the new set of dependencies ordered by name. var names = _newLockFile.packages.keys.toList(); - names.remove(_root.name); + names.remove(_rootPubspec.name); names.sort(); var hasChanges = false; for (final name in names) { @@ -227,7 +229,7 @@ // Show any removed ones. var removed = _previousLockFile.packages.keys.toSet(); removed.removeAll(names); - removed.remove(_root.name); // Never consider root. + removed.remove(_rootPubspec.name); // Never consider root. if (removed.isNotEmpty) { output.writeln('These packages are no longer being depended on:'); for (var name in ordered(removed)) { @@ -249,8 +251,8 @@ final status = await id.source .status(id.toRef(), id.version, _cache, maxAge: Duration(days: 3)); if (status.isDiscontinued && - (_root.dependencyType(id.name) == DependencyType.direct || - _root.dependencyType(id.name) == DependencyType.dev)) { + (_rootPubspec.dependencyType(id.name) == DependencyType.direct || + _rootPubspec.dependencyType(id.name) == DependencyType.dev)) { numDiscontinued++; } } @@ -306,7 +308,7 @@ var oldId = _previousLockFile.packages[name]; var id = newId ?? oldId!; - var isOverridden = _root.dependencyOverrides.containsKey(id.name); + var isOverridden = _rootPubspec.dependencyOverrides.containsKey(id.name); // If the package was previously a dependency but the dependency has // changed in some way. @@ -386,8 +388,8 @@ message = '(retracted)'; } } else if (status.isDiscontinued && - (_root.dependencyType(name) == DependencyType.direct || - _root.dependencyType(name) == DependencyType.dev)) { + [DependencyType.direct, DependencyType.dev] + .contains(_rootPubspec.dependencyType(name))) { if (status.discontinuedReplacedBy == null) { message = '(discontinued)'; } else { @@ -428,10 +430,12 @@ // Highlight overridden packages. if (isOverridden) { - final location = _root.pubspec.dependencyOverridesFromOverridesFile - ? ' in ${p.join(_root.dir, Pubspec.pubspecOverridesFilename)}' - : ''; - output.write(' ${log.magenta('(overridden$location)')}'); + final location = _location; + final overrideLocation = + location != null && _rootPubspec.dependencyOverridesFromOverridesFile + ? ' in ${p.join(location, Pubspec.pubspecOverridesFilename)}' + : ''; + output.write(' ${log.magenta('(overridden$overrideLocation)')}'); } if (message != null) output.write(' ${log.cyan(message)}');
diff --git a/lib/src/solver/result.dart b/lib/src/solver/result.dart index 2be099e..bb70ef1 100644 --- a/lib/src/solver/result.dart +++ b/lib/src/solver/result.dart
@@ -65,7 +65,7 @@ final resolvedPackageIds = await Future.wait( packages.map((id) async { if (id.source is CachedSource) { - return await withDependencyType(_root.dependencyType(id.name), + return await withDependencyType(_root.pubspec.dependencyType(id.name), () async { return await cache.downloadPackage( id, @@ -153,7 +153,7 @@ DependencyType.dev: 'dev', DependencyType.direct: 'direct', DependencyType.none: 'transitive' - }[_root.dependencyType(package.name)]!; + }[_root.pubspec.dependencyType(package.name)]!; analytics.sendEvent( 'pub-get', package.name,
diff --git a/lib/src/solver/version_solver.dart b/lib/src/solver/version_solver.dart index 1b341d4..e8a2346 100644 --- a/lib/src/solver/version_solver.dart +++ b/lib/src/solver/version_solver.dart
@@ -512,7 +512,7 @@ _systemCache, ref, locked, - _root.dependencyType(package.name), + _root.pubspec.dependencyType(package.name), overridden, _getAllowedRetracted(ref.name), downgrade: _type == SolveType.downgrade,
diff --git a/lib/src/validator.dart b/lib/src/validator.dart index 6fcab2a..07ef9b3 100644 --- a/lib/src/validator.dart +++ b/lib/src/validator.dart
@@ -215,7 +215,7 @@ /// entrypoint). // TODO(sigurdm): Consider moving this to a more central location. List<String> filesBeneath(String dir, {required bool recursive}) { - final base = p.canonicalize(p.join(entrypoint.root.dir, dir)); + final base = p.canonicalize(p.join(entrypoint.rootDir, dir)); return files .where( recursive
diff --git a/lib/src/validator/analyze.dart b/lib/src/validator/analyze.dart index b1e70a2..d22da8d 100644 --- a/lib/src/validator/analyze.dart +++ b/lib/src/validator/analyze.dart
@@ -18,7 +18,7 @@ @override Future<void> validate() async { final dirsToAnalyze = ['lib', 'test', 'bin'] - .map((dir) => p.join(entrypoint.root.dir, dir)) + .map((dir) => p.join(entrypoint.rootDir, dir)) .where(dirExists); final result = await runProcess( Platform.resolvedExecutable, @@ -26,7 +26,7 @@ 'analyze', '--fatal-infos', ...dirsToAnalyze, - p.join(entrypoint.root.dir, 'pubspec.yaml') + p.join(entrypoint.rootDir, 'pubspec.yaml') ], ); if (result.exitCode != 0) {
diff --git a/lib/src/validator/directory.dart b/lib/src/validator/directory.dart index 8a3e7f2..a7ef221 100644 --- a/lib/src/validator/directory.dart +++ b/lib/src/validator/directory.dart
@@ -27,8 +27,8 @@ for (final file in files) { // Find the topmost directory name of [file]. final dir = path.join( - entrypoint.root.dir, - path.split(path.relative(file, from: entrypoint.root.dir)).first, + entrypoint.rootDir, + path.split(path.relative(file, from: entrypoint.rootDir)).first, ); if (!visited.add(dir)) continue; if (!dirExists(dir)) continue;
diff --git a/lib/src/validator/gitignore.dart b/lib/src/validator/gitignore.dart index 9a1770a..e30bb01 100644 --- a/lib/src/validator/gitignore.dart +++ b/lib/src/validator/gitignore.dart
@@ -30,7 +30,7 @@ '--exclude-standard', '--recurse-submodules' ], - workingDir: entrypoint.root.dir, + workingDir: entrypoint.rootDir, ); } on git.GitException catch (e) { log.fine('Could not run `git ls-files` files in repo (${e.message}).'); @@ -39,9 +39,9 @@ // --recurse-submodules we just continue silently. return; } - final root = git.repoRoot(entrypoint.root.dir) ?? entrypoint.root.dir; + final root = git.repoRoot(entrypoint.rootDir) ?? entrypoint.rootDir; var beneath = p.posix.joinAll( - p.split(p.normalize(p.relative(entrypoint.root.dir, from: root))), + p.split(p.normalize(p.relative(entrypoint.rootDir, from: root))), ); if (beneath == './') { beneath = ''; @@ -73,7 +73,7 @@ }, isDir: (dir) => dirExists(resolve(dir)), ).map((file) { - final relative = p.relative(resolve(file), from: entrypoint.root.dir); + final relative = p.relative(resolve(file), from: entrypoint.rootDir); return Platform.isWindows ? p.posix.joinAll(p.split(relative)) : relative;
diff --git a/lib/src/validator/strict_dependencies.dart b/lib/src/validator/strict_dependencies.dart index 2fec2f6..728f592 100644 --- a/lib/src/validator/strict_dependencies.dart +++ b/lib/src/validator/strict_dependencies.dart
@@ -23,7 +23,7 @@ /// Files that do not parse and directives that don't import or export /// `package:` URLs are ignored. Iterable<_Usage> _findPackages(Iterable<String> files) sync* { - final packagePath = p.normalize(p.absolute(entrypoint.root.dir)); + final packagePath = p.normalize(p.absolute(entrypoint.rootDir)); final AnalysisContextManager analysisContextManager = AnalysisContextManager(packagePath);
diff --git a/test/add/common/version_resolution_test.dart b/test/add/common/version_resolution_test.dart index b526dab..5e73a86 100644 --- a/test/add/common/version_resolution_test.dart +++ b/test/add/common/version_resolution_test.dart
@@ -28,6 +28,12 @@ server.serve('foo', '3.1.0'); server.serve('foo', '2.5.0'); + await pubAdd( + args: ['foo', '--dry-run'], + output: allOf( + contains('> foo 3.5.0 (was 3.2.1)'), + ), + ); await pubAdd(args: ['foo']); await d.appDir(dependencies: {'foo': '^3.5.0', 'bar': '1.0.0'}).validate();
diff --git a/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt b/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt index f8d34c3..f350065 100644 --- a/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt +++ b/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt
@@ -325,6 +325,12 @@ Command: dart pub fail Platform: $OS +---- $SANDBOX/empty/pubspec.yaml ---- +<No pubspec.yaml> +---- End pubspec.yaml ---- +---- $SANDBOX/empty/pubspec.lock ---- +<No pubspec.lock> +---- End pubspec.lock ---- ---- Log transcript ---- FINE: Pub 3.1.2+3 ERR : Bad state: Pub has crashed