mirror of
https://github.com/dart-lang/sdk
synced 2024-09-20 03:51:33 +00:00
Support removing dead packages with pub install
.
Review URL: https://chromiumcodereview.appspot.com//10867070 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@11356 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
af2d533d41
commit
89b1f5ec12
|
@ -143,10 +143,12 @@ class Entrypoint {
|
|||
* [LockFile].
|
||||
*/
|
||||
Future _installDependencies(List<PackageId> packageVersions) {
|
||||
return Futures.wait(packageVersions.map((id) {
|
||||
if (id.source is RootSource) return new Future.immediate(id);
|
||||
return install(id);
|
||||
})).chain(_saveLockFile).chain(_installSelfReference);
|
||||
return _removeUnusedDependencies(packageVersions).chain((_) {
|
||||
return Futures.wait(packageVersions.map((id) {
|
||||
if (id.source is RootSource) return new Future.immediate(id);
|
||||
return install(id);
|
||||
}));
|
||||
}).chain(_saveLockFile).chain(_installSelfReference);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -179,6 +181,27 @@ class Entrypoint {
|
|||
return completer.future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all dependencies that are no longer depended on from the `packages`
|
||||
* directory. [packageIds] is a list of all packages that are still depended
|
||||
* on.
|
||||
*/
|
||||
Future _removeUnusedDependencies(List<PackageId> packageIds) {
|
||||
var dependenciesToKeep = packageIds.map((id) => id.name);
|
||||
|
||||
return dirExists(path).chain((exists) {
|
||||
if (exists) return listDir(path);
|
||||
return new Future.immediate([]);
|
||||
}).chain((existingDependencies) {
|
||||
existingDependencies = existingDependencies.map(basename);
|
||||
var dependenciesToRemove =
|
||||
new List.from(setMinus(existingDependencies, dependenciesToKeep));
|
||||
return Futures.wait(dependenciesToRemove.map((dependency) {
|
||||
return deleteDir(join(path, dependency));
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a list of concrete package versions to the `pubspec.lock` file.
|
||||
*/
|
||||
|
|
|
@ -80,6 +80,16 @@ only(Iterable iter) {
|
|||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set containing all elements in [minuend] that are not in
|
||||
* [subtrahend].
|
||||
*/
|
||||
Set setMinus(Collection minuend, Collection subtrahend) {
|
||||
var minuendSet = new Set.from(minuend);
|
||||
minuendSet.removeAll(subtrahend);
|
||||
return minuendSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace each instance of [matcher] in [source] with the return value of [fn].
|
||||
*/
|
||||
|
|
|
@ -229,4 +229,104 @@ main() {
|
|||
|
||||
run();
|
||||
});
|
||||
|
||||
test("removes a dependency that's been removed from the pubspec", () {
|
||||
servePackages([
|
||||
package("foo", "1.0.0"),
|
||||
package("bar", "1.0.0")
|
||||
]);
|
||||
|
||||
appDir([dependency("foo"), dependency("bar")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['install'],
|
||||
output: const RegExp(@"Dependencies installed!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": "1.0.0"
|
||||
}).scheduleValidate();
|
||||
|
||||
appDir([dependency("foo")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['install'],
|
||||
output: const RegExp(@"Dependencies installed!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": null
|
||||
}).scheduleValidate();
|
||||
|
||||
run();
|
||||
});
|
||||
|
||||
test("removes a transitive dependency that's no longer depended on", () {
|
||||
servePackages([
|
||||
package("foo", "1.0.0", [dependency("shared-dep")]),
|
||||
package("bar", "1.0.0", [
|
||||
dependency("shared-dep"),
|
||||
dependency("bar-dep")
|
||||
]),
|
||||
package("shared-dep", "1.0.0"),
|
||||
package("bar-dep", "1.0.0")
|
||||
]);
|
||||
|
||||
appDir([dependency("foo"), dependency("bar")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['install'],
|
||||
output: const RegExp(@"Dependencies installed!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": "1.0.0",
|
||||
"shared-dep": "1.0.0",
|
||||
"bar-dep": "1.0.0",
|
||||
}).scheduleValidate();
|
||||
|
||||
appDir([dependency("foo")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['install'],
|
||||
output: const RegExp(@"Dependencies installed!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": null,
|
||||
"shared-dep": "1.0.0",
|
||||
"bar-dep": null,
|
||||
}).scheduleValidate();
|
||||
|
||||
run();
|
||||
});
|
||||
|
||||
test("doesn't update dependencies whose constraints have been removed", () {
|
||||
servePackages([
|
||||
package("foo", "1.0.0", [dependency("shared-dep")]),
|
||||
package("bar", "1.0.0", [dependency("shared-dep", "<2.0.0")]),
|
||||
package("shared-dep", "1.0.0"),
|
||||
package("shared-dep", "2.0.0")
|
||||
]);
|
||||
|
||||
appDir([dependency("foo"), dependency("bar")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['install'],
|
||||
output: const RegExp(@"Dependencies installed!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": "1.0.0",
|
||||
"shared-dep": "1.0.0"
|
||||
}).scheduleValidate();
|
||||
|
||||
appDir([dependency("foo")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['install'],
|
||||
output: const RegExp(@"Dependencies installed!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": null,
|
||||
"shared-dep": "1.0.0"
|
||||
}).scheduleValidate();
|
||||
|
||||
run();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -75,4 +75,104 @@ main() {
|
|||
|
||||
run();
|
||||
});
|
||||
|
||||
test("removes a dependency that's been removed from the pubspec", () {
|
||||
servePackages([
|
||||
package("foo", "1.0.0"),
|
||||
package("bar", "1.0.0")
|
||||
]);
|
||||
|
||||
appDir([dependency("foo"), dependency("bar")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['update'],
|
||||
output: const RegExp(@"Dependencies updated!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": "1.0.0"
|
||||
}).scheduleValidate();
|
||||
|
||||
appDir([dependency("foo")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['update'],
|
||||
output: const RegExp(@"Dependencies updated!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": null
|
||||
}).scheduleValidate();
|
||||
|
||||
run();
|
||||
});
|
||||
|
||||
test("removes a transitive dependency that's no longer depended on", () {
|
||||
servePackages([
|
||||
package("foo", "1.0.0", [dependency("shared-dep")]),
|
||||
package("bar", "1.0.0", [
|
||||
dependency("shared-dep"),
|
||||
dependency("bar-dep")
|
||||
]),
|
||||
package("shared-dep", "1.0.0"),
|
||||
package("bar-dep", "1.0.0")
|
||||
]);
|
||||
|
||||
appDir([dependency("foo"), dependency("bar")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['update'],
|
||||
output: const RegExp(@"Dependencies updated!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": "1.0.0",
|
||||
"shared-dep": "1.0.0",
|
||||
"bar-dep": "1.0.0",
|
||||
}).scheduleValidate();
|
||||
|
||||
appDir([dependency("foo")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['update'],
|
||||
output: const RegExp(@"Dependencies updated!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": null,
|
||||
"shared-dep": "1.0.0",
|
||||
"bar-dep": null,
|
||||
}).scheduleValidate();
|
||||
|
||||
run();
|
||||
});
|
||||
|
||||
test("updates dependencies whose constraints have been removed", () {
|
||||
servePackages([
|
||||
package("foo", "1.0.0", [dependency("shared-dep")]),
|
||||
package("bar", "1.0.0", [dependency("shared-dep", "<2.0.0")]),
|
||||
package("shared-dep", "1.0.0"),
|
||||
package("shared-dep", "2.0.0")
|
||||
]);
|
||||
|
||||
appDir([dependency("foo"), dependency("bar")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['update'],
|
||||
output: const RegExp(@"Dependencies updated!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": "1.0.0",
|
||||
"shared-dep": "1.0.0"
|
||||
}).scheduleValidate();
|
||||
|
||||
appDir([dependency("foo")]).scheduleCreate();
|
||||
|
||||
schedulePub(args: ['update'],
|
||||
output: const RegExp(@"Dependencies updated!$"));
|
||||
|
||||
packagesDir({
|
||||
"foo": "1.0.0",
|
||||
"bar": null,
|
||||
"shared-dep": "2.0.0"
|
||||
}).scheduleValidate();
|
||||
|
||||
run();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -55,6 +55,11 @@ GitRepoDescriptor git(Pattern name, [List<Descriptor> contents]) =>
|
|||
TarFileDescriptor tar(Pattern name, [List<Descriptor> contents]) =>
|
||||
new TarFileDescriptor(name, contents);
|
||||
|
||||
/**
|
||||
* Creates a new [NothingDescriptor] with [name].
|
||||
*/
|
||||
NothingDescriptor nothing(String name) => new NothingDescriptor(name);
|
||||
|
||||
/**
|
||||
* The current [HttpServer] created using [serve].
|
||||
*/
|
||||
|
@ -301,11 +306,17 @@ DirectoryDescriptor gitPackageCacheDir(String name, [int modifier]) {
|
|||
* Describes the `packages/` directory containing all the given [packages],
|
||||
* which should be name/version pairs. The packages will be validated against
|
||||
* the format produced by the mock package server.
|
||||
*
|
||||
* A package with a null version should not be installed.
|
||||
*/
|
||||
DirectoryDescriptor packagesDir(Map<String, String> packages) {
|
||||
var contents = <Descriptor>[];
|
||||
packages.forEach((name, version) {
|
||||
contents.add(packageDir(name, version));
|
||||
if (version == null) {
|
||||
contents.add(nothing(name));
|
||||
} else {
|
||||
contents.add(packageDir(name, version));
|
||||
}
|
||||
});
|
||||
return dir(packagesPath, contents);
|
||||
}
|
||||
|
@ -1021,6 +1032,31 @@ class TarFileDescriptor extends Descriptor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A descriptor that validates that no file exists with the given name.
|
||||
*/
|
||||
class NothingDescriptor extends Descriptor {
|
||||
NothingDescriptor(String name) : super(name);
|
||||
|
||||
Future create(dir) => new Future.immediate(null);
|
||||
Future delete(dir) => new Future.immediate(null);
|
||||
|
||||
Future validate(String dir) {
|
||||
return exists(join(dir, name)).transform((exists) {
|
||||
if (exists) Expect.fail('File $name in $dir should not exist.');
|
||||
});
|
||||
}
|
||||
|
||||
InputStream load(List<String> path) {
|
||||
if (path.isEmpty()) {
|
||||
throw "Can't load the contents of $name: it doesn't exist.";
|
||||
} else {
|
||||
throw "Can't load ${Strings.join(path, '/')} from within $name: $name "
|
||||
"doesn't exist.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a simple data structure (composed of [Map]s, [List]s, scalar objects,
|
||||
* and [Future]s) and recursively resolves all the [Future]s contained within.
|
||||
|
|
Loading…
Reference in a new issue