summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/__init__.py6
-rw-r--r--lib/spack/spack/cmd/__init__.py2
-rw-r--r--lib/spack/spack/cmd/deactivate.py2
-rw-r--r--lib/spack/spack/cmd/extensions.py2
-rw-r--r--lib/spack/spack/cmd/find.py4
-rw-r--r--lib/spack/spack/cmd/module.py4
-rw-r--r--lib/spack/spack/cmd/uninstall.py2
-rw-r--r--lib/spack/spack/database.py140
-rw-r--r--lib/spack/spack/directory_layout.py6
-rw-r--r--lib/spack/spack/package.py6
-rw-r--r--lib/spack/spack/packages.py42
11 files changed, 121 insertions, 95 deletions
diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py
index caa09eb6e0..4d10bc2da6 100644
--- a/lib/spack/spack/__init__.py
+++ b/lib/spack/spack/__init__.py
@@ -54,6 +54,12 @@ packages_path = join_path(var_path, "packages")
db = PackageDB(packages_path)
#
+# Set up the installed packages database
+#
+from spack.database import Database
+installed_db = Database(install_path)
+
+#
# Paths to mock files for testing.
#
mock_packages_path = join_path(var_path, "mock_packages")
diff --git a/lib/spack/spack/cmd/__init__.py b/lib/spack/spack/cmd/__init__.py
index b96ac5af51..fd3ef3ed27 100644
--- a/lib/spack/spack/cmd/__init__.py
+++ b/lib/spack/spack/cmd/__init__.py
@@ -124,7 +124,7 @@ def elide_list(line_list, max_num=10):
def disambiguate_spec(spec):
- matching_specs = spack.db.get_installed(spec)
+ matching_specs = spack.installed_db.get_installed(spec)
if not matching_specs:
tty.die("Spec '%s' matches no installed packages." % spec)
diff --git a/lib/spack/spack/cmd/deactivate.py b/lib/spack/spack/cmd/deactivate.py
index e44be41029..1f0e303cdf 100644
--- a/lib/spack/spack/cmd/deactivate.py
+++ b/lib/spack/spack/cmd/deactivate.py
@@ -54,7 +54,7 @@ def deactivate(parser, args):
if args.all:
if pkg.extendable:
tty.msg("Deactivating all extensions of %s" % pkg.spec.short_spec)
- ext_pkgs = spack.db.installed_extensions_for(spec)
+ ext_pkgs = spack.installed_db.installed_extensions_for(spec)
for ext_pkg in ext_pkgs:
ext_pkg.spec.normalize()
diff --git a/lib/spack/spack/cmd/extensions.py b/lib/spack/spack/cmd/extensions.py
index fc8e6842c3..e919b1c4fb 100644
--- a/lib/spack/spack/cmd/extensions.py
+++ b/lib/spack/spack/cmd/extensions.py
@@ -80,7 +80,7 @@ def extensions(parser, args):
colify(ext.name for ext in extensions)
# List specs of installed extensions.
- installed = [s.spec for s in spack.db.installed_extensions_for(spec)]
+ installed = [s.spec for s in spack.installed_db.installed_extensions_for(spec)]
print
if not installed:
tty.msg("None installed.")
diff --git a/lib/spack/spack/cmd/find.py b/lib/spack/spack/cmd/find.py
index 3c993990b1..6f9072e311 100644
--- a/lib/spack/spack/cmd/find.py
+++ b/lib/spack/spack/cmd/find.py
@@ -138,9 +138,9 @@ def find(parser, args):
# Get all the specs the user asked for
if not query_specs:
- specs = set(spack.db.installed_package_specs())
+ specs = set(spack.installed_db.installed_package_specs())
else:
- results = [set(spack.db.get_installed(qs)) for qs in query_specs]
+ results = [set(spack.installed_db.get_installed(qs)) for qs in query_specs]
specs = set.union(*results)
if not args.mode:
diff --git a/lib/spack/spack/cmd/module.py b/lib/spack/spack/cmd/module.py
index 34f0855a50..215d877bd0 100644
--- a/lib/spack/spack/cmd/module.py
+++ b/lib/spack/spack/cmd/module.py
@@ -65,7 +65,7 @@ def module_find(mtype, spec_array):
tty.die("You can only pass one spec.")
spec = specs[0]
- specs = [s for s in spack.db.installed_package_specs() if s.satisfies(spec)]
+ specs = [s for s in spack.installed_db.installed_package_specs() if s.satisfies(spec)]
if len(specs) == 0:
tty.die("No installed packages match spec %s" % spec)
@@ -86,7 +86,7 @@ def module_find(mtype, spec_array):
def module_refresh():
"""Regenerate all module files for installed packages known to
spack (some packages may no longer exist)."""
- specs = [s for s in spack.db.installed_known_package_specs()]
+ specs = [s for s in spack.installed_db.installed_known_package_specs()]
for name, cls in module_types.items():
tty.msg("Regenerating %s module files." % name)
diff --git a/lib/spack/spack/cmd/uninstall.py b/lib/spack/spack/cmd/uninstall.py
index aa62510fed..4870712eb6 100644
--- a/lib/spack/spack/cmd/uninstall.py
+++ b/lib/spack/spack/cmd/uninstall.py
@@ -59,7 +59,7 @@ def uninstall(parser, args):
# Fail and ask user to be unambiguous if it doesn't
pkgs = []
for spec in specs:
- matching_specs = spack.db.get_installed(spec)
+ matching_specs = spack.installed_db.get_installed(spec)
if not args.all and len(matching_specs) > 1:
tty.error("%s matches multiple packages:" % spec)
print
diff --git a/lib/spack/spack/database.py b/lib/spack/spack/database.py
index 4646530d52..d027db312f 100644
--- a/lib/spack/spack/database.py
+++ b/lib/spack/spack/database.py
@@ -28,6 +28,9 @@ import inspect
import glob
import imp
+import time
+import copy
+
from external import yaml
from external.yaml.error import MarkedYAMLError
@@ -43,8 +46,18 @@ from spack.virtual import ProviderIndex
from spack.util.naming import mod_to_class, validate_module_name
+def _autospec(function):
+ """Decorator that automatically converts the argument of a single-arg
+ function to a Spec."""
+ def converter(self, spec_like, **kwargs):
+ if not isinstance(spec_like, spack.spec.Spec):
+ spec_like = spack.spec.Spec(spec_like)
+ return function(self, spec_like, **kwargs)
+ return converter
+
+
class Database(object):
- def __init__(self,file_name="specDB.yaml"):
+ def __init__(self,root,file_name="specDB.yaml"):
"""
Create an empty Database
Location defaults to root/specDB.yaml
@@ -53,13 +66,16 @@ class Database(object):
path: the path to the install of that package
dep_hash: a hash of the dependence DAG for that package
"""
+ self.root = root
self.file_name = file_name
+ self.file_path = join_path(self.root,self.file_name)
self.data = []
+ self.last_write_time = 0
def from_yaml(self,stream):
"""
- Fill database from YAML
+ Fill database from YAML, do not maintain old data
Translate the spec portions from node-dict form to spec from
"""
try:
@@ -70,6 +86,7 @@ class Database(object):
if file==None:
return
+ self.data = []
for sp in file['database']:
spec = Spec.from_node_dict(sp['spec'])
path = sp['path']
@@ -78,19 +95,14 @@ class Database(object):
self.data.append(db_entry)
- @staticmethod
- def read_database(root):
- """Create a Database from the data in the standard location"""
- database = Database()
- full_path = join_path(root,database.file_name)
- if os.path.isfile(full_path):
- with open(full_path,'r') as f:
- database.from_yaml(f)
+ def read_database(self):
+ """Reread Database from the data in the set location"""
+ if os.path.isfile(self.file_path):
+ with open(self.file_path,'r') as f:
+ self.from_yaml(f)
else:
- with open(full_path,'w+') as f:
- database.from_yaml(f)
-
- return database
+ #The file doesn't exist, construct empty data.
+ self.data = []
def write_database_to_yaml(self,stream):
@@ -110,48 +122,104 @@ class Database(object):
stream=stream, default_flow_style=False)
- def write(self,root):
+ def write(self):
"""Write the database to the standard location"""
- full_path = join_path(root,self.file_name)
- #test for file existence
- with open(full_path,'w') as f:
+ #creates file if necessary
+ with open(self.file_path,'w') as f:
+ self.last_write_time = int(time.time())
self.write_database_to_yaml(f)
- @staticmethod
- def add(root, spec, path):
- """Read the database from the standard location
+ def is_dirty(self):
+ """
+ Returns true iff the database file exists
+ and was most recently written to by another spack instance.
+ """
+ return (os.path.isfile(self.file_path) and (os.path.getmtime(self.file_path) > self.last_write_time))
+
+
+# @_autospec
+ def add(self, spec, path):
+ """Re-read the database from the set location if data is dirty
Add the specified entry as a dict
Write the database back to memory
-
- TODO: Caching databases
"""
- database = Database.read_database(root)
+ if self.is_dirty():
+ self.read_database()
sph = {}
sph['spec']=spec
sph['path']=path
sph['hash']=spec.dag_hash()
- database.data.append(sph)
+ self.data.append(sph)
- database.write(root)
+ self.write()
- @staticmethod
- def remove(root, spec):
+ @_autospec
+ def remove(self, spec):
"""
- Reads the database from the standard location
+ Re-reads the database from the set location if data is dirty
Searches for and removes the specified spec
Writes the database back to memory
+ """
+ if self.is_dirty():
+ self.read_database()
+
+ for sp in self.data:
+
+ if sp['hash'] == spec.dag_hash() and sp['spec'] == Spec.from_node_dict(spec.to_node_dict()):
+ self.data.remove(sp)
+
+ self.write()
- TODO: Caching databases
+
+ @_autospec
+ def get_installed(self, spec):
+ """
+ Get all the installed specs that satisfy the provided spec constraint
+ """
+ return [s for s in self.installed_package_specs() if s.satisfies(spec)]
+
+
+ @_autospec
+ def installed_extensions_for(self, extendee_spec):
+ """
+ Return the specs of all packages that extend
+ the given spec
+ """
+ for s in self.installed_package_specs():
+ try:
+ if s.package.extends(extendee_spec):
+ yield s.package
+ except UnknownPackageError, e:
+ continue
+ #skips unknown packages
+ #TODO: conditional way to do this instead of catching exceptions
+
+
+ def installed_package_specs(self):
"""
- database = Database.read_database(root)
+ Read installed package names from the database
+ and return their specs
+ """
+ if self.is_dirty():
+ self.read_database()
+
+ installed = []
+ for sph in self.data:
+ sph['spec'].normalize()
+ sph['spec'].concretize()
+ installed.append(sph['spec'])
+ return installed
- for sp in database.data:
- #This requires specs w/o dependencies, is that sustainable?
- if sp['spec'] == spec:
- database.data.remove(sp)
- database.write(root)
+ def installed_known_package_specs(self):
+ """
+ Read installed package names from the database.
+ Return only the specs for which the package is known
+ to this version of spack
+ """
+ return [s for s in self.installed_package_specs() if spack.db.exists(s.name)]
+
diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py
index 0bbe6c9d85..6dc1b0e550 100644
--- a/lib/spack/spack/directory_layout.py
+++ b/lib/spack/spack/directory_layout.py
@@ -153,7 +153,6 @@ class DirectoryLayout(object):
os.rmdir(path)
path = os.path.dirname(path)
- Database.remove(self.root,spec)
class YamlDirectoryLayout(DirectoryLayout):
@@ -266,11 +265,6 @@ class YamlDirectoryLayout(DirectoryLayout):
self.write_spec(spec, spec_file_path)
- def add_to_database(self, spec):
- """Simply adds a spec to the database"""
- Database.add(self.root, spec, self.path_for_spec(spec))
-
-
@memoized
def all_specs(self):
if not os.path.isdir(self.root):
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index a425c555a2..acf558d639 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -565,7 +565,7 @@ class Package(object):
"""Return a list of the specs of all installed packages that depend
on this one."""
dependents = []
- for spec in spack.db.installed_package_specs():
+ for spec in spack.installed_db.installed_package_specs():
if self.name == spec.name:
continue
for dep in spec.traverse():
@@ -601,7 +601,7 @@ class Package(object):
def remove_prefix(self):
"""Removes the prefix for a package along with any empty parent directories."""
spack.install_layout.remove_install_directory(self.spec)
-
+ spack.installed_db.remove(self.spec)
def do_fetch(self):
"""Creates a stage directory and downloads the taball for this package.
@@ -818,7 +818,7 @@ class Package(object):
install(log_path, log_install_path)
#Update the database once we know install successful
- spack.install_layout.add_to_database(self.spec)
+ spack.installed_db.add(self.spec, spack.install_layout.path_for_spec(self.spec))
# On successful install, remove the stage.
if not keep_stage:
diff --git a/lib/spack/spack/packages.py b/lib/spack/spack/packages.py
index adfbc26c1d..2e3e95ca40 100644
--- a/lib/spack/spack/packages.py
+++ b/lib/spack/spack/packages.py
@@ -96,12 +96,6 @@ class PackageDB(object):
@_autospec
- def get_installed(self, spec):
- """Get all the installed specs that satisfy the provided spec constraint."""
- return [s for s in self.installed_package_specs() if s.satisfies(spec)]
-
-
- @_autospec
def providers_for(self, vpkg_spec):
if self.provider_index is None:
self.provider_index = ProviderIndex(self.all_package_names())
@@ -117,19 +111,6 @@ class PackageDB(object):
return [p for p in self.all_packages() if p.extends(extendee_spec)]
- @_autospec
- def installed_extensions_for(self, extendee_spec):
- for s in self.installed_package_specs():
- try:
- if s.package.extends(extendee_spec):
- yield s.package
- except UnknownPackageError, e:
- # Skip packages we know nothing about
- continue
- # TODO: add some conditional way to do this instead of
- # catching exceptions.
-
-
def dirname_for_package_name(self, pkg_name):
"""Get the directory name for a particular package. This is the
directory that contains its package.py file."""
@@ -150,29 +131,6 @@ class PackageDB(object):
return join_path(pkg_dir, _package_file_name)
- def installed_package_specs(self):
- """Read installed package names straight from the install directory
- layout.
- """
- # Get specs from the directory layout but ensure that they're
- # all normalized properly.
- installed = []
- for spec in spack.install_layout.all_specs():
- spec.normalize()
- installed.append(spec)
- return installed
-
-
- def installed_known_package_specs(self):
- """Read installed package names straight from the install
- directory layout, but return only specs for which the
- package is known to this version of spack.
- """
- for spec in spack.install_layout.all_specs():
- if self.exists(spec.name):
- yield spec
-
-
@memoized
def all_package_names(self):
"""Generator function for all packages. This looks for