aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/build.py
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 /mesonbuild/build.py
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.
Diffstat (limited to 'mesonbuild/build.py')
-rw-r--r--mesonbuild/build.py229
1 files changed, 108 insertions, 121 deletions
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):
'''