typing: Fully annotate dependencies.base

pull/8846/head
Daniel Mensinger 4 years ago committed by Dylan Baker
parent f502a38d1c
commit df4d2bd247
  1. 128
      mesonbuild/dependencies/base.py
  2. 1
      run_mypy.py

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

@ -18,6 +18,7 @@ modules = [
# specific files # specific files
'mesonbuild/arglist.py', 'mesonbuild/arglist.py',
# 'mesonbuild/coredata.py', # 'mesonbuild/coredata.py',
'mesonbuild/dependencies/base.py',
'mesonbuild/dependencies/boost.py', 'mesonbuild/dependencies/boost.py',
'mesonbuild/dependencies/hdf5.py', 'mesonbuild/dependencies/hdf5.py',
'mesonbuild/dependencies/mpi.py', 'mesonbuild/dependencies/mpi.py',

Loading…
Cancel
Save