summaryrefslogtreecommitdiff
path: root/lib/spack/spack/database.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/spack/database.py')
-rw-r--r--lib/spack/spack/database.py77
1 files changed, 51 insertions, 26 deletions
diff --git a/lib/spack/spack/database.py b/lib/spack/spack/database.py
index e8902ec024..a01795ca0f 100644
--- a/lib/spack/spack/database.py
+++ b/lib/spack/spack/database.py
@@ -55,6 +55,7 @@ from spack.version import Version
import spack.spec
from spack.error import SpackError
import spack.util.spack_yaml as syaml
+import spack.util.spack_json as sjson
# DB goes in this directory underneath the root
@@ -134,10 +135,12 @@ class Database(object):
under ``root/.spack-db``, which is created if it does not
exist. This is the ``db_dir``.
- The Database will attempt to read an ``index.yaml`` file in
- ``db_dir``. If it does not find one, it will be created when
- needed by scanning the entire Database root for ``spec.yaml``
- files according to Spack's ``DirectoryLayout``.
+ The Database will attempt to read an ``index.json`` file in
+ ``db_dir``. If it does not find one, it will fall back to read
+ an ``index.yaml`` if one is present. If that does not exist, it
+ will create a database when needed by scanning the entire
+ Database root for ``spec.yaml`` files according to Spack's
+ ``DirectoryLayout``.
Caller may optionally provide a custom ``db_dir`` parameter
where data will be stored. This is intended to be used for
@@ -154,7 +157,8 @@ class Database(object):
self._db_dir = db_dir
# Set up layout of database files within the db dir
- self._index_path = join_path(self._db_dir, 'index.yaml')
+ self._old_yaml_index_path = join_path(self._db_dir, 'index.yaml')
+ self._index_path = join_path(self._db_dir, 'index.json')
self._lock_path = join_path(self._db_dir, 'lock')
# This is for other classes to use to lock prefix directories.
@@ -179,8 +183,8 @@ class Database(object):
"""Get a read lock context manager for use in a `with` block."""
return ReadTransaction(self.lock, self._read, timeout=timeout)
- def _write_to_yaml(self, stream):
- """Write out the databsae to a YAML file.
+ def _write_to_file(self, stream):
+ """Write out the databsae to a JSON file.
This function does not do any locking or transactions.
"""
@@ -201,12 +205,12 @@ class Database(object):
}
try:
- return syaml.dump(
- database, stream=stream, default_flow_style=False)
+ sjson.dump(database, stream)
except YAMLError as e:
- raise SpackYAMLError("error writing YAML database:", str(e))
+ raise syaml.SpackYAMLError(
+ "error writing YAML database:", str(e))
- def _read_spec_from_yaml(self, hash_key, installs):
+ def _read_spec_from_dict(self, hash_key, installs):
"""Recursively construct a spec from a hash in a YAML database.
Does not do any locking.
@@ -241,24 +245,32 @@ class Database(object):
child = data[dhash].spec
spec._add_dependency(child, dtypes)
- def _read_from_yaml(self, stream):
+ def _read_from_file(self, stream, format='json'):
"""
- Fill database from YAML, do not maintain old data
+ Fill database from file, do not maintain old data
Translate the spec portions from node-dict form to spec form
Does not do any locking.
"""
+ if format.lower() == 'json':
+ load = sjson.load
+ elif format.lower() == 'yaml':
+ load = syaml.load
+ else:
+ raise ValueError("Invalid database format: %s" % format)
+
try:
if isinstance(stream, basestring):
with open(stream, 'r') as f:
- yfile = syaml.load(f)
+ fdata = load(f)
else:
- yfile = syaml.load(stream)
-
+ fdata = load(stream)
except MarkedYAMLError as e:
- raise SpackYAMLError("error parsing YAML database:", str(e))
+ raise syaml.SpackYAMLError("error parsing YAML database:", str(e))
+ except Exception as e:
+ raise CorruptDatabaseError("error parsing database:", str(e))
- if yfile is None:
+ if fdata is None:
return
def check(cond, msg):
@@ -266,10 +278,10 @@ class Database(object):
raise CorruptDatabaseError(
"Spack database is corrupt: %s" % msg, self._index_path)
- check('database' in yfile, "No 'database' attribute in YAML.")
+ check('database' in fdata, "No 'database' attribute in YAML.")
# High-level file checks
- db = yfile['database']
+ db = fdata['database']
check('installs' in db, "No 'installs' in YAML DB.")
check('version' in db, "No 'version' in YAML DB.")
@@ -303,7 +315,7 @@ class Database(object):
for hash_key, rec in installs.items():
try:
# This constructs a spec DAG from the list of all installs
- spec = self._read_spec_from_yaml(hash_key, installs)
+ spec = self._read_spec_from_dict(hash_key, installs)
# Insert the brand new spec in the database. Each
# spec has its own copies of its dependency specs.
@@ -342,7 +354,7 @@ class Database(object):
def _read_suppress_error():
try:
if os.path.isfile(self._index_path):
- self._read_from_yaml(self._index_path)
+ self._read_from_file(self._index_path)
except CorruptDatabaseError as e:
self._error = e
self._data = {}
@@ -426,7 +438,7 @@ class Database(object):
# Write a temporary database file them move it into place
try:
with open(temp_file, 'w') as f:
- self._write_to_yaml(f)
+ self._write_to_file(f)
os.rename(temp_file, self._index_path)
except:
# Clean up temp file if something goes wrong.
@@ -437,11 +449,24 @@ class Database(object):
def _read(self):
"""Re-read Database from the data in the set location.
- This does no locking.
+ This does no locking, with one exception: it will automatically
+ migrate an index.yaml to an index.json if possible. This requires
+ taking a write lock.
+
"""
if os.path.isfile(self._index_path):
- # Read from YAML file if a database exists
- self._read_from_yaml(self._index_path)
+ # Read from JSON file if a JSON database exists
+ self._read_from_file(self._index_path, format='json')
+
+ elif os.path.isfile(self._old_yaml_index_path):
+ if os.access(self._db_dir, os.R_OK | os.W_OK):
+ # if we can write, then read AND write a JSON file.
+ self._read_from_file(self._old_yaml_index_path, format='yaml')
+ with WriteTransaction(self.lock, timeout=_db_lock_timeout):
+ self._write(None, None, None)
+ else:
+ # Read chck for a YAML file if we can't find JSON.
+ self._read_from_file(self._old_yaml_index_path, format='yaml')
else:
# The file doesn't exist, try to traverse the directory.