Rework do_activate/activate and do_deactivate/deactivate semantics.

- packages can now extend only one other package.
- do_activate() and do_deactivate() are now called on the extension,
  and they automatically find the extendee
- activate() and deactivate() are still called on the extendee and are
  passed the extension.
This commit is contained in:
Todd Gamblin 2015-01-12 22:39:18 -08:00
parent d13bbeb605
commit acc62abbd0
7 changed files with 105 additions and 74 deletions

View file

@ -121,3 +121,18 @@ def elide_list(line_list, max_num=10):
return line_list[:max_num-1] + ['...'] + line_list[-1:]
else:
return line_list
def disambiguate_spec(spec):
matching_specs = spack.db.get_installed(spec)
if not matching_specs:
tty.die("Spec '%s' matches no installed packages." % spec)
elif len(matching_specs) > 1:
args = ["%s matches multiple packages." % spec,
"Matching packages:"]
args += [" " + str(s) for s in matching_specs]
args += ["Use a more specific spec."]
tty.die(*args)
return matching_specs[0]

View file

@ -77,24 +77,16 @@ def location(parser, args):
tty.die("You must supply a spec.")
if len(specs) != 1:
tty.die("Too many specs. Supply only one.")
spec = specs[0]
if args.install_dir:
# install_dir command matches against installed specs.
matching_specs = spack.db.get_installed(spec)
if not matching_specs:
tty.die("Spec '%s' matches no installed packages." % spec)
spec = spack.cmd.disambiguate_spec(specs[0])
print spec.prefix
elif len(matching_specs) > 1:
args = ["%s matches multiple packages." % spec,
"Matching packages:"]
args += [" " + str(s) for s in matching_specs]
args += ["Use a more specific spec."]
tty.die(*args)
else:
spec = specs[0]
print matching_specs[0].prefix
elif args.package_dir:
if args.package_dir:
# This one just needs the spec name.
print join_path(spack.db.root, spec.name)
@ -111,3 +103,4 @@ def location(parser, args):
tty.die("Build directory does not exist yet. Run this to create it:",
"spack stage " + " ".join(args.spec))
print pkg.stage.source_path

View file

@ -65,7 +65,6 @@ def uninstall(parser, args):
" b) use a more specific spec."]
tty.die(*args)
if len(matching_specs) == 0:
tty.die("%s does not match any installed packages." % spec)

View file

@ -27,23 +27,12 @@
def post_install(pkg):
assert(pkg.spec.concrete)
for name, spec in pkg.extendees.items():
ext = pkg.spec[name]
epkg = ext.package
if epkg.installed:
epkg.do_activate(pkg)
pkg.do_activate()
def pre_uninstall(pkg):
assert(pkg.spec.concrete)
# Need to do this b/c uninstall does not automatically do it.
# TODO: store full graph info in stored .spec file.
pkg.spec.normalize()
for name, spec in pkg.extendees.items():
ext = pkg.spec[name]
epkg = ext.package
if epkg.installed:
epkg.do_deactivate(pkg)
pkg.do_deactivate()

View file

@ -315,15 +315,18 @@ class SomePackage(Package):
"""Specs of virtual packages provided by this package, keyed by name."""
provided = {}
"""Specs of packages this one extends, keyed by name."""
extendees = {}
"""Specs of conflicting packages, keyed by name. """
conflicted = {}
"""Patches to apply to newly expanded source, if any."""
patches = {}
"""Specs of package this one extends, or None.
Currently, ppackages can extend at most one other package.
"""
extendees = {}
#
# These are default values for instance variables.
#
@ -402,8 +405,8 @@ def ensure_has_dict(attr_name):
self._fetch_time = 0.0
self._total_time = 0.0
for name, spec in self.extendees.items():
spack.db.get(spec)._check_extendable()
if self.is_extension:
spack.db.get(self.extendee_spec)._check_extendable()
@property
@ -491,6 +494,34 @@ def fetcher(self, f):
self._fetcher = f
@property
def extendee_spec(self):
"""Spec of the extendee of this package, or None if it is not an extension."""
if not self.extendees: return None
name = next(iter(self.extendees))
if not name in self.spec:
return self.extendees[name]
# Need to do this to get the concrete version of the spec
return self.spec[name]
@property
def is_extension(self):
return len(self.extendees) > 0
@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.")
return self.spec in spack.install_layout.get_extensions(self.extendee_spec)
def preorder_traversal(self, visited=None, **kwargs):
"""This does a preorder traversal of the package's dependence DAG."""
virtual = kwargs.get("virtual", False)
@ -784,10 +815,9 @@ def do_install(self, **kwargs):
build_env.setup_package(self)
# Allow extendees to further set up the environment.
for ext_name in self.extendees:
ext_spec = self.spec[ext_name]
ext_spec.package.setup_extension_environment(
self.module, ext_spec, self.spec)
if self.is_extension:
self.extendee_spec.package.setup_extension_environment(
self.module, self.extendee_spec, self.spec)
if fake_install:
self.do_fake_install()
@ -840,7 +870,6 @@ def do_install(self, **kwargs):
if returncode != 0:
sys.exit(1)
# Once everything else is done, run post install hooks
spack.hooks.post_install(self)
@ -919,25 +948,30 @@ def _check_extendable(self):
raise ValueError("Package %s is not extendable!" % self.name)
def _sanity_check_extension(self, extension):
self._check_extendable()
if not self.installed:
def _sanity_check_extension(self):
extendee_package = self.extendee_spec.package
extendee_package._check_extendable()
if not extendee_package.installed:
raise ValueError("Can only (de)activate extensions for installed packages.")
if not extension.installed:
if not self.installed:
raise ValueError("Extensions must first be installed.")
if not self.name in extension.extendees:
raise ValueError("%s does not extend %s!" % (extension.name, self.name))
if not self.spec.satisfies(extension.extendees[self.name]):
raise ValueError("%s does not satisfy %s!" % (self.spec, extension.spec))
if not self.extendee_spec.name in self.extendees:
raise ValueError("%s does not extend %s!" % (self.name, self.extendee.name))
def do_activate(self, extension):
self._sanity_check_extension(extension)
def do_activate(self):
"""Called on an etension to invoke the extendee's activate method.
self.activate(extension)
spack.install_layout.add_extension(self.spec, extension.spec)
Commands should call this routine, and should not call
activate() directly.
"""
self._sanity_check_extension()
self.extendee_spec.package.activate(self)
spack.install_layout.add_extension(self.extendee_spec, self.spec)
tty.msg("Activated extension %s for %s."
% (extension.spec.short_spec, self.spec.short_spec))
% (self.spec.short_spec, self.extendee_spec.short_spec))
def activate(self, extension):
@ -957,20 +991,19 @@ def activate(self, extension):
tree.merge(self.prefix, ignore=spack.install_layout.hidden_file_paths)
def do_deactivate(self, extension):
self._sanity_check_extension(extension)
self.deactivate(extension)
def do_deactivate(self):
self._sanity_check_extension()
self.extendee_spec.package.deactivate(self)
ext = extension.spec
if ext in spack.install_layout.get_extensions(self.spec):
spack.install_layout.remove_extension(self.spec, ext)
if self.spec in spack.install_layout.get_extensions(self.extendee_spec):
spack.install_layout.remove_extension(self.extendee_spec, self.spec)
tty.msg("Deactivated extension %s for %s."
% (extension.spec.short_spec, self.spec.short_spec))
% (self.spec.short_spec, self.extendee_spec.short_spec))
def deactivate(self, extension):
"""Unlinks all files from extension out of extendee's install dir.
"""Unlinks all files from extension out of this package's install dir.
Package authors can override this method to support other
extension mechanisms. Spack internals (commands, hooks, etc.)
@ -980,8 +1013,6 @@ def deactivate(self, extension):
"""
tree = LinkTree(extension.prefix)
tree.unmerge(self.prefix, ignore=spack.install_layout.hidden_file_paths)
tty.msg("Deactivated %s as extension of %s."
% (extension.spec.short_spec, self.spec.short_spec))
def do_clean(self):

View file

@ -77,6 +77,8 @@ def get(self, spec, **kwargs):
copy = spec.copy()
self.instances[copy] = package_class(copy)
except Exception, e:
if spack.debug:
sys.excepthook(*sys.exc_info())
raise FailedConstructorError(spec.name, e)
return self.instances[spec]

View file

@ -131,6 +131,8 @@ def extends(*specs):
clocals = caller_locals()
dependencies = clocals.setdefault('dependencies', {})
extendees = clocals.setdefault('extendees', {})
if extendees:
raise RelationError("Packages can extend at most one other package.")
for string in specs:
for spec in spack.spec.parse(string):