From d0b179962b87470edbaf4e05e41e748bebe27a3d Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Mon, 4 Aug 2014 07:40:53 -0700 Subject: find and uninstall work when installed package is no longer in spack. - Make switching between git branches easier. - Make future removal of packages easier. --- lib/spack/spack/cmd/uninstall.py | 11 ++++++++- lib/spack/spack/directory_layout.py | 3 ++- lib/spack/spack/package.py | 9 ++++---- lib/spack/spack/spec.py | 45 +++++++++++++++++++++++++++++++------ 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 -- cgit v1.2.3-60-g2f50