summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2014-08-04 07:40:53 -0700
committerTodd Gamblin <tgamblin@llnl.gov>2014-08-04 07:40:53 -0700
commitd0b179962b87470edbaf4e05e41e748bebe27a3d (patch)
treeaf791e5941d66780107f1c8543787bbdab8b9847
parent2f21ca64e079e2d52bb50d84797554ae4ec969d9 (diff)
downloadspack-d0b179962b87470edbaf4e05e41e748bebe27a3d.tar.gz
spack-d0b179962b87470edbaf4e05e41e748bebe27a3d.tar.bz2
spack-d0b179962b87470edbaf4e05e41e748bebe27a3d.tar.xz
spack-d0b179962b87470edbaf4e05e41e748bebe27a3d.zip
find and uninstall work when installed package is no longer in spack.
- Make switching between git branches easier. - Make future removal of packages easier.
-rw-r--r--lib/spack/spack/cmd/uninstall.py11
-rw-r--r--lib/spack/spack/directory_layout.py3
-rw-r--r--lib/spack/spack/package.py9
-rw-r--r--lib/spack/spack/spec.py45
4 files changed, 54 insertions, 14 deletions
diff --git a/lib/spack/spack/cmd/uninstall.py b/lib/spack/spack/cmd/uninstall.py
index 28a2f659ae..73c98a203b 100644
--- a/lib/spack/spack/cmd/uninstall.py
+++ b/lib/spack/spack/cmd/uninstall.py
@@ -28,6 +28,7 @@ import llnl.util.tty as tty
import spack
import spack.cmd
+import spack.packages
description="Remove an installed package"
@@ -68,7 +69,15 @@ def uninstall(parser, args):
if len(matching_specs) == 0:
tty.die("%s does not match any installed packages." % spec)
- pkgs.extend(spack.db.get(s) for s in matching_specs)
+ for s in matching_specs:
+ try:
+ # should work if package is known to spack
+ pkgs.append(spack.db.get(s))
+
+ except spack.packages.UnknownPackageError, e:
+ # The package.py file has gone away -- but still want to uninstall.
+ spack.Package(s).do_uninstall(force=True)
+
# Sort packages to be uninstalled by the number of installed dependents
# This ensures we do things in the right order
diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py
index 28719521d2..4fc00d536e 100644
--- a/lib/spack/spack/directory_layout.py
+++ b/lib/spack/spack/directory_layout.py
@@ -155,7 +155,8 @@ class SpecHashDirectoryLayout(DirectoryLayout):
"""Read the contents of a file and parse them as a spec"""
with closing(open(path)) as spec_file:
string = spec_file.read().replace('\n', '')
- return Spec(string)
+ # Specs from files are assumed normal and concrete
+ return Spec(string, concrete=True)
def make_path_for_spec(self, spec):
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 90e77b5e82..8df658e660 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -322,9 +322,6 @@ class Package(object):
def __init__(self, spec):
- # These attributes are required for all packages.
- attr_required(self.__class__, 'homepage')
-
# this determines how the package should be built.
self.spec = spec
@@ -396,9 +393,13 @@ class Package(object):
raise ValueError("Can only get a stage for a concrete package.")
if self._stage is None:
+ if not self.url:
+ raise PackageVersionError(self.version)
+
# TODO: move this logic into a mirror module.
mirror_path = "%s/%s" % (self.name, "%s-%s.%s" % (
self.name, self.version, extension(self.url)))
+
self._stage = Stage(
self.url, mirror_path=mirror_path, name=self.spec.short_spec)
return self._stage
@@ -531,8 +532,6 @@ class Package(object):
break
if self.versions[v].url:
url = self.versions[v].url
- if not url:
- raise PackageVersionError(v)
return url
if version in self.versions:
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index 1e2da10dcc..45c3402617 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -345,6 +345,17 @@ class Spec(object):
self.compiler = other.compiler
self.dependencies = other.dependencies
+ # Specs are by default not assumed to be normal, but in some
+ # cases we've read them from a file want to assume normal.
+ # This allows us to manipulate specs that Spack doesn't have
+ # package.py files for.
+ self._normal = kwargs.get('normal', False)
+ self._concrete = kwargs.get('concrete', False)
+
+ # Specs cannot be concrete and non-normal.
+ if self._concrete:
+ self._normal = True
+
# This allows users to construct a spec DAG with literals.
# Note that given two specs a and b, Spec(a) copies a, but
# Spec(a, b) will copy a but just add b as a dep.
@@ -432,11 +443,15 @@ class Spec(object):
If any of the name, version, architecture, compiler, or depdenencies
are ambiguous,then it is not concrete.
"""
- return bool(not self.virtual
- and self.versions.concrete
- and self.architecture
- and self.compiler and self.compiler.concrete
- and self.dependencies.concrete)
+ if self._concrete:
+ return True
+
+ self._concrete = bool(not self.virtual
+ and self.versions.concrete
+ and self.architecture
+ and self.compiler and self.compiler.concrete
+ and self.dependencies.concrete)
+ return self._concrete
def preorder_traversal(self, visited=None, d=0, **kwargs):
@@ -606,7 +621,7 @@ class Spec(object):
# If there are duplicate providers or duplicate provider deps, this
# consolidates them and merges constraints.
- self.normalize()
+ self.normalize(force=True)
def concretize(self):
@@ -621,9 +636,13 @@ class Spec(object):
with requirements of its pacakges. See flatten() and normalize() for
more details on this.
"""
+ if self._concrete:
+ return
+
self.normalize()
self._expand_virtual_packages()
self._concretize_helper()
+ self._concrete = True
def concretized(self):
@@ -754,7 +773,7 @@ class Spec(object):
dependency._normalize_helper(visited, spec_deps, provider_index)
- def normalize(self):
+ def normalize(self, **kwargs):
"""When specs are parsed, any dependencies specified are hanging off
the root, and ONLY the ones that were explicitly provided are there.
Normalization turns a partial flat spec into a DAG, where:
@@ -772,6 +791,9 @@ class Spec(object):
TODO: normalize should probably implement some form of cycle detection,
to ensure that the spec is actually a DAG.
"""
+ if self._normal and not kwargs.get('force', False):
+ return
+
# Ensure first that all packages & compilers in the DAG exist.
self.validate_names()
@@ -805,6 +827,9 @@ class Spec(object):
raise InvalidDependencyException(
self.name + " does not depend on " + comma_or(extra))
+ # Mark the spec as normal once done.
+ self._normal = True
+
def normalized(self):
"""Return a normalized copy of this spec without modifying this spec."""
@@ -1009,6 +1034,9 @@ class Spec(object):
else:
self.dependencies = DependencyMap()
+ self._normal = other._normal
+ self._concrete = other._concrete
+
def copy(self, **kwargs):
"""Return a copy of this spec.
@@ -1261,6 +1289,9 @@ class SpecParser(spack.parse.Parser):
spec.dependents = DependencyMap()
spec.dependencies = DependencyMap()
+ spec._normal = False
+ spec._concrete = False
+
# record this so that we know whether version is
# unspecified or not.
added_version = False