aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/utils/core.py
diff options
context:
space:
mode:
authorXavier Claessens <xavier.claessens@collabora.com>2022-09-13 09:38:19 -0400
committerEli Schwartz <eschwartz93@gmail.com>2022-09-28 19:36:13 -0400
commit2dfd952eb99590878430510644ffe99dd4a6a41d (patch)
tree53001a096d997511a8d12a44cf5019a2ff8d5a2b /mesonbuild/utils/core.py
parenta58ec322b3a9a07af83ea268341c16f972f52791 (diff)
downloadmeson-2dfd952eb99590878430510644ffe99dd4a6a41d.zip
meson-2dfd952eb99590878430510644ffe99dd4a6a41d.tar.gz
meson-2dfd952eb99590878430510644ffe99dd4a6a41d.tar.bz2
Move classes used by scripts to their own module
Those classes are used by wrapper scripts and we should not have to import the rest of mesonlib, build.py, and all their dependencies for that. This renames mesonlib/ directory to utils/ and add a mesonlib.py module that imports everything from utils/ to not have to change `import mesonlib` everywhere. It allows to import utils.core without importing the rest of mesonlib.
Diffstat (limited to 'mesonbuild/utils/core.py')
-rw-r--r--mesonbuild/utils/core.py160
1 files changed, 160 insertions, 0 deletions
diff --git a/mesonbuild/utils/core.py b/mesonbuild/utils/core.py
new file mode 100644
index 0000000..ed413ca
--- /dev/null
+++ b/mesonbuild/utils/core.py
@@ -0,0 +1,160 @@
+# Copyright 2012-2022 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+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 typing_extensions import Literal
+ from ..mparser import BaseNode
+ from . import programs
+
+
+__all__ = [
+ 'MesonException',
+ 'MesonBugException',
+ 'HoldableObject',
+ 'EnvInitValueType',
+ 'EnvironmentVariables',
+ 'ExecutableSerialisation',
+]
+
+
+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 '''
+
+EnvInitValueType = T.Dict[str, T.Union[str, T.List[str]]]
+
+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], 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()
+
+ 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: T.Any):
+ 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 set(self, name: str, values: T.List[str], separator: str = os.pathsep) -> None:
+ self.varnames.add(name)
+ self.envvars.append((self._set, name, values, separator))
+
+ def append(self, name: str, values: T.List[str], separator: str = os.pathsep) -> None:
+ 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.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) -> str:
+ return separator.join(values)
+
+ @staticmethod
+ def _append(env: T.Dict[str, str], name: str, values: T.List[str], separator: str) -> str:
+ curr = env.get(name)
+ 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) -> str:
+ curr = env.get(name)
+ return separator.join(values if curr is None else values + [curr])
+
+ def get_env(self, full_env: T.MutableMapping[str, str]) -> T.Dict[str, str]:
+ env = full_env.copy()
+ for method, name, values, separator in self.envvars:
+ env[name] = method(env, name, values, separator)
+ return env
+
+
+@dataclass(eq=False)
+class ExecutableSerialisation:
+
+ # XXX: should capture and feed default to False, instead of None?
+
+ 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[bool] = None
+ feed: T.Optional[bool] = None
+ tag: T.Optional[str] = None
+ verbose: bool = False
+
+ def __post_init__(self) -> None:
+ self.pickled = False
+ self.skip_if_destdir = False
+ self.subproject = ''