summaryrefslogtreecommitdiff
path: root/lib/spack/spack/package.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/spack/package.py')
-rw-r--r--lib/spack/spack/package.py119
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