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
|
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.
|
"""Map each spec to a list of its installed dependents.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -160,10 +160,10 @@ def installed_dependents(specs, env):
|
|||||||
|
|
||||||
for spec in specs:
|
for spec in specs:
|
||||||
for dpt in traverse.traverse_nodes(
|
for dpt in traverse.traverse_nodes(
|
||||||
spec.dependents(deptype="all"),
|
spec.dependents(deptype=("link", "run")),
|
||||||
direction="parents",
|
direction="parents",
|
||||||
visited=visited,
|
visited=visited,
|
||||||
deptype="all",
|
deptype=("link", "run"),
|
||||||
root=True,
|
root=True,
|
||||||
key=lambda s: s.dag_hash(),
|
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
|
# 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))
|
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
|
# 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
|
# well as to separately track specs in the current env with dependents
|
||||||
spec_to_dpts = {}
|
spec_to_dpts = {}
|
||||||
|
@ -50,15 +50,17 @@ def test_correct_installed_dependents(mutable_database):
|
|||||||
callpath = spack.store.db.query_local("callpath")[0]
|
callpath = spack.store.db.query_local("callpath")[0]
|
||||||
|
|
||||||
# Ensure it still has dependents and dependencies
|
# Ensure it still has dependents and dependencies
|
||||||
dependents = callpath.dependents(deptype="all")
|
dependents = callpath.dependents(deptype=("run", "link"))
|
||||||
dependencies = callpath.dependencies(deptype="all")
|
dependencies = callpath.dependencies(deptype=("run", "link"))
|
||||||
assert dependents and dependencies
|
assert dependents and dependencies
|
||||||
|
|
||||||
# Uninstall it, so it's missing.
|
# Uninstall it, so it's missing.
|
||||||
callpath.package.do_uninstall(force=True)
|
callpath.package.do_uninstall(force=True)
|
||||||
|
|
||||||
# Retrieve all dependent hashes
|
# 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())]
|
dependent_hashes = [s.dag_hash() for s in itertools.chain(*outside_dpts.values())]
|
||||||
set_dependent_hashes = set(dependent_hashes)
|
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
|
"""Tests an installation with two environments e1 and e2, which each have
|
||||||
shared package installations:
|
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")
|
env = SpackCommand("env")
|
||||||
@ -230,16 +232,16 @@ def environment_setup(
|
|||||||
TestUninstallFromEnv.env("create", "e1")
|
TestUninstallFromEnv.env("create", "e1")
|
||||||
e1 = spack.environment.read("e1")
|
e1 = spack.environment.read("e1")
|
||||||
with e1:
|
with e1:
|
||||||
TestUninstallFromEnv.add("dt-diamond-left")
|
TestUninstallFromEnv.add("diamond-link-left")
|
||||||
TestUninstallFromEnv.add("dt-diamond-bottom")
|
TestUninstallFromEnv.add("diamond-link-bottom")
|
||||||
TestUninstallFromEnv.concretize()
|
TestUninstallFromEnv.concretize()
|
||||||
install("--fake")
|
install("--fake")
|
||||||
|
|
||||||
TestUninstallFromEnv.env("create", "e2")
|
TestUninstallFromEnv.env("create", "e2")
|
||||||
e2 = spack.environment.read("e2")
|
e2 = spack.environment.read("e2")
|
||||||
with e2:
|
with e2:
|
||||||
TestUninstallFromEnv.add("dt-diamond-right")
|
TestUninstallFromEnv.add("diamond-link-right")
|
||||||
TestUninstallFromEnv.add("dt-diamond-bottom")
|
TestUninstallFromEnv.add("diamond-link-bottom")
|
||||||
TestUninstallFromEnv.concretize()
|
TestUninstallFromEnv.concretize()
|
||||||
install("--fake")
|
install("--fake")
|
||||||
|
|
||||||
@ -251,47 +253,47 @@ def test_basic_env_sanity(self, environment_setup):
|
|||||||
assert concretized_spec.package.installed
|
assert concretized_spec.package.installed
|
||||||
|
|
||||||
def test_uninstall_force_dependency_shared_between_envs(self, environment_setup):
|
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
|
e1, then all packages should be uninstalled (but not removed) from
|
||||||
both e1 and e2.
|
both e1 and e2.
|
||||||
"""
|
"""
|
||||||
e1 = spack.environment.read("e1")
|
e1 = spack.environment.read("e1")
|
||||||
with 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
|
# The specs should still be in the environment, since
|
||||||
# --remove was not specified
|
# --remove was not specified
|
||||||
assert set(root.name for (root, _) in e1.concretized_specs()) == set(
|
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():
|
for _, concretized_spec in e1.concretized_specs():
|
||||||
assert not concretized_spec.package.installed
|
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.
|
# have been uninstalled. The roots should be unchanged though.
|
||||||
e2 = spack.environment.read("e2")
|
e2 = spack.environment.read("e2")
|
||||||
with e2:
|
with e2:
|
||||||
assert set(root.name for (root, _) in e2.concretized_specs()) == set(
|
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():
|
for _, concretized_spec in e2.concretized_specs():
|
||||||
assert not concretized_spec.package.installed
|
assert not concretized_spec.package.installed
|
||||||
|
|
||||||
def test_uninstall_remove_dependency_shared_between_envs(self, environment_setup):
|
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);
|
e1, then all packages are removed from e1 (it is now empty);
|
||||||
dt-diamond-left is also uninstalled (since only e1 needs it) but
|
diamond-link-left is also uninstalled (since only e1 needs it) but
|
||||||
dt-diamond-bottom is not uninstalled (since e2 needs it).
|
diamond-link-bottom is not uninstalled (since e2 needs it).
|
||||||
"""
|
"""
|
||||||
e1 = spack.environment.read("e1")
|
e1 = spack.environment.read("e1")
|
||||||
with e1:
|
with e1:
|
||||||
dtdiamondleft = next(
|
dtdiamondleft = next(
|
||||||
concrete
|
concrete
|
||||||
for (_, concrete) in e1.concretized_specs()
|
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 "The following specs will be removed but not uninstalled" in output
|
||||||
assert not list(e1.roots())
|
assert not list(e1.roots())
|
||||||
assert not dtdiamondleft.package.installed
|
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")
|
e2 = spack.environment.read("e2")
|
||||||
with e2:
|
with e2:
|
||||||
assert set(root.name for (root, _) in e2.concretized_specs()) == set(
|
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():
|
for _, concretized_spec in e2.concretized_specs():
|
||||||
assert concretized_spec.package.installed
|
assert concretized_spec.package.installed
|
||||||
|
|
||||||
def test_uninstall_dependency_shared_between_envs_fail(self, environment_setup):
|
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
|
e1 (without --remove or -f), then this should fail (this is needed by
|
||||||
e2).
|
e2).
|
||||||
"""
|
"""
|
||||||
e1 = spack.environment.read("e1")
|
e1 = spack.environment.read("e1")
|
||||||
with 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 "There are still dependents." in output
|
||||||
assert "use `spack env remove`" in output
|
assert "use `spack env remove`" in output
|
||||||
|
|
||||||
# The environment should be unchanged and nothing should have been
|
# The environment should be unchanged and nothing should have been
|
||||||
# uninstalled
|
# uninstalled
|
||||||
assert set(root.name for (root, _) in e1.concretized_specs()) == set(
|
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():
|
for _, concretized_spec in e1.concretized_specs():
|
||||||
assert concretized_spec.package.installed
|
assert concretized_spec.package.installed
|
||||||
|
|
||||||
def test_uninstall_force_and_remove_dependency_shared_between_envs(self, environment_setup):
|
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.
|
e1, then all packages should be uninstalled and removed from e1.
|
||||||
All packages will also be uninstalled from e2, but the roots will
|
All packages will also be uninstalled from e2, but the roots will
|
||||||
remain unchanged.
|
remain unchanged.
|
||||||
@ -336,53 +338,53 @@ def test_uninstall_force_and_remove_dependency_shared_between_envs(self, environ
|
|||||||
dtdiamondleft = next(
|
dtdiamondleft = next(
|
||||||
concrete
|
concrete
|
||||||
for (_, concrete) in e1.concretized_specs()
|
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 list(e1.roots())
|
||||||
assert not dtdiamondleft.package.installed
|
assert not dtdiamondleft.package.installed
|
||||||
|
|
||||||
e2 = spack.environment.read("e2")
|
e2 = spack.environment.read("e2")
|
||||||
with e2:
|
with e2:
|
||||||
assert set(root.name for (root, _) in e2.concretized_specs()) == set(
|
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():
|
for _, concretized_spec in e2.concretized_specs():
|
||||||
assert not concretized_spec.package.installed
|
assert not concretized_spec.package.installed
|
||||||
|
|
||||||
def test_uninstall_keep_dependents_dependency_shared_between_envs(self, environment_setup):
|
def test_uninstall_keep_dependents_dependency_shared_between_envs(self, environment_setup):
|
||||||
"""If you "spack uninstall -f --remove dt-diamond-bottom" from
|
"""If you "spack uninstall -f --remove diamond-link-bottom" from
|
||||||
e1, then dt-diamond-bottom should be uninstalled, which leaves
|
e1, then diamond-link-bottom should be uninstalled, which leaves
|
||||||
"dangling" references in both environments, since
|
"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")
|
e1 = spack.environment.read("e1")
|
||||||
with e1:
|
with e1:
|
||||||
dtdiamondleft = next(
|
dtdiamondleft = next(
|
||||||
concrete
|
concrete
|
||||||
for (_, concrete) in e1.concretized_specs()
|
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")
|
uninstall("-f", "-y", "--remove", "diamond-link-bottom")
|
||||||
# dt-diamond-bottom was removed from the list of roots (note that
|
# diamond-link-bottom was removed from the list of roots (note that
|
||||||
# it would still be installed since dt-diamond-left depends on it)
|
# it would still be installed since diamond-link-left depends on it)
|
||||||
assert set(x.name for x in e1.roots()) == set(["dt-diamond-left"])
|
assert set(x.name for x in e1.roots()) == set(["diamond-link-left"])
|
||||||
assert dtdiamondleft.package.installed
|
assert dtdiamondleft.package.installed
|
||||||
|
|
||||||
e2 = spack.environment.read("e2")
|
e2 = spack.environment.read("e2")
|
||||||
with e2:
|
with e2:
|
||||||
assert set(root.name for (root, _) in e2.concretized_specs()) == set(
|
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(
|
dtdiamondright = next(
|
||||||
concrete
|
concrete
|
||||||
for (_, concrete) in e2.concretized_specs()
|
for (_, concrete) in e2.concretized_specs()
|
||||||
if concrete.name == "dt-diamond-right"
|
if concrete.name == "diamond-link-right"
|
||||||
)
|
)
|
||||||
assert dtdiamondright.package.installed
|
assert dtdiamondright.package.installed
|
||||||
dtdiamondbottom = next(
|
dtdiamondbottom = next(
|
||||||
concrete
|
concrete
|
||||||
for (_, concrete) in e2.concretized_specs()
|
for (_, concrete) in e2.concretized_specs()
|
||||||
if concrete.name == "dt-diamond-bottom"
|
if concrete.name == "diamond-link-bottom"
|
||||||
)
|
)
|
||||||
assert not dtdiamondbottom.package.installed
|
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