find and uninstall work when installed package is no longer in spack.

- Make switching between git branches easier.
- Make future removal of packages easier.
This commit is contained in:
Todd Gamblin 2014-08-04 07:40:53 -07:00
parent 2f21ca64e0
commit d0b179962b
4 changed files with 54 additions and 14 deletions

View File

@ -28,6 +28,7 @@
import spack import spack
import spack.cmd import spack.cmd
import spack.packages
description="Remove an installed package" description="Remove an installed package"
@ -68,7 +69,15 @@ def uninstall(parser, args):
if len(matching_specs) == 0: if len(matching_specs) == 0:
tty.die("%s does not match any installed packages." % spec) 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 # Sort packages to be uninstalled by the number of installed dependents
# This ensures we do things in the right order # This ensures we do things in the right order

View File

@ -155,7 +155,8 @@ def read_spec(self, path):
"""Read the contents of a file and parse them as a spec""" """Read the contents of a file and parse them as a spec"""
with closing(open(path)) as spec_file: with closing(open(path)) as spec_file:
string = spec_file.read().replace('\n', '') 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): def make_path_for_spec(self, spec):

View File

@ -322,9 +322,6 @@ class SomePackage(Package):
def __init__(self, spec): def __init__(self, spec):
# These attributes are required for all packages.
attr_required(self.__class__, 'homepage')
# this determines how the package should be built. # this determines how the package should be built.
self.spec = spec self.spec = spec
@ -396,9 +393,13 @@ def stage(self):
raise ValueError("Can only get a stage for a concrete package.") raise ValueError("Can only get a stage for a concrete package.")
if self._stage is None: if self._stage is None:
if not self.url:
raise PackageVersionError(self.version)
# TODO: move this logic into a mirror module. # TODO: move this logic into a mirror module.
mirror_path = "%s/%s" % (self.name, "%s-%s.%s" % ( mirror_path = "%s/%s" % (self.name, "%s-%s.%s" % (
self.name, self.version, extension(self.url))) self.name, self.version, extension(self.url)))
self._stage = Stage( self._stage = Stage(
self.url, mirror_path=mirror_path, name=self.spec.short_spec) self.url, mirror_path=mirror_path, name=self.spec.short_spec)
return self._stage return self._stage
@ -531,8 +532,6 @@ def nearest_url(version):
break break
if self.versions[v].url: if self.versions[v].url:
url = self.versions[v].url url = self.versions[v].url
if not url:
raise PackageVersionError(v)
return url return url
if version in self.versions: if version in self.versions:

View File

@ -345,6 +345,17 @@ def __init__(self, spec_like, *dep_like, **kwargs):
self.compiler = other.compiler self.compiler = other.compiler
self.dependencies = other.dependencies 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. # This allows users to construct a spec DAG with literals.
# Note that given two specs a and b, Spec(a) copies a, but # 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. # Spec(a, b) will copy a but just add b as a dep.
@ -432,11 +443,15 @@ def concrete(self):
If any of the name, version, architecture, compiler, or depdenencies If any of the name, version, architecture, compiler, or depdenencies
are ambiguous,then it is not concrete. are ambiguous,then it is not concrete.
""" """
return bool(not self.virtual if self._concrete:
and self.versions.concrete return True
and self.architecture
and self.compiler and self.compiler.concrete self._concrete = bool(not self.virtual
and self.dependencies.concrete) 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): def preorder_traversal(self, visited=None, d=0, **kwargs):
@ -606,7 +621,7 @@ def _expand_virtual_packages(self):
# If there are duplicate providers or duplicate provider deps, this # If there are duplicate providers or duplicate provider deps, this
# consolidates them and merges constraints. # consolidates them and merges constraints.
self.normalize() self.normalize(force=True)
def concretize(self): def concretize(self):
@ -621,9 +636,13 @@ def concretize(self):
with requirements of its pacakges. See flatten() and normalize() for with requirements of its pacakges. See flatten() and normalize() for
more details on this. more details on this.
""" """
if self._concrete:
return
self.normalize() self.normalize()
self._expand_virtual_packages() self._expand_virtual_packages()
self._concretize_helper() self._concretize_helper()
self._concrete = True
def concretized(self): def concretized(self):
@ -754,7 +773,7 @@ def _normalize_helper(self, visited, spec_deps, provider_index):
dependency._normalize_helper(visited, spec_deps, provider_index) 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 """When specs are parsed, any dependencies specified are hanging off
the root, and ONLY the ones that were explicitly provided are there. the root, and ONLY the ones that were explicitly provided are there.
Normalization turns a partial flat spec into a DAG, where: Normalization turns a partial flat spec into a DAG, where:
@ -772,6 +791,9 @@ def normalize(self):
TODO: normalize should probably implement some form of cycle detection, TODO: normalize should probably implement some form of cycle detection,
to ensure that the spec is actually a DAG. 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. # Ensure first that all packages & compilers in the DAG exist.
self.validate_names() self.validate_names()
@ -805,6 +827,9 @@ def normalize(self):
raise InvalidDependencyException( raise InvalidDependencyException(
self.name + " does not depend on " + comma_or(extra)) self.name + " does not depend on " + comma_or(extra))
# Mark the spec as normal once done.
self._normal = True
def normalized(self): def normalized(self):
"""Return a normalized copy of this spec without modifying this spec.""" """Return a normalized copy of this spec without modifying this spec."""
@ -1009,6 +1034,9 @@ def _dup(self, other, **kwargs):
else: else:
self.dependencies = DependencyMap() self.dependencies = DependencyMap()
self._normal = other._normal
self._concrete = other._concrete
def copy(self, **kwargs): def copy(self, **kwargs):
"""Return a copy of this spec. """Return a copy of this spec.
@ -1261,6 +1289,9 @@ def spec(self):
spec.dependents = DependencyMap() spec.dependents = DependencyMap()
spec.dependencies = DependencyMap() spec.dependencies = DependencyMap()
spec._normal = False
spec._concrete = False
# record this so that we know whether version is # record this so that we know whether version is
# unspecified or not. # unspecified or not.
added_version = False added_version = False