summaryrefslogtreecommitdiff
path: root/lib/spack/spack/deptypes.py
blob: af196bd4ad00a31294ad3f36bdb137d67e352d6a (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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# Copyright 2013-2024 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)
"""Data structures that represent Spack's edge types."""

from typing import Iterable, List, Tuple, Union

#: Type hint for the low-level dependency input (enum.Flag is too slow)
DepFlag = int

#: Type hint for the high-level dependency input
DepTypes = Union[str, List[str], Tuple[str, ...]]

#: Individual dependency types
DepType = str  # Python 3.8: Literal["build", "link", "run", "test"]

# Flag values. NOTE: these values are not arbitrary, since hash computation imposes
# the order (link, run, build, test) when depending on the same package multiple times,
# and we rely on default integer comparison to sort dependency types.
# New dependency types should be appended.
LINK = 0b0001
RUN = 0b0010
BUILD = 0b0100
TEST = 0b1000

#: The types of dependency relationships that Spack understands.
ALL_TYPES: Tuple[DepType, ...] = ("build", "link", "run", "test")

#: Default dependency type if none is specified
DEFAULT_TYPES: Tuple[DepType, ...] = ("build", "link")

#: A flag with all dependency types set
ALL: DepFlag = BUILD | LINK | RUN | TEST

#: Default dependency type if none is specified
DEFAULT: DepFlag = BUILD | LINK

#: An iterator of all flag components
ALL_FLAGS: Tuple[DepFlag, DepFlag, DepFlag, DepFlag] = (BUILD, LINK, RUN, TEST)


def flag_from_string(s: str) -> DepFlag:
    if s == "build":
        return BUILD
    elif s == "link":
        return LINK
    elif s == "run":
        return RUN
    elif s == "test":
        return TEST
    else:
        raise ValueError(f"Invalid dependency type: {s}")


def flag_from_strings(deptype: Iterable[str]) -> DepFlag:
    """Transform an iterable of deptype strings into a flag."""
    flag = 0
    for deptype_str in deptype:
        flag |= flag_from_string(deptype_str)
    return flag


def canonicalize(deptype: DepTypes) -> DepFlag:
    """Convert deptype user input to a DepFlag, or raise ValueError.

    Args:
        deptype: string representing dependency type, or a list/tuple of such strings.
            Can also be the builtin function ``all`` or the string 'all', which result in
            a tuple of all dependency types known to Spack.
    """
    if deptype in ("all", all):
        return ALL

    if isinstance(deptype, str):
        return flag_from_string(deptype)

    if isinstance(deptype, (tuple, list, set)):
        return flag_from_strings(deptype)

    raise ValueError(f"Invalid dependency type: {deptype!r}")


def flag_to_tuple(x: DepFlag) -> Tuple[DepType, ...]:
    deptype: List[DepType] = []
    if x & BUILD:
        deptype.append("build")
    if x & LINK:
        deptype.append("link")
    if x & RUN:
        deptype.append("run")
    if x & TEST:
        deptype.append("test")
    return tuple(deptype)


def flag_to_string(x: DepFlag) -> DepType:
    if x == BUILD:
        return "build"
    elif x == LINK:
        return "link"
    elif x == RUN:
        return "run"
    elif x == TEST:
        return "test"
    else:
        raise ValueError(f"Invalid dependency type flag: {x}")


def flag_to_chars(depflag: DepFlag) -> str:
    """Create a string representing deptypes for many dependencies.

    The string will be some subset of 'blrt', like 'bl ', 'b t', or
    ' lr ' where each letter in 'blrt' stands for 'build', 'link',
    'run', and 'test' (the dependency types).

    For a single dependency, this just indicates that the dependency has
    the indicated deptypes. For a list of dependnecies, this shows
    whether ANY dpeendency in the list has the deptypes (so the deptypes
    are merged)."""
    return "".join(
        t_str[0] if t_flag & depflag else " " for t_str, t_flag in zip(ALL_TYPES, ALL_FLAGS)
    )