diff options
Diffstat (limited to 'lib/spack/spack/package.py')
-rw-r--r-- | lib/spack/spack/package.py | 119 |
1 files changed, 108 insertions, 11 deletions
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index c5cae4f9b0..d5cb3065a8 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -11,6 +11,7 @@ packages. """ import base64 +import collections import contextlib import copy import functools @@ -23,19 +24,14 @@ import sys import textwrap import time import traceback -from six import StringIO -from six import string_types -from six import with_metaclass -from ordereddict_backport import OrderedDict -import llnl.util.tty as tty +import six -import spack.config -import spack.paths -import spack.store +import llnl.util.tty as tty import spack.compilers -import spack.directives +import spack.config import spack.dependency +import spack.directives import spack.directory_layout import spack.error import spack.fetch_strategy as fs @@ -43,15 +39,19 @@ import spack.hooks import spack.mirror import spack.mixins import spack.multimethod +import spack.paths import spack.repo +import spack.store import spack.url import spack.util.environment import spack.util.web -import spack.multimethod - from llnl.util.filesystem import mkdirp, touch, working_dir from llnl.util.lang import memoized from llnl.util.link_tree import LinkTree +from ordereddict_backport import OrderedDict +from six import StringIO +from six import string_types +from six import with_metaclass from spack.filesystem_view import YamlFilesystemView from spack.installer import \ install_args_docstring, PackageInstaller, InstallError @@ -141,7 +141,104 @@ class InstallPhase(object): return other +#: Registers which are the detectable packages, by repo and package name +#: Need a pass of package repositories to be filled. +detectable_packages = collections.defaultdict(list) + + +class DetectablePackageMeta(object): + """Check if a package is detectable and add default implementations + for the detection function. + """ + def __init__(cls, name, bases, attr_dict): + # If a package has the executables attribute then it's + # assumed to be detectable + if hasattr(cls, 'executables'): + @classmethod + def determine_spec_details(cls, prefix, exes_in_prefix): + """Allow ``spack external find ...`` to locate installations. + + Args: + prefix (str): the directory containing the executables + exes_in_prefix (set): the executables that match the regex + + Returns: + The list of detected specs for this package + """ + exes_by_version = collections.defaultdict(list) + # The default filter function is the identity function for the + # list of executables + filter_fn = getattr(cls, 'filter_detected_exes', + lambda x, exes: exes) + exes_in_prefix = filter_fn(prefix, exes_in_prefix) + for exe in exes_in_prefix: + try: + version_str = cls.determine_version(exe) + if version_str: + exes_by_version[version_str].append(exe) + except Exception as e: + msg = ('An error occurred when trying to detect ' + 'the version of "{0}" [{1}]') + tty.debug(msg.format(exe, str(e))) + + specs = [] + for version_str, exes in exes_by_version.items(): + variants = cls.determine_variants(exes, version_str) + # Normalize output to list + if not isinstance(variants, list): + variants = [variants] + + for variant in variants: + if isinstance(variant, six.string_types): + variant = (variant, {}) + variant_str, extra_attributes = variant + spec_str = '{0}@{1} {2}'.format( + cls.name, version_str, variant_str + ) + + # Pop a few reserved keys from extra attributes, since + # they have a different semantics + external_path = extra_attributes.pop('prefix', None) + external_modules = extra_attributes.pop( + 'modules', None + ) + spec = spack.spec.Spec( + spec_str, + external_path=external_path, + external_modules=external_modules + ) + specs.append(spack.spec.Spec.from_detection( + spec, extra_attributes=extra_attributes + )) + + return sorted(specs) + + @classmethod + def determine_variants(cls, exes, version_str): + return '' + + # Register the class as a detectable package + detectable_packages[cls.namespace].append(cls.name) + + # Attach function implementations to the detectable class + default = False + if not hasattr(cls, 'determine_spec_details'): + default = True + cls.determine_spec_details = determine_spec_details + + if default and not hasattr(cls, 'determine_version'): + msg = ('the package "{0}" in the "{1}" repo needs to define' + ' the "determine_version" method to be detectable') + NotImplementedError(msg.format(cls.name, cls.namespace)) + + if default and not hasattr(cls, 'determine_variants'): + cls.determine_variants = determine_variants + + super(DetectablePackageMeta, cls).__init__(name, bases, attr_dict) + + class PackageMeta( + DetectablePackageMeta, spack.directives.DirectiveMeta, spack.mixins.PackageMixinsMeta, spack.multimethod.MultiMethodMeta |