diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/cmd/concretize.py | 9 | ||||
-rw-r--r-- | lib/spack/spack/cmd/install.py | 5 | ||||
-rw-r--r-- | lib/spack/spack/cmd/solve.py | 19 | ||||
-rw-r--r-- | lib/spack/spack/cmd/spec.py | 10 | ||||
-rw-r--r-- | lib/spack/spack/environment/environment.py | 31 | ||||
-rw-r--r-- | lib/spack/spack/spec.py | 174 |
6 files changed, 161 insertions, 87 deletions
diff --git a/lib/spack/spack/cmd/concretize.py b/lib/spack/spack/cmd/concretize.py index e07c0eb18c..061a03fc4d 100644 --- a/lib/spack/spack/cmd/concretize.py +++ b/lib/spack/spack/cmd/concretize.py @@ -3,6 +3,9 @@ # # SPDX-License-Identifier: (Apache-2.0 OR MIT) +import llnl.util.tty as tty +from llnl.string import plural + import spack.cmd import spack.cmd.common.arguments import spack.environment as ev @@ -43,5 +46,9 @@ def concretize(parser, args): with env.write_transaction(): concretized_specs = env.concretize(force=args.force, tests=tests) if not args.quiet: - ev.display_specs(concretized_specs) + if concretized_specs: + tty.msg(f"Concretized {plural(len(concretized_specs), 'spec')}:") + ev.display_specs([concrete for _, concrete in concretized_specs]) + else: + tty.msg("No new specs to concretize.") env.write() diff --git a/lib/spack/spack/cmd/install.py b/lib/spack/spack/cmd/install.py index 69de44df57..8458e7ce05 100644 --- a/lib/spack/spack/cmd/install.py +++ b/lib/spack/spack/cmd/install.py @@ -10,6 +10,7 @@ import sys from typing import List import llnl.util.filesystem as fs +from llnl.string import plural from llnl.util import lang, tty import spack.build_environment @@ -375,7 +376,9 @@ def _maybe_add_and_concretize(args, env, specs): # `spack concretize` tests = compute_tests_install_kwargs(env.user_specs, args.test) concretized_specs = env.concretize(tests=tests) - ev.display_specs(concretized_specs) + if concretized_specs: + tty.msg(f"Concretized {plural(len(concretized_specs), 'spec')}") + ev.display_specs([concrete for _, concrete in concretized_specs]) # save view regeneration for later, so that we only do it # once, as it can be slow. diff --git a/lib/spack/spack/cmd/solve.py b/lib/spack/spack/cmd/solve.py index 165ede0dbc..2d6197f758 100644 --- a/lib/spack/spack/cmd/solve.py +++ b/lib/spack/spack/cmd/solve.py @@ -114,15 +114,16 @@ def _process_result(result, show, required_format, kwargs): # dump the solutions as concretized specs if "solutions" in show: - for spec in result.specs: - # With -y, just print YAML to output. - if required_format == "yaml": - # use write because to_yaml already has a newline. - sys.stdout.write(spec.to_yaml(hash=ht.dag_hash)) - elif required_format == "json": - sys.stdout.write(spec.to_json(hash=ht.dag_hash)) - else: - sys.stdout.write(spec.tree(color=sys.stdout.isatty(), **kwargs)) + if required_format: + for spec in result.specs: + # With -y, just print YAML to output. + if required_format == "yaml": + # use write because to_yaml already has a newline. + sys.stdout.write(spec.to_yaml(hash=ht.dag_hash)) + elif required_format == "json": + sys.stdout.write(spec.to_json(hash=ht.dag_hash)) + else: + sys.stdout.write(spack.spec.tree(result.specs, color=sys.stdout.isatty(), **kwargs)) print() if result.unsolved_specs and "solutions" in show: diff --git a/lib/spack/spack/cmd/spec.py b/lib/spack/spack/cmd/spec.py index e2d5cb1055..ae08e1f977 100644 --- a/lib/spack/spack/cmd/spec.py +++ b/lib/spack/spack/cmd/spec.py @@ -105,11 +105,19 @@ def spec(parser, args): if env: env.concretize() specs = env.concretized_specs() + + # environments are printed together in a combined tree() invocation, + # except when using --yaml or --json, which we print spec by spec below. + if not args.format: + tree_kwargs["key"] = spack.traverse.by_dag_hash + tree_kwargs["hashes"] = args.long or args.very_long + print(spack.spec.tree([concrete for _, concrete in specs], **tree_kwargs)) + return else: tty.die("spack spec requires at least one spec or an active environment") for input, output in specs: - # With -y, just print YAML to output. + # With --yaml or --json, just print the raw specs to output if args.format: if args.format == "yaml": # use write because to_yaml already has a newline. diff --git a/lib/spack/spack/environment/environment.py b/lib/spack/spack/environment/environment.py index 22a1a0d322..54a9ab1795 100644 --- a/lib/spack/spack/environment/environment.py +++ b/lib/spack/spack/environment/environment.py @@ -24,6 +24,7 @@ import llnl.util.tty.color as clr from llnl.util.link_tree import ConflictingSpecsError from llnl.util.symlink import readlink, symlink +import spack.cmd import spack.compilers import spack.concretize import spack.config @@ -2473,27 +2474,21 @@ def _equiv_dict(first, second): return same_values and same_keys_with_same_overrides -def display_specs(concretized_specs): - """Displays the list of specs returned by `Environment.concretize()`. +def display_specs(specs): + """Displays a list of specs traversed breadth-first, covering nodes, with install status. Args: - concretized_specs (list): list of specs returned by - `Environment.concretize()` + specs (list): list of specs """ - - def _tree_to_display(spec): - return spec.tree( - recurse_dependencies=True, - format=spack.spec.DISPLAY_FORMAT, - status_fn=spack.spec.Spec.install_status, - hashlen=7, - hashes=True, - ) - - for user_spec, concrete_spec in concretized_specs: - tty.msg("Concretized {0}".format(user_spec)) - sys.stdout.write(_tree_to_display(concrete_spec)) - print("") + tree_string = spack.spec.tree( + specs, + format=spack.spec.DISPLAY_FORMAT, + hashes=True, + hashlen=7, + status_fn=spack.spec.Spec.install_status, + key=traverse.by_dag_hash, + ) + print(tree_string) def _concretize_from_constraints(spec_constraints, tests=False): diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index fe859e1fc4..5bdb00173d 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -1287,6 +1287,102 @@ class SpecBuildInterface(lang.ObjectWrapper): return self.wrapped_obj.copy(*args, **kwargs) +def tree( + specs: List["spack.spec.Spec"], + *, + color: Optional[bool] = None, + depth: bool = False, + hashes: bool = False, + hashlen: Optional[int] = None, + cover: str = "nodes", + indent: int = 0, + format: str = DEFAULT_FORMAT, + deptypes: Union[Tuple[str, ...], str] = "all", + show_types: bool = False, + depth_first: bool = False, + recurse_dependencies: bool = True, + status_fn: Optional[Callable[["Spec"], InstallStatus]] = None, + prefix: Optional[Callable[["Spec"], str]] = None, + key=id, +) -> str: + """Prints out specs and their dependencies, tree-formatted with indentation. + + Status function may either output a boolean or an InstallStatus + + Args: + color: if True, always colorize the tree. If False, don't colorize the tree. If None, + use the default from llnl.tty.color + depth: print the depth from the root + hashes: if True, print the hash of each node + hashlen: length of the hash to be printed + cover: either "nodes" or "edges" + indent: extra indentation for the tree being printed + format: format to be used to print each node + deptypes: dependency types to be represented in the tree + show_types: if True, show the (merged) dependency type of a node + depth_first: if True, traverse the DAG depth first when representing it as a tree + recurse_dependencies: if True, recurse on dependencies + status_fn: optional callable that takes a node as an argument and return its + installation status + prefix: optional callable that takes a node as an argument and return its + installation prefix + """ + out = "" + + if color is None: + color = clr.get_color_when() + + for d, dep_spec in traverse.traverse_tree( + sorted(specs), cover=cover, deptype=deptypes, depth_first=depth_first, key=key + ): + node = dep_spec.spec + + if prefix is not None: + out += prefix(node) + out += " " * indent + + if depth: + out += "%-4d" % d + + if status_fn: + status = status_fn(node) + if status in list(InstallStatus): + out += clr.colorize(status.value, color=color) + elif status: + out += clr.colorize("@g{[+]} ", color=color) + else: + out += clr.colorize("@r{[-]} ", color=color) + + if hashes: + out += clr.colorize("@K{%s} ", color=color) % node.dag_hash(hashlen) + + if show_types: + if cover == "nodes": + # when only covering nodes, we merge dependency types + # from all dependents before showing them. + depflag = 0 + for ds in node.edges_from_dependents(): + depflag |= ds.depflag + else: + # when covering edges or paths, we show dependency + # types only for the edge through which we visited + depflag = dep_spec.depflag + + type_chars = dt.flag_to_chars(depflag) + out += "[%s] " % type_chars + + out += " " * d + if d > 0: + out += "^" + out += node.format(format, color=color) + "\n" + + # Check if we wanted just the first line + if not recurse_dependencies: + break + + return out + + @lang.lazy_lexicographic_ordering(set_hash=False) class Spec: #: Cache for spec's prefix, computed lazily in the corresponding property @@ -4604,13 +4700,14 @@ class Spec: recurse_dependencies: bool = True, status_fn: Optional[Callable[["Spec"], InstallStatus]] = None, prefix: Optional[Callable[["Spec"], str]] = None, + key=id, ) -> str: - """Prints out this spec and its dependencies, tree-formatted - with indentation. + """Prints out this spec and its dependencies, tree-formatted with indentation. - Status function may either output a boolean or an InstallStatus + See multi-spec ``spack.spec.tree()`` function for details. Args: + specs: List of specs to format. color: if True, always colorize the tree. If False, don't colorize the tree. If None, use the default from llnl.tty.color depth: print the depth from the root @@ -4628,60 +4725,23 @@ class Spec: prefix: optional callable that takes a node as an argument and return its installation prefix """ - out = "" - - if color is None: - color = clr.get_color_when() - - for d, dep_spec in traverse.traverse_tree( - [self], cover=cover, deptype=deptypes, depth_first=depth_first - ): - node = dep_spec.spec - - if prefix is not None: - out += prefix(node) - out += " " * indent - - if depth: - out += "%-4d" % d - - if status_fn: - status = status_fn(node) - if status in list(InstallStatus): - out += clr.colorize(status.value, color=color) - elif status: - out += clr.colorize("@g{[+]} ", color=color) - else: - out += clr.colorize("@r{[-]} ", color=color) - - if hashes: - out += clr.colorize("@K{%s} ", color=color) % node.dag_hash(hashlen) - - if show_types: - if cover == "nodes": - # when only covering nodes, we merge dependency types - # from all dependents before showing them. - depflag = 0 - for ds in node.edges_from_dependents(): - depflag |= ds.depflag - else: - # when covering edges or paths, we show dependency - # types only for the edge through which we visited - depflag = dep_spec.depflag - - type_chars = dt.flag_to_chars(depflag) - out += "[%s] " % type_chars - - out += " " * d - if d > 0: - out += "^" - out += node.format(format, color=color) + "\n" - - # Check if we wanted just the first line - if not recurse_dependencies: - break - - return out + return tree( + [self], + color=color, + depth=depth, + hashes=hashes, + hashlen=hashlen, + cover=cover, + indent=indent, + format=format, + deptypes=deptypes, + show_types=show_types, + depth_first=depth_first, + recurse_dependencies=recurse_dependencies, + status_fn=status_fn, + prefix=prefix, + key=key, + ) def __repr__(self): return str(self) |