YamlDirectoryLayout now working.
This commit is contained in:
		@@ -42,7 +42,8 @@
 | 
				
			|||||||
hooks_path     = join_path(module_path, "hooks")
 | 
					hooks_path     = join_path(module_path, "hooks")
 | 
				
			||||||
var_path       = join_path(prefix, "var", "spack")
 | 
					var_path       = join_path(prefix, "var", "spack")
 | 
				
			||||||
stage_path     = join_path(var_path, "stage")
 | 
					stage_path     = join_path(var_path, "stage")
 | 
				
			||||||
install_path   = join_path(prefix, "opt")
 | 
					opt_path       = join_path(prefix, "opt")
 | 
				
			||||||
 | 
					install_path   = join_path(opt_path, "spack")
 | 
				
			||||||
share_path     = join_path(prefix, "share", "spack")
 | 
					share_path     = join_path(prefix, "share", "spack")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
@@ -65,8 +66,8 @@
 | 
				
			|||||||
# This controls how spack lays out install prefixes and
 | 
					# This controls how spack lays out install prefixes and
 | 
				
			||||||
# stage directories.
 | 
					# stage directories.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
from spack.directory_layout import SpecHashDirectoryLayout
 | 
					from spack.directory_layout import YamlDirectoryLayout
 | 
				
			||||||
install_layout = SpecHashDirectoryLayout(install_path)
 | 
					install_layout = YamlDirectoryLayout(install_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This controls how things are concretized in spack.
 | 
					# This controls how things are concretized in spack.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,8 +27,9 @@
 | 
				
			|||||||
import exceptions
 | 
					import exceptions
 | 
				
			||||||
import hashlib
 | 
					import hashlib
 | 
				
			||||||
import shutil
 | 
					import shutil
 | 
				
			||||||
 | 
					import glob
 | 
				
			||||||
import tempfile
 | 
					import tempfile
 | 
				
			||||||
from contextlib import closing
 | 
					from external import yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import llnl.util.tty as tty
 | 
					import llnl.util.tty as tty
 | 
				
			||||||
from llnl.util.lang import memoized
 | 
					from llnl.util.lang import memoized
 | 
				
			||||||
@@ -81,7 +82,7 @@ def relative_path_for_spec(self, spec):
 | 
				
			|||||||
        raise NotImplementedError()
 | 
					        raise NotImplementedError()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def make_path_for_spec(self, spec):
 | 
					    def create_install_directory(self, spec):
 | 
				
			||||||
        """Creates the installation directory for a spec."""
 | 
					        """Creates the installation directory for a spec."""
 | 
				
			||||||
        raise NotImplementedError()
 | 
					        raise NotImplementedError()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -131,7 +132,7 @@ def path_for_spec(self, spec):
 | 
				
			|||||||
        return os.path.join(self.root, path)
 | 
					        return os.path.join(self.root, path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def remove_path_for_spec(self, spec):
 | 
					    def remove_install_directory(self, spec):
 | 
				
			||||||
        """Removes a prefix and any empty parent directories from the root.
 | 
					        """Removes a prefix and any empty parent directories from the root.
 | 
				
			||||||
           Raised RemoveFailedError if something goes wrong.
 | 
					           Raised RemoveFailedError if something goes wrong.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@@ -153,94 +154,70 @@ def remove_path_for_spec(self, spec):
 | 
				
			|||||||
            path = os.path.dirname(path)
 | 
					            path = os.path.dirname(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def traverse_dirs_at_depth(root, depth, path_tuple=(), curdepth=0):
 | 
					class YamlDirectoryLayout(DirectoryLayout):
 | 
				
			||||||
    """For each directory at <depth> within <root>, return a tuple representing
 | 
					 | 
				
			||||||
       the ancestors of that directory.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    if curdepth == depth and curdepth != 0:
 | 
					 | 
				
			||||||
        yield path_tuple
 | 
					 | 
				
			||||||
    elif depth > curdepth:
 | 
					 | 
				
			||||||
        for filename in os.listdir(root):
 | 
					 | 
				
			||||||
            child = os.path.join(root, filename)
 | 
					 | 
				
			||||||
            if os.path.isdir(child):
 | 
					 | 
				
			||||||
                child_tuple = path_tuple + (filename,)
 | 
					 | 
				
			||||||
                for tup in traverse_dirs_at_depth(
 | 
					 | 
				
			||||||
                        child, depth, child_tuple, curdepth+1):
 | 
					 | 
				
			||||||
                    yield tup
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class SpecHashDirectoryLayout(DirectoryLayout):
 | 
					 | 
				
			||||||
    """Lays out installation directories like this::
 | 
					    """Lays out installation directories like this::
 | 
				
			||||||
           <install_root>/
 | 
					           <install root>/
 | 
				
			||||||
               <architecture>/
 | 
					               <architecture>/
 | 
				
			||||||
                   <compiler>/
 | 
					                   <compiler>-<compiler version>/
 | 
				
			||||||
                       name@version+variant-<dependency_hash>
 | 
					                       <name>-<version>-<variants>-<hash>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       Where dependency_hash is a SHA-1 hash prefix for the full package spec.
 | 
					       The hash here is a SHA-1 hash for the full DAG plus the build
 | 
				
			||||||
       This accounts for dependencies.
 | 
					       spec.  TODO: implement the build spec.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       If there is ever a hash collision, you won't be able to install a new
 | 
					       To avoid special characters (like ~) in the directory name,
 | 
				
			||||||
       package unless you use a larger prefix.  However, the full spec is stored
 | 
					       only enabled variants are included in the install path.
 | 
				
			||||||
       in a file called .spec in each directory, so you can migrate an entire
 | 
					       Disabled variants are omitted.
 | 
				
			||||||
       install directory to a new hash size pretty easily.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
       TODO: make a tool to migrate install directories to different hash sizes.
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    def __init__(self, root, **kwargs):
 | 
					    def __init__(self, root, **kwargs):
 | 
				
			||||||
        """Prefix size is number of characters in the SHA-1 prefix to use
 | 
					        super(YamlDirectoryLayout, self).__init__(root)
 | 
				
			||||||
           to make each hash unique.
 | 
					        self.metadata_dir   = kwargs.get('metadata_dir', '.spack')
 | 
				
			||||||
        """
 | 
					        self.hash_len       = kwargs.get('hash_len', None)
 | 
				
			||||||
        spec_file_name = kwargs.get('spec_file_name', '.spec')
 | 
					
 | 
				
			||||||
        extension_file_name = kwargs.get('extension_file_name', '.extensions')
 | 
					        self.spec_file_name = 'spec'
 | 
				
			||||||
        super(SpecHashDirectoryLayout, self).__init__(root)
 | 
					        self.extension_file_name = 'extensions'
 | 
				
			||||||
        self.spec_file_name = spec_file_name
 | 
					 | 
				
			||||||
        self.extension_file_name = extension_file_name
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Cache of already written/read extension maps.
 | 
					        # Cache of already written/read extension maps.
 | 
				
			||||||
        self._extension_maps = {}
 | 
					        self._extension_maps = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def hidden_file_paths(self):
 | 
					    def hidden_file_paths(self):
 | 
				
			||||||
        return ('.spec', '.extensions')
 | 
					        return (self.metadata_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def relative_path_for_spec(self, spec):
 | 
					    def relative_path_for_spec(self, spec):
 | 
				
			||||||
        _check_concrete(spec)
 | 
					        _check_concrete(spec)
 | 
				
			||||||
        dir_name = spec.format('$_$@$+$#')
 | 
					        enabled_variants = (
 | 
				
			||||||
        return join_path(spec.architecture, spec.compiler, dir_name)
 | 
					            '-' + v.name for v in spec.variants.values()
 | 
				
			||||||
 | 
					            if v.enabled)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dir_name = "%s-%s%s-%s" % (
 | 
				
			||||||
 | 
					            spec.name,
 | 
				
			||||||
 | 
					            spec.version,
 | 
				
			||||||
 | 
					            ''.join(enabled_variants),
 | 
				
			||||||
 | 
					            spec.dag_hash(self.hash_len))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        path = join_path(
 | 
				
			||||||
 | 
					            spec.architecture,
 | 
				
			||||||
 | 
					            "%s-%s" % (spec.compiler.name, spec.compiler.version),
 | 
				
			||||||
 | 
					            dir_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def write_spec(self, spec, path):
 | 
					    def write_spec(self, spec, path):
 | 
				
			||||||
        """Write a spec out to a file."""
 | 
					        """Write a spec out to a file."""
 | 
				
			||||||
        with closing(open(path, 'w')) as spec_file:
 | 
					        _check_concrete(spec)
 | 
				
			||||||
            spec_file.write(spec.tree(ids=False, cover='nodes'))
 | 
					        with open(path, 'w') as f:
 | 
				
			||||||
 | 
					            f.write(spec.to_yaml())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def read_spec(self, path):
 | 
					    def read_spec(self, path):
 | 
				
			||||||
        """Read the contents of a file and parse them as a spec"""
 | 
					        """Read the contents of a file and parse them as a spec"""
 | 
				
			||||||
        with closing(open(path)) as spec_file:
 | 
					        with open(path) as f:
 | 
				
			||||||
            # Specs from files are assumed normal and concrete
 | 
					            yaml_text = f.read()
 | 
				
			||||||
            spec = Spec(spec_file.read().replace('\n', ''))
 | 
					        spec = Spec.from_yaml(yaml_text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if all(spack.db.exists(s.name) for s in spec.traverse()):
 | 
					        # Specs read from actual installations are always concrete
 | 
				
			||||||
            copy = spec.copy()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # TODO: It takes a lot of time to normalize every spec on read.
 | 
					 | 
				
			||||||
            # TODO: Storing graph info with spec files would fix this.
 | 
					 | 
				
			||||||
            copy.normalize()
 | 
					 | 
				
			||||||
            if copy.concrete:
 | 
					 | 
				
			||||||
                return copy   # These are specs spack still understands.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # If we get here, either the spec is no longer in spack, or
 | 
					 | 
				
			||||||
        # something about its dependencies has changed. So we need to
 | 
					 | 
				
			||||||
        # just assume the read spec is correct.  We'll lose graph
 | 
					 | 
				
			||||||
        # information if we do this, but this is just for best effort
 | 
					 | 
				
			||||||
        # for commands like uninstall and find.  Currently Spack
 | 
					 | 
				
			||||||
        # doesn't do anything that needs the graph info after install.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # TODO: store specs with full connectivity information, so
 | 
					 | 
				
			||||||
        # that we don't have to normalize or reconstruct based on
 | 
					 | 
				
			||||||
        # changing dependencies in the Spack tree.
 | 
					 | 
				
			||||||
        spec._normal = True
 | 
					        spec._normal = True
 | 
				
			||||||
        spec._concrete = True
 | 
					        spec._concrete = True
 | 
				
			||||||
        return spec
 | 
					        return spec
 | 
				
			||||||
@@ -249,10 +226,14 @@ def read_spec(self, path):
 | 
				
			|||||||
    def spec_file_path(self, spec):
 | 
					    def spec_file_path(self, spec):
 | 
				
			||||||
        """Gets full path to spec file"""
 | 
					        """Gets full path to spec file"""
 | 
				
			||||||
        _check_concrete(spec)
 | 
					        _check_concrete(spec)
 | 
				
			||||||
        return join_path(self.path_for_spec(spec), self.spec_file_name)
 | 
					        return join_path(self.metadata_path(spec), self.spec_file_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def make_path_for_spec(self, spec):
 | 
					    def metadata_path(self, spec):
 | 
				
			||||||
 | 
					        return join_path(self.path_for_spec(spec), self.metadata_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_install_directory(self, spec):
 | 
				
			||||||
        _check_concrete(spec)
 | 
					        _check_concrete(spec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        path = self.path_for_spec(spec)
 | 
					        path = self.path_for_spec(spec)
 | 
				
			||||||
@@ -267,16 +248,13 @@ def make_path_for_spec(self, spec):
 | 
				
			|||||||
            if installed_spec == self.spec:
 | 
					            if installed_spec == self.spec:
 | 
				
			||||||
                raise InstallDirectoryAlreadyExistsError(path)
 | 
					                raise InstallDirectoryAlreadyExistsError(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            spec_hash = self.hash_spec(spec)
 | 
					            if spec.dag_hash() == installed_spec.dag_hash():
 | 
				
			||||||
            installed_hash = self.hash_spec(installed_spec)
 | 
					 | 
				
			||||||
            if installed_spec == spec_hash:
 | 
					 | 
				
			||||||
                raise SpecHashCollisionError(installed_hash, spec_hash)
 | 
					                raise SpecHashCollisionError(installed_hash, spec_hash)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                raise InconsistentInstallDirectoryError(
 | 
					                raise InconsistentInstallDirectoryError(
 | 
				
			||||||
                    'Spec file in %s does not match SHA-1 hash!'
 | 
					                    'Spec file in %s does not match hash!' % spec_file_path)
 | 
				
			||||||
                    % spec_file_path)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        mkdirp(path)
 | 
					        mkdirp(self.metadata_path(spec))
 | 
				
			||||||
        self.write_spec(spec, spec_file_path)
 | 
					        self.write_spec(spec, spec_file_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -284,22 +262,14 @@ def make_path_for_spec(self, spec):
 | 
				
			|||||||
    def all_specs(self):
 | 
					    def all_specs(self):
 | 
				
			||||||
        if not os.path.isdir(self.root):
 | 
					        if not os.path.isdir(self.root):
 | 
				
			||||||
            return []
 | 
					            return []
 | 
				
			||||||
 | 
					        spec_files = glob.glob("%s/*/*/*/.spack/spec" % self.root)
 | 
				
			||||||
        specs = []
 | 
					        return [self.read_spec(s) for s in spec_files]
 | 
				
			||||||
        for path in traverse_dirs_at_depth(self.root, 3):
 | 
					 | 
				
			||||||
            arch, compiler, last_dir = path
 | 
					 | 
				
			||||||
            spec_file_path = join_path(
 | 
					 | 
				
			||||||
                self.root, arch, compiler, last_dir, self.spec_file_name)
 | 
					 | 
				
			||||||
            if os.path.exists(spec_file_path):
 | 
					 | 
				
			||||||
                spec = self.read_spec(spec_file_path)
 | 
					 | 
				
			||||||
                specs.append(spec)
 | 
					 | 
				
			||||||
        return specs
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def extension_file_path(self, spec):
 | 
					    def extension_file_path(self, spec):
 | 
				
			||||||
        """Gets full path to an installed package's extension file"""
 | 
					        """Gets full path to an installed package's extension file"""
 | 
				
			||||||
        _check_concrete(spec)
 | 
					        _check_concrete(spec)
 | 
				
			||||||
        return join_path(self.path_for_spec(spec), self.extension_file_name)
 | 
					        return join_path(self.metadata_path(spec), self.extension_file_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _extension_map(self, spec):
 | 
					    def _extension_map(self, spec):
 | 
				
			||||||
@@ -314,7 +284,7 @@ def _extension_map(self, spec):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                exts = {}
 | 
					                exts = {}
 | 
				
			||||||
                with closing(open(path)) as ext_file:
 | 
					                with open(path) as ext_file:
 | 
				
			||||||
                    for line in ext_file:
 | 
					                    for line in ext_file:
 | 
				
			||||||
                        try:
 | 
					                        try:
 | 
				
			||||||
                            spec = Spec(line.strip())
 | 
					                            spec = Spec(line.strip())
 | 
				
			||||||
@@ -358,7 +328,7 @@ def _write_extensions(self, spec, extensions):
 | 
				
			|||||||
            prefix=basename, dir=dirname, delete=False)
 | 
					            prefix=basename, dir=dirname, delete=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Write temp file.
 | 
					        # Write temp file.
 | 
				
			||||||
        with closing(tmp):
 | 
					        with tmp:
 | 
				
			||||||
            for extension in sorted(extensions.values()):
 | 
					            for extension in sorted(extensions.values()):
 | 
				
			||||||
                tmp.write("%s\n" % extension)
 | 
					                tmp.write("%s\n" % extension)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -392,6 +362,7 @@ def remove_extension(self, spec, ext_spec):
 | 
				
			|||||||
        self._write_extensions(spec, exts)
 | 
					        self._write_extensions(spec, exts)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DirectoryLayoutError(SpackError):
 | 
					class DirectoryLayoutError(SpackError):
 | 
				
			||||||
    """Superclass for directory layout errors."""
 | 
					    """Superclass for directory layout errors."""
 | 
				
			||||||
    def __init__(self, message):
 | 
					    def __init__(self, message):
 | 
				
			||||||
@@ -399,9 +370,9 @@ def __init__(self, message):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SpecHashCollisionError(DirectoryLayoutError):
 | 
					class SpecHashCollisionError(DirectoryLayoutError):
 | 
				
			||||||
    """Raised when there is a hash collision in an SpecHashDirectoryLayout."""
 | 
					    """Raised when there is a hash collision in an install layout."""
 | 
				
			||||||
    def __init__(self, installed_spec, new_spec):
 | 
					    def __init__(self, installed_spec, new_spec):
 | 
				
			||||||
        super(SpecHashDirectoryLayout, self).__init__(
 | 
					        super(SpecHashCollisionError, self).__init__(
 | 
				
			||||||
            'Specs %s and %s have the same SHA-1 prefix!'
 | 
					            'Specs %s and %s have the same SHA-1 prefix!'
 | 
				
			||||||
            % installed_spec, new_spec)
 | 
					            % installed_spec, new_spec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -422,7 +393,7 @@ def __init__(self, message):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class InstallDirectoryAlreadyExistsError(DirectoryLayoutError):
 | 
					class InstallDirectoryAlreadyExistsError(DirectoryLayoutError):
 | 
				
			||||||
    """Raised when make_path_for_sec is called unnecessarily."""
 | 
					    """Raised when create_install_directory is called unnecessarily."""
 | 
				
			||||||
    def __init__(self, path):
 | 
					    def __init__(self, path):
 | 
				
			||||||
        super(InstallDirectoryAlreadyExistsError, self).__init__(
 | 
					        super(InstallDirectoryAlreadyExistsError, self).__init__(
 | 
				
			||||||
            "Install path %s already exists!")
 | 
					            "Install path %s already exists!")
 | 
				
			||||||
@@ -455,5 +426,3 @@ def __init__(self, spec, ext_spec):
 | 
				
			|||||||
        super(NoSuchExtensionError, self).__init__(
 | 
					        super(NoSuchExtensionError, self).__init__(
 | 
				
			||||||
            "%s cannot be removed from %s because it's not activated."% (
 | 
					            "%s cannot be removed from %s because it's not activated."% (
 | 
				
			||||||
                ext_spec.short_spec, spec.short_spec))
 | 
					                ext_spec.short_spec, spec.short_spec))
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -658,7 +658,7 @@ def url_version(self, version):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def remove_prefix(self):
 | 
					    def remove_prefix(self):
 | 
				
			||||||
        """Removes the prefix for a package along with any empty parent directories."""
 | 
					        """Removes the prefix for a package along with any empty parent directories."""
 | 
				
			||||||
        spack.install_layout.remove_path_for_spec(self.spec)
 | 
					        spack.install_layout.remove_install_directory(self.spec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def do_fetch(self):
 | 
					    def do_fetch(self):
 | 
				
			||||||
@@ -810,7 +810,7 @@ def do_install(self, **kwargs):
 | 
				
			|||||||
        # create the install directory.  The install layout
 | 
					        # create the install directory.  The install layout
 | 
				
			||||||
        # handles this in case so that it can use whatever
 | 
					        # handles this in case so that it can use whatever
 | 
				
			||||||
        # package naming scheme it likes.
 | 
					        # package naming scheme it likes.
 | 
				
			||||||
        spack.install_layout.make_path_for_spec(self.spec)
 | 
					        spack.install_layout.create_install_directory(self.spec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def cleanup():
 | 
					        def cleanup():
 | 
				
			||||||
            if not keep_prefix:
 | 
					            if not keep_prefix:
 | 
				
			||||||
@@ -831,11 +831,11 @@ def real_work():
 | 
				
			|||||||
                spack.hooks.pre_install(self)
 | 
					                spack.hooks.pre_install(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # Set up process's build environment before running install.
 | 
					                # Set up process's build environment before running install.
 | 
				
			||||||
                self.stage.chdir_to_source()
 | 
					 | 
				
			||||||
                if fake_install:
 | 
					                if fake_install:
 | 
				
			||||||
                    self.do_fake_install()
 | 
					                    self.do_fake_install()
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    # Subclasses implement install() to do the real work.
 | 
					                    # Subclasses implement install() to do the real work.
 | 
				
			||||||
 | 
					                    self.stage.chdir_to_source()
 | 
				
			||||||
                    self.install(self.spec, self.prefix)
 | 
					                    self.install(self.spec, self.prefix)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # Ensure that something was actually installed.
 | 
					                # Ensure that something was actually installed.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,6 +93,7 @@
 | 
				
			|||||||
import sys
 | 
					import sys
 | 
				
			||||||
import itertools
 | 
					import itertools
 | 
				
			||||||
import hashlib
 | 
					import hashlib
 | 
				
			||||||
 | 
					import base64
 | 
				
			||||||
from StringIO import StringIO
 | 
					from StringIO import StringIO
 | 
				
			||||||
from operator import attrgetter
 | 
					from operator import attrgetter
 | 
				
			||||||
from external import yaml
 | 
					from external import yaml
 | 
				
			||||||
@@ -578,27 +579,12 @@ def prefix(self):
 | 
				
			|||||||
        return Prefix(spack.install_layout.path_for_spec(self))
 | 
					        return Prefix(spack.install_layout.path_for_spec(self))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dep_hash(self, length=None):
 | 
					 | 
				
			||||||
        """Return a hash representing all dependencies of this spec
 | 
					 | 
				
			||||||
           (direct and indirect).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
           If you want this hash to be consistent, you should
 | 
					 | 
				
			||||||
           concretize the spec first so that it is not ambiguous.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        sha = hashlib.sha1()
 | 
					 | 
				
			||||||
        sha.update(self.dep_string())
 | 
					 | 
				
			||||||
        full_hash = sha.hexdigest()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return full_hash[:length]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def dag_hash(self, length=None):
 | 
					    def dag_hash(self, length=None):
 | 
				
			||||||
        """Return a hash of the entire spec DAG, including connectivity."""
 | 
					        """Return a hash of the entire spec DAG, including connectivity."""
 | 
				
			||||||
        sha = hashlib.sha1()
 | 
					        yaml_text = yaml.dump(
 | 
				
			||||||
        hash_text = yaml.dump(
 | 
					 | 
				
			||||||
            self.to_node_dict(), default_flow_style=True, width=sys.maxint)
 | 
					            self.to_node_dict(), default_flow_style=True, width=sys.maxint)
 | 
				
			||||||
        sha.update(hash_text)
 | 
					        sha = hashlib.sha1(yaml_text)
 | 
				
			||||||
        return sha.hexdigest()[:length]
 | 
					        return base64.b32encode(sha.digest()).lower()[:length]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def to_node_dict(self):
 | 
					    def to_node_dict(self):
 | 
				
			||||||
@@ -1363,7 +1349,7 @@ def write(s, c):
 | 
				
			|||||||
                        write(fmt % (c + str(self.architecture)), c)
 | 
					                        write(fmt % (c + str(self.architecture)), c)
 | 
				
			||||||
                elif c == '#':
 | 
					                elif c == '#':
 | 
				
			||||||
                    if self.dependencies:
 | 
					                    if self.dependencies:
 | 
				
			||||||
                        out.write(fmt % ('-' + self.dep_hash(8)))
 | 
					                        out.write(fmt % ('-' + self.dag_hash(8)))
 | 
				
			||||||
                elif c == '$':
 | 
					                elif c == '$':
 | 
				
			||||||
                    if fmt != '':
 | 
					                    if fmt != '':
 | 
				
			||||||
                        raise ValueError("Can't use format width with $$.")
 | 
					                        raise ValueError("Can't use format width with $$.")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,11 @@
 | 
				
			|||||||
import spack
 | 
					import spack
 | 
				
			||||||
from spack.spec import Spec
 | 
					from spack.spec import Spec
 | 
				
			||||||
from spack.packages import PackageDB
 | 
					from spack.packages import PackageDB
 | 
				
			||||||
from spack.directory_layout import SpecHashDirectoryLayout
 | 
					from spack.directory_layout import YamlDirectoryLayout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# number of packages to test (to reduce test time)
 | 
				
			||||||
 | 
					max_packages = 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DirectoryLayoutTest(unittest.TestCase):
 | 
					class DirectoryLayoutTest(unittest.TestCase):
 | 
				
			||||||
    """Tests that a directory layout works correctly and produces a
 | 
					    """Tests that a directory layout works correctly and produces a
 | 
				
			||||||
@@ -44,11 +48,11 @@ class DirectoryLayoutTest(unittest.TestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        self.tmpdir = tempfile.mkdtemp()
 | 
					        self.tmpdir = tempfile.mkdtemp()
 | 
				
			||||||
        self.layout = SpecHashDirectoryLayout(self.tmpdir)
 | 
					        self.layout = YamlDirectoryLayout(self.tmpdir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def tearDown(self):
 | 
					    def tearDown(self):
 | 
				
			||||||
        shutil.rmtree(self.tmpdir, ignore_errors=True)
 | 
					        #shutil.rmtree(self.tmpdir, ignore_errors=True)
 | 
				
			||||||
        self.layout = None
 | 
					        self.layout = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,7 +63,9 @@ def test_read_and_write_spec(self):
 | 
				
			|||||||
           finally that the directory can be removed by the directory
 | 
					           finally that the directory can be removed by the directory
 | 
				
			||||||
           layout.
 | 
					           layout.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        for pkg in spack.db.all_packages():
 | 
					        packages = list(spack.db.all_packages())[:max_packages]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for pkg in packages:
 | 
				
			||||||
            spec = pkg.spec
 | 
					            spec = pkg.spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # If a spec fails to concretize, just skip it.  If it is a
 | 
					            # If a spec fails to concretize, just skip it.  If it is a
 | 
				
			||||||
@@ -69,7 +75,7 @@ def test_read_and_write_spec(self):
 | 
				
			|||||||
            except:
 | 
					            except:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.layout.make_path_for_spec(spec)
 | 
					            self.layout.create_install_directory(spec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            install_dir = self.layout.path_for_spec(spec)
 | 
					            install_dir = self.layout.path_for_spec(spec)
 | 
				
			||||||
            spec_path = self.layout.spec_file_path(spec)
 | 
					            spec_path = self.layout.spec_file_path(spec)
 | 
				
			||||||
@@ -90,7 +96,7 @@ def test_read_and_write_spec(self):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            # Ensure that specs that come out "normal" are really normal.
 | 
					            # Ensure that specs that come out "normal" are really normal.
 | 
				
			||||||
            with closing(open(spec_path)) as spec_file:
 | 
					            with closing(open(spec_path)) as spec_file:
 | 
				
			||||||
                read_separately = Spec(spec_file.read())
 | 
					                read_separately = Spec.from_yaml(spec_file.read())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                read_separately.normalize()
 | 
					                read_separately.normalize()
 | 
				
			||||||
                self.assertEqual(read_separately, spec_from_file)
 | 
					                self.assertEqual(read_separately, spec_from_file)
 | 
				
			||||||
@@ -98,11 +104,11 @@ def test_read_and_write_spec(self):
 | 
				
			|||||||
                read_separately.concretize()
 | 
					                read_separately.concretize()
 | 
				
			||||||
                self.assertEqual(read_separately, spec_from_file)
 | 
					                self.assertEqual(read_separately, spec_from_file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Make sure the dep hash of the read-in spec is the same
 | 
					            # Make sure the hash of the read-in spec is the same
 | 
				
			||||||
            self.assertEqual(spec.dep_hash(), spec_from_file.dep_hash())
 | 
					            self.assertEqual(spec.dag_hash(), spec_from_file.dag_hash())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Ensure directories are properly removed
 | 
					            # Ensure directories are properly removed
 | 
				
			||||||
            self.layout.remove_path_for_spec(spec)
 | 
					            self.layout.remove_install_directory(spec)
 | 
				
			||||||
            self.assertFalse(os.path.isdir(install_dir))
 | 
					            self.assertFalse(os.path.isdir(install_dir))
 | 
				
			||||||
            self.assertFalse(os.path.exists(install_dir))
 | 
					            self.assertFalse(os.path.exists(install_dir))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -120,12 +126,14 @@ def test_handle_unknown_package(self):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        mock_db = PackageDB(spack.mock_packages_path)
 | 
					        mock_db = PackageDB(spack.mock_packages_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        not_in_mock = set(spack.db.all_package_names()).difference(
 | 
					        not_in_mock = set.difference(
 | 
				
			||||||
 | 
					            set(spack.db.all_package_names()),
 | 
				
			||||||
            set(mock_db.all_package_names()))
 | 
					            set(mock_db.all_package_names()))
 | 
				
			||||||
 | 
					        packages = list(not_in_mock)[:max_packages]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Create all the packages that are not in mock.
 | 
					        # Create all the packages that are not in mock.
 | 
				
			||||||
        installed_specs = {}
 | 
					        installed_specs = {}
 | 
				
			||||||
        for pkg_name in not_in_mock:
 | 
					        for pkg_name in packages:
 | 
				
			||||||
            spec = spack.db.get(pkg_name).spec
 | 
					            spec = spack.db.get(pkg_name).spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # If a spec fails to concretize, just skip it.  If it is a
 | 
					            # If a spec fails to concretize, just skip it.  If it is a
 | 
				
			||||||
@@ -135,7 +143,7 @@ def test_handle_unknown_package(self):
 | 
				
			|||||||
            except:
 | 
					            except:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.layout.make_path_for_spec(spec)
 | 
					            self.layout.create_install_directory(spec)
 | 
				
			||||||
            installed_specs[spec] = self.layout.path_for_spec(spec)
 | 
					            installed_specs[spec] = self.layout.path_for_spec(spec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tmp = spack.db
 | 
					        tmp = spack.db
 | 
				
			||||||
@@ -144,12 +152,28 @@ def test_handle_unknown_package(self):
 | 
				
			|||||||
        # Now check that even without the package files, we know
 | 
					        # Now check that even without the package files, we know
 | 
				
			||||||
        # enough to read a spec from the spec file.
 | 
					        # enough to read a spec from the spec file.
 | 
				
			||||||
        for spec, path in installed_specs.items():
 | 
					        for spec, path in installed_specs.items():
 | 
				
			||||||
            spec_from_file = self.layout.read_spec(join_path(path, '.spec'))
 | 
					            spec_from_file = self.layout.read_spec(join_path(path, '.spack', 'spec'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # To satisfy these conditions, directory layouts need to
 | 
					            # To satisfy these conditions, directory layouts need to
 | 
				
			||||||
            # read in concrete specs from their install dirs somehow.
 | 
					            # read in concrete specs from their install dirs somehow.
 | 
				
			||||||
            self.assertEqual(path, self.layout.path_for_spec(spec_from_file))
 | 
					            self.assertEqual(path, self.layout.path_for_spec(spec_from_file))
 | 
				
			||||||
            self.assertEqual(spec, spec_from_file)
 | 
					            self.assertEqual(spec, spec_from_file)
 | 
				
			||||||
            self.assertEqual(spec.dep_hash(), spec_from_file.dep_hash())
 | 
					            self.assertTrue(spec.eq_dag(spec_from_file))
 | 
				
			||||||
 | 
					            self.assertEqual(spec.dag_hash(), spec_from_file.dag_hash())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        spack.db = tmp
 | 
					        spack.db = tmp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_find(self):
 | 
				
			||||||
 | 
					        """Test that finding specs within an install layout works."""
 | 
				
			||||||
 | 
					        packages = list(spack.db.all_packages())[:max_packages]
 | 
				
			||||||
 | 
					        installed_specs = {}
 | 
				
			||||||
 | 
					        for pkg in packages:
 | 
				
			||||||
 | 
					            spec = pkg.spec.concretized()
 | 
				
			||||||
 | 
					            installed_specs[spec.name] = spec
 | 
				
			||||||
 | 
					            self.layout.create_install_directory(spec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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)
 | 
				
			||||||
 | 
					            self.assertTrue(found_specs[name].eq_dag(spec))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@
 | 
				
			|||||||
import spack
 | 
					import spack
 | 
				
			||||||
from spack.stage import Stage
 | 
					from spack.stage import Stage
 | 
				
			||||||
from spack.fetch_strategy import URLFetchStrategy
 | 
					from spack.fetch_strategy import URLFetchStrategy
 | 
				
			||||||
from spack.directory_layout import SpecHashDirectoryLayout
 | 
					from spack.directory_layout import YamlDirectoryLayout
 | 
				
			||||||
from spack.util.executable import which
 | 
					from spack.util.executable import which
 | 
				
			||||||
from spack.test.mock_packages_test import *
 | 
					from spack.test.mock_packages_test import *
 | 
				
			||||||
from spack.test.mock_repo import MockArchive
 | 
					from spack.test.mock_repo import MockArchive
 | 
				
			||||||
@@ -55,7 +55,7 @@ def setUp(self):
 | 
				
			|||||||
        # installed pkgs and mock packages.
 | 
					        # installed pkgs and mock packages.
 | 
				
			||||||
        self.tmpdir = tempfile.mkdtemp()
 | 
					        self.tmpdir = tempfile.mkdtemp()
 | 
				
			||||||
        self.orig_layout = spack.install_layout
 | 
					        self.orig_layout = spack.install_layout
 | 
				
			||||||
        spack.install_layout = SpecHashDirectoryLayout(self.tmpdir)
 | 
					        spack.install_layout = YamlDirectoryLayout(self.tmpdir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def tearDown(self):
 | 
					    def tearDown(self):
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user