aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Schwartz <eschwartz@archlinux.org>2021-12-04 21:11:51 -0500
committerEli Schwartz <eschwartz@archlinux.org>2022-01-10 18:36:57 -0500
commit4b351aef26a19b4c73f6ef295f64da1c74bc713d (patch)
tree44f0b5c34c6bfb06360ffc22870ff423fc9305e2
parent98a41ec24e77d7670ea83fd986853d0fe7cb2f5b (diff)
downloadmeson-4b351aef26a19b4c73f6ef295f64da1c74bc713d.zip
meson-4b351aef26a19b4c73f6ef295f64da1c74bc713d.tar.gz
meson-4b351aef26a19b4c73f6ef295f64da1c74bc713d.tar.bz2
first pass at migrating to dataclasses
In some cases, init variables that accept None as a sentinel and immediately overwrite with [], are migrated to dataclass field factories. \o/ Note: dataclasses by default cannot provide eq methods, as they then become unhashable. In the future we may wish to opt into declaring them frozen, instead/additionally.
-rw-r--r--mesonbuild/backend/backends.py207
-rw-r--r--mesonbuild/build.py229
-rw-r--r--mesonbuild/envconfig.py27
-rw-r--r--mesonbuild/interpreterbase/decorators.py8
-rw-r--r--mesonbuild/mparser.py35
-rwxr-xr-xmesonbuild/msubprojects.py19
-rw-r--r--mesonbuild/wrap/wrap.py15
7 files changed, 259 insertions, 281 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 89febcb..957b259 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -13,6 +13,7 @@
# limitations under the License.
from collections import OrderedDict
+from dataclasses import dataclass, InitVar
from functools import lru_cache
from itertools import chain
from pathlib import Path
@@ -61,11 +62,11 @@ if T.TYPE_CHECKING:
# Assembly files cannot be unitified and neither can LLVM IR files
LANGS_CANT_UNITY = ('d', 'fortran', 'vala')
+@dataclass(eq=False)
class RegenInfo:
- def __init__(self, source_dir: str, build_dir: str, depfiles: T.List[str]):
- self.source_dir = source_dir
- self.build_dir = build_dir
- self.depfiles = depfiles
+ source_dir: str
+ build_dir: str
+ depfiles: T.List[str]
class TestProtocol(enum.Enum):
@@ -97,28 +98,30 @@ class TestProtocol(enum.Enum):
return 'tap'
+@dataclass(eq=False)
class CleanTrees:
'''
Directories outputted by custom targets that have to be manually cleaned
because on Linux `ninja clean` only deletes empty directories.
'''
- def __init__(self, build_dir: str, trees: T.List[str]):
- self.build_dir = build_dir
- self.trees = trees
+ build_dir: str
+ trees: T.List[str]
+@dataclass(eq=False)
class InstallData:
- def __init__(self, source_dir: str, build_dir: str, prefix: str, libdir: str,
- strip_bin: T.List[str], install_umask: T.Union[str, int],
- mesonintrospect: T.List[str], version: str,
- is_cross_build: bool):
- # TODO: in python 3.8 or with typing_Extensions install_umask could be:
- # `T.Union[T.Literal['preserve'], int]`, which would be more accurate.
- self.source_dir = source_dir
- self.build_dir = build_dir
- self.prefix = prefix
- self.libdir = libdir
- self.strip_bin = strip_bin
- self.install_umask = install_umask
+ source_dir: str
+ build_dir: str
+ prefix: str
+ libdir: str
+ strip_bin: T.List[str]
+ # TODO: in python 3.8 or with typing_Extensions this could be:
+ # `T.Union[T.Literal['preserve'], int]`, which would be more accurate.
+ install_umask: T.Union[str, int]
+ mesonintrospect: T.List[str]
+ version: str
+ is_cross_build: bool
+
+ def __post_init__(self) -> None:
self.targets: T.List[TargetInstallData] = []
self.headers: T.List[InstallDataBase] = []
self.man: T.List[InstallDataBase] = []
@@ -127,59 +130,52 @@ class InstallData:
self.symlinks: T.List[InstallSymlinkData] = []
self.install_scripts: T.List[ExecutableSerialisation] = []
self.install_subdirs: T.List[SubdirInstallData] = []
- self.mesonintrospect = mesonintrospect
- self.version = version
- self.is_cross_build = is_cross_build
+@dataclass(eq=False)
class TargetInstallData:
-
+ fname: str
+ outdir: str
+ outdir_name: InitVar[str]
+ aliases: T.Dict[str, str]
+ strip: bool
+ install_name_mappings: T.Mapping[str, str]
+ rpath_dirs_to_remove: T.Set[bytes]
+ install_rpath: str
# TODO: install_mode should just always be a FileMode object
+ install_mode: T.Optional['FileMode']
+ subproject: str
+ optional: bool = False
+ tag: T.Optional[str] = None
- def __init__(self, fname: str, outdir: str, outdir_name: str, aliases: T.Dict[str, str],
- strip: bool, install_name_mappings: T.Mapping[str, str], rpath_dirs_to_remove: T.Set[bytes],
- install_rpath: str, install_mode: T.Optional['FileMode'],
- subproject: str, optional: bool = False, tag: T.Optional[str] = None):
- self.fname = fname
- self.outdir = outdir
- self.out_name = os.path.join(outdir_name, os.path.basename(fname))
- self.aliases = aliases
- self.strip = strip
- self.install_name_mappings = install_name_mappings
- self.rpath_dirs_to_remove = rpath_dirs_to_remove
- self.install_rpath = install_rpath
- self.install_mode = install_mode
- self.subproject = subproject
- self.optional = optional
- self.tag = tag
+ def __post_init__(self, outdir_name: str) -> None:
+ self.out_name = os.path.join(outdir_name, os.path.basename(self.fname))
+@dataclass(eq=False)
class InstallEmptyDir:
- def __init__(self, path: str, install_mode: 'FileMode', subproject: str, tag: T.Optional[str] = None):
- self.path = path
- self.install_mode = install_mode
- self.subproject = subproject
- self.tag = tag
+ path: str
+ install_mode: 'FileMode'
+ subproject: str
+ tag: T.Optional[str] = None
+@dataclass(eq=False)
class InstallDataBase:
- def __init__(self, path: str, install_path: str, install_path_name: str,
- install_mode: 'FileMode', subproject: str, tag: T.Optional[str] = None,
- data_type: T.Optional[str] = None):
- self.path = path
- self.install_path = install_path
- self.install_path_name = install_path_name
- self.install_mode = install_mode
- self.subproject = subproject
- self.tag = tag
- self.data_type = data_type
-
+ path: str
+ install_path: str
+ install_path_name: str
+ install_mode: 'FileMode'
+ subproject: str
+ tag: T.Optional[str] = None
+ data_type: T.Optional[str] = None
+
+@dataclass(eq=False)
class InstallSymlinkData:
- def __init__(self, target: str, name: str, install_path: str,
- subproject: str, tag: T.Optional[str] = None):
- self.target = target
- self.name = name
- self.install_path = install_path
- self.subproject = subproject
- self.tag = tag
+ target: str
+ name: str
+ install_path: str
+ subproject: str
+ tag: T.Optional[str] = None
+# cannot use dataclass here because "exclude" is out of order
class SubdirInstallData(InstallDataBase):
def __init__(self, path: str, install_path: str, install_path_name: str,
install_mode: 'FileMode', exclude: T.Tuple[T.Set[str], T.Set[str]],
@@ -187,64 +183,53 @@ class SubdirInstallData(InstallDataBase):
super().__init__(path, install_path, install_path_name, install_mode, subproject, tag, data_type)
self.exclude = exclude
+@dataclass(eq=False)
class ExecutableSerialisation:
# XXX: should capture and feed default to False, instead of None?
- def __init__(self, cmd_args: T.List[str],
- env: T.Optional[build.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,
- ) -> None:
- self.cmd_args = cmd_args
- self.env = env
- if exe_wrapper is not None:
- assert isinstance(exe_wrapper, programs.ExternalProgram)
- self.exe_wrapper = exe_wrapper
- self.workdir = workdir
- self.extra_paths = extra_paths
- self.capture = capture
- self.feed = feed
+ cmd_args: T.List[str]
+ env: T.Optional[build.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:
+ if self.exe_wrapper is not None:
+ assert isinstance(self.exe_wrapper, programs.ExternalProgram)
self.pickled = False
self.skip_if_destdir = False
- self.verbose = verbose
self.subproject = ''
- self.tag = tag
+@dataclass(eq=False)
class TestSerialisation:
- def __init__(self, name: str, project_name: str, suite: T.List[str], fname: T.List[str],
- is_cross_built: bool, exe_wrapper: T.Optional[programs.ExternalProgram],
- needs_exe_wrapper: bool, is_parallel: bool, cmd_args: T.List[str],
- env: build.EnvironmentVariables, should_fail: bool,
- timeout: T.Optional[int], workdir: T.Optional[str],
- extra_paths: T.List[str], protocol: TestProtocol, priority: int,
- cmd_is_built: bool, depends: T.List[str], version: str):
- self.name = name
- self.project_name = project_name
- self.suite = suite
- self.fname = fname
- self.is_cross_built = is_cross_built
- if exe_wrapper is not None:
- assert isinstance(exe_wrapper, programs.ExternalProgram)
- self.exe_wrapper = exe_wrapper
- self.is_parallel = is_parallel
- self.cmd_args = cmd_args
- self.env = env
- self.should_fail = should_fail
- self.timeout = timeout
- self.workdir = workdir
- self.extra_paths = extra_paths
- self.protocol = protocol
- self.priority = priority
- self.needs_exe_wrapper = needs_exe_wrapper
- self.cmd_is_built = cmd_is_built
- self.depends = depends
- self.version = version
+ name: str
+ project_name: str
+ suite: T.List[str]
+ fname: T.List[str]
+ is_cross_built: bool
+ exe_wrapper: T.Optional[programs.ExternalProgram]
+ needs_exe_wrapper: bool
+ is_parallel: bool
+ cmd_args: T.List[str]
+ env: build.EnvironmentVariables
+ should_fail: bool
+ timeout: T.Optional[int]
+ workdir: T.Optional[str]
+ extra_paths: T.List[str]
+ protocol: TestProtocol
+ priority: int
+ cmd_is_built: bool
+ depends: T.List[str]
+ version: str
+
+ def __post_init__(self) -> None:
+ if self.exe_wrapper is not None:
+ assert isinstance(self.exe_wrapper, programs.ExternalProgram)
def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None, interpreter: T.Optional['Interpreter'] = None) -> T.Optional['Backend']:
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index d8002bf..93bf29d 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -13,6 +13,7 @@
# limitations under the License.
from collections import OrderedDict
+from dataclasses import dataclass, field
from functools import lru_cache
import copy
import hashlib
@@ -130,22 +131,19 @@ def get_target_macos_dylib_install_name(ld) -> str:
class InvalidArguments(MesonException):
pass
+@dataclass(eq=False)
class DependencyOverride(HoldableObject):
- def __init__(self, dep: dependencies.Dependency, node: 'BaseNode', explicit: bool = True):
- self.dep = dep
- self.node = node
- self.explicit = explicit
+ dep: dependencies.Dependency
+ node: 'BaseNode'
+ explicit: bool = True
+@dataclass(eq=False)
class Headers(HoldableObject):
-
- def __init__(self, sources: T.List[File], install_subdir: T.Optional[str],
- custom_install_dir: T.Optional[str], custom_install_mode: 'FileMode',
- subproject: str):
- self.sources = sources
- self.install_subdir = install_subdir
- self.custom_install_dir = custom_install_dir
- self.custom_install_mode = custom_install_mode
- self.subproject = subproject
+ sources: T.List[File]
+ install_subdir: T.Optional[str]
+ custom_install_dir: T.Optional[str]
+ custom_install_mode: 'FileMode'
+ subproject: str
# TODO: we really don't need any of these methods, but they're preserved to
# keep APIs relying on them working.
@@ -166,16 +164,13 @@ class Headers(HoldableObject):
return self.custom_install_mode
+@dataclass(eq=False)
class Man(HoldableObject):
-
- def __init__(self, sources: T.List[File], custom_install_dir: T.Optional[str],
- custom_install_mode: 'FileMode', subproject: str,
- locale: T.Optional[str]):
- self.sources = sources
- self.custom_install_dir = custom_install_dir
- self.custom_install_mode = custom_install_mode
- self.subproject = subproject
- self.locale = locale
+ sources: T.List[File]
+ custom_install_dir: T.Optional[str]
+ custom_install_mode: 'FileMode'
+ subproject: str
+ locale: T.Optional[str]
def get_custom_install_dir(self) -> T.Optional[str]:
return self.custom_install_dir
@@ -187,40 +182,30 @@ class Man(HoldableObject):
return self.sources
+@dataclass(eq=False)
class EmptyDir(HoldableObject):
-
- def __init__(self, path: str, install_mode: 'FileMode', subproject: str,
- install_tag: T.Optional[str] = None):
- self.path = path
- self.install_mode = install_mode
- self.subproject = subproject
- self.install_tag = install_tag
+ path: str
+ install_mode: 'FileMode'
+ subproject: str
+ install_tag: T.Optional[str] = None
+@dataclass(eq=False)
class InstallDir(HoldableObject):
+ source_subdir: str
+ installable_subdir: str
+ install_dir: str
+ install_mode: 'FileMode'
+ exclude: T.Tuple[T.Set[str], T.Set[str]]
+ strip_directory: bool
+ subproject: str
+ from_source_dir: bool = True
+ install_tag: T.Optional[str] = None
- def __init__(self, source_subdir: str, installable_subdir: str, install_dir: str,
- install_mode: 'FileMode',
- exclude: T.Tuple[T.Set[str], T.Set[str]],
- strip_directory: bool, subproject: str,
- from_source_dir: bool = True,
- install_tag: T.Optional[str] = None):
- self.source_subdir = source_subdir
- self.installable_subdir = installable_subdir
- self.install_dir = install_dir
- self.install_mode = install_mode
- self.exclude = exclude
- self.strip_directory = strip_directory
- self.from_source_dir = from_source_dir
- self.subproject = subproject
- self.install_tag = install_tag
-
-
+@dataclass(eq=False)
class DepManifest:
-
- def __init__(self, version: str, license: T.List[str]):
- self.version = version
- self.license = license
+ version: str
+ license: T.List[str]
def to_json(self) -> T.Dict[str, T.Union[str, T.List[str]]]:
return {
@@ -229,6 +214,7 @@ class DepManifest:
}
+# literally everything isn't dataclass stuff
class Build:
"""A class that holds the status of one build including
all dependencies and so on.
@@ -363,18 +349,17 @@ class Build:
return link_args.get(compiler.get_language(), [])
+@dataclass(eq=False)
class IncludeDirs(HoldableObject):
"""Internal representation of an include_directories call."""
- def __init__(self, curdir: str, incdirs: T.List[str], is_system: bool, extra_build_dirs: T.Optional[T.List[str]] = None):
- self.curdir = curdir
- self.incdirs = incdirs
- self.is_system = is_system
-
- # Interpreter has validated that all given directories
- # actually exist.
- self.extra_build_dirs: T.List[str] = extra_build_dirs or []
+ curdir: str
+ incdirs: T.List[str]
+ is_system: bool
+ # Interpreter has validated that all given directories
+ # actually exist.
+ extra_build_dirs: T.List[str] = field(default_factory=list)
def __repr__(self) -> str:
r = '<{} {}/{}>'
@@ -404,21 +389,18 @@ class IncludeDirs(HoldableObject):
strlist.append(os.path.join(builddir, self.curdir, idir))
return strlist
+@dataclass(eq=False)
class ExtractedObjects(HoldableObject):
'''
Holds a list of sources for which the objects must be extracted
'''
- def __init__(self, target: 'BuildTarget',
- srclist: T.Optional[T.List[File]] = None,
- genlist: T.Optional[T.List['GeneratedTypes']] = None,
- objlist: T.Optional[T.List[T.Union[str, 'File', 'ExtractedObjects']]] = None,
- recursive: bool = True):
- self.target = target
- self.recursive = recursive
- self.srclist: T.List[File] = srclist if srclist is not None else []
- self.genlist: T.List['GeneratedTypes'] = genlist if genlist is not None else []
- self.objlist: T.Optional[T.List[T.Union[str, 'File', 'ExtractedObjects']]] = \
- objlist if objlist is not None else []
+ target: 'BuildTarget'
+ srclist: T.List[File] = field(default_factory=list)
+ genlist: T.List['GeneratedTypes'] = field(default_factory=list)
+ objlist: T.List[T.Union[str, 'File', 'ExtractedObjects']] = field(default_factory=list)
+ recursive: bool = True
+
+ def __post_init__(self) -> None:
if self.target.is_unity:
self.check_unity_compatible()
@@ -523,23 +505,25 @@ class EnvironmentVariables(HoldableObject):
env[name] = method(env, name, values, separator)
return env
+@dataclass(eq=False)
class Target(HoldableObject):
# TODO: should Target be an abc.ABCMeta?
- def __init__(self, name: str, subdir: str, subproject: str, build_by_default: bool, for_machine: MachineChoice):
- if has_path_sep(name):
+ name: str
+ subdir: str
+ subproject: str
+ build_by_default: bool
+ for_machine: MachineChoice
+
+ def __post_init__(self) -> None:
+ if has_path_sep(self.name):
# Fix failing test 53 when this becomes an error.
mlog.warning(textwrap.dedent(f'''\
- Target "{name}" has a path separator in its name.
+ Target "{self.name}" has a path separator in its name.
This is not supported, it can cause unexpected failures and will become
a hard error in the future.\
'''))
- self.name = name
- self.subdir = subdir
- self.subproject = subproject
- self.build_by_default = build_by_default
- self.for_machine = for_machine
self.install = False
self.build_always_stale = False
self.option_overrides_base: T.Dict[OptionKey, str] = {}
@@ -548,6 +532,7 @@ class Target(HoldableObject):
if not hasattr(self, 'typename'):
raise RuntimeError(f'Target type is not set for target class "{type(self).__name__}". This is a bug')
+ # dataclass comparators?
def __lt__(self, other: object) -> bool:
if not isinstance(other, Target):
return NotImplemented
@@ -1619,6 +1604,7 @@ class Generator(HoldableObject):
def __init__(self, exe: T.Union['Executable', programs.ExternalProgram],
arguments: T.List[str],
output: T.List[str],
+ # how2dataclass
*,
depfile: T.Optional[str] = None,
capture: bool = False,
@@ -1691,24 +1677,27 @@ class Generator(HoldableObject):
return output
+@dataclass(eq=False)
class GeneratedList(HoldableObject):
"""The output of generator.process."""
- def __init__(self, generator: Generator, subdir: str,
- preserve_path_from: T.Optional[str],
- extra_args: T.List[str]):
- self.generator = generator
- self.name = generator.exe
+ generator: Generator
+ subdir: str
+ preserve_path_from: T.Optional[str]
+ extra_args: T.List[str]
+
+ def __post_init__(self) -> None:
+ self.name = self.generator.exe
self.depends: T.Set['CustomTarget'] = set() # Things this target depends on (because e.g. a custom target was used as input)
- self.subdir = subdir
self.infilelist: T.List['File'] = []
self.outfilelist: T.List[str] = []
self.outmap: T.Dict[File, T.List[str]] = {}
self.extra_depends = [] # XXX: Doesn't seem to be used?
self.depend_files: T.List[File] = []
- self.preserve_path_from = preserve_path_from
- self.extra_args: T.List[str] = extra_args if extra_args is not None else []
+
+ if self.extra_args is None:
+ self.extra_args: T.List[str] = []
if isinstance(self.generator.exe, programs.ExternalProgram):
if not self.generator.exe.found():
@@ -2712,6 +2701,7 @@ class Jar(BuildTarget):
return ['-cp', os.pathsep.join(cp_paths)]
return []
+@dataclass(eq=False)
class CustomTargetIndex(HoldableObject):
"""A special opaque object returned by indexing a CustomTarget. This object
@@ -2720,11 +2710,12 @@ class CustomTargetIndex(HoldableObject):
the sources.
"""
- def __init__(self, target: CustomTarget, output: str):
+ target: CustomTarget
+ output: str
+
+ def __post_init__(self) -> None:
self.typename = 'custom'
- self.target = target
- self.output = output
- self.for_machine = target.for_machine
+ self.for_machine = self.target.for_machine
@property
def name(self) -> str:
@@ -2800,45 +2791,41 @@ class ConfigurationData(HoldableObject):
# A bit poorly named, but this represents plain data files to copy
# during install.
+@dataclass(eq=False)
class Data(HoldableObject):
- def __init__(self, sources: T.List[File], install_dir: str, install_dir_name: str,
- install_mode: 'FileMode', subproject: str,
- rename: T.List[str] = None,
- install_tag: T.Optional[str] = None,
- data_type: str = None):
- self.sources = sources
- self.install_dir = install_dir
- self.install_dir_name = install_dir_name
- self.install_mode = install_mode
- self.install_tag = install_tag
- if rename is None:
+ sources: T.List[File]
+ install_dir: str
+ install_dir_name: str
+ install_mode: 'FileMode'
+ subproject: str
+ rename: T.List[str] = None
+ install_tag: T.Optional[str] = None
+ data_type: str = None
+
+ def __post_init__(self) -> None:
+ if self.rename is None:
self.rename = [os.path.basename(f.fname) for f in self.sources]
- else:
- self.rename = rename
- self.subproject = subproject
- self.data_type = data_type
+@dataclass(eq=False)
class SymlinkData(HoldableObject):
- def __init__(self, target: str, name: str, install_dir: str,
- subproject: str, install_tag: T.Optional[str] = None):
- self.target = target
- if name != os.path.basename(name):
- raise InvalidArguments(f'Link name is "{name}", but link names cannot contain path separators. '
+ target: str
+ name: str
+ install_dir: str
+ subproject: str
+ install_tag: T.Optional[str] = None
+
+ def __post_init__(self) -> None:
+ if self.name != os.path.basename(self.name):
+ raise InvalidArguments(f'Link name is "{self.name}", but link names cannot contain path separators. '
'The dir part should be in install_dir.')
- self.name = name
- self.install_dir = install_dir
- self.subproject = subproject
- self.install_tag = install_tag
+@dataclass(eq=False)
class TestSetup:
- def __init__(self, exe_wrapper: T.List[str], gdb: bool,
- timeout_multiplier: int, env: EnvironmentVariables,
- exclude_suites: T.List[str]):
- self.exe_wrapper = exe_wrapper
- self.gdb = gdb
- self.timeout_multiplier = timeout_multiplier
- self.env = env
- self.exclude_suites = exclude_suites
+ exe_wrapper: T.List[str]
+ gdb: bool
+ timeout_multiplier: int
+ env: EnvironmentVariables
+ exclude_suites: T.List[str]
def get_sources_string_names(sources, backend):
'''
diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py
index 3a2923b..1b5f728 100644
--- a/mesonbuild/envconfig.py
+++ b/mesonbuild/envconfig.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from dataclasses import dataclass
import subprocess
import typing as T
from enum import Enum
@@ -234,27 +235,15 @@ class Properties:
def get(self, key: str, default: T.Optional[T.Union[str, bool, int, T.List[str]]] = None) -> T.Optional[T.Union[str, bool, int, T.List[str]]]:
return self.properties.get(key, default)
+@dataclass(unsafe_hash=True)
class MachineInfo(HoldableObject):
- def __init__(self, system: str, cpu_family: str, cpu: str, endian: str):
- self.system = system
- self.cpu_family = cpu_family
- self.cpu = cpu
- self.endian = endian
- self.is_64_bit = cpu_family in CPU_FAMILIES_64_BIT # type: bool
+ system: str
+ cpu_family: str
+ cpu: str
+ endian: str
- def __eq__(self, other: object) -> bool:
- if not isinstance(other, MachineInfo):
- return NotImplemented
- return \
- self.system == other.system and \
- self.cpu_family == other.cpu_family and \
- self.cpu == other.cpu and \
- self.endian == other.endian
-
- def __ne__(self, other: object) -> bool:
- if not isinstance(other, MachineInfo):
- return NotImplemented
- return not self.__eq__(other)
+ def __post_init__(self) -> None:
+ self.is_64_bit: bool = self.cpu_family in CPU_FAMILIES_64_BIT
def __repr__(self) -> str:
return f'<MachineInfo: {self.system} {self.cpu_family} ({self.cpu})>'
diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py
index 0f493f2..c6fcaa0 100644
--- a/mesonbuild/interpreterbase/decorators.py
+++ b/mesonbuild/interpreterbase/decorators.py
@@ -19,6 +19,7 @@ from .exceptions import InterpreterException, InvalidArguments
from .operator import MesonOperator
from ._unholder import _unholder
+from dataclasses import dataclass
from functools import wraps
import abc
import itertools
@@ -99,10 +100,9 @@ def disablerIfNotFound(f: TV_func) -> TV_func:
return ret
return T.cast(TV_func, wrapped)
+@dataclass(repr=False, eq=False)
class permittedKwargs:
-
- def __init__(self, permitted: T.Set[str]):
- self.permitted = permitted # type: T.Set[str]
+ permitted: T.Set[str]
def __call__(self, f: TV_func) -> TV_func:
@wraps(f)
@@ -575,6 +575,7 @@ def typed_kwargs(name: str, *types: KwargInfo) -> T.Callable[..., T.Any]:
return inner
+# This cannot be a dataclass due to https://github.com/python/mypy/issues/5374
class FeatureCheckBase(metaclass=abc.ABCMeta):
"Base class for feature version checks"
@@ -738,6 +739,7 @@ class FeatureDeprecated(FeatureCheckBase):
mlog.warning(*args, location=self.location)
+# This cannot be a dataclass due to https://github.com/python/mypy/issues/5374
class FeatureCheckKwargsBase(metaclass=abc.ABCMeta):
@property
diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py
index c470e29..735766e 100644
--- a/mesonbuild/mparser.py
+++ b/mesonbuild/mparser.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from dataclasses import dataclass
import re
import codecs
import textwrap
@@ -87,15 +88,15 @@ class BlockParseException(MesonException):
TV_TokenTypes = T.TypeVar('TV_TokenTypes', int, str, bool)
+@dataclass(eq=False)
class Token(T.Generic[TV_TokenTypes]):
- def __init__(self, tid: str, filename: str, line_start: int, lineno: int, colno: int, bytespan: T.Tuple[int, int], value: TV_TokenTypes):
- self.tid = tid # type: str
- self.filename = filename # type: str
- self.line_start = line_start # type: int
- self.lineno = lineno # type: int
- self.colno = colno # type: int
- self.bytespan = bytespan # type: T.Tuple[int, int]
- self.value = value # type: TV_TokenTypes
+ tid: str
+ filename: str
+ line_start: int
+ lineno: int
+ colno: int
+ bytespan: T.Tuple[int, int]
+ value: TV_TokenTypes
def __eq__(self, other: object) -> bool:
if isinstance(other, str):
@@ -237,13 +238,19 @@ class Lexer:
if not matched:
raise ParseException('lexer', self.getline(line_start), lineno, col)
+@dataclass(eq=False)
class BaseNode:
- def __init__(self, lineno: int, colno: int, filename: str, end_lineno: T.Optional[int] = None, end_colno: T.Optional[int] = None):
- self.lineno = lineno # type: int
- self.colno = colno # type: int
- self.filename = filename # type: str
- self.end_lineno = end_lineno if end_lineno is not None else self.lineno
- self.end_colno = end_colno if end_colno is not None else self.colno
+ lineno: int
+ colno: int
+ filename: str
+ end_lineno: T.Optional[int] = None
+ end_colno: T.Optional[int] = None
+
+ def __post_init__(self) -> None:
+ if self.end_lineno is None:
+ self.end_lineno = self.lineno
+ if self.end_colno is None:
+ self.end_colno = self.colno
# Attributes for the visitors
self.level = 0 # type: int
diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py
index 67bfca8..4eb3632 100755
--- a/mesonbuild/msubprojects.py
+++ b/mesonbuild/msubprojects.py
@@ -1,3 +1,4 @@
+from dataclasses import dataclass, InitVar
import os, subprocess
import argparse
import asyncio
@@ -94,18 +95,22 @@ class Logger:
self.print_progress()
+@dataclass(eq=False)
class Runner:
- def __init__(self, logger: Logger, r: Resolver, wrap: PackageDefinition, repo_dir: str, options: 'Arguments') -> None:
+ logger: Logger
+ r: InitVar[Resolver]
+ wrap: PackageDefinition
+ repo_dir: str
+ options: 'Arguments'
+
+ def __post_init__(self, r: Resolver) -> None:
# FIXME: Do a copy because Resolver.resolve() is stateful method that
# cannot be called from multiple threads.
self.wrap_resolver = copy.copy(r)
- self.wrap_resolver.dirname = os.path.join(r.subdir_root, wrap.directory)
- self.wrap = self.wrap_resolver.wrap = wrap
- self.repo_dir = repo_dir
- self.options = options
- self.run_method: T.Callable[[], bool] = options.subprojects_func.__get__(self) # type: ignore
+ self.wrap_resolver.dirname = os.path.join(r.subdir_root, self.wrap.directory)
+ self.wrap_resolver.wrap = self.wrap
+ self.run_method: T.Callable[[], bool] = self.options.subprojects_func.__get__(self) # type: ignore
self.log_queue: T.List[T.Tuple[mlog.TV_LoggableList, T.Any]] = []
- self.logger = logger
def log(self, *args: mlog.TV_Loggable, **kwargs: T.Any) -> None:
self.log_queue.append((list(args), kwargs))
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index f2b59bd..80756af 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -14,6 +14,7 @@
from .. import mlog
import contextlib
+from dataclasses import dataclass
import urllib.request
import urllib.error
import urllib.parse
@@ -203,13 +204,15 @@ def verbose_git(cmd: T.List[str], workingdir: str, check: bool = False) -> bool:
except mesonlib.GitException as e:
raise WrapException(str(e))
+@dataclass(eq=False)
class Resolver:
- def __init__(self, source_dir: str, subdir: str, subproject: str = '', wrap_mode: WrapMode = WrapMode.default) -> None:
- self.source_dir = source_dir
- self.subdir = subdir
- self.subproject = subproject
- self.wrap_mode = wrap_mode
- self.subdir_root = os.path.join(source_dir, subdir)
+ source_dir: str
+ subdir: str
+ subproject: str = ''
+ wrap_mode: WrapMode = WrapMode.default
+
+ def __post_init__(self) -> None:
+ self.subdir_root = os.path.join(self.source_dir, self.subdir)
self.cachedir = os.path.join(self.subdir_root, 'packagecache')
self.wraps = {} # type: T.Dict[str, PackageDefinition]
self.provided_deps = {} # type: T.Dict[str, PackageDefinition]