summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2015-05-10 02:56:50 -0700
committerTodd Gamblin <tgamblin@llnl.gov>2015-05-10 02:56:50 -0700
commit29e833dfefe47fa82d3115d23299921643997fbd (patch)
tree8f1eb2c23ed66b5805d4aae49be3b79f02bfc675
parent2f3b0481def800fb1e3009bf878ba28d82412453 (diff)
downloadspack-29e833dfefe47fa82d3115d23299921643997fbd.tar.gz
spack-29e833dfefe47fa82d3115d23299921643997fbd.tar.bz2
spack-29e833dfefe47fa82d3115d23299921643997fbd.tar.xz
spack-29e833dfefe47fa82d3115d23299921643997fbd.zip
extensions file now in YAML format
-rw-r--r--lib/spack/spack/directory_layout.py92
-rw-r--r--lib/spack/spack/spec.py12
-rw-r--r--lib/spack/spack/test/directory_layout.py3
3 files changed, 69 insertions, 38 deletions
diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py
index 67708c47b5..c2e2ea4deb 100644
--- a/lib/spack/spack/directory_layout.py
+++ b/lib/spack/spack/directory_layout.py
@@ -173,12 +173,13 @@ class YamlDirectoryLayout(DirectoryLayout):
self.metadata_dir = kwargs.get('metadata_dir', '.spack')
self.hash_len = kwargs.get('hash_len', None)
- self.spec_file_name = 'spec'
- self.extension_file_name = 'extensions'
+ self.spec_file_name = 'spec.yaml'
+ self.extension_file_name = 'extensions.yaml'
# Cache of already written/read extension maps.
self._extension_maps = {}
+
@property
def hidden_file_paths(self):
return (self.metadata_dir)
@@ -208,14 +209,13 @@ class YamlDirectoryLayout(DirectoryLayout):
"""Write a spec out to a file."""
_check_concrete(spec)
with open(path, 'w') as f:
- f.write(spec.to_yaml())
+ spec.to_yaml(f)
def read_spec(self, path):
"""Read the contents of a file and parse them as a spec"""
with open(path) as f:
- yaml_text = f.read()
- spec = Spec.from_yaml(yaml_text)
+ spec = Spec.from_yaml(f)
# Specs read from actual installations are always concrete
spec._normal = True
@@ -262,18 +262,51 @@ class YamlDirectoryLayout(DirectoryLayout):
def all_specs(self):
if not os.path.isdir(self.root):
return []
- spec_files = glob.glob("%s/*/*/*/.spack/spec" % self.root)
+
+ pattern = join_path(
+ self.root, '*', '*', '*', self.metadata_dir, self.spec_file_name)
+ spec_files = glob.glob(pattern)
return [self.read_spec(s) for s in spec_files]
+ @memoized
+ 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)
+
+ # Create a temp file in the same directory as the actual file.
+ dirname, basename = os.path.split(path)
+ tmp = tempfile.NamedTemporaryFile(
+ prefix=basename, dir=dirname, delete=False)
+
+ # write tmp file
+ with tmp:
+ yaml.dump({
+ '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 currnetly
+ """Get a dict<name -> spec> for all extensions currently
installed for this package."""
_check_concrete(spec)
@@ -283,16 +316,26 @@ class YamlDirectoryLayout(DirectoryLayout):
self._extension_maps[spec] = {}
else:
+ by_hash = self.specs_by_hash()
exts = {}
with open(path) as ext_file:
- for line in ext_file:
- try:
- spec = Spec(line.strip())
- exts[spec.name] = spec
- except spack.error.SpackError, e:
- # TODO: do something better here -- should be
- # resilient to corrupt files.
- raise InvalidExtensionSpecError(str(e))
+ yaml_file = yaml.load(ext_file)
+ for entry in yaml_file['extensions']:
+ name = next(iter(entry))
+ dag_hash = entry[name]['hash']
+ prefix = entry[name]['path']
+
+ if not dag_hash 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:
+ raise InvalidExtensionSpecError(
+ "Prefix %s does not match spec with hash %s: %s"
+ % (prefix, dag_hash, ext_spec))
+
+ exts[ext_spec.name] = ext_spec
self._extension_maps[spec] = exts
return self._extension_maps[spec]
@@ -300,6 +343,7 @@ class YamlDirectoryLayout(DirectoryLayout):
def extension_map(self, spec):
"""Defensive copying version of _extension_map() for external API."""
+ _check_concrete(spec)
return self._extension_map(spec).copy()
@@ -319,23 +363,6 @@ class YamlDirectoryLayout(DirectoryLayout):
raise NoSuchExtensionError(spec, ext_spec)
- def _write_extensions(self, spec, extensions):
- path = self.extension_file_path(spec)
-
- # Create a temp file in the same directory as the actual file.
- dirname, basename = os.path.split(path)
- tmp = tempfile.NamedTemporaryFile(
- prefix=basename, dir=dirname, delete=False)
-
- # Write temp file.
- with tmp:
- for extension in sorted(extensions.values()):
- tmp.write("%s\n" % extension)
-
- # Atomic update by moving tmpfile on top of old one.
- os.rename(tmp.name, path)
-
-
def add_extension(self, spec, ext_spec):
_check_concrete(spec)
_check_concrete(ext_spec)
@@ -362,7 +389,6 @@ class YamlDirectoryLayout(DirectoryLayout):
self._write_extensions(spec, exts)
-
class DirectoryLayoutError(SpackError):
"""Superclass for directory layout errors."""
def __init__(self, message):
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index e36a1d6f0b..21e36de14d 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -603,13 +603,14 @@ class Spec(object):
return { self.name : d }
- def to_yaml(self):
+ def to_yaml(self, stream=None):
node_list = []
for s in self.traverse(order='pre'):
node = s.to_node_dict()
node[s.name]['hash'] = s.dag_hash()
node_list.append(node)
- return yaml.dump({ 'spec' : node_list }, default_flow_style=False)
+ return yaml.dump({ 'spec' : node_list },
+ stream=stream, default_flow_style=False)
@staticmethod
@@ -633,9 +634,12 @@ class Spec(object):
@staticmethod
- def from_yaml(string):
+ def from_yaml(stream):
"""Construct a spec from YAML.
+ Parameters:
+ stream -- string or file object to read from.
+
TODO: currently discards hashes. Include hashes when they
represent more than the DAG does.
@@ -644,7 +648,7 @@ class Spec(object):
spec = None
try:
- yfile = yaml.load(string)
+ yfile = yaml.load(stream)
except MarkedYAMLError, e:
raise SpackYAMLError("error parsing YMAL spec:", str(e))
diff --git a/lib/spack/spack/test/directory_layout.py b/lib/spack/spack/test/directory_layout.py
index 34374628be..7ca84090f2 100644
--- a/lib/spack/spack/test/directory_layout.py
+++ b/lib/spack/spack/test/directory_layout.py
@@ -152,7 +152,8 @@ class DirectoryLayoutTest(unittest.TestCase):
# Now check that even without the package files, we know
# enough to read a spec from the spec file.
for spec, path in installed_specs.items():
- spec_from_file = self.layout.read_spec(join_path(path, '.spack', 'spec'))
+ spec_from_file = self.layout.read_spec(
+ join_path(path, '.spack', 'spec.yaml'))
# To satisfy these conditions, directory layouts need to
# read in concrete specs from their install dirs somehow.