Fix .packages entries of relative path deps when using --directory (#2916)
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index 3c949c3..bb3e3c6 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -186,15 +186,18 @@
/// Writes .packages and .dart_tool/package_config.json
Future<void> writePackagesFiles() async {
- writeTextFile(packagesFile, lockFile.packagesFile(cache, root.name));
+ writeTextFile(
+ packagesFile,
+ lockFile.packagesFile(cache,
+ entrypoint: root.name, relativeFrom: root.dir));
ensureDir(p.dirname(packageConfigFile));
writeTextFile(
packageConfigFile,
- await lockFile.packageConfigFile(
- cache,
- entrypoint: root.name,
- entrypointSdkConstraint: root.pubspec.sdkConstraints[sdk.identifier],
- ));
+ await lockFile.packageConfigFile(cache,
+ entrypoint: root.name,
+ entrypointSdkConstraint:
+ root.pubspec.sdkConstraints[sdk.identifier],
+ relativeFrom: root.dir));
}
/// Gets all dependencies of the [root] package.
@@ -549,7 +552,8 @@
// If we can't load the pubspec, the user needs to re-run "pub get".
}
- dataError('${p.join(source.getDirectory(id), 'pubspec.yaml')} has '
+ dataError(
+ '${p.join(source.getDirectory(id, relativeFrom: '.'), 'pubspec.yaml')} has '
'changed since the pubspec.lock file was generated, please run "pub '
'get" again.');
}
@@ -576,7 +580,7 @@
if (source is! CachedSource) return true;
// Get the directory.
- var dir = source.getDirectory(package);
+ var dir = source.getDirectory(package, relativeFrom: '.');
// See if the directory is there and looks like a package.
return dirExists(dir) && fileExists(p.join(dir, 'pubspec.yaml'));
});
@@ -616,7 +620,9 @@
}
final source = cache.source(lockFileId.source);
- final lockFilePackagePath = root.path(source.getDirectory(lockFileId));
+ final lockFilePackagePath = root.path(
+ source.getDirectory(lockFileId, relativeFrom: root.dir),
+ );
// Make sure that the packagePath agrees with the lock file about the
// path to the package.
@@ -765,7 +771,8 @@
cache.load(id).pubspec.sdkConstraints[sdk.identifier],
);
if (pkg.languageVersion != languageVersion) {
- dataError('${p.join(source.getDirectory(id), 'pubspec.yaml')} has '
+ dataError(
+ '${p.join(source.getDirectory(id, relativeFrom: '.'), 'pubspec.yaml')} has '
'changed since the pubspec.lock file was generated, please run '
'"pub get" again.');
}
diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart
index c9fc8b7..726a1a4 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -236,11 +236,14 @@
// Load the package graph from [result] so we don't need to re-parse all
// the pubspecs.
final entrypoint = Entrypoint.global(
- Package(result.pubspecs[dep.name],
- cache.source(dep.source).getDirectory(id)),
- lockFile,
- cache,
- solveResult: result);
+ Package(
+ result.pubspecs[dep.name],
+ (cache.source(dep.source) as CachedSource).getDirectoryInCache(id),
+ ),
+ lockFile,
+ cache,
+ solveResult: result,
+ );
if (!sameVersions) {
// Only precompile binaries if we have a new resolution.
await entrypoint.precompileExecutables();
@@ -261,10 +264,12 @@
// TODO(sigurdm): Use [Entrypoint.writePackagesFiles] instead.
final packagesFilePath = _getPackagesFilePath(package);
final packageConfigFilePath = _getPackageConfigFilePath(package);
- writeTextFile(packagesFilePath, lockFile.packagesFile(cache));
- ensureDir(p.dirname(packageConfigFilePath));
+ final dir = p.dirname(packagesFilePath);
writeTextFile(
- packageConfigFilePath, await lockFile.packageConfigFile(cache));
+ packagesFilePath, lockFile.packagesFile(cache, relativeFrom: dir));
+ ensureDir(p.dirname(packageConfigFilePath));
+ writeTextFile(packageConfigFilePath,
+ await lockFile.packageConfigFile(cache, relativeFrom: dir));
}
/// Finishes activating package [package] by saving [lockFile] in the cache.
@@ -355,7 +360,7 @@
if (source is CachedSource) {
// For cached sources, the package itself is in the cache and the
// lockfile is the one we just loaded.
- entrypoint = Entrypoint.global(cache.load(id), lockFile, cache);
+ entrypoint = Entrypoint.global(cache.loadCached(id), lockFile, cache);
} else {
// For uncached sources (i.e. path), the ID just points to the real
// directory for the package.
@@ -515,8 +520,13 @@
await _writePackageConfigFiles(id.name, entrypoint.lockFile);
await entrypoint.precompileExecutables();
var packageExecutables = executables.remove(id.name) ?? [];
- _updateBinStubs(entrypoint, cache.load(id), packageExecutables,
- overwriteBinStubs: true, suggestIfNotOnPath: false);
+ _updateBinStubs(
+ entrypoint,
+ cache.load(id),
+ packageExecutables,
+ overwriteBinStubs: true,
+ suggestIfNotOnPath: false,
+ );
successes.add(id.name);
} catch (error, stackTrace) {
var message = 'Failed to reactivate '
diff --git a/lib/src/lock_file.dart b/lib/src/lock_file.dart
index b51dd78..4902099 100644
--- a/lib/src/lock_file.dart
+++ b/lib/src/lock_file.dart
@@ -6,6 +6,7 @@
import 'dart:convert';
import 'package:collection/collection.dart' hide mapMap;
+import 'package:meta/meta.dart';
import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';
import 'package:source_span/source_span.dart';
@@ -218,7 +219,11 @@
///
/// If [entrypoint] is passed, a relative entry is added for its "lib/"
/// directory.
- String packagesFile(SystemCache cache, [String entrypoint]) {
+ String packagesFile(
+ SystemCache cache, {
+ String entrypoint,
+ @required String relativeFrom,
+ }) {
var header = '''
This file is deprecated. Tools should instead consume
`.dart_tools/package_config.json`.
@@ -231,7 +236,8 @@
Map<String, Uri>.fromIterable(ordered(packages.keys), value: (name) {
var id = packages[name];
var source = cache.source(id.source);
- return p.toUri(p.join(source.getDirectory(id), 'lib'));
+ return p.toUri(
+ p.join(source.getDirectory(id, relativeFrom: relativeFrom), 'lib'));
});
if (entrypoint != null) map[entrypoint] = Uri.parse('lib/');
@@ -254,12 +260,13 @@
SystemCache cache, {
String entrypoint,
VersionConstraint entrypointSdkConstraint,
+ @required String relativeFrom,
}) async {
final entries = <PackageConfigEntry>[];
for (final name in ordered(packages.keys)) {
final id = packages[name];
final source = cache.source(id.source);
- final rootPath = source.getDirectory(id);
+ final rootPath = source.getDirectory(id, relativeFrom: relativeFrom);
Uri rootUri;
if (p.isRelative(rootPath)) {
// Relative paths are relative to the root project, we want them
diff --git a/lib/src/null_safety_analysis.dart b/lib/src/null_safety_analysis.dart
index 746999c..6f85887 100644
--- a/lib/src/null_safety_analysis.dart
+++ b/lib/src/null_safety_analysis.dart
@@ -118,7 +118,8 @@
return nullSafetyComplianceOfPackages(
result.packages.where((id) => id.name != fakeRootName),
Package(rootPubspec,
- packageId.source.bind(_systemCache).getDirectory(packageId)));
+ packageId.source.bind(_systemCache).getDirectory(packageId)),
+ containingPath);
}
/// Decides if all dependendencies (transitively) have a language version
@@ -133,7 +134,10 @@
///
/// Assumes the root package is opted in.
Future<NullSafetyAnalysisResult> nullSafetyComplianceOfPackages(
- Iterable<PackageId> packages, Package rootPackage) async {
+ Iterable<PackageId> packages,
+ Package rootPackage,
+ String containingPath,
+ ) async {
NullSafetyAnalysisResult firstBadPackage;
for (final dependencyId in packages) {
final packageInternalAnalysis =
diff --git a/lib/src/solver/package_lister.dart b/lib/src/solver/package_lister.dart
index 5851e0e..8679fad 100644
--- a/lib/src/solver/package_lister.dart
+++ b/lib/src/solver/package_lister.dart
@@ -448,5 +448,6 @@
@override
Future<Pubspec> doDescribe(PackageId id) => throw _unsupported;
@override
- String getDirectory(PackageId id) => throw _unsupported;
+ String getDirectory(PackageId id, {String relativeFrom}) =>
+ throw _unsupported;
}
diff --git a/lib/src/source.dart b/lib/src/source.dart
index fcfe2fe..c2b9fbb 100644
--- a/lib/src/source.dart
+++ b/lib/src/source.dart
@@ -234,7 +234,10 @@
/// Returns the directory where this package can (or could) be found locally.
///
/// If the source is cached, this will be a path in the system cache.
- String getDirectory(PackageId id);
+ ///
+ /// If id is a relative path id, the directory will be relative from
+ /// [relativeFrom]. Returns an absolute path if [relativeFrom] is not passed.
+ String getDirectory(PackageId id, {String relativeFrom});
/// Returns metadata about a given package. Information about remotely hosted
/// packages can be cached for up to [maxAge].
diff --git a/lib/src/source/cached.dart b/lib/src/source/cached.dart
index 0ec9b1f..0952472 100644
--- a/lib/src/source/cached.dart
+++ b/lib/src/source/cached.dart
@@ -31,7 +31,7 @@
/// Otherwise, defers to the subclass.
@override
Future<Pubspec> doDescribe(PackageId id) async {
- var packageDir = getDirectory(id);
+ var packageDir = getDirectoryInCache(id);
if (fileExists(path.join(packageDir, 'pubspec.yaml'))) {
return Pubspec.load(packageDir, systemCache.sources,
expectedName: id.name);
@@ -40,6 +40,12 @@
return await describeUncached(id);
}
+ @override
+ String getDirectory(PackageId id, {String relativeFrom}) =>
+ getDirectoryInCache(id);
+
+ String getDirectoryInCache(PackageId id);
+
/// Loads the (possibly remote) pubspec for the package version identified by
/// [id].
///
@@ -49,7 +55,7 @@
/// Determines if the package identified by [id] is already downloaded to the
/// system cache.
- bool isInSystemCache(PackageId id) => dirExists(getDirectory(id));
+ bool isInSystemCache(PackageId id) => dirExists(getDirectoryInCache(id));
/// Downloads the package identified by [id] to the system cache.
Future<Package> downloadToSystemCache(PackageId id);
diff --git a/lib/src/source/git.dart b/lib/src/source/git.dart
index 3a3e18d..c986015 100644
--- a/lib/src/source/git.dart
+++ b/lib/src/source/git.dart
@@ -323,7 +323,7 @@
/// Returns the path to the revision-specific cache of [id].
@override
- String getDirectory(PackageId id) =>
+ String getDirectoryInCache(PackageId id) =>
p.join(_revisionCachePath(id), id.description['path']);
@override
@@ -396,7 +396,7 @@
result.add(RepairResult(id, success: false));
// Delete the revision cache path, not the subdirectory that contains the package.
- tryDeleteEntry(getDirectory(id));
+ tryDeleteEntry(getDirectoryInCache(id));
}
}
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart
index f6f240e..d563839 100644
--- a/lib/src/source/hosted.dart
+++ b/lib/src/source/hosted.dart
@@ -417,12 +417,12 @@
@override
Future<Package> downloadToSystemCache(PackageId id) async {
if (!isInSystemCache(id)) {
- var packageDir = getDirectory(id);
+ var packageDir = getDirectoryInCache(id);
ensureDir(p.dirname(packageDir));
await _download(id, packageDir);
}
- return Package.load(id.name, getDirectory(id), systemCache.sources);
+ return Package.load(id.name, getDirectoryInCache(id), systemCache.sources);
}
/// The system cache directory for the hosted source contains subdirectories
@@ -431,7 +431,7 @@
/// Each of these subdirectories then contains a subdirectory for each
/// package downloaded from that site.
@override
- String getDirectory(PackageId id) {
+ String getDirectoryInCache(PackageId id) {
var parsed = source._parseDescription(id.description);
var dir = _urlToDirectory(parsed.last);
return p.join(systemCacheRoot, dir, '${parsed.first}-${id.version}');
diff --git a/lib/src/source/path.dart b/lib/src/source/path.dart
index a84b92f..0e08098 100644
--- a/lib/src/source/path.dart
+++ b/lib/src/source/path.dart
@@ -181,7 +181,11 @@
}
@override
- String getDirectory(PackageId id) => id.description['path'];
+ String getDirectory(PackageId id, {String relativeFrom}) {
+ return id.description['relative']
+ ? p.relative(id.description['path'], from: relativeFrom)
+ : id.description['path'];
+ }
/// Ensures that [description] is a valid path description and returns a
/// normalized path to the package.
diff --git a/lib/src/source/sdk.dart b/lib/src/source/sdk.dart
index c2d4edf..9950e66 100644
--- a/lib/src/source/sdk.dart
+++ b/lib/src/source/sdk.dart
@@ -110,7 +110,7 @@
}
@override
- String getDirectory(PackageId id) {
+ String getDirectory(PackageId id, {String relativeFrom}) {
try {
return _verifiedPackagePath(id);
} on PackageNotFoundException catch (error) {
diff --git a/lib/src/source/unknown.dart b/lib/src/source/unknown.dart
index 53b9a42..0a0ad98 100644
--- a/lib/src/source/unknown.dart
+++ b/lib/src/source/unknown.dart
@@ -71,6 +71,7 @@
/// Returns the directory where this package can be found locally.
@override
- String getDirectory(PackageId id) => throw UnsupportedError(
- "Cannot find a package from an unknown source '${source.name}'.");
+ String getDirectory(PackageId id, {String relativeFrom}) =>
+ throw UnsupportedError(
+ "Cannot find a package from an unknown source '${source.name}'.");
}
diff --git a/lib/src/system_cache.dart b/lib/src/system_cache.dart
index 00c918f..47333af 100644
--- a/lib/src/system_cache.dart
+++ b/lib/src/system_cache.dart
@@ -107,6 +107,15 @@
return Package.load(id.name, source(id.source).getDirectory(id), sources);
}
+ Package loadCached(PackageId id) {
+ final bound = source(id.source);
+ if (bound is CachedSource) {
+ return Package.load(id.name, bound.getDirectoryInCache(id), sources);
+ } else {
+ throw ArgumentError('Call only on Cached ids.');
+ }
+ }
+
/// Determines if the system cache contains the package identified by [id].
bool contains(PackageId id) {
var source = this.source(id.source);
diff --git a/test/get/path/relative_path_test.dart b/test/get/path/relative_path_test.dart
index 03683be..5bf9e0c 100644
--- a/test/get/path/relative_path_test.dart
+++ b/test/get/path/relative_path_test.dart
@@ -50,6 +50,34 @@
{'foo': '../relative/foo', 'bar': '../relative/bar'}).validate();
});
+ test('path is relative to containing pubspec when using --directory',
+ () async {
+ await d.dir('relative', [
+ d.dir('foo', [
+ d.libDir('foo'),
+ d.libPubspec('foo', '0.0.1', deps: {
+ 'bar': {'path': '../bar'}
+ })
+ ]),
+ d.dir('bar', [d.libDir('bar'), d.libPubspec('bar', '0.0.1')])
+ ]).create();
+
+ await d.dir(appPath, [
+ d.appPubspec({
+ 'foo': {'path': '../relative/foo'}
+ })
+ ]).create();
+
+ await pubGet(
+ args: ['--directory', appPath],
+ workingDirectory: d.sandbox,
+ output: contains('Changed 2 dependencies in myapp!'));
+
+ await d.appPackagesFile(
+ {'foo': '../relative/foo', 'bar': '../relative/bar'},
+ ).validate();
+ });
+
test('relative path preserved in the lockfile', () async {
await d
.dir('foo', [d.libDir('foo'), d.libPubspec('foo', '0.0.1')]).create();
diff --git a/test/must_pub_get_test.dart b/test/must_pub_get_test.dart
index 6ad636f..8008ee3 100644
--- a/test/must_pub_get_test.dart
+++ b/test/must_pub_get_test.dart
@@ -102,7 +102,7 @@
await pubGet();
- await createLockFile(appPath, sandbox: ['foo']);
+ await createLockFile(appPath, dependenciesInSandBox: ['foo']);
// Ensure that the pubspec looks newer than the lockfile.
await _touch('pubspec.yaml');
@@ -154,7 +154,7 @@
await pubGet();
- await createLockFile(appPath, sandbox: ['foo']);
+ await createLockFile(appPath, dependenciesInSandBox: ['foo']);
// Ensure that the pubspec looks newer than the lockfile.
await _touch('pubspec.yaml');
@@ -285,7 +285,7 @@
await pubGet();
- await createPackagesFile(appPath, sandbox: ['foo']);
+ await createPackagesFile(appPath, dependenciesInSandBox: ['foo']);
// Ensure that the pubspec looks newer than the lockfile.
await _touch('pubspec.lock');
diff --git a/test/test_pub.dart b/test/test_pub.dart
index d757cb6..5d84a97 100644
--- a/test/test_pub.dart
+++ b/test/test_pub.dart
@@ -123,14 +123,17 @@
///
/// If [exitCode] is given, expects the command to exit with that code.
// TODO(rnystrom): Clean up other tests to call this when possible.
-Future<void> pubCommand(RunCommand command,
- {Iterable<String> args,
- output,
- error,
- silent,
- warning,
- int exitCode,
- Map<String, String> environment}) async {
+Future<void> pubCommand(
+ RunCommand command, {
+ Iterable<String> args,
+ output,
+ error,
+ silent,
+ warning,
+ int exitCode,
+ Map<String, String> environment,
+ String workingDirectory,
+}) async {
if (error != null && warning != null) {
throw ArgumentError("Cannot pass both 'error' and 'warning'.");
}
@@ -152,38 +155,49 @@
error: error,
silent: silent,
exitCode: exitCode,
- environment: environment);
+ environment: environment,
+ workingDirectory: workingDirectory);
}
-Future<void> pubAdd(
- {Iterable<String> args,
- output,
- error,
- warning,
- int exitCode,
- Map<String, String> environment}) async =>
- await pubCommand(RunCommand.add,
- args: args,
- output: output,
- error: error,
- warning: warning,
- exitCode: exitCode,
- environment: environment);
+Future<void> pubAdd({
+ Iterable<String> args,
+ output,
+ error,
+ warning,
+ int exitCode,
+ Map<String, String> environment,
+ String workingDirectory,
+}) async =>
+ await pubCommand(
+ RunCommand.add,
+ args: args,
+ output: output,
+ error: error,
+ warning: warning,
+ exitCode: exitCode,
+ environment: environment,
+ workingDirectory: workingDirectory,
+ );
-Future<void> pubGet(
- {Iterable<String> args,
- output,
- error,
- warning,
- int exitCode,
- Map<String, String> environment}) async =>
- await pubCommand(RunCommand.get,
- args: args,
- output: output,
- error: error,
- warning: warning,
- exitCode: exitCode,
- environment: environment);
+Future<void> pubGet({
+ Iterable<String> args,
+ output,
+ error,
+ warning,
+ int exitCode,
+ Map<String, String> environment,
+ String workingDirectory,
+}) async =>
+ await pubCommand(
+ RunCommand.get,
+ args: args,
+ output: output,
+ error: error,
+ warning: warning,
+ exitCode: exitCode,
+ environment: environment,
+ workingDirectory: workingDirectory,
+ );
Future<void> pubUpgrade(
{Iterable<String> args,
@@ -191,44 +205,58 @@
error,
warning,
int exitCode,
- Map<String, String> environment}) async =>
- await pubCommand(RunCommand.upgrade,
- args: args,
- output: output,
- error: error,
- warning: warning,
- exitCode: exitCode,
- environment: environment);
+ Map<String, String> environment,
+ String workingDirectory}) async =>
+ await pubCommand(
+ RunCommand.upgrade,
+ args: args,
+ output: output,
+ error: error,
+ warning: warning,
+ exitCode: exitCode,
+ environment: environment,
+ workingDirectory: workingDirectory,
+ );
-Future<void> pubDowngrade(
- {Iterable<String> args,
- output,
- error,
- warning,
- int exitCode,
- Map<String, String> environment}) async =>
- await pubCommand(RunCommand.downgrade,
- args: args,
- output: output,
- error: error,
- warning: warning,
- exitCode: exitCode,
- environment: environment);
+Future<void> pubDowngrade({
+ Iterable<String> args,
+ output,
+ error,
+ warning,
+ int exitCode,
+ Map<String, String> environment,
+ String workingDirectory,
+}) async =>
+ await pubCommand(
+ RunCommand.downgrade,
+ args: args,
+ output: output,
+ error: error,
+ warning: warning,
+ exitCode: exitCode,
+ environment: environment,
+ workingDirectory: workingDirectory,
+ );
-Future<void> pubRemove(
- {Iterable<String> args,
- output,
- error,
- warning,
- int exitCode,
- Map<String, String> environment}) async =>
- await pubCommand(RunCommand.remove,
- args: args,
- output: output,
- error: error,
- warning: warning,
- exitCode: exitCode,
- environment: environment);
+Future<void> pubRemove({
+ Iterable<String> args,
+ output,
+ error,
+ warning,
+ int exitCode,
+ Map<String, String> environment,
+ String workingDirectory,
+}) async =>
+ await pubCommand(
+ RunCommand.remove,
+ args: args,
+ output: output,
+ error: error,
+ warning: warning,
+ exitCode: exitCode,
+ environment: environment,
+ workingDirectory: workingDirectory,
+ );
/// Schedules starting the "pub [global] run" process and validates the
/// expected startup output.
@@ -572,34 +600,51 @@
/// Creates a lock file for [package] without running `pub get`.
///
-/// [sandbox] is a list of path dependencies to be found in the sandbox
+/// [dependenciesInSandBox] is a list of path dependencies to be found in the sandbox
/// directory.
///
/// [hosted] is a list of package names to version strings for dependencies on
/// hosted packages.
Future<void> createLockFile(String package,
- {Iterable<String> sandbox, Map<String, String> hosted}) async {
+ {Iterable<String> dependenciesInSandBox,
+ Map<String, String> hosted}) async {
var cache = SystemCache(rootDir: _pathInSandbox(cachePath));
- var lockFile =
- _createLockFile(cache.sources, sandbox: sandbox, hosted: hosted);
+ var lockFile = _createLockFile(cache.sources,
+ sandbox: dependenciesInSandBox, hosted: hosted);
await d.dir(package, [
d.file('pubspec.lock', lockFile.serialize(null)),
- d.file('.packages', lockFile.packagesFile(cache, package))
+ d.file(
+ '.packages',
+ lockFile.packagesFile(
+ cache,
+ entrypoint: package,
+ relativeFrom: p.join(d.sandbox, package),
+ ),
+ )
]).create();
}
/// Like [createLockFile], but creates only a `.packages` file without a
/// lockfile.
Future<void> createPackagesFile(String package,
- {Iterable<String> sandbox, Map<String, String> hosted}) async {
+ {Iterable<String> dependenciesInSandBox,
+ Map<String, String> hosted}) async {
var cache = SystemCache(rootDir: _pathInSandbox(cachePath));
- var lockFile =
- _createLockFile(cache.sources, sandbox: sandbox, hosted: hosted);
+ var lockFile = _createLockFile(cache.sources,
+ sandbox: dependenciesInSandBox, hosted: hosted);
- await d.dir(package,
- [d.file('.packages', lockFile.packagesFile(cache, package))]).create();
+ await d.dir(package, [
+ d.file(
+ '.packages',
+ lockFile.packagesFile(
+ cache,
+ entrypoint: package,
+ relativeFrom: d.sandbox,
+ ),
+ )
+ ]).create();
}
/// Creates a lock file for [sources] without running `pub get`.