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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
# SPDX-License-Identifier: Apache-2.0
# Copyright 2012-2022 The Meson development team
"""
Contains the strict minimum to run scripts.
When the backend needs to call back into Meson during compilation for running
scripts or wrapping commands, it is important to load as little python modules
as possible for performance reasons.
"""
from __future__ import annotations
from dataclasses import dataclass
import os
import abc
import typing as T
if T.TYPE_CHECKING:
from hashlib import _Hash
from typing_extensions import Literal
from ..mparser import BaseNode
from .. import programs
EnvironOrDict = T.Union[T.Dict[str, str], os._Environ[str]]
EnvInitValueType = T.Dict[str, T.Union[str, T.List[str]]]
class MesonException(Exception):
'''Exceptions thrown by Meson'''
def __init__(self, *args: object, file: T.Optional[str] = None,
lineno: T.Optional[int] = None, colno: T.Optional[int] = None):
super().__init__(*args)
self.file = file
self.lineno = lineno
self.colno = colno
@classmethod
def from_node(cls, *args: object, node: BaseNode) -> MesonException:
"""Create a MesonException with location data from a BaseNode
:param node: A BaseNode to set location data from
:return: A Meson Exception instance
"""
return cls(*args, file=node.filename, lineno=node.lineno, colno=node.colno)
class MesonBugException(MesonException):
'''Exceptions thrown when there is a clear Meson bug that should be reported'''
def __init__(self, msg: str, file: T.Optional[str] = None,
lineno: T.Optional[int] = None, colno: T.Optional[int] = None):
super().__init__(msg + '\n\n This is a Meson bug and should be reported!',
file=file, lineno=lineno, colno=colno)
class HoldableObject(metaclass=abc.ABCMeta):
''' Dummy base class for all objects that can be
held by an interpreter.baseobjects.ObjectHolder '''
class EnvironmentVariables(HoldableObject):
def __init__(self, values: T.Optional[EnvInitValueType] = None,
init_method: Literal['set', 'prepend', 'append'] = 'set', separator: str = os.pathsep) -> None:
self.envvars: T.List[T.Tuple[T.Callable[[T.Dict[str, str], str, T.List[str], str, T.Optional[str]], str], str, T.List[str], str]] = []
# The set of all env vars we have operations for. Only used for self.has_name()
self.varnames: T.Set[str] = set()
self.unset_vars: T.Set[str] = set()
self.can_use_env = True
if values:
init_func = getattr(self, init_method)
for name, value in values.items():
v = value if isinstance(value, list) else [value]
init_func(name, v, separator)
def __repr__(self) -> str:
repr_str = "<{0}: {1}>"
return repr_str.format(self.__class__.__name__, self.envvars)
def hash(self, hasher: _Hash) -> None:
myenv = self.get_env({})
for key in sorted(myenv.keys()):
hasher.update(bytes(key, encoding='utf-8'))
hasher.update(b',')
hasher.update(bytes(myenv[key], encoding='utf-8'))
hasher.update(b';')
def has_name(self, name: str) -> bool:
return name in self.varnames
def get_names(self) -> T.Set[str]:
return self.varnames
def merge(self, other: EnvironmentVariables) -> None:
for method, name, values, separator in other.envvars:
self.varnames.add(name)
self.envvars.append((method, name, values, separator))
if name in self.unset_vars:
self.unset_vars.remove(name)
if other.unset_vars:
self.can_use_env = False
self.unset_vars.update(other.unset_vars)
def set(self, name: str, values: T.List[str], separator: str = os.pathsep) -> None:
if name in self.unset_vars:
raise MesonException(f'You cannot set the already unset variable {name!r}')
self.varnames.add(name)
self.envvars.append((self._set, name, values, separator))
def unset(self, name: str) -> None:
self.can_use_env = False
if name in self.varnames:
raise MesonException(f'You cannot unset the {name!r} variable because it is already set')
self.unset_vars.add(name)
def append(self, name: str, values: T.List[str], separator: str = os.pathsep) -> None:
self.can_use_env = False
if name in self.unset_vars:
raise MesonException(f'You cannot append to unset variable {name!r}')
self.varnames.add(name)
self.envvars.append((self._append, name, values, separator))
def prepend(self, name: str, values: T.List[str], separator: str = os.pathsep) -> None:
self.can_use_env = False
if name in self.unset_vars:
raise MesonException(f'You cannot prepend to unset variable {name!r}')
self.varnames.add(name)
self.envvars.append((self._prepend, name, values, separator))
@staticmethod
def _set(env: T.Dict[str, str], name: str, values: T.List[str], separator: str, default_value: T.Optional[str]) -> str:
return separator.join(values)
@staticmethod
def _append(env: T.Dict[str, str], name: str, values: T.List[str], separator: str, default_value: T.Optional[str]) -> str:
curr = env.get(name, default_value)
return separator.join(values if curr is None else [curr] + values)
@staticmethod
def _prepend(env: T.Dict[str, str], name: str, values: T.List[str], separator: str, default_value: T.Optional[str]) -> str:
curr = env.get(name, default_value)
return separator.join(values if curr is None else values + [curr])
def get_env(self, full_env: EnvironOrDict, default_fmt: T.Optional[str] = None) -> T.Dict[str, str]:
env = full_env.copy()
for method, name, values, separator in self.envvars:
default_value = default_fmt.format(name) if default_fmt else None
env[name] = method(env, name, values, separator, default_value)
for name in self.unset_vars:
env.pop(name, None)
return env
@dataclass(eq=False)
class ExecutableSerialisation:
cmd_args: T.List[str]
env: T.Optional[EnvironmentVariables] = None
exe_wrapper: T.Optional['programs.ExternalProgram'] = None
workdir: T.Optional[str] = None
extra_paths: T.Optional[T.List] = None
capture: T.Optional[str] = None
feed: T.Optional[str] = None
tag: T.Optional[str] = None
verbose: bool = False
installdir_map: T.Optional[T.Dict[str, str]] = None
def __post_init__(self) -> None:
self.pickled = False
self.skip_if_destdir = False
self.subproject = ''
self.dry_run = False
|