summaryrefslogtreecommitdiff
path: root/lib/spack/spack/directory_layout.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/spack/directory_layout.py')
-rw-r--r--lib/spack/spack/directory_layout.py85
1 files changed, 31 insertions, 54 deletions
diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py
index 8150a6da2b..0ae6f765f4 100644
--- a/lib/spack/spack/directory_layout.py
+++ b/lib/spack/spack/directory_layout.py
@@ -22,16 +22,13 @@
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
-import re
import os
import exceptions
-import hashlib
import shutil
import glob
import tempfile
import yaml
-import llnl.util.tty as tty
from llnl.util.filesystem import join_path, mkdirp
import spack
@@ -51,10 +48,10 @@ class DirectoryLayout(object):
install, and they can use this to customize the nesting structure of
spack installs.
"""
+
def __init__(self, root):
self.root = root
-
@property
def hidden_file_paths(self):
"""Return a list of hidden files used by the directory layout.
@@ -67,25 +64,21 @@ class DirectoryLayout(object):
"""
raise NotImplementedError()
-
def all_specs(self):
"""To be implemented by subclasses to traverse all specs for which there is
a directory within the root.
"""
raise NotImplementedError()
-
def relative_path_for_spec(self, spec):
"""Implemented by subclasses to return a relative path from the install
root to a unique location for the provided spec."""
raise NotImplementedError()
-
def create_install_directory(self, spec):
"""Creates the installation directory for a spec."""
raise NotImplementedError()
-
def check_installed(self, spec):
"""Checks whether a spec is installed.
@@ -95,7 +88,6 @@ class DirectoryLayout(object):
"""
raise NotImplementedError()
-
def extension_map(self, spec):
"""Get a dict of currently installed extension packages for a spec.
@@ -104,7 +96,6 @@ class DirectoryLayout(object):
"""
raise NotImplementedError()
-
def check_extension_conflict(self, spec, ext_spec):
"""Ensure that ext_spec can be activated in spec.
@@ -113,7 +104,6 @@ class DirectoryLayout(object):
"""
raise NotImplementedError()
-
def check_activated(self, spec, ext_spec):
"""Ensure that ext_spec can be removed from spec.
@@ -121,26 +111,22 @@ class DirectoryLayout(object):
"""
raise NotImplementedError()
-
def add_extension(self, spec, ext_spec):
"""Add to the list of currently installed extensions."""
raise NotImplementedError()
-
def remove_extension(self, spec, ext_spec):
"""Remove from the list of currently installed extensions."""
raise NotImplementedError()
-
def path_for_spec(self, spec):
- """Return an absolute path from the root to a directory for the spec."""
+ """Return absolute path from the root to a directory for the spec."""
_check_concrete(spec)
path = self.relative_path_for_spec(spec)
assert(not path.startswith(self.root))
return os.path.join(self.root, path)
-
def remove_install_directory(self, spec):
"""Removes a prefix and any empty parent directories from the root.
Raised RemoveFailedError if something goes wrong.
@@ -177,6 +163,7 @@ class YamlDirectoryLayout(DirectoryLayout):
only enabled variants are included in the install path.
Disabled variants are omitted.
"""
+
def __init__(self, root, **kwargs):
super(YamlDirectoryLayout, self).__init__(root)
self.metadata_dir = kwargs.get('metadata_dir', '.spack')
@@ -191,12 +178,10 @@ class YamlDirectoryLayout(DirectoryLayout):
# Cache of already written/read extension maps.
self._extension_maps = {}
-
@property
def hidden_file_paths(self):
return (self.metadata_dir,)
-
def relative_path_for_spec(self, spec):
_check_concrete(spec)
@@ -208,20 +193,19 @@ class YamlDirectoryLayout(DirectoryLayout):
spec.version,
spec.dag_hash(self.hash_len))
- path = join_path(spec.architecture,
+ path = join_path(
+ spec.architecture,
"%s-%s" % (spec.compiler.name, spec.compiler.version),
dir_name)
return path
-
def write_spec(self, spec, path):
"""Write a spec out to a file."""
_check_concrete(spec)
with open(path, 'w') as f:
spec.to_yaml(f)
-
def read_spec(self, path):
"""Read the contents of a file and parse them as a spec"""
try:
@@ -237,32 +221,26 @@ class YamlDirectoryLayout(DirectoryLayout):
spec._mark_concrete()
return spec
-
def spec_file_path(self, spec):
"""Gets full path to spec file"""
_check_concrete(spec)
return join_path(self.metadata_path(spec), self.spec_file_name)
-
def metadata_path(self, spec):
return join_path(self.path_for_spec(spec), self.metadata_dir)
-
def build_log_path(self, spec):
return join_path(self.path_for_spec(spec), self.metadata_dir,
self.build_log_name)
-
def build_env_path(self, spec):
return join_path(self.path_for_spec(spec), self.metadata_dir,
self.build_env_name)
-
def build_packages_path(self, spec):
return join_path(self.path_for_spec(spec), self.metadata_dir,
self.packages_dir)
-
def create_install_directory(self, spec):
_check_concrete(spec)
@@ -273,7 +251,6 @@ class YamlDirectoryLayout(DirectoryLayout):
mkdirp(self.metadata_path(spec))
self.write_spec(spec, self.spec_file_path(spec))
-
def check_installed(self, spec):
_check_concrete(spec)
path = self.path_for_spec(spec)
@@ -284,7 +261,7 @@ class YamlDirectoryLayout(DirectoryLayout):
if not os.path.isfile(spec_file_path):
raise InconsistentInstallDirectoryError(
- 'Inconsistent state: install prefix exists but contains no spec.yaml:',
+ 'Install prefix exists but contains no spec.yaml:',
" " + path)
installed_spec = self.read_spec(spec_file_path)
@@ -297,7 +274,6 @@ class YamlDirectoryLayout(DirectoryLayout):
raise InconsistentInstallDirectoryError(
'Spec file in %s does not match hash!' % spec_file_path)
-
def all_specs(self):
if not os.path.isdir(self.root):
return []
@@ -307,20 +283,17 @@ class YamlDirectoryLayout(DirectoryLayout):
spec_files = glob.glob(pattern)
return [self.read_spec(s) for s in spec_files]
-
def specs_by_hash(self):
by_hash = {}
for spec in self.all_specs():
by_hash[spec.dag_hash()] = spec
return by_hash
-
def extension_file_path(self, spec):
"""Gets full path to an installed package's extension file"""
_check_concrete(spec)
return join_path(self.metadata_path(spec), self.extension_file_name)
-
def _write_extensions(self, spec, extensions):
path = self.extension_file_path(spec)
@@ -332,23 +305,22 @@ class YamlDirectoryLayout(DirectoryLayout):
# write tmp file
with tmp:
yaml.dump({
- 'extensions' : [
- { ext.name : {
- 'hash' : ext.dag_hash(),
- 'path' : str(ext.prefix)
+ 'extensions': [
+ {ext.name: {
+ 'hash': ext.dag_hash(),
+ 'path': str(ext.prefix)
}} for ext in sorted(extensions.values())]
}, tmp, default_flow_style=False)
# Atomic update by moving tmpfile on top of old one.
os.rename(tmp.name, path)
-
def _extension_map(self, spec):
"""Get a dict<name -> spec> for all extensions currently
installed for this package."""
_check_concrete(spec)
- if not spec in self._extension_maps:
+ if spec not in self._extension_maps:
path = self.extension_file_path(spec)
if not os.path.exists(path):
self._extension_maps[spec] = {}
@@ -363,14 +335,14 @@ class YamlDirectoryLayout(DirectoryLayout):
dag_hash = entry[name]['hash']
prefix = entry[name]['path']
- if not dag_hash in by_hash:
+ if dag_hash not in by_hash:
raise InvalidExtensionSpecError(
"Spec %s not found in %s" % (dag_hash, prefix))
ext_spec = by_hash[dag_hash]
- if not prefix == ext_spec.prefix:
+ if prefix != ext_spec.prefix:
raise InvalidExtensionSpecError(
- "Prefix %s does not match spec with hash %s: %s"
+ "Prefix %s does not match spec hash %s: %s"
% (prefix, dag_hash, ext_spec))
exts[ext_spec.name] = ext_spec
@@ -378,13 +350,11 @@ class YamlDirectoryLayout(DirectoryLayout):
return self._extension_maps[spec]
-
def extension_map(self, spec):
"""Defensive copying version of _extension_map() for external API."""
_check_concrete(spec)
return self._extension_map(spec).copy()
-
def check_extension_conflict(self, spec, ext_spec):
exts = self._extension_map(spec)
if ext_spec.name in exts:
@@ -394,13 +364,11 @@ class YamlDirectoryLayout(DirectoryLayout):
else:
raise ExtensionConflictError(spec, ext_spec, installed_spec)
-
def check_activated(self, spec, ext_spec):
exts = self._extension_map(spec)
- if (not ext_spec.name in exts) or (ext_spec != exts[ext_spec.name]):
+ if (ext_spec.name not in exts) or (ext_spec != exts[ext_spec.name]):
raise NoSuchExtensionError(spec, ext_spec)
-
def add_extension(self, spec, ext_spec):
_check_concrete(spec)
_check_concrete(ext_spec)
@@ -413,7 +381,6 @@ class YamlDirectoryLayout(DirectoryLayout):
exts[ext_spec.name] = ext_spec
self._write_extensions(spec, exts)
-
def remove_extension(self, spec, ext_spec):
_check_concrete(spec)
_check_concrete(ext_spec)
@@ -429,12 +396,14 @@ class YamlDirectoryLayout(DirectoryLayout):
class DirectoryLayoutError(SpackError):
"""Superclass for directory layout errors."""
+
def __init__(self, message, long_msg=None):
super(DirectoryLayoutError, self).__init__(message, long_msg)
class SpecHashCollisionError(DirectoryLayoutError):
"""Raised when there is a hash collision in an install layout."""
+
def __init__(self, installed_spec, new_spec):
super(SpecHashCollisionError, self).__init__(
'Specs %s and %s have the same SHA-1 prefix!'
@@ -443,6 +412,7 @@ class SpecHashCollisionError(DirectoryLayoutError):
class RemoveFailedError(DirectoryLayoutError):
"""Raised when a DirectoryLayout cannot remove an install prefix."""
+
def __init__(self, installed_spec, prefix, error):
super(RemoveFailedError, self).__init__(
'Could not remove prefix %s for %s : %s'
@@ -452,12 +422,15 @@ class RemoveFailedError(DirectoryLayoutError):
class InconsistentInstallDirectoryError(DirectoryLayoutError):
"""Raised when a package seems to be installed to the wrong place."""
+
def __init__(self, message, long_msg=None):
- super(InconsistentInstallDirectoryError, self).__init__(message, long_msg)
+ super(InconsistentInstallDirectoryError, self).__init__(
+ message, long_msg)
class InstallDirectoryAlreadyExistsError(DirectoryLayoutError):
"""Raised when create_install_directory is called unnecessarily."""
+
def __init__(self, path):
super(InstallDirectoryAlreadyExistsError, self).__init__(
"Install path %s already exists!")
@@ -473,22 +446,26 @@ class InvalidExtensionSpecError(DirectoryLayoutError):
class ExtensionAlreadyInstalledError(DirectoryLayoutError):
"""Raised when an extension is added to a package that already has it."""
+
def __init__(self, spec, ext_spec):
super(ExtensionAlreadyInstalledError, self).__init__(
- "%s is already installed in %s" % (ext_spec.short_spec, spec.short_spec))
+ "%s is already installed in %s"
+ % (ext_spec.short_spec, spec.short_spec))
class ExtensionConflictError(DirectoryLayoutError):
"""Raised when an extension is added to a package that already has it."""
+
def __init__(self, spec, ext_spec, conflict):
super(ExtensionConflictError, self).__init__(
- "%s cannot be installed in %s because it conflicts with %s"% (
- ext_spec.short_spec, spec.short_spec, conflict.short_spec))
+ "%s cannot be installed in %s because it conflicts with %s"
+ % (ext_spec.short_spec, spec.short_spec, conflict.short_spec))
class NoSuchExtensionError(DirectoryLayoutError):
"""Raised when an extension isn't there on deactivate."""
+
def __init__(self, spec, ext_spec):
super(NoSuchExtensionError, self).__init__(
- "%s cannot be removed from %s because it's not activated."% (
- ext_spec.short_spec, spec.short_spec))
+ "%s cannot be removed from %s because it's not activated."
+ % (ext_spec.short_spec, spec.short_spec))