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
```
This commit is contained in:
Harmen Stoppels 2023-02-22 10:35:44 +01:00 committed by GitHub
parent b8d15e816b
commit 9d6630e245
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 2 deletions

View File

@ -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"

View File

@ -12,7 +12,11 @@
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:

View File

@ -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"

View File

@ -6,6 +6,7 @@
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")