summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2015-02-17 00:24:58 -0800
committerTodd Gamblin <tgamblin@llnl.gov>2015-02-17 00:24:58 -0800
commitd800c23cecd9c87b17991411512e3aa42855815d (patch)
tree7886416214f932128ec272c5b156aa2449926ef1
parent57f331e2acf75c5bf4c464d0df888fd882295a68 (diff)
downloadspack-d800c23cecd9c87b17991411512e3aa42855815d.tar.gz
spack-d800c23cecd9c87b17991411512e3aa42855815d.tar.bz2
spack-d800c23cecd9c87b17991411512e3aa42855815d.tar.xz
spack-d800c23cecd9c87b17991411512e3aa42855815d.zip
Better activate/deactivate logic.
spack activate - now activates dependency extensions - ensures dependencies are activated in the python installation. - -f/--force option still allows the old activate behavior. spack deactivate - checks for dependents before deactivating (like uninstall) - deactivate -a/--all <extension> will deactviate a package and ALL of its dependency extensions. - deactivate -a/--all <extendee> activates all extensions of <extendee> e.g.: spack deactivate -a python - deactivate -f/--force option allows removing regardless of dependents. - deactivate -f can be run EVEN if a package is not activated. - allows for clenup of activations gone wrong.
-rw-r--r--lib/spack/spack/cmd/activate.py6
-rw-r--r--lib/spack/spack/cmd/deactivate.py54
-rw-r--r--lib/spack/spack/cmd/find.py2
-rw-r--r--lib/spack/spack/directory_layout.py8
-rw-r--r--lib/spack/spack/hooks/extensions.py2
-rw-r--r--lib/spack/spack/package.py42
6 files changed, 95 insertions, 19 deletions
diff --git a/lib/spack/spack/cmd/activate.py b/lib/spack/spack/cmd/activate.py
index c1e23852d6..71eca4f453 100644
--- a/lib/spack/spack/cmd/activate.py
+++ b/lib/spack/spack/cmd/activate.py
@@ -31,6 +31,9 @@ description = "Activate a package extension."
def setup_parser(subparser):
subparser.add_argument(
+ '-f', '--force', action='store_true',
+ help="Activate without first activating dependencies.")
+ subparser.add_argument(
'spec', nargs=argparse.REMAINDER, help="spec of package extension to activate.")
@@ -44,6 +47,9 @@ def activate(parser, args):
spack.db.get(specs[0])
spec = spack.cmd.disambiguate_spec(specs[0])
+ if not spec.package.is_extension:
+ tty.die("%s is not an extension." % spec.name)
+
if spec.package.activated:
tty.die("Package %s is already activated." % specs[0].short_spec)
diff --git a/lib/spack/spack/cmd/deactivate.py b/lib/spack/spack/cmd/deactivate.py
index f37dfd79ed..bfec618c8e 100644
--- a/lib/spack/spack/cmd/deactivate.py
+++ b/lib/spack/spack/cmd/deactivate.py
@@ -24,8 +24,10 @@
##############################################################################
from external import argparse
import llnl.util.tty as tty
+
import spack
import spack.cmd
+from spack.graph import topological_sort
description = "Deactivate a package extension."
@@ -34,6 +36,10 @@ def setup_parser(subparser):
'-f', '--force', action='store_true',
help="Run deactivation even if spec is NOT currently activated.")
subparser.add_argument(
+ '-a', '--all', action='store_true',
+ help="Deactivate all extensions of an extendable pacakge, or "
+ "deactivate an extension AND its dependencies.")
+ subparser.add_argument(
'spec', nargs=argparse.REMAINDER, help="spec of package extension to deactivate.")
@@ -42,12 +48,52 @@ def deactivate(parser, args):
if len(specs) != 1:
tty.die("deactivate requires one spec. %d given." % len(specs))
- # TODO: remove this hack when DAG info is stored in dir layout.
+ # TODO: remove this hack when DAG info is stored properly.
# This ensures the ext spec is always normalized properly.
spack.db.get(specs[0])
spec = spack.cmd.disambiguate_spec(specs[0])
- if not args.force and not spec.package.activated:
- tty.die("Package %s is not activated." % specs[0].short_spec)
+ pkg = spec.package
+
+ if args.all:
+ if pkg.extendable:
+ tty.msg("Deactivating all extensions of %s" % pkg.spec.short_spec)
+ ext_pkgs = spack.db.installed_extensions_for(spec)
+ for ext_pkg in ext_pkgs:
+ ext_pkg.spec.normalize()
+ if ext_pkg.activated:
+ ext_pkg.do_deactivate(force=True)
+
+ elif pkg.is_extension:
+ # TODO: store DAG info properly (see above)
+ spec.normalize()
+
+ tty.msg("Deactivating %s and all dependencies." % pkg.spec.short_spec)
+
+ topo_order = topological_sort(spec)
+ index = spec.index()
+
+ for name in topo_order:
+ espec = index[name]
+ epkg = espec.package
+
+ # TODO: store DAG info properly (see above)
+ epkg.spec.normalize()
+
+ if epkg.extends(pkg.extendee_spec):
+ if epkg.activated or args.force:
+
+ epkg.do_deactivate(force=args.force)
+
+ else:
+ tty.die("spack deactivate --all requires an extendable package or an extension.")
+
+ else:
+ if not pkg.is_extension:
+ tty.die("spack deactivate requires an extension.",
+ "Did you mean 'spack deactivate --all'?")
+
+ if not args.force and not spec.package.activated:
+ tty.die("Package %s is not activated." % specs[0].short_spec)
- spec.package.do_deactivate(force=args.force)
+ spec.package.do_deactivate(force=args.force)
diff --git a/lib/spack/spack/cmd/find.py b/lib/spack/spack/cmd/find.py
index dee1dfece7..70b10edb4e 100644
--- a/lib/spack/spack/cmd/find.py
+++ b/lib/spack/spack/cmd/find.py
@@ -85,7 +85,7 @@ def display_specs(specs, **kwargs):
elif mode == 'deps':
for spec in specs:
- print spec.tree(indent=4, format='$_$@$+', color=True),
+ print spec.tree(indent=4, format='$_$@$+$#', color=True),
elif mode in ('short', 'long'):
fmt = '$-_$@$+'
diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py
index 5b80e93d6b..b2cf5dc801 100644
--- a/lib/spack/spack/directory_layout.py
+++ b/lib/spack/spack/directory_layout.py
@@ -371,7 +371,7 @@ class SpecHashDirectoryLayout(DirectoryLayout):
_check_concrete(ext_spec)
# Check whether it's already installed or if it's a conflict.
- exts = self.extension_map(spec)
+ exts = self._extension_map(spec)
self.check_extension_conflict(spec, ext_spec)
# do the actual adding.
@@ -384,7 +384,7 @@ class SpecHashDirectoryLayout(DirectoryLayout):
_check_concrete(ext_spec)
# Make sure it's installed before removing.
- exts = self.extension_map(spec)
+ exts = self._extension_map(spec)
self.check_activated(spec, ext_spec)
# do the actual removing.
@@ -450,10 +450,10 @@ class ExtensionConflictError(DirectoryLayoutError):
class NoSuchExtensionError(DirectoryLayoutError):
- """Raised when an extension isn't there on remove."""
+ """Raised when an extension isn't there on deactivate."""
def __init__(self, spec, ext_spec):
super(NoSuchExtensionError, self).__init__(
- "%s cannot be removed from %s because it's not installed."% (
+ "%s cannot be removed from %s because it's not activated."% (
ext_spec.short_spec, spec.short_spec))
diff --git a/lib/spack/spack/hooks/extensions.py b/lib/spack/spack/hooks/extensions.py
index 9d6fa23d03..cf87a78c8c 100644
--- a/lib/spack/spack/hooks/extensions.py
+++ b/lib/spack/spack/hooks/extensions.py
@@ -33,4 +33,4 @@ def pre_uninstall(pkg):
if pkg.is_extension:
if pkg.activated:
- pkg.do_deactivate()
+ pkg.do_deactivate(force=True)
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 5d04fed8ff..bc8541a184 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -529,11 +529,8 @@ class Package(object):
@property
def activated(self):
- if not self.spec.concrete:
- raise ValueError("Only concrete package extensions can be activated.")
if not self.is_extension:
raise ValueError("is_extension called on package that is not an extension.")
-
exts = spack.install_layout.extension_map(self.extendee_spec)
return (self.name in exts) and (exts[self.name] == self.spec)
@@ -956,20 +953,33 @@ class Package(object):
raise ValueError("%s does not extend %s!" % (self.name, self.extendee.name))
- def do_activate(self):
+ def do_activate(self, **kwargs):
"""Called on an etension to invoke the extendee's activate method.
Commands should call this routine, and should not call
activate() directly.
"""
self._sanity_check_extension()
+ force = kwargs.get('force', False)
+
+ # TODO: get rid of this normalize - DAG handling.
+ self.spec.normalize()
+
spack.install_layout.check_extension_conflict(self.extendee_spec, self.spec)
+ if not force:
+ for spec in self.spec.traverse(root=False):
+ if spec.package.extends(self.extendee_spec):
+ # TODO: fix this normalize() requirement -- revisit DAG handling.
+ spec.package.spec.normalize()
+ if not spec.package.activated:
+ spec.package.do_activate(**kwargs)
+
self.extendee_spec.package.activate(self, **self.extendee_args)
spack.install_layout.add_extension(self.extendee_spec, self.spec)
tty.msg("Activated extension %s for %s."
- % (self.spec.short_spec, self.extendee_spec.short_spec))
+ % (self.spec.short_spec, self.extendee_spec.format("$_$@$+$%@")))
def activate(self, extension, **kwargs):
@@ -994,15 +1004,21 @@ class Package(object):
def do_deactivate(self, **kwargs):
"""Called on the extension to invoke extendee's deactivate() method."""
- force = kwargs.get('force', False)
-
self._sanity_check_extension()
+ force = kwargs.get('force', False)
# Allow a force deactivate to happen. This can unlink
# spurious files if something was corrupted.
if not force:
spack.install_layout.check_activated(self.extendee_spec, self.spec)
+ activated = spack.install_layout.extension_map(self.extendee_spec)
+ for name, aspec in activated.items():
+ if aspec != self.spec and self.spec in aspec:
+ raise ActivationError(
+ "Cannot deactivate %s beacuse %s is activated and depends on it."
+ % (self.spec.short_spec, aspec.short_spec))
+
self.extendee_spec.package.deactivate(self, **self.extendee_args)
# redundant activation check -- makes SURE the spec is not
@@ -1011,7 +1027,7 @@ class Package(object):
spack.install_layout.remove_extension(self.extendee_spec, self.spec)
tty.msg("Deactivated extension %s for %s."
- % (self.spec.short_spec, self.extendee_spec.short_spec))
+ % (self.spec.short_spec, self.extendee_spec.format("$_$@$+$%@")))
def deactivate(self, extension, **kwargs):
@@ -1236,7 +1252,15 @@ class NoURLError(PackageError):
"Package %s has no version with a URL." % cls.__name__)
-class ExtensionConflictError(PackageError):
+class ExtensionError(PackageError): pass
+
+
+class ExtensionConflictError(ExtensionError):
def __init__(self, path):
super(ExtensionConflictError, self).__init__(
"Extension blocked by file: %s" % path)
+
+
+class ActivationError(ExtensionError):
+ def __init__(self, msg, long_msg=None):
+ super(ActivationError, self).__init__(msg, long_msg)