From df4d2bd24729a06c604a52aad4b44c5ddbd608fa Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Tue, 1 Jun 2021 11:23:12 +0200 Subject: typing: Fully annotate dependencies.base --- mesonbuild/dependencies/base.py | 128 ++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 64 deletions(-) (limited to 'mesonbuild/dependencies/base.py') diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index f18a7cc..9b835d0 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -15,30 +15,21 @@ # This file contains the detection logic for external dependencies. # Custom logic for several other packages are in separate files. import copy -import functools import os -import re import itertools -import json -import shlex -import shutil -import textwrap import typing as T from enum import Enum -from pathlib import Path, PurePath from .. import mlog -from .. import mesonlib from ..compilers import clib_langs -from ..environment import Environment, MachineInfo -from ..mesonlib import MachineChoice, MesonException, OrderedSet, PerMachine -from ..mesonlib import Popen_safe, version_compare_many, version_compare, listify, stringlistify, extract_as_list, split_args -from ..mesonlib import Version, LibType, OptionKey -from ..programs import ExternalProgram, find_external_program +from ..mesonlib import MachineChoice, MesonException +from ..mesonlib import version_compare_many from ..interpreterbase import FeatureDeprecated if T.TYPE_CHECKING: from ..compilers.compilers import Compiler + from ..environment import Environment + from ..build import BuildTarget DependencyType = T.TypeVar('DependencyType', bound='Dependency') @@ -72,7 +63,7 @@ class DependencyMethods(Enum): class Dependency: @classmethod - def _process_include_type_kw(cls, kwargs) -> str: + def _process_include_type_kw(cls, kwargs: T.Dict[str, T.Any]) -> str: if 'include_type' not in kwargs: return 'preserve' if not isinstance(kwargs['include_type'], str): @@ -81,25 +72,24 @@ class Dependency: raise DependencyException("include_type may only be one of ['preserve', 'system', 'non-system']") return kwargs['include_type'] - def __init__(self, type_name, kwargs): + def __init__(self, type_name: str, kwargs: T.Dict[str, T.Any]) -> None: self.name = "null" - self.version = None # type: T.Optional[str] - self.language = None # None means C-like + self.version: T.Optional[str] = None + self.language: T.Optional[str] = None # None means C-like self.is_found = False self.type_name = type_name - self.compile_args = [] # type: T.List[str] - self.link_args = [] + self.compile_args: T.List[str] = [] + self.link_args: T.List[str] = [] # Raw -L and -l arguments without manual library searching # If None, self.link_args will be used - self.raw_link_args = None - self.sources = [] + self.raw_link_args: T.Optional[T.List[str]] = None + self.sources: T.List[str] = [] self.methods = process_method_kw(self.get_methods(), kwargs) self.include_type = self._process_include_type_kw(kwargs) - self.ext_deps = [] # type: T.List[Dependency] + self.ext_deps: T.List[Dependency] = [] - def __repr__(self): - s = '<{0} {1}: {2}>' - return s.format(self.__class__.__name__, self.name, self.is_found) + def __repr__(self) -> str: + return f'<{self.__class__.__name__} {self.name}: {self.is_found}>' def is_built(self) -> bool: return False @@ -109,7 +99,7 @@ class Dependency: return mlog.red('NO') if not self.version: return mlog.green('YES') - return mlog.AnsiText(mlog.green('YES'), ' ', mlog.cyan(self.version)) + return mlog.AnsiText([mlog.green('YES'), ' ', mlog.cyan(self.version)]) def get_compile_args(self) -> T.List[str]: if self.include_type == 'system': @@ -135,7 +125,7 @@ class Dependency: return list(itertools.chain(self.get_compile_args(), *[d.get_all_compile_args() for d in self.ext_deps])) - def get_link_args(self, raw: bool = False) -> T.List[str]: + def get_link_args(self, language: T.Optional[str] = None, raw: bool = False) -> T.List[str]: if raw and self.raw_link_args is not None: return self.raw_link_args return self.link_args @@ -148,16 +138,16 @@ class Dependency: def found(self) -> bool: return self.is_found - def get_sources(self): + def get_sources(self) -> T.List[str]: """Source files that need to be added to the target. As an example, gtest-all.cc when using GTest.""" return self.sources @staticmethod - def get_methods(): + def get_methods() -> T.List[DependencyMethods]: return [DependencyMethods.AUTO] - def get_name(self): + def get_name(self) -> str: return self.name def get_version(self) -> str: @@ -169,18 +159,18 @@ class Dependency: def get_include_type(self) -> str: return self.include_type - def get_exe_args(self, compiler): + def get_exe_args(self, compiler: 'Compiler') -> T.List[str]: return [] def get_pkgconfig_variable(self, variable_name: str, kwargs: T.Dict[str, T.Any]) -> str: raise DependencyException(f'{self.name!r} is not a pkgconfig dependency') - def get_configtool_variable(self, variable_name): + def get_configtool_variable(self, variable_name: str) -> str: raise DependencyException(f'{self.name!r} is not a config-tool dependency') def get_partial_dependency(self, *, compile_args: bool = False, link_args: bool = False, links: bool = False, - includes: bool = False, sources: bool = False): + includes: bool = False, sources: bool = False) -> 'Dependency': """Create a new dependency that contains part of the parent dependency. The following options can be inherited: @@ -220,14 +210,16 @@ class Dependency: return default_value raise DependencyException(f'No default provided for dependency {self!r}, which is not pkg-config, cmake, or config-tool based.') - def generate_system_dependency(self, include_type: str) -> T.Type['Dependency']: + def generate_system_dependency(self, include_type: str) -> 'Dependency': new_dep = copy.deepcopy(self) new_dep.include_type = self._process_include_type_kw({'include_type': include_type}) return new_dep class InternalDependency(Dependency): - def __init__(self, version, incdirs, compile_args, link_args, libraries, - whole_libraries, sources, ext_deps, variables: T.Dict[str, T.Any]): + def __init__(self, version: str, incdirs: T.List[str], compile_args: T.List[str], + link_args: T.List[str], libraries: T.List['BuildTarget'], + whole_libraries: T.List['BuildTarget'], sources: T.List[str], + ext_deps: T.List[Dependency], variables: T.Dict[str, T.Any]): super().__init__('internal', {}) self.version = version self.is_found = True @@ -240,8 +232,9 @@ class InternalDependency(Dependency): self.ext_deps = ext_deps self.variables = variables - def __deepcopy__(self, memo: dict) -> 'InternalDependency': + def __deepcopy__(self, memo: T.Dict[int, 'InternalDependency']) -> 'InternalDependency': result = self.__class__.__new__(self.__class__) + assert isinstance(result, InternalDependency) memo[id(self)] = result for k, v in self.__dict__.items(): if k in ['libraries', 'whole_libraries']: @@ -264,13 +257,13 @@ class InternalDependency(Dependency): raise DependencyException('Method "get_pkgconfig_variable()" is ' 'invalid for an internal dependency') - def get_configtool_variable(self, variable_name): + def get_configtool_variable(self, variable_name: str) -> str: raise DependencyException('Method "get_configtool_variable()" is ' 'invalid for an internal dependency') def get_partial_dependency(self, *, compile_args: bool = False, link_args: bool = False, links: bool = False, - includes: bool = False, sources: bool = False): + includes: bool = False, sources: bool = False) -> 'InternalDependency': final_compile_args = self.compile_args.copy() if compile_args else [] final_link_args = self.link_args.copy() if link_args else [] final_libraries = self.libraries.copy() if links else [] @@ -291,10 +284,16 @@ class InternalDependency(Dependency): pkgconfig_define: T.Optional[T.List[str]] = None) -> T.Union[str, T.List[str]]: val = self.variables.get(internal, default_value) if val is not None: - return val + # TODO: Try removing this assert by better typing self.variables + if isinstance(val, str): + return val + if isinstance(val, list): + for i in val: + assert isinstance(i, str) + return val raise DependencyException(f'Could not get an internal variable and no default provided for {self!r}') - def generate_link_whole_dependency(self) -> T.Type['Dependency']: + def generate_link_whole_dependency(self) -> Dependency: new_dep = copy.deepcopy(self) new_dep.whole_libraries += new_dep.libraries new_dep.libraries = [] @@ -308,7 +307,7 @@ class HasNativeKwarg: return MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST class ExternalDependency(Dependency, HasNativeKwarg): - def __init__(self, type_name, environment: Environment, kwargs, language: T.Optional[str] = None): + def __init__(self, type_name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None): Dependency.__init__(self, type_name, kwargs) self.env = environment self.name = type_name # default @@ -326,12 +325,12 @@ class ExternalDependency(Dependency, HasNativeKwarg): HasNativeKwarg.__init__(self, kwargs) self.clib_compiler = detect_compiler(self.name, environment, self.for_machine, self.language) - def get_compiler(self): + def get_compiler(self) -> 'Compiler': return self.clib_compiler def get_partial_dependency(self, *, compile_args: bool = False, link_args: bool = False, links: bool = False, - includes: bool = False, sources: bool = False): + includes: bool = False, sources: bool = False) -> Dependency: new = copy.copy(self) if not compile_args: new.compile_args = [] @@ -340,32 +339,32 @@ class ExternalDependency(Dependency, HasNativeKwarg): if not sources: new.sources = [] if not includes: - new.include_directories = [] + pass # TODO maybe filter compile_args? if not sources: new.sources = [] return new - def log_details(self): + def log_details(self) -> str: return '' - def log_info(self): + def log_info(self) -> str: return '' - def log_tried(self): + def log_tried(self) -> str: return '' # Check if dependency version meets the requirements - def _check_version(self): + def _check_version(self) -> None: if not self.is_found: return if self.version_reqs: # an unknown version can never satisfy any requirement if not self.version: - found_msg = ['Dependency', mlog.bold(self.name), 'found:'] - found_msg += [mlog.red('NO'), 'unknown version, but need:', - self.version_reqs] + found_msg: mlog.TV_LoggableList = [] + found_msg += ['Dependency', mlog.bold(self.name), 'found:'] + found_msg += [mlog.red('NO'), 'unknown version, but need:', self.version_reqs] mlog.log(*found_msg) if self.required: @@ -392,7 +391,7 @@ class ExternalDependency(Dependency, HasNativeKwarg): class NotFoundDependency(Dependency): - def __init__(self, environment): + def __init__(self, environment: 'Environment') -> None: super().__init__('not-found', {}) self.env = environment self.name = 'not-found' @@ -400,12 +399,13 @@ class NotFoundDependency(Dependency): def get_partial_dependency(self, *, compile_args: bool = False, link_args: bool = False, links: bool = False, - includes: bool = False, sources: bool = False): + includes: bool = False, sources: bool = False) -> 'NotFoundDependency': return copy.copy(self) class ExternalLibrary(ExternalDependency): - def __init__(self, name, link_args, environment, language, silent=False): + def __init__(self, name: str, link_args: T.List[str], environment: 'Environment', + language: str, silent: bool = False) -> None: super().__init__('library', environment, {}, language=language) self.name = name self.language = language @@ -419,7 +419,7 @@ class ExternalLibrary(ExternalDependency): else: mlog.log('Library', mlog.bold(name), 'found:', mlog.red('NO')) - def get_link_args(self, language=None, **kwargs): + def get_link_args(self, language: T.Optional[str] = None, raw: bool = False) -> T.List[str]: ''' External libraries detected using a compiler must only be used with compatible code. For instance, Vala libraries (.vapi files) cannot be @@ -432,11 +432,11 @@ class ExternalLibrary(ExternalDependency): if (self.language == 'vala' and language != 'vala') or \ (language == 'vala' and self.language != 'vala'): return [] - return super().get_link_args(**kwargs) + return super().get_link_args(language=language, raw=raw) def get_partial_dependency(self, *, compile_args: bool = False, link_args: bool = False, links: bool = False, - includes: bool = False, sources: bool = False): + includes: bool = False, sources: bool = False) -> 'ExternalLibrary': # External library only has link_args, so ignore the rest of the # interface. new = copy.copy(self) @@ -454,11 +454,11 @@ def sort_libpaths(libpaths: T.List[str], refpaths: T.List[str]) -> T.List[str]: if len(refpaths) == 0: return list(libpaths) - def key_func(libpath): - common_lengths = [] + def key_func(libpath: str) -> T.Tuple[int, int]: + common_lengths: T.List[int] = [] for refpath in refpaths: try: - common_path = os.path.commonpath([libpath, refpath]) + common_path: str = os.path.commonpath([libpath, refpath]) except ValueError: common_path = '' common_lengths.append(len(common_path)) @@ -468,7 +468,7 @@ def sort_libpaths(libpaths: T.List[str], refpaths: T.List[str]) -> T.List[str]: return (max_index, reversed_max_length) return sorted(libpaths, key=key_func) -def strip_system_libdirs(environment, for_machine: MachineChoice, link_args): +def strip_system_libdirs(environment: 'Environment', for_machine: MachineChoice, link_args: T.List[str]) -> T.List[str]: """Remove -L arguments. leaving these in will break builds where a user has a version of a library @@ -478,7 +478,7 @@ def strip_system_libdirs(environment, for_machine: MachineChoice, link_args): exclude = {f'-L{p}' for p in environment.get_compiler_system_dirs(for_machine)} return [l for l in link_args if l not in exclude] -def process_method_kw(possible: T.Iterable[DependencyMethods], kwargs) -> T.List[DependencyMethods]: +def process_method_kw(possible: T.Iterable[DependencyMethods], kwargs: T.Dict[str, T.Any]) -> T.List[DependencyMethods]: method = kwargs.get('method', 'auto') # type: T.Union[DependencyMethods, str] if isinstance(method, DependencyMethods): return [method] @@ -511,7 +511,7 @@ def process_method_kw(possible: T.Iterable[DependencyMethods], kwargs) -> T.List return methods -def detect_compiler(name: str, env: Environment, for_machine: MachineChoice, +def detect_compiler(name: str, env: 'Environment', for_machine: MachineChoice, language: T.Optional[str]) -> T.Optional['Compiler']: """Given a language and environment find the compiler used.""" compilers = env.coredata.compilers[for_machine] -- cgit v1.1