From ec108dbebdbb00ab517dd8f84dbb976d6078a93a Mon Sep 17 00:00:00 2001 From: Greg Becker Date: Fri, 26 Jun 2020 09:57:22 -0500 Subject: Allow `spack remove -f` and `spack uninstall` to work on matrices (#17222) * Allow `spack remove -f` and `spack uninstall` to work on matrices Allow Environment.remove(force=True) to remove the concrete spec from the environment even when the user spec cannot be removed because it is in a matrix. --- lib/spack/spack/environment.py | 20 +++++++++++++----- lib/spack/spack/test/cmd/env.py | 45 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/lib/spack/spack/environment.py b/lib/spack/spack/environment.py index 8eab70b544..5ec683f6b2 100644 --- a/lib/spack/spack/environment.py +++ b/lib/spack/spack/environment.py @@ -892,13 +892,23 @@ class Environment(object): old_specs = set(self.user_specs) for spec in matches: if spec in list_to_change: - list_to_change.remove(spec) - - self.update_stale_references(list_name) + try: + list_to_change.remove(spec) + self.update_stale_references(list_name) + new_specs = set(self.user_specs) + except spack.spec_list.SpecListError: + # define new specs list + new_specs = set(self.user_specs) + msg = "Spec '%s' is part of a spec matrix and " % spec + msg += "cannot be removed from list '%s'." % list_to_change + if force: + msg += " It will be removed from the concrete specs." + # Mock new specs so we can remove this spec from + # concrete spec lists + new_specs.remove(spec) + tty.warn(msg) # If force, update stale concretized specs - # Only check specs removed by this operation - new_specs = set(self.user_specs) for spec in old_specs - new_specs: if force and spec in self.concretized_user_specs: i = self.concretized_user_specs.index(spec) diff --git a/lib/spack/spack/test/cmd/env.py b/lib/spack/spack/test/cmd/env.py index 46977d8eec..b1cc9ce8b5 100644 --- a/lib/spack/spack/test/cmd/env.py +++ b/lib/spack/spack/test/cmd/env.py @@ -19,7 +19,6 @@ from spack.spec import Spec from spack.main import SpackCommand from spack.stage import stage_prefix -from spack.spec_list import SpecListError from spack.util.mock_package import MockPackageMultiRepo import spack.util.spack_json as sjson from spack.util.path import substitute_path_variables @@ -1234,7 +1233,7 @@ env: assert Spec('callpath ^mpich') in test.user_specs -def test_stack_yaml_attempt_remove_from_matrix(tmpdir): +def test_stack_yaml_remove_from_matrix_no_effect(tmpdir): filename = str(tmpdir.join('spack.yaml')) with open(filename, 'w') as f: f.write("""\ @@ -1249,9 +1248,45 @@ env: """) with tmpdir.as_cwd(): env('create', 'test', './spack.yaml') - with pytest.raises(SpecListError): - with ev.read('test'): - remove('-l', 'packages', 'mpileaks') + with ev.read('test') as e: + before = e.user_specs.specs + remove('-l', 'packages', 'mpileaks') + after = e.user_specs.specs + + assert before == after + + +def test_stack_yaml_force_remove_from_matrix(tmpdir): + filename = str(tmpdir.join('spack.yaml')) + with open(filename, 'w') as f: + f.write("""\ +env: + definitions: + - packages: + - matrix: + - [mpileaks, callpath] + - [target=be] + specs: + - $packages +""") + with tmpdir.as_cwd(): + env('create', 'test', './spack.yaml') + with ev.read('test') as e: + concretize() + + before_user = e.user_specs.specs + before_conc = e.concretized_user_specs + + remove('-f', '-l', 'packages', 'mpileaks') + + after_user = e.user_specs.specs + after_conc = e.concretized_user_specs + + assert before_user == after_user + + mpileaks_spec = Spec('mpileaks target=be') + assert mpileaks_spec in before_conc + assert mpileaks_spec not in after_conc def test_stack_concretize_extraneous_deps(tmpdir, config, mock_packages): -- cgit v1.2.3-60-g2f50