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.
			
			
This commit is contained in:
		| @@ -30,6 +30,9 @@ | |||||||
| description = "Activate a package extension." | description = "Activate a package extension." | ||||||
|  |  | ||||||
| def setup_parser(subparser): | def setup_parser(subparser): | ||||||
|  |     subparser.add_argument( | ||||||
|  |         '-f', '--force', action='store_true', | ||||||
|  |         help="Activate without first activating dependencies.") | ||||||
|     subparser.add_argument( |     subparser.add_argument( | ||||||
|         'spec', nargs=argparse.REMAINDER, help="spec of package extension to activate.") |         'spec', nargs=argparse.REMAINDER, help="spec of package extension to activate.") | ||||||
|  |  | ||||||
| @@ -44,6 +47,9 @@ def activate(parser, args): | |||||||
|     spack.db.get(specs[0]) |     spack.db.get(specs[0]) | ||||||
|  |  | ||||||
|     spec = spack.cmd.disambiguate_spec(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: |     if spec.package.activated: | ||||||
|         tty.die("Package %s is already activated." % specs[0].short_spec) |         tty.die("Package %s is already activated." % specs[0].short_spec) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,8 +24,10 @@ | |||||||
| ############################################################################## | ############################################################################## | ||||||
| from external import argparse | from external import argparse | ||||||
| import llnl.util.tty as tty | import llnl.util.tty as tty | ||||||
|  |  | ||||||
| import spack | import spack | ||||||
| import spack.cmd | import spack.cmd | ||||||
|  | from spack.graph import topological_sort | ||||||
|  |  | ||||||
| description = "Deactivate a package extension." | description = "Deactivate a package extension." | ||||||
|  |  | ||||||
| @@ -33,6 +35,10 @@ def setup_parser(subparser): | |||||||
|     subparser.add_argument( |     subparser.add_argument( | ||||||
|         '-f', '--force', action='store_true', |         '-f', '--force', action='store_true', | ||||||
|         help="Run deactivation even if spec is NOT currently activated.") |         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( |     subparser.add_argument( | ||||||
|         'spec', nargs=argparse.REMAINDER, help="spec of package extension to deactivate.") |         'spec', nargs=argparse.REMAINDER, help="spec of package extension to deactivate.") | ||||||
|  |  | ||||||
| @@ -42,11 +48,51 @@ def deactivate(parser, args): | |||||||
|     if len(specs) != 1: |     if len(specs) != 1: | ||||||
|         tty.die("deactivate requires one spec.  %d given." % len(specs)) |         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. |     # This ensures the ext spec is always normalized properly. | ||||||
|     spack.db.get(specs[0]) |     spack.db.get(specs[0]) | ||||||
|  |  | ||||||
|     spec = spack.cmd.disambiguate_spec(specs[0]) |     spec = spack.cmd.disambiguate_spec(specs[0]) | ||||||
|  |     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: |         if not args.force and not spec.package.activated: | ||||||
|             tty.die("Package %s is not activated." % specs[0].short_spec) |             tty.die("Package %s is not activated." % specs[0].short_spec) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -85,7 +85,7 @@ def display_specs(specs, **kwargs): | |||||||
|  |  | ||||||
|         elif mode == 'deps': |         elif mode == 'deps': | ||||||
|             for spec in specs: |             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'): |         elif mode in ('short', 'long'): | ||||||
|             fmt = '$-_$@$+' |             fmt = '$-_$@$+' | ||||||
|   | |||||||
| @@ -371,7 +371,7 @@ def add_extension(self, spec, ext_spec): | |||||||
|         _check_concrete(ext_spec) |         _check_concrete(ext_spec) | ||||||
|  |  | ||||||
|         # Check whether it's already installed or if it's a conflict. |         # 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) |         self.check_extension_conflict(spec, ext_spec) | ||||||
|  |  | ||||||
|         # do the actual adding. |         # do the actual adding. | ||||||
| @@ -384,7 +384,7 @@ def remove_extension(self, spec, ext_spec): | |||||||
|         _check_concrete(ext_spec) |         _check_concrete(ext_spec) | ||||||
|  |  | ||||||
|         # Make sure it's installed before removing. |         # Make sure it's installed before removing. | ||||||
|         exts = self.extension_map(spec) |         exts = self._extension_map(spec) | ||||||
|         self.check_activated(spec, ext_spec) |         self.check_activated(spec, ext_spec) | ||||||
|  |  | ||||||
|         # do the actual removing. |         # do the actual removing. | ||||||
| @@ -450,10 +450,10 @@ def __init__(self, spec, ext_spec, conflict): | |||||||
|  |  | ||||||
|  |  | ||||||
| class NoSuchExtensionError(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): |     def __init__(self, spec, ext_spec): | ||||||
|         super(NoSuchExtensionError, self).__init__( |         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)) |                 ext_spec.short_spec, spec.short_spec)) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -33,4 +33,4 @@ def pre_uninstall(pkg): | |||||||
|  |  | ||||||
|     if pkg.is_extension: |     if pkg.is_extension: | ||||||
|         if pkg.activated: |         if pkg.activated: | ||||||
|             pkg.do_deactivate() |             pkg.do_deactivate(force=True) | ||||||
|   | |||||||
| @@ -529,11 +529,8 @@ def extends(self, spec): | |||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def activated(self): |     def activated(self): | ||||||
|         if not self.spec.concrete: |  | ||||||
|             raise ValueError("Only concrete package extensions can be activated.") |  | ||||||
|         if not self.is_extension: |         if not self.is_extension: | ||||||
|             raise ValueError("is_extension called on package that is not an extension.") |             raise ValueError("is_extension called on package that is not an extension.") | ||||||
|  |  | ||||||
|         exts = spack.install_layout.extension_map(self.extendee_spec) |         exts = spack.install_layout.extension_map(self.extendee_spec) | ||||||
|         return (self.name in exts) and (exts[self.name] == self.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)) |             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. |         """Called on an etension to invoke the extendee's activate method. | ||||||
|  |  | ||||||
|         Commands should call this routine, and should not call |         Commands should call this routine, and should not call | ||||||
|         activate() directly. |         activate() directly. | ||||||
|         """ |         """ | ||||||
|         self._sanity_check_extension() |         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) |         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) |         self.extendee_spec.package.activate(self, **self.extendee_args) | ||||||
|  |  | ||||||
|         spack.install_layout.add_extension(self.extendee_spec, self.spec) |         spack.install_layout.add_extension(self.extendee_spec, self.spec) | ||||||
|         tty.msg("Activated extension %s for %s." |         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): |     def activate(self, extension, **kwargs): | ||||||
| @@ -994,15 +1004,21 @@ def ignore(filename): | |||||||
|  |  | ||||||
|     def do_deactivate(self, **kwargs): |     def do_deactivate(self, **kwargs): | ||||||
|         """Called on the extension to invoke extendee's deactivate() method.""" |         """Called on the extension to invoke extendee's deactivate() method.""" | ||||||
|         force = kwargs.get('force', False) |  | ||||||
|  |  | ||||||
|         self._sanity_check_extension() |         self._sanity_check_extension() | ||||||
|  |         force = kwargs.get('force', False) | ||||||
|  |  | ||||||
|         # Allow a force deactivate to happen.  This can unlink |         # Allow a force deactivate to happen.  This can unlink | ||||||
|         # spurious files if something was corrupted. |         # spurious files if something was corrupted. | ||||||
|         if not force: |         if not force: | ||||||
|             spack.install_layout.check_activated(self.extendee_spec, self.spec) |             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) |         self.extendee_spec.package.deactivate(self, **self.extendee_args) | ||||||
|  |  | ||||||
|         # redundant activation check -- makes SURE the spec is not |         # 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) |             spack.install_layout.remove_extension(self.extendee_spec, self.spec) | ||||||
|  |  | ||||||
|         tty.msg("Deactivated extension %s for %s." |         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): |     def deactivate(self, extension, **kwargs): | ||||||
| @@ -1236,7 +1252,15 @@ def __init__(self, cls): | |||||||
|             "Package %s has no version with a URL." % cls.__name__) |             "Package %s has no version with a URL." % cls.__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
| class ExtensionConflictError(PackageError): | class ExtensionError(PackageError): pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class ExtensionConflictError(ExtensionError): | ||||||
|     def __init__(self, path): |     def __init__(self, path): | ||||||
|         super(ExtensionConflictError, self).__init__( |         super(ExtensionConflictError, self).__init__( | ||||||
|             "Extension blocked by file: %s" % path) |             "Extension blocked by file: %s" % path) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class ActivationError(ExtensionError): | ||||||
|  |     def __init__(self, msg, long_msg=None): | ||||||
|  |         super(ActivationError, self).__init__(msg, long_msg) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Todd Gamblin
					Todd Gamblin