summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorHarmen Stoppels <harmenstoppels@gmail.com>2023-02-16 14:26:30 +0100
committerGitHub <noreply@github.com>2023-02-16 14:26:30 +0100
commit09eb86e077d1273412862d773c169e03822adb82 (patch)
tree486284f4d0c91e0c56fab1e9d14762841aa2cf7c /lib
parent50691ccdd9d69a5a5e239bbdaa2d6199f1b8a153 (diff)
downloadspack-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')
-rw-r--r--lib/spack/spack/cmd/uninstall.py8
-rw-r--r--lib/spack/spack/test/cmd/uninstall.py78
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