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.
This commit is contained in:
parent
50691ccdd9
commit
09eb86e077
@ -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 = {}
|
||||
|
@ -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 @@ def environment_setup(
|
||||
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 @@ def test_basic_env_sanity(self, environment_setup):
|
||||
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 @@ def test_uninstall_remove_dependency_shared_between_envs(self, environment_setup
|
||||
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 @@ def test_uninstall_force_and_remove_dependency_shared_between_envs(self, environ
|
||||
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
|
||||
|
@ -0,0 +1,15 @@
|
||||
# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from spack.package import *
|
||||
|
||||
|
||||
class DiamondLinkBottom(Package):
|
||||
"""Part of diamond-link-{top,left,right,bottom} group"""
|
||||
|
||||
homepage = "http://www.example.com"
|
||||
url = "http://www.example.com/diamond-link-bottom-1.0.tar.gz"
|
||||
|
||||
version("1.0", "0123456789abcdef0123456789abcdef")
|
@ -0,0 +1,17 @@
|
||||
# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from spack.package import *
|
||||
|
||||
|
||||
class DiamondLinkLeft(Package):
|
||||
"""Part of diamond-link-{top,left,right,bottom} group"""
|
||||
|
||||
homepage = "http://www.example.com"
|
||||
url = "http://www.example.com/diamond-link-left-1.0.tar.gz"
|
||||
|
||||
version("1.0", "0123456789abcdef0123456789abcdef")
|
||||
|
||||
depends_on("diamond-link-bottom", type="link")
|
@ -0,0 +1,17 @@
|
||||
# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from spack.package import *
|
||||
|
||||
|
||||
class DiamondLinkRight(Package):
|
||||
"""Part of diamond-link-{top,left,right,bottom} group"""
|
||||
|
||||
homepage = "http://www.example.com"
|
||||
url = "http://www.example.com/diamond-link-right-1.0.tar.gz"
|
||||
|
||||
version("1.0", "0123456789abcdef0123456789abcdef")
|
||||
|
||||
depends_on("diamond-link-bottom", type="link")
|
@ -0,0 +1,18 @@
|
||||
# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from spack.package import *
|
||||
|
||||
|
||||
class DiamondLinkTop(Package):
|
||||
"""Part of diamond-link-{top,left,right,bottom} group"""
|
||||
|
||||
homepage = "http://www.example.com"
|
||||
url = "http://www.example.com/diamond-link-top-1.0.tar.gz"
|
||||
|
||||
version("1.0", "0123456789abcdef0123456789abcdef")
|
||||
|
||||
depends_on("diamond-link-left", type="link")
|
||||
depends_on("diamond-link-right", type="link")
|
Loading…
Reference in New Issue
Block a user