summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarmen Stoppels <harmenstoppels@gmail.com>2023-02-22 10:35:44 +0100
committerGitHub <noreply@github.com>2023-02-22 10:35:44 +0100
commit9d6630e24530ce981378378f9a138e3dc52ad96f (patch)
tree4748406856db1a90c42e343b6070c6723d853c90
parentb8d15e816bf7388774e65ca59daf3831b4fa4180 (diff)
downloadspack-9d6630e24530ce981378378f9a138e3dc52ad96f.tar.gz
spack-9d6630e24530ce981378378f9a138e3dc52ad96f.tar.bz2
spack-9d6630e24530ce981378378f9a138e3dc52ad96f.tar.xz
spack-9d6630e24530ce981378378f9a138e3dc52ad96f.zip
spack build-env: error when deps are not installed (#35533)
Currently we attempt to setup the build environment even when dependencies are not installed, which typically results in error while searching for libraries or executables in a dependency's prefix. With this change, we get a more user friendly error: ``` $ spack build-env perl ==> Error: Not all dependencies of perl are installed, cannot setup build environment: - qpj6dw5 perl@5.36.0%apple-clang@14.0.0+cpanm+open+shared+threads build_system=generic arch=darwin-ventura-m1 - jq2plbe ^berkeley-db@18.1.40%apple-clang@14.0.0+cxx~docs+stl build_system=autotools patches=26090f4,b231fcc arch=darwin-ventura-m1 ... $ echo $? 1 ```
-rw-r--r--lib/spack/spack/cmd/build_env.py2
-rw-r--r--lib/spack/spack/cmd/common/env_utility.py60
-rw-r--r--lib/spack/spack/cmd/test_env.py2
-rw-r--r--lib/spack/spack/test/cmd/build_env.py8
4 files changed, 70 insertions, 2 deletions
diff --git a/lib/spack/spack/cmd/build_env.py b/lib/spack/spack/cmd/build_env.py
index cd19e5aa10..7da9213c5b 100644
--- a/lib/spack/spack/cmd/build_env.py
+++ b/lib/spack/spack/cmd/build_env.py
@@ -5,7 +5,7 @@
import spack.cmd.common.env_utility as env_utility
description = (
- "run a command in a spec's install environment, " "or dump its environment to screen or file"
+ "run a command in a spec's install environment, or dump its environment to screen or file"
)
section = "build"
level = "long"
diff --git a/lib/spack/spack/cmd/common/env_utility.py b/lib/spack/spack/cmd/common/env_utility.py
index f2c4132b3b..095360ee04 100644
--- a/lib/spack/spack/cmd/common/env_utility.py
+++ b/lib/spack/spack/cmd/common/env_utility.py
@@ -12,7 +12,11 @@ import llnl.util.tty as tty
import spack.build_environment as build_environment
import spack.cmd
import spack.cmd.common.arguments as arguments
+import spack.error
import spack.paths
+import spack.spec
+import spack.store
+from spack import traverse
from spack.util.environment import dump_environment, pickle_environment
@@ -38,6 +42,41 @@ def setup_parser(subparser):
)
+class AreDepsInstalledVisitor:
+ def __init__(self, context="build"):
+ if context not in ("build", "test"):
+ raise ValueError("context can only be build or test")
+
+ if context == "build":
+ self.direct_deps = ("build", "link", "run")
+ else:
+ self.direct_deps = ("build", "test", "link", "run")
+
+ self.has_uninstalled_deps = False
+
+ def accept(self, item):
+ # The root may be installed or uninstalled.
+ if item.depth == 0:
+ return True
+
+ # Early exit after we've seen an uninstalled dep.
+ if self.has_uninstalled_deps:
+ return False
+
+ spec = item.edge.spec
+ if not spec.external and not spec.installed:
+ self.has_uninstalled_deps = True
+ return False
+
+ return True
+
+ def neighbors(self, item):
+ # Direct deps: follow build & test edges.
+ # Transitive deps: follow link / run.
+ deptypes = self.direct_deps if item.depth == 0 else ("link", "run")
+ return item.edge.spec.edges_to_dependencies(deptype=deptypes)
+
+
def emulate_env_utility(cmd_name, context, args):
if not args.spec:
tty.die("spack %s requires a spec." % cmd_name)
@@ -65,6 +104,27 @@ def emulate_env_utility(cmd_name, context, args):
spec = spack.cmd.matching_spec_from_env(spec)
+ # Require that dependencies are installed.
+ visitor = AreDepsInstalledVisitor(context=context)
+
+ # Mass install check needs read transaction.
+ with spack.store.db.read_transaction():
+ traverse.traverse_breadth_first_with_visitor([spec], traverse.CoverNodesVisitor(visitor))
+
+ if visitor.has_uninstalled_deps:
+ raise spack.error.SpackError(
+ f"Not all dependencies of {spec.name} are installed. "
+ f"Cannot setup {context} environment:",
+ spec.tree(
+ status_fn=spack.spec.Spec.install_status,
+ hashlen=7,
+ hashes=True,
+ # This shows more than necessary, but we cannot dynamically change deptypes
+ # in Spec.tree(...).
+ deptypes="all" if context == "build" else ("build", "test", "link", "run"),
+ ),
+ )
+
build_environment.setup_package(spec.package, args.dirty, context)
if args.dump:
diff --git a/lib/spack/spack/cmd/test_env.py b/lib/spack/spack/cmd/test_env.py
index 0a7bee6376..049df9d5c0 100644
--- a/lib/spack/spack/cmd/test_env.py
+++ b/lib/spack/spack/cmd/test_env.py
@@ -5,7 +5,7 @@
import spack.cmd.common.env_utility as env_utility
description = (
- "run a command in a spec's test environment, " "or dump its environment to screen or file"
+ "run a command in a spec's test environment, or dump its environment to screen or file"
)
section = "admin"
level = "long"
diff --git a/lib/spack/spack/test/cmd/build_env.py b/lib/spack/spack/test/cmd/build_env.py
index 2efc59fb8e..0e10a629ed 100644
--- a/lib/spack/spack/test/cmd/build_env.py
+++ b/lib/spack/spack/test/cmd/build_env.py
@@ -6,6 +6,7 @@ import pickle
import pytest
+import spack.error
from spack.main import SpackCommand
build_env = SpackCommand("build-env")
@@ -48,3 +49,10 @@ def test_pickle(tmpdir):
environment = pickle.load(open(_out_file, "rb"))
assert type(environment) == dict
assert "PATH" in environment
+
+
+def test_failure_when_uninstalled_deps(config, mock_packages):
+ with pytest.raises(
+ spack.error.SpackError, match="Not all dependencies of dttop are installed"
+ ):
+ build_env("dttop")