summaryrefslogtreecommitdiff
path: root/lib/spack/spack/cmd/deconcretize.py
blob: 706986e2b36889cd2c84468f0af16c3e466e7fb7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import argparse
import sys
from typing import List

import llnl.util.tty as tty

import spack.cmd
import spack.cmd.common.confirmation as confirmation
import spack.environment as ev
import spack.spec
from spack.cmd.common import arguments

description = "remove specs from the concretized lockfile of an environment"
section = "environments"
level = "long"

# Arguments for display_specs when we find ambiguity
display_args = {"long": True, "show_flags": False, "variants": False, "indent": 4}


def setup_parser(subparser):
    subparser.add_argument(
        "--root", action="store_true", help="deconcretize only specific environment roots"
    )
    arguments.add_common_arguments(subparser, ["yes_to_all", "specs"])
    subparser.add_argument(
        "-a",
        "--all",
        action="store_true",
        dest="all",
        help="deconcretize ALL specs that match each supplied spec",
    )


def get_deconcretize_list(
    args: argparse.Namespace, specs: List[spack.spec.Spec], env: ev.Environment
) -> List[spack.spec.Spec]:
    """
    Get list of environment roots to deconcretize
    """
    env_specs = [s for _, s in env.concretized_specs()]
    to_deconcretize = []
    errors = []

    for s in specs:
        if args.root:
            # find all roots matching given spec
            to_deconc = [e for e in env_specs if e.satisfies(s)]
        else:
            # find all roots matching or depending on a matching spec
            to_deconc = [e for e in env_specs if any(d.satisfies(s) for d in e.traverse())]

        if len(to_deconc) < 1:
            tty.warn(f"No matching specs to deconcretize for {s}")

        elif len(to_deconc) > 1 and not args.all:
            errors.append((s, to_deconc))

        to_deconcretize.extend(to_deconc)

    if errors:
        for spec, matching in errors:
            tty.error(f"{spec} matches multiple concrete specs:")
            sys.stderr.write("\n")
            spack.cmd.display_specs(matching, output=sys.stderr, **display_args)
            sys.stderr.write("\n")
            sys.stderr.flush()
        tty.die("Use '--all' to deconcretize all matching specs, or be more specific")

    return to_deconcretize


def deconcretize_specs(args, specs):
    env = spack.cmd.require_active_env(cmd_name="deconcretize")

    if args.specs:
        deconcretize_list = get_deconcretize_list(args, specs, env)
    else:
        deconcretize_list = [s for _, s in env.concretized_specs()]

    if not args.yes_to_all:
        confirmation.confirm_action(deconcretize_list, "deconcretized", "deconcretization")

    with env.write_transaction():
        for spec in deconcretize_list:
            env.deconcretize(spec)
        env.write()


def deconcretize(parser, args):
    if not args.specs and not args.all:
        tty.die(
            "deconcretize requires at least one spec argument.",
            " Use `spack deconcretize --all` to deconcretize ALL specs.",
        )

    specs = spack.cmd.parse_specs(args.specs) if args.specs else [any]
    deconcretize_specs(args, specs)