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 @@ -30,6 +30,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." @@ -33,6 +35,10 @@ def setup_parser(subparser): subparser.add_argument( '-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 - spec.package.do_deactivate(force=args.force) + 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) 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 @@ def add_extension(self, spec, ext_spec): _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 @@ def remove_extension(self, spec, ext_spec): _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 @@ def __init__(self, spec, ext_spec, conflict): 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 @@ def extends(self, spec): @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 @@ def _sanity_check_extension(self): 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 @@ def ignore(filename): 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 @@ def do_deactivate(self, **kwargs): 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 @@ def __init__(self, cls): "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)