diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/llnl/util/filesystem.py | 12 | ||||
-rw-r--r-- | lib/spack/spack/build_environment.py | 4 | ||||
-rw-r--r-- | lib/spack/spack/concretize.py | 24 | ||||
-rw-r--r-- | lib/spack/spack/error.py | 15 | ||||
-rw-r--r-- | lib/spack/spack/package.py | 46 | ||||
-rw-r--r-- | lib/spack/spack/spec.py | 33 | ||||
-rw-r--r-- | lib/spack/spack/test/concretize.py | 9 | ||||
-rw-r--r-- | lib/spack/spack/test/directory_layout.py | 3 | ||||
-rw-r--r-- | lib/spack/spack/test/spec_semantics.py | 61 | ||||
-rw-r--r-- | lib/spack/spack/util/executable.py | 5 |
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)) |