diff options
author | Peter Scheibel <scheibel1@llnl.gov> | 2020-07-07 11:37:36 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-07 11:37:36 -0700 |
commit | 650ab563f42d68ea7e9bf0fe296e2b9b1e78755c (patch) | |
tree | 6dde44ad275604f07955d4ec15fca0e1d1487c3d /lib | |
parent | 90285c7d6105d6ec1d77d207338ba4eea54cc212 (diff) | |
download | spack-650ab563f42d68ea7e9bf0fe296e2b9b1e78755c.tar.gz spack-650ab563f42d68ea7e9bf0fe296e2b9b1e78755c.tar.bz2 spack-650ab563f42d68ea7e9bf0fe296e2b9b1e78755c.tar.xz spack-650ab563f42d68ea7e9bf0fe296e2b9b1e78755c.zip |
Uninstall: tolerate hook failures when force=true (#16513)
Fixes #16478
This allows an uninstall to proceed even when encountering pre-uninstall
hook failures if the user chooses the --force option for the uninstall.
This also prevents post-uninstall hook failures from raising an exception,
which would terminate a sequence of uninstalls. This isn't likely essential
for #16478, but I think overall it will improve the user experience: if
the post-uninstall hook fails, there isn't much point in terminating a
sequence of spec uninstalls because at the point where the post-uninstall
hook is run, the spec has already been removed from the database (so it
will never have another chance to run).
Notes:
* When doing spack uninstall -a, certain pre/post-uninstall hooks aren't
important to run, but this isn't easy to track with the current model.
For example: if you are uninstalling a package and its extension, you
do not have to do the activation check for the extension.
* This doesn't handle the uninstallation of specs that are not in the DB,
so it may leave "dangling" specs in the installation prefix
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/package.py | 34 |
1 files changed, 32 insertions, 2 deletions
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index 5c4ff23d40..cedcfffed2 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -22,6 +22,7 @@ import shutil import sys import textwrap import time +import traceback from six import StringIO from six import string_types from six import with_metaclass @@ -1744,7 +1745,23 @@ class PackageBase(with_metaclass(PackageMeta, PackageViewMixin, object)): with spack.store.db.prefix_write_lock(spec): if pkg is not None: - spack.hooks.pre_uninstall(spec) + try: + spack.hooks.pre_uninstall(spec) + except Exception as error: + if force: + error_msg = ( + "One or more pre_uninstall hooks have failed" + " for {0}, but Spack is continuing with the" + " uninstall".format(str(spec))) + if isinstance(error, spack.error.SpackError): + error_msg += ( + "\n\nError message: {0}".format(str(error))) + tty.warn(error_msg) + # Note that if the uninstall succeeds then we won't be + # seeing this error again and won't have another chance + # to run the hook. + else: + raise # Uninstalling in Spack only requires removing the prefix. if not spec.external: @@ -1765,7 +1782,20 @@ class PackageBase(with_metaclass(PackageMeta, PackageViewMixin, object)): spack.store.db.remove(spec) if pkg is not None: - spack.hooks.post_uninstall(spec) + try: + spack.hooks.post_uninstall(spec) + except Exception: + # If there is a failure here, this is our only chance to do + # something about it: at this point the Spec has been removed + # from the DB and prefix, so the post-uninstallation hooks + # will not have another chance to run. + error_msg = ( + "One or more post-uninstallation hooks failed for" + " {0}, but the prefix has been removed (if it is not" + " external).".format(str(spec))) + tb_msg = traceback.format_exc() + error_msg += "\n\nThe error:\n\n{0}".format(tb_msg) + tty.warn(error_msg) tty.msg("Successfully uninstalled %s" % spec.short_spec) |