diff options
author | Eli Schwartz <eschwartz@archlinux.org> | 2021-12-04 21:11:51 -0500 |
---|---|---|
committer | Eli Schwartz <eschwartz@archlinux.org> | 2022-01-10 18:36:57 -0500 |
commit | 4b351aef26a19b4c73f6ef295f64da1c74bc713d (patch) | |
tree | 44f0b5c34c6bfb06360ffc22870ff423fc9305e2 /mesonbuild/build.py | |
parent | 98a41ec24e77d7670ea83fd986853d0fe7cb2f5b (diff) | |
download | meson-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.py | 229 |
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): ''' |