Avoid packageGraph (and therefore getExecutableForCommand) depending on the lock-file (#3533)
diff --git a/lib/src/command/deps.dart b/lib/src/command/deps.dart
index 678aafa..c860f02 100644
--- a/lib/src/command/deps.dart
+++ b/lib/src/command/deps.dart
@@ -102,8 +102,7 @@
? 'dev'
: 'transitive'));
final source =
- entrypoint.packageGraph.lockFile.packages[current]?.source.name ??
- 'root';
+ entrypoint.lockFile.packages[current]?.source.name ?? 'root';
packagesJson.add({
'name': current,
'version': currentPackage.version.toString(),
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index eb643fe..d250cc0 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -107,6 +107,34 @@
LockFile? _lockFile;
+ /// The `.dart_tool/package_config.json` package-config of this entrypoint.
+ ///
+ /// Lazily initialized. Will throw [DataError] when initializing if the
+ /// `.dart_tool/packageConfig.json` file doesn't exist or has a bad format .
+ late PackageConfig packageConfig = () {
+ late String packageConfigRaw;
+ try {
+ packageConfigRaw = readTextFile(packageConfigPath);
+ } on FileException {
+ dataError(
+ 'The "$packageConfigPath" file does not exist, please run "$topLevelProgram pub get".');
+ }
+ late PackageConfig result;
+ try {
+ result = PackageConfig.fromJson(json.decode(packageConfigRaw));
+ } on FormatException {
+ badPackageConfig();
+ }
+ // Version 2 is the initial version number for `package_config.json`,
+ // because `.packages` was version 1 (even if it was a different file).
+ // If the version is different from 2, then it must be a newer incompatible
+ // version, hence, the user should run `pub get` with the downgraded SDK.
+ if (result.configVersion != 2) {
+ badPackageConfig();
+ }
+ return result;
+ }();
+
/// The package graph for the application and all of its transitive
/// dependencies.
///
@@ -117,11 +145,16 @@
PackageGraph _createPackageGraph() {
assertUpToDate();
var packages = {
- for (var id in lockFile.packages.values) id.name: cache.load(id)
+ for (var packageEntry in packageConfig.nonInjectedPackages)
+ packageEntry.name: Package.load(
+ packageEntry.name,
+ packageEntry.resolvedRootDir(packageConfigPath),
+ cache.sources,
+ ),
};
packages[root.name] = root;
- return PackageGraph(this, lockFile, packages);
+ return PackageGraph(this, packages);
}
PackageGraph? _packageGraph;
@@ -139,9 +172,10 @@
/// not require it or make use of it within pub.
String get packagesFile => p.normalize(p.join(_configRoot!, '.packages'));
- /// The path to the entrypoint's ".dart_tool/package_config.json" file.
- String get packageConfigFile =>
- p.normalize(p.join(_configRoot!, '.dart_tool', 'package_config.json'));
+ /// The path to the entrypoint's ".dart_tool/package_config.json" file
+ /// relative to the current working directory .
+ late String packageConfigPath = p.relative(
+ p.normalize(p.join(_configRoot!, '.dart_tool', 'package_config.json')));
/// The path to the entrypoint package's pubspec.
String get pubspecPath => p.normalize(root.path('pubspec.yaml'));
@@ -226,9 +260,9 @@
/// Writes .packages and .dart_tool/package_config.json
Future<void> writePackageConfigFile() async {
final entrypointName = isGlobal ? null : root.name;
- ensureDir(p.dirname(packageConfigFile));
+ ensureDir(p.dirname(packageConfigPath));
writeTextFile(
- packageConfigFile,
+ packageConfigPath,
await lockFile.packageConfigFile(
cache,
entrypoint: entrypointName,
@@ -421,7 +455,7 @@
executablePath: resolveExecutable(executable),
outputPath: pathOfExecutable(executable),
incrementalDillPath: incrementalDillPathOfExecutable(executable),
- packageConfigPath: packageConfigFile,
+ packageConfigPath: packageConfigPath,
name:
'$package:${p.basenameWithoutExtension(executable.relativePath)}');
}
@@ -512,9 +546,9 @@
dataError(
'No $lockFilePath file found, please run "$topLevelProgram pub get" first.');
}
- if (!entryExists(packageConfigFile)) {
+ if (!entryExists(packageConfigPath)) {
dataError(
- 'No $packageConfigFile file found, please run "$topLevelProgram pub get".\n'
+ 'No $packageConfigPath file found, please run "$topLevelProgram pub get".\n'
'\n'
'Starting with Dart 2.7, the package_config.json file configures '
'resolution of package import URIs; run "$topLevelProgram pub get" to generate it.',
@@ -559,7 +593,7 @@
}
}
- var packageConfigModified = File(packageConfigFile).lastModifiedSync();
+ var packageConfigModified = File(packageConfigPath).lastModifiedSync();
if (packageConfigModified.isBefore(lockFileModified) ||
hasPathDependencies) {
// If `package_config.json` is older than `pubspec.lock` or we have
@@ -568,9 +602,9 @@
// * Mitigate issues when copying a folder from one machine to another.
// * Force `pub get` if a path dependency has changed language version.
_checkPackageConfigUpToDate();
- touch(packageConfigFile);
+ touch(packageConfigPath);
} else if (touchedLockFile) {
- touch(packageConfigFile);
+ touch(packageConfigPath);
}
for (var match in _sdkConstraint.allMatches(lockFileText)) {
@@ -724,49 +758,13 @@
void _checkPackageConfigUpToDate() {
void outOfDate() {
dataError('The $lockFilePath file has changed since the '
- '$packageConfigFile file '
+ '$packageConfigPath file '
'was generated, please run "$topLevelProgram pub get" again.');
}
- void badPackageConfig() {
- dataError('The "$packageConfigFile" file is not recognized by '
- '"pub" version, please run "$topLevelProgram pub get".');
- }
-
- late String packageConfigRaw;
- try {
- packageConfigRaw = readTextFile(packageConfigFile);
- } on FileException {
- dataError(
- 'The "$packageConfigFile" file does not exist, please run "$topLevelProgram pub get".');
- }
-
- late PackageConfig cfg;
- try {
- cfg = PackageConfig.fromJson(json.decode(packageConfigRaw));
- } on FormatException {
- badPackageConfig();
- }
-
- // Version 2 is the initial version number for `package_config.json`,
- // because `.packages` was version 1 (even if it was a different file).
- // If the version is different from 2, then it must be a newer incompatible
- // version, hence, the user should run `pub get` with the downgraded SDK.
- if (cfg.configVersion != 2) {
- badPackageConfig();
- }
-
final packagePathsMapping = <String, String>{};
- // We allow the package called 'flutter_gen' to be injected into
- // package_config.
- //
- // This is somewhat a hack. But it allows flutter to generate code in a
- // package as it likes.
- //
- // See https://github.com/flutter/flutter/issues/73870 .
- final packagesToCheck =
- cfg.packages.where((package) => package.name != 'flutter_gen');
+ final packagesToCheck = packageConfig.nonInjectedPackages;
for (final pkg in packagesToCheck) {
// Pub always makes a packageUri of lib/
if (pkg.packageUri == null || pkg.packageUri.toString() != 'lib/') {
@@ -781,7 +779,7 @@
// Check if language version specified in the `package_config.json` is
// correct. This is important for path dependencies as these can mutate.
- for (final pkg in cfg.packages) {
+ for (final pkg in packageConfig.packages) {
if (pkg.name == root.name || pkg.name == 'flutter_gen') continue;
final id = lockFile.packages[pkg.name];
if (id == null) {
@@ -909,6 +907,11 @@
}
}
}
+
+ Never badPackageConfig() {
+ dataError('The "$packageConfigPath" file is not recognized by '
+ '"pub" version, please run "$topLevelProgram pub get".');
+ }
}
/// Returns `true` if the [text] looks like it uses windows line endings.
diff --git a/lib/src/executable.dart b/lib/src/executable.dart
index 5d8ea8b..d926023 100644
--- a/lib/src/executable.dart
+++ b/lib/src/executable.dart
@@ -106,7 +106,7 @@
// helpful for the subprocess to be able to spawn Dart with
// Platform.executableArguments and have that work regardless of the working
// directory.
- final packageConfigAbsolute = p.absolute(entrypoint.packageConfigFile);
+ final packageConfigAbsolute = p.absolute(entrypoint.packageConfigPath);
try {
return await _runDartProgram(
@@ -335,14 +335,13 @@
command = package;
}
- if (!entrypoint.packageGraph.packages.containsKey(package)) {
+ if (!entrypoint.packageConfig.packages.any((p) => p.name == package)) {
throw CommandResolutionFailedException._(
'Could not find package `$package` or file `$descriptor`',
CommandResolutionIssue.packageNotFound,
);
}
final executable = Executable(package, p.join('bin', '$command.dart'));
- final packageConfig = p.join('.dart_tool', 'package_config.json');
final path = entrypoint.resolveExecutable(executable);
if (!fileExists(path)) {
@@ -351,10 +350,12 @@
CommandResolutionIssue.noBinaryFound,
);
}
+ final packageConfigPath =
+ p.relative(entrypoint.packageConfigPath, from: root);
if (!allowSnapshot) {
return DartExecutableWithPackageConfig(
executable: p.relative(path, from: root),
- packageConfig: packageConfig,
+ packageConfig: packageConfigPath,
);
} else {
final snapshotPath = entrypoint.pathOfExecutable(executable);
@@ -373,7 +374,7 @@
}
return DartExecutableWithPackageConfig(
executable: p.relative(snapshotPath, from: root),
- packageConfig: packageConfig,
+ packageConfig: packageConfigPath,
);
}
}
diff --git a/lib/src/package_config.dart b/lib/src/package_config.dart
index e5e5e7d..dec8b5c 100644
--- a/lib/src/package_config.dart
+++ b/lib/src/package_config.dart
@@ -4,6 +4,7 @@
import 'dart:convert';
+import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';
import 'language_version.dart';
@@ -139,6 +140,16 @@
'generator': generator,
'generatorVersion': generatorVersion?.toString(),
}..addAll(additionalProperties);
+
+ // We allow the package called 'flutter_gen' to be injected into
+ // package_config.
+ //
+ // This is somewhat a hack. But it allows flutter to generate code in a
+ // package as it likes.
+ //
+ // See https://github.com/flutter/flutter/issues/73870 .
+ Iterable<PackageConfigEntry> get nonInjectedPackages =>
+ packages.where((package) => package.name != 'flutter_gen');
}
class PackageConfigEntry {
@@ -260,4 +271,8 @@
// TODO: implement toString
return JsonEncoder.withIndent(' ').convert(toJson());
}
+
+ String resolvedRootDir(String packageConfigPath) {
+ return p.join(p.dirname(packageConfigPath), p.fromUri(rootUri));
+ }
}
diff --git a/lib/src/package_graph.dart b/lib/src/package_graph.dart
index cbd91ec..6c736e6 100644
--- a/lib/src/package_graph.dart
+++ b/lib/src/package_graph.dart
@@ -3,12 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:collection/collection.dart' hide mapMap;
+import 'package:path/path.dart' as p;
import 'entrypoint.dart';
-import 'lock_file.dart';
import 'package.dart';
import 'solver.dart';
-import 'source/cached.dart';
import 'utils.dart';
/// A holistic view of the entire transitive dependency graph for an entrypoint.
@@ -16,12 +15,6 @@
/// The entrypoint.
final Entrypoint entrypoint;
- /// The entrypoint's lockfile.
- ///
- /// This describes the sources and resolved descriptions of everything in
- /// [packages].
- final LockFile lockFile;
-
/// The transitive dependencies of the entrypoint (including itself).
///
/// This may not include all transitive dependencies of the entrypoint if the
@@ -32,7 +25,7 @@
/// A map of transitive dependencies for each package.
Map<String, Set<Package>>? _transitiveDependencies;
- PackageGraph(this.entrypoint, this.lockFile, this.packages);
+ PackageGraph(this.entrypoint, this.packages);
/// Creates a package graph using the data from [result].
///
@@ -50,7 +43,7 @@
)
};
- return PackageGraph(entrypoint, result.lockFile, packages);
+ return PackageGraph(entrypoint, packages);
}
/// Returns all transitive dependencies of [package].
@@ -81,12 +74,7 @@
// the entrypoint.
// TODO(sigurdm): there should be a way to get the id of any package
// including the root.
- if (package == entrypoint.root.name) {
- return entrypoint.isCached;
- } else {
- var id = lockFile.packages[package]!;
- return id.source is CachedSource;
- }
+ return p.isWithin(entrypoint.cache.rootDir, packages[package]!.dir);
}
/// Returns whether [package] is mutable.
diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart
index d06b025..0b6cd5b 100644
--- a/lib/src/pubspec.dart
+++ b/lib/src/pubspec.dart
@@ -301,7 +301,6 @@
{String? expectedName, bool allowOverridesFile = false}) {
var pubspecPath = path.join(packageDir, pubspecYamlFilename);
var overridesPath = path.join(packageDir, pubspecOverridesFilename);
-
if (!fileExists(pubspecPath)) {
throw FileException(
// Make the package dir absolute because for the entrypoint it'll just
diff --git a/test/global/run/runs_script_without_packages_file_test.dart b/test/global/run/runs_script_without_packages_file_test.dart
deleted file mode 100644
index dc225ba..0000000
--- a/test/global/run/runs_script_without_packages_file_test.dart
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2015, 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 'package:pub/src/io.dart';
-import 'package:test/test.dart';
-
-import '../../descriptor.dart' as d;
-import '../../test_pub.dart';
-
-void main() {
- test('runs a snapshotted script without a .dart_tool/package_config file',
- () async {
- final server = await servePackages();
- server.serve('foo', '1.0.0', contents: [
- d.dir('bin', [d.file('script.dart', "main(args) => print('ok');")])
- ]);
-
- await runPub(args: ['global', 'activate', 'foo']);
-
- // Mimic the global packages installed by pub <1.12, which didn't create a
- // .packages file for global installs.
- deleteEntry(p.join(d.sandbox, cachePath,
- 'global_packages/foo/.dart_tool/package_config.json'));
-
- var pub = await pubRun(global: true, args: ['foo:script']);
- expect(pub.stdout, emitsThrough('ok'));
- await pub.shouldExit();
- });
-
- test(
- 'runs an unsnapshotted script without a .dart_tool/package_config.json file',
- () async {
- await d.dir('foo', [
- d.libPubspec('foo', '1.0.0'),
- d.dir('bin', [d.file('foo.dart', "main() => print('ok');")])
- ]).create();
-
- await runPub(args: ['global', 'activate', '--source', 'path', '../foo']);
-
- deleteEntry(p.join(d.sandbox, cachePath,
- 'global_packages/foo/.dart_tool/package_config.json'));
-
- var pub = await pubRun(global: true, args: ['foo']);
- expect(pub.stdout, emitsThrough('ok'));
- await pub.shouldExit();
- });
-}