summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorPeter Scheibel <scheibel1@llnl.gov>2024-03-29 09:36:31 -0700
committerGitHub <noreply@github.com>2024-03-29 09:36:31 -0700
commit179e4f3ad138c664dd0387e2998d87895e744518 (patch)
tree263146c8061153a4f9b954ac6f568403323f3407 /lib
parente97787691b78f5745318b571be7ce0296af1a698 (diff)
downloadspack-179e4f3ad138c664dd0387e2998d87895e744518.tar.gz
spack-179e4f3ad138c664dd0387e2998d87895e744518.tar.bz2
spack-179e4f3ad138c664dd0387e2998d87895e744518.tar.xz
spack-179e4f3ad138c664dd0387e2998d87895e744518.zip
Don't delete "spack develop" build artifacts after install (#43424)
After #41373, where we stopped considering the source directory to be the stage for develop builds, we resumed *deleting* the stage even after a successful build. We don't want this for develop builds because developers need to iterate; we should keep the artifacts unless they explicitly run `spack clean`. Now: - [x] Build artifacts for develop packages are not removed after a successful install - [x] They are also not removed before an install starts, i.e. develop packages always reuse prior artifacts, if available. - [x] They can be deleted in any other context, e.g. by running `spack clean --stage`
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/installer.py10
-rw-r--r--lib/spack/spack/spec.py7
-rw-r--r--lib/spack/spack/stage.py4
-rw-r--r--lib/spack/spack/test/cmd/dev_build.py10
-rw-r--r--lib/spack/spack/test/cmd/env.py35
-rw-r--r--lib/spack/spack/test/stage.py4
6 files changed, 60 insertions, 10 deletions
diff --git a/lib/spack/spack/installer.py b/lib/spack/spack/installer.py
index 915327e3b4..7cfcd35150 100644
--- a/lib/spack/spack/installer.py
+++ b/lib/spack/spack/installer.py
@@ -2274,11 +2274,15 @@ class BuildProcessInstaller:
# whether to install source code with the packag
self.install_source = install_args.get("install_source", False)
+ is_develop = pkg.spec.is_develop
# whether to keep the build stage after installation
- self.keep_stage = install_args.get("keep_stage", False)
-
+ # Note: user commands do not have an explicit choice to disable
+ # keeping stages (i.e., we have a --keep-stage option, but not
+ # a --destroy-stage option), so we can override a default choice
+ # to destroy
+ self.keep_stage = is_develop or install_args.get("keep_stage", False)
# whether to restage
- self.restage = install_args.get("restage", False)
+ self.restage = (not is_develop) and install_args.get("restage", False)
# whether to skip the patch phase
self.skip_patch = install_args.get("skip_patch", False)
diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py
index 53d2dc6b05..ff3248321a 100644
--- a/lib/spack/spack/spec.py
+++ b/lib/spack/spack/spec.py
@@ -1408,6 +1408,13 @@ class Spec:
def external(self):
return bool(self.external_path) or bool(self.external_modules)
+ @property
+ def is_develop(self):
+ """Return whether the Spec represents a user-developed package
+ in a Spack ``Environment`` (i.e. using `spack develop`).
+ """
+ return bool(self.variants.get("dev_path", False))
+
def clear_dependencies(self):
"""Trim the dependencies of this spec."""
self._dependencies.clear()
diff --git a/lib/spack/spack/stage.py b/lib/spack/spack/stage.py
index 46848c0016..67a81a68ee 100644
--- a/lib/spack/spack/stage.py
+++ b/lib/spack/spack/stage.py
@@ -927,6 +927,10 @@ class DevelopStage(LockableStagingDir):
shutil.rmtree(self.path)
except FileNotFoundError:
pass
+ try:
+ os.remove(self.reference_link)
+ except FileNotFoundError:
+ pass
self.created = False
def restage(self):
diff --git a/lib/spack/spack/test/cmd/dev_build.py b/lib/spack/spack/test/cmd/dev_build.py
index 3e6631cf51..88b85dc1dd 100644
--- a/lib/spack/spack/test/cmd/dev_build.py
+++ b/lib/spack/spack/test/cmd/dev_build.py
@@ -20,7 +20,10 @@ dev_build = SpackCommand("dev-build")
install = SpackCommand("install")
env = SpackCommand("env")
-pytestmark = pytest.mark.not_on_windows("does not run on windows")
+pytestmark = [
+ pytest.mark.not_on_windows("does not run on windows"),
+ pytest.mark.disable_clean_stage_check,
+]
def test_dev_build_basics(tmpdir, install_mockery):
@@ -41,7 +44,6 @@ def test_dev_build_basics(tmpdir, install_mockery):
assert os.path.exists(str(tmpdir))
-@pytest.mark.disable_clean_stage_check
def test_dev_build_before(tmpdir, install_mockery):
spec = spack.spec.Spec(f"dev-build-test-install@0.0.0 dev_path={tmpdir}").concretized()
@@ -58,7 +60,6 @@ def test_dev_build_before(tmpdir, install_mockery):
assert not os.path.exists(spec.prefix)
-@pytest.mark.disable_clean_stage_check
def test_dev_build_until(tmpdir, install_mockery):
spec = spack.spec.Spec(f"dev-build-test-install@0.0.0 dev_path={tmpdir}").concretized()
@@ -76,7 +77,6 @@ def test_dev_build_until(tmpdir, install_mockery):
assert not spack.store.STORE.db.query(spec, installed=True)
-@pytest.mark.disable_clean_stage_check
def test_dev_build_until_last_phase(tmpdir, install_mockery):
# Test that we ignore the last_phase argument if it is already last
spec = spack.spec.Spec(f"dev-build-test-install@0.0.0 dev_path={tmpdir}").concretized()
@@ -96,7 +96,6 @@ def test_dev_build_until_last_phase(tmpdir, install_mockery):
assert os.path.exists(str(tmpdir))
-@pytest.mark.disable_clean_stage_check
def test_dev_build_before_until(tmpdir, install_mockery, capsys):
spec = spack.spec.Spec(f"dev-build-test-install@0.0.0 dev_path={tmpdir}").concretized()
@@ -134,7 +133,6 @@ def mock_module_noop(*args):
pass
-@pytest.mark.disable_clean_stage_check
def test_dev_build_drop_in(tmpdir, mock_packages, monkeypatch, install_mockery, working_env):
monkeypatch.setattr(os, "execvp", print_spack_cc)
monkeypatch.setattr(spack.build_environment, "module", mock_module_noop)
diff --git a/lib/spack/spack/test/cmd/env.py b/lib/spack/spack/test/cmd/env.py
index 0cf09421c0..5c23c61b61 100644
--- a/lib/spack/spack/test/cmd/env.py
+++ b/lib/spack/spack/test/cmd/env.py
@@ -3161,6 +3161,41 @@ spack:
assert spec.prefix in contents
+@pytest.mark.disable_clean_stage_check
+def test_install_develop_keep_stage(
+ environment_from_manifest, install_mockery, mock_fetch, monkeypatch, tmpdir
+):
+ """Develop a dependency of a package and make sure that the associated
+ stage for the package is retained after a successful install.
+ """
+ environment_from_manifest(
+ """
+spack:
+ specs:
+ - mpileaks
+"""
+ )
+
+ monkeypatch.setattr(spack.stage.DevelopStage, "destroy", _always_fail)
+
+ with ev.read("test") as e:
+ libelf_dev_path = tmpdir.ensure("libelf-test-dev-path", dir=True)
+ develop(f"--path={libelf_dev_path}", "libelf@0.8.13")
+ concretize()
+ (libelf_spec,) = e.all_matching_specs("libelf")
+ (mpileaks_spec,) = e.all_matching_specs("mpileaks")
+ assert not os.path.exists(libelf_spec.package.stage.path)
+ assert not os.path.exists(mpileaks_spec.package.stage.path)
+ install()
+ assert os.path.exists(libelf_spec.package.stage.path)
+ assert not os.path.exists(mpileaks_spec.package.stage.path)
+
+
+# Helper method for test_install_develop_keep_stage
+def _always_fail(cls, *args, **kwargs):
+ raise Exception("Restage or destruction of dev stage detected during install")
+
+
@pytest.mark.regression("24148")
def test_virtual_spec_concretize_together(tmpdir):
# An environment should permit to concretize "mpi"
diff --git a/lib/spack/spack/test/stage.py b/lib/spack/spack/test/stage.py
index 37e8fd4559..9648ef34e1 100644
--- a/lib/spack/spack/test/stage.py
+++ b/lib/spack/spack/test/stage.py
@@ -909,18 +909,20 @@ class TestDevelopStage:
"""
devtree, srcdir = develop_path
stage = DevelopStage("test-stage", srcdir, reference_link="link-to-stage")
+ assert not os.path.exists(stage.reference_link)
stage.create()
+ assert os.path.exists(stage.reference_link)
srctree1 = _create_tree_from_dir_recursive(stage.source_path)
assert os.path.samefile(srctree1["link-to-stage"], stage.path)
del srctree1["link-to-stage"]
assert srctree1 == devtree
stage.destroy()
+ assert not os.path.exists(stage.reference_link)
# Make sure destroying the stage doesn't change anything
# about the path
assert not os.path.exists(stage.path)
srctree2 = _create_tree_from_dir_recursive(srcdir)
- del srctree2["link-to-stage"] # Note the symlink persists but is broken
assert srctree2 == devtree