diff options
author | Harmen Stoppels <harmenstoppels@gmail.com> | 2023-02-16 14:26:30 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-16 14:26:30 +0100 |
commit | 09eb86e077d1273412862d773c169e03822adb82 (patch) | |
tree | 486284f4d0c91e0c56fab1e9d14762841aa2cf7c /lib/spack | |
parent | 50691ccdd9d69a5a5e239bbdaa2d6199f1b8a153 (diff) | |
download | spack-09eb86e077d1273412862d773c169e03822adb82.tar.gz spack-09eb86e077d1273412862d773c169e03822adb82.tar.bz2 spack-09eb86e077d1273412862d773c169e03822adb82.tar.xz spack-09eb86e077d1273412862d773c169e03822adb82.zip |
spack uninstall: follow run/link edges on --dependents (#34058)
`spack gc` removes build deps of explicitly installed specs, but somehow
if you take one of the specs that `spack gc` would remove, and feed it
to `spack uninstall /<hash>` by hash, it complains about all the
dependents that still rely on it.
This resolves the inconsistency by only following run/link type deps in
spack uninstall.
That way you can finally do `spack uninstall cmake` without having to
remove all packages built with cmake.
Diffstat (limited to 'lib/spack')
-rw-r--r-- | lib/spack/spack/cmd/uninstall.py | 8 | ||||
-rw-r--r-- | lib/spack/spack/test/cmd/uninstall.py | 78 |
2 files changed, 44 insertions, 42 deletions
diff --git a/lib/spack/spack/cmd/uninstall.py b/lib/spack/spack/cmd/uninstall.py index 000ec8a470..869ab88f62 100644 --- a/lib/spack/spack/cmd/uninstall.py +++ b/lib/spack/spack/cmd/uninstall.py @@ -133,7 +133,7 @@ def find_matching_specs(env, specs, allow_multiple_matches=False, force=False, o return specs_from_cli -def installed_dependents(specs, env): +def installed_runtime_dependents(specs, env): """Map each spec to a list of its installed dependents. Args: @@ -160,10 +160,10 @@ def installed_dependents(specs, env): for spec in specs: for dpt in traverse.traverse_nodes( - spec.dependents(deptype="all"), + spec.dependents(deptype=("link", "run")), direction="parents", visited=visited, - deptype="all", + deptype=("link", "run"), root=True, key=lambda s: s.dag_hash(), ): @@ -265,7 +265,7 @@ def get_uninstall_list(args, specs, env): # args.all takes care of the case where '-a' is given in the cli base_uninstall_specs = set(find_matching_specs(env, specs, args.all, args.force)) - active_dpts, outside_dpts = installed_dependents(base_uninstall_specs, env) + active_dpts, outside_dpts = installed_runtime_dependents(base_uninstall_specs, env) # It will be useful to track the unified set of specs with dependents, as # well as to separately track specs in the current env with dependents spec_to_dpts = {} diff --git a/lib/spack/spack/test/cmd/uninstall.py b/lib/spack/spack/test/cmd/uninstall.py index bd1e8a1b49..a4b6410ef8 100644 --- a/lib/spack/spack/test/cmd/uninstall.py +++ b/lib/spack/spack/test/cmd/uninstall.py @@ -50,15 +50,17 @@ def test_correct_installed_dependents(mutable_database): callpath = spack.store.db.query_local("callpath")[0] # Ensure it still has dependents and dependencies - dependents = callpath.dependents(deptype="all") - dependencies = callpath.dependencies(deptype="all") + dependents = callpath.dependents(deptype=("run", "link")) + dependencies = callpath.dependencies(deptype=("run", "link")) assert dependents and dependencies # Uninstall it, so it's missing. callpath.package.do_uninstall(force=True) # Retrieve all dependent hashes - inside_dpts, outside_dpts = spack.cmd.uninstall.installed_dependents(dependencies, None) + inside_dpts, outside_dpts = spack.cmd.uninstall.installed_runtime_dependents( + dependencies, None + ) dependent_hashes = [s.dag_hash() for s in itertools.chain(*outside_dpts.values())] set_dependent_hashes = set(dependent_hashes) @@ -213,9 +215,9 @@ class TestUninstallFromEnv(object): """Tests an installation with two environments e1 and e2, which each have shared package installations: - e1 has dt-diamond-left -> dt-diamond-bottom + e1 has diamond-link-left -> diamond-link-bottom - e2 has dt-diamond-right -> dt-diamond-bottom + e2 has diamond-link-right -> diamond-link-bottom """ env = SpackCommand("env") @@ -230,16 +232,16 @@ class TestUninstallFromEnv(object): TestUninstallFromEnv.env("create", "e1") e1 = spack.environment.read("e1") with e1: - TestUninstallFromEnv.add("dt-diamond-left") - TestUninstallFromEnv.add("dt-diamond-bottom") + TestUninstallFromEnv.add("diamond-link-left") + TestUninstallFromEnv.add("diamond-link-bottom") TestUninstallFromEnv.concretize() install("--fake") TestUninstallFromEnv.env("create", "e2") e2 = spack.environment.read("e2") with e2: - TestUninstallFromEnv.add("dt-diamond-right") - TestUninstallFromEnv.add("dt-diamond-bottom") + TestUninstallFromEnv.add("diamond-link-right") + TestUninstallFromEnv.add("diamond-link-bottom") TestUninstallFromEnv.concretize() install("--fake") @@ -251,47 +253,47 @@ class TestUninstallFromEnv(object): assert concretized_spec.package.installed def test_uninstall_force_dependency_shared_between_envs(self, environment_setup): - """If you "spack uninstall -f --dependents dt-diamond-bottom" from + """If you "spack uninstall -f --dependents diamond-link-bottom" from e1, then all packages should be uninstalled (but not removed) from both e1 and e2. """ e1 = spack.environment.read("e1") with e1: - uninstall("-f", "-y", "--dependents", "dt-diamond-bottom") + uninstall("-f", "-y", "--dependents", "diamond-link-bottom") # The specs should still be in the environment, since # --remove was not specified assert set(root.name for (root, _) in e1.concretized_specs()) == set( - ["dt-diamond-left", "dt-diamond-bottom"] + ["diamond-link-left", "diamond-link-bottom"] ) for _, concretized_spec in e1.concretized_specs(): assert not concretized_spec.package.installed - # Everything in e2 depended on dt-diamond-bottom, so should also + # Everything in e2 depended on diamond-link-bottom, so should also # have been uninstalled. The roots should be unchanged though. e2 = spack.environment.read("e2") with e2: assert set(root.name for (root, _) in e2.concretized_specs()) == set( - ["dt-diamond-right", "dt-diamond-bottom"] + ["diamond-link-right", "diamond-link-bottom"] ) for _, concretized_spec in e2.concretized_specs(): assert not concretized_spec.package.installed def test_uninstall_remove_dependency_shared_between_envs(self, environment_setup): - """If you "spack uninstall --dependents --remove dt-diamond-bottom" from + """If you "spack uninstall --dependents --remove diamond-link-bottom" from e1, then all packages are removed from e1 (it is now empty); - dt-diamond-left is also uninstalled (since only e1 needs it) but - dt-diamond-bottom is not uninstalled (since e2 needs it). + diamond-link-left is also uninstalled (since only e1 needs it) but + diamond-link-bottom is not uninstalled (since e2 needs it). """ e1 = spack.environment.read("e1") with e1: dtdiamondleft = next( concrete for (_, concrete) in e1.concretized_specs() - if concrete.name == "dt-diamond-left" + if concrete.name == "diamond-link-left" ) - output = uninstall("-y", "--dependents", "--remove", "dt-diamond-bottom") + output = uninstall("-y", "--dependents", "--remove", "diamond-link-bottom") assert "The following specs will be removed but not uninstalled" in output assert not list(e1.roots()) assert not dtdiamondleft.package.installed @@ -301,32 +303,32 @@ class TestUninstallFromEnv(object): e2 = spack.environment.read("e2") with e2: assert set(root.name for (root, _) in e2.concretized_specs()) == set( - ["dt-diamond-right", "dt-diamond-bottom"] + ["diamond-link-right", "diamond-link-bottom"] ) for _, concretized_spec in e2.concretized_specs(): assert concretized_spec.package.installed def test_uninstall_dependency_shared_between_envs_fail(self, environment_setup): - """If you "spack uninstall --dependents dt-diamond-bottom" from + """If you "spack uninstall --dependents diamond-link-bottom" from e1 (without --remove or -f), then this should fail (this is needed by e2). """ e1 = spack.environment.read("e1") with e1: - output = uninstall("-y", "--dependents", "dt-diamond-bottom", fail_on_error=False) + output = uninstall("-y", "--dependents", "diamond-link-bottom", fail_on_error=False) assert "There are still dependents." in output assert "use `spack env remove`" in output # The environment should be unchanged and nothing should have been # uninstalled assert set(root.name for (root, _) in e1.concretized_specs()) == set( - ["dt-diamond-left", "dt-diamond-bottom"] + ["diamond-link-left", "diamond-link-bottom"] ) for _, concretized_spec in e1.concretized_specs(): assert concretized_spec.package.installed def test_uninstall_force_and_remove_dependency_shared_between_envs(self, environment_setup): - """If you "spack uninstall -f --dependents --remove dt-diamond-bottom" from + """If you "spack uninstall -f --dependents --remove diamond-link-bottom" from e1, then all packages should be uninstalled and removed from e1. All packages will also be uninstalled from e2, but the roots will remain unchanged. @@ -336,53 +338,53 @@ class TestUninstallFromEnv(object): dtdiamondleft = next( concrete for (_, concrete) in e1.concretized_specs() - if concrete.name == "dt-diamond-left" + if concrete.name == "diamond-link-left" ) - uninstall("-f", "-y", "--dependents", "--remove", "dt-diamond-bottom") + uninstall("-f", "-y", "--dependents", "--remove", "diamond-link-bottom") assert not list(e1.roots()) assert not dtdiamondleft.package.installed e2 = spack.environment.read("e2") with e2: assert set(root.name for (root, _) in e2.concretized_specs()) == set( - ["dt-diamond-right", "dt-diamond-bottom"] + ["diamond-link-right", "diamond-link-bottom"] ) for _, concretized_spec in e2.concretized_specs(): assert not concretized_spec.package.installed def test_uninstall_keep_dependents_dependency_shared_between_envs(self, environment_setup): - """If you "spack uninstall -f --remove dt-diamond-bottom" from - e1, then dt-diamond-bottom should be uninstalled, which leaves + """If you "spack uninstall -f --remove diamond-link-bottom" from + e1, then diamond-link-bottom should be uninstalled, which leaves "dangling" references in both environments, since - dt-diamond-left and dt-diamond-right both need it. + diamond-link-left and diamond-link-right both need it. """ e1 = spack.environment.read("e1") with e1: dtdiamondleft = next( concrete for (_, concrete) in e1.concretized_specs() - if concrete.name == "dt-diamond-left" + if concrete.name == "diamond-link-left" ) - uninstall("-f", "-y", "--remove", "dt-diamond-bottom") - # dt-diamond-bottom was removed from the list of roots (note that - # it would still be installed since dt-diamond-left depends on it) - assert set(x.name for x in e1.roots()) == set(["dt-diamond-left"]) + uninstall("-f", "-y", "--remove", "diamond-link-bottom") + # diamond-link-bottom was removed from the list of roots (note that + # it would still be installed since diamond-link-left depends on it) + assert set(x.name for x in e1.roots()) == set(["diamond-link-left"]) assert dtdiamondleft.package.installed e2 = spack.environment.read("e2") with e2: assert set(root.name for (root, _) in e2.concretized_specs()) == set( - ["dt-diamond-right", "dt-diamond-bottom"] + ["diamond-link-right", "diamond-link-bottom"] ) dtdiamondright = next( concrete for (_, concrete) in e2.concretized_specs() - if concrete.name == "dt-diamond-right" + if concrete.name == "diamond-link-right" ) assert dtdiamondright.package.installed dtdiamondbottom = next( concrete for (_, concrete) in e2.concretized_specs() - if concrete.name == "dt-diamond-bottom" + if concrete.name == "diamond-link-bottom" ) assert not dtdiamondbottom.package.installed |