summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/llnl/util/filesystem.py12
-rw-r--r--lib/spack/spack/build_environment.py4
-rw-r--r--lib/spack/spack/concretize.py24
-rw-r--r--lib/spack/spack/error.py15
-rw-r--r--lib/spack/spack/package.py46
-rw-r--r--lib/spack/spack/spec.py33
-rw-r--r--lib/spack/spack/test/concretize.py9
-rw-r--r--lib/spack/spack/test/directory_layout.py3
-rw-r--r--lib/spack/spack/test/spec_semantics.py61
-rw-r--r--lib/spack/spack/util/executable.py5
10 files changed, 179 insertions, 33 deletions
diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py
index 3b34e04740..029a7536df 100644
--- a/lib/spack/llnl/util/filesystem.py
+++ b/lib/spack/llnl/util/filesystem.py
@@ -25,7 +25,7 @@
__all__ = ['set_install_permissions', 'install', 'install_tree', 'traverse_tree',
'expand_user', 'working_dir', 'touch', 'touchp', 'mkdirp',
'force_remove', 'join_path', 'ancestor', 'can_access', 'filter_file',
- 'change_sed_delimiter', 'is_exe', 'force_symlink']
+ 'FileFilter', 'change_sed_delimiter', 'is_exe', 'force_symlink']
import os
import sys
@@ -40,7 +40,6 @@ from tempfile import NamedTemporaryFile
import llnl.util.tty as tty
from spack.util.compression import ALLOWED_ARCHIVE_TYPES
-
def filter_file(regex, repl, *filenames, **kwargs):
"""Like sed, but uses python regular expressions.
@@ -97,6 +96,15 @@ def filter_file(regex, repl, *filenames, **kwargs):
shutil.rmtree(backup, ignore_errors=True)
+class FileFilter(object):
+ """Convenience class for calling filter_file a lot."""
+ def __init__(self, *filenames):
+ self.filenames = filenames
+
+ def filter(self, regex, repl, **kwargs):
+ return filter_file(regex, repl, *self.filenames, **kwargs)
+
+
def change_sed_delimiter(old_delim, new_delim, *filenames):
"""Find all sed search/replace commands and change the delimiter.
e.g., if the file contains seds that look like 's///', you can
diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py
index f9e795ac42..81fbedc689 100644
--- a/lib/spack/spack/build_environment.py
+++ b/lib/spack/spack/build_environment.py
@@ -280,6 +280,10 @@ def fork(pkg, function):
# Use os._exit here to avoid raising a SystemExit exception,
# which interferes with unit tests.
os._exit(0)
+
+ except spack.error.SpackError, e:
+ e.die()
+
except:
# Child doesn't raise or return to main spack code.
# Just runs default exception handler and exits.
diff --git a/lib/spack/spack/concretize.py b/lib/spack/spack/concretize.py
index 15e886ad3c..2e1d5d7f03 100644
--- a/lib/spack/spack/concretize.py
+++ b/lib/spack/spack/concretize.py
@@ -75,7 +75,23 @@ class DefaultConcretizer(object):
if valid_versions:
spec.versions = ver([valid_versions[-1]])
else:
- raise NoValidVersionError(spec)
+ # We don't know of any SAFE versions that match the given
+ # spec. Grab the spec's versions and grab the highest
+ # *non-open* part of the range of versions it specifies.
+ # Someone else can raise an error if this happens,
+ # e.g. when we go to fetch it and don't know how. But it
+ # *might* work.
+ if not spec.versions or spec.versions == VersionList([':']):
+ raise NoValidVersionError(spec)
+ else:
+ last = spec.versions[-1]
+ if isinstance(last, VersionRange):
+ if last.end:
+ spec.versions = ver([last.end])
+ else:
+ spec.versions = ver([last.start])
+ else:
+ spec.versions = ver([last])
def concretize_architecture(self, spec):
@@ -174,8 +190,8 @@ class UnavailableCompilerVersionError(spack.error.SpackError):
class NoValidVersionError(spack.error.SpackError):
- """Raised when there is no available version for a package that
- satisfies a spec."""
+ """Raised when there is no way to have a concrete version for a
+ particular spec."""
def __init__(self, spec):
super(NoValidVersionError, self).__init__(
- "No available version of %s matches '%s'" % (spec.name, spec.versions))
+ "There are no valid versions for %s that match '%s'" % (spec.name, spec.versions))
diff --git a/lib/spack/spack/error.py b/lib/spack/spack/error.py
index e8fa756682..bfa7951a47 100644
--- a/lib/spack/spack/error.py
+++ b/lib/spack/spack/error.py
@@ -22,6 +22,10 @@
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
+import os
+import sys
+import llnl.util.tty as tty
+import spack
class SpackError(Exception):
"""This is the superclass for all Spack errors.
@@ -38,6 +42,17 @@ class SpackError(Exception):
return self._long_message
+ def die(self):
+ if spack.debug:
+ sys.excepthook(*sys.exc_info())
+ os._exit(1)
+ else:
+ tty.error(self.message)
+ if self.long_message:
+ print self.long_message
+ os._exit(1)
+
+
def __str__(self):
msg = self.message
if self.long_message:
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 5dd410d0e4..5abf2a6bb3 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -471,13 +471,18 @@ class Package(object):
"""Spec of the extendee of this package, or None if it is not an extension."""
if not self.extendees:
return None
+
+ # TODO: allow more than one extendee.
name = next(iter(self.extendees))
- if not name in self.spec:
- spec, kwargs = self.extendees[name]
- return spec
- # Need to do this to get the concrete version of the spec
- return self.spec[name]
+ # If the extendee is in the spec's deps already, return that.
+ for dep in self.spec.traverse():
+ if name == dep.name:
+ return dep
+
+ # Otherwise return the spec from the extends() directive
+ spec, kwargs = self.extendees[name]
+ return spec
@property
@@ -542,7 +547,7 @@ class Package(object):
def provides(self, vpkg_name):
"""True if this package provides a virtual package with the specified name."""
- return vpkg_name in self.provided
+ return any(s.name == vpkg_name for s in self.provided)
def virtual_dependencies(self, visited=None):
@@ -561,8 +566,11 @@ class Package(object):
on this one."""
dependents = []
for spec in spack.db.installed_package_specs():
- if self.name != spec.name and self.spec in spec:
- dependents.append(spec)
+ if self.name == spec.name:
+ continue
+ for dep in spec.traverse():
+ if spec == dep:
+ dependents.append(spec)
return dependents
@@ -816,17 +824,8 @@ class Package(object):
except ProcessError, e:
# Annotate with location of build log.
e.build_log = log_path
-
- # One of the processes returned an error code.
- # Suppress detailed stack trace here unless in debug mode
- if spack.debug:
- raise e
- else:
- tty.error(e)
-
- # Still need to clean up b/c there was an error.
cleanup()
- os._exit(1)
+ raise e
except:
# other exceptions just clean up and raise.
@@ -994,10 +993,13 @@ class Package(object):
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))
+ if aspec == self.spec:
+ continue
+ for dep in aspec.traverse():
+ if self.spec == dep:
+ 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)
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index aa13f0422c..5876fc6cf8 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -498,7 +498,13 @@ class Spec(object):
Possible idea: just use conventin and make virtual deps all
caps, e.g., MPI vs mpi.
"""
- return not spack.db.exists(self.name)
+ return Spec.is_virtual(self.name)
+
+
+ @staticmethod
+ def is_virtual(name):
+ """Test if a name is virtual without requiring a Spec."""
+ return not spack.db.exists(name)
@property
@@ -1224,7 +1230,17 @@ class Spec(object):
"""
other = self._autospec(other)
- # First thing we care about is whether the name matches
+ # A concrete provider can satisfy a virtual dependency.
+ if not self.virtual and other.virtual:
+ pkg = spack.db.get(self.name)
+ if pkg.provides(other.name):
+ for provided, when_spec in pkg.provided.items():
+ if self.satisfies(when_spec, deps=False, strict=strict):
+ if provided.satisfies(other):
+ return True
+ return False
+
+ # Otherwise, first thing we care about is whether the name matches
if self.name != other.name:
return False
@@ -1364,11 +1380,21 @@ class Spec(object):
def __getitem__(self, name):
- """TODO: reconcile __getitem__, _add_dependency, __contains__"""
+ """Get a dependency from the spec by its name."""
for spec in self.traverse():
if spec.name == name:
return spec
+ if Spec.is_virtual(name):
+ # TODO: this is a kind of kludgy way to find providers
+ # TODO: should we just keep virtual deps in the DAG instead of
+ # TODO: removing them on concretize?
+ for spec in self.traverse():
+ if spec.virtual:
+ continue
+ if spec.package.provides(name):
+ return spec
+
raise KeyError("No spec with name %s in %s" % (name, self))
@@ -1380,6 +1406,7 @@ class Spec(object):
for s in self.traverse():
if s.satisfies(spec, strict=True):
return True
+
return False
diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py
index cc839a2340..b3a77d076a 100644
--- a/lib/spack/spack/test/concretize.py
+++ b/lib/spack/spack/test/concretize.py
@@ -152,7 +152,10 @@ class ConcretizeTest(MockPackagesTest):
spec.concretize()
self.assertTrue('zmpi' in spec.dependencies)
- self.assertFalse('mpi' in spec)
+ self.assertTrue(all(not 'mpi' in d.dependencies for d in spec.traverse()))
+ self.assertTrue('zmpi' in spec)
+ self.assertTrue('mpi' in spec)
+
self.assertTrue('fake' in spec.dependencies['zmpi'])
@@ -168,7 +171,9 @@ class ConcretizeTest(MockPackagesTest):
self.assertTrue('zmpi' in spec.dependencies['callpath'].dependencies)
self.assertTrue('fake' in spec.dependencies['callpath'].dependencies['zmpi'].dependencies)
- self.assertFalse('mpi' in spec)
+ self.assertTrue(all(not 'mpi' in d.dependencies for d in spec.traverse()))
+ self.assertTrue('zmpi' in spec)
+ self.assertTrue('mpi' in spec)
def test_my_dep_depends_on_provider_of_my_virtual_dep(self):
diff --git a/lib/spack/spack/test/directory_layout.py b/lib/spack/spack/test/directory_layout.py
index beac038410..b3ad8efec4 100644
--- a/lib/spack/spack/test/directory_layout.py
+++ b/lib/spack/spack/test/directory_layout.py
@@ -167,12 +167,15 @@ class DirectoryLayoutTest(unittest.TestCase):
def test_find(self):
"""Test that finding specs within an install layout works."""
packages = list(spack.db.all_packages())[:max_packages]
+
+ # Create install prefixes for all packages in the list
installed_specs = {}
for pkg in packages:
spec = pkg.spec.concretized()
installed_specs[spec.name] = spec
self.layout.create_install_directory(spec)
+ # Make sure all the installed specs appear in DirectoryLayout.all_specs()
found_specs = dict((s.name, s) for s in self.layout.all_specs())
for name, spec in found_specs.items():
self.assertTrue(name in found_specs)
diff --git a/lib/spack/spack/test/spec_semantics.py b/lib/spack/spack/test/spec_semantics.py
index 20df2603f5..6666dbbb52 100644
--- a/lib/spack/spack/test/spec_semantics.py
+++ b/lib/spack/spack/test/spec_semantics.py
@@ -189,6 +189,67 @@ class SpecSematicsTest(MockPackagesTest):
self.check_unsatisfiable('mpich+foo', 'mpich~foo')
+ def test_satisfies_virtual(self):
+ self.assertTrue(Spec('mpich').satisfies(Spec('mpi')))
+ self.assertTrue(Spec('mpich2').satisfies(Spec('mpi')))
+ self.assertTrue(Spec('zmpi').satisfies(Spec('mpi')))
+
+
+ # ================================================================================
+ # Indexing specs
+ # ================================================================================
+ def test_self_index(self):
+ s = Spec('callpath')
+ self.assertTrue(s['callpath'] == s)
+
+
+ def test_dep_index(self):
+ s = Spec('callpath')
+ s.normalize()
+
+ self.assertTrue(s['callpath'] == s)
+ self.assertTrue(type(s['dyninst']) == Spec)
+ self.assertTrue(type(s['libdwarf']) == Spec)
+ self.assertTrue(type(s['libelf']) == Spec)
+ self.assertTrue(type(s['mpi']) == Spec)
+
+ self.assertTrue(s['dyninst'].name == 'dyninst')
+ self.assertTrue(s['libdwarf'].name == 'libdwarf')
+ self.assertTrue(s['libelf'].name == 'libelf')
+ self.assertTrue(s['mpi'].name == 'mpi')
+
+
+ def test_spec_contains_deps(self):
+ s = Spec('callpath')
+ s.normalize()
+ self.assertTrue('dyninst' in s)
+ self.assertTrue('libdwarf' in s)
+ self.assertTrue('libelf' in s)
+ self.assertTrue('mpi' in s)
+
+
+ def test_virtual_index(self):
+ s = Spec('callpath')
+ s.concretize()
+
+ s_mpich = Spec('callpath ^mpich')
+ s_mpich.concretize()
+
+ s_mpich2 = Spec('callpath ^mpich2')
+ s_mpich2.concretize()
+
+ s_zmpi = Spec('callpath ^zmpi')
+ s_zmpi.concretize()
+
+
+ self.assertTrue(s['mpi'].name != 'mpi')
+ self.assertTrue(s_mpich['mpi'].name == 'mpich')
+ self.assertTrue(s_mpich2['mpi'].name == 'mpich2')
+ self.assertTrue(s_zmpi['zmpi'].name == 'zmpi')
+
+ for spec in [s, s_mpich, s_mpich2, s_zmpi]:
+ self.assertTrue('mpi' in spec)
+
# ================================================================================
# Constraints
diff --git a/lib/spack/spack/util/executable.py b/lib/spack/spack/util/executable.py
index 6eede0f78e..1dcda0d87f 100644
--- a/lib/spack/spack/util/executable.py
+++ b/lib/spack/spack/util/executable.py
@@ -124,6 +124,11 @@ class Executable(object):
return "<exe: %s>" % self.exe
+ def __str__(self):
+ return ' '.join(self.exe)
+
+
+
def which(name, **kwargs):
"""Finds an executable in the path like command-line which."""
path = kwargs.get('path', os.environ.get('PATH', '').split(os.pathsep))