compilers/mixins/clike: fix mypy issues

pull/7786/head
Dylan Baker 4 years ago
parent 4cfddd1fe6
commit bb3fe3c451
  1. 7
      mesonbuild/backend/ninjabackend.py
  2. 83
      mesonbuild/compilers/compilers.py
  3. 2
      mesonbuild/compilers/fortran.py
  4. 358
      mesonbuild/compilers/mixins/clike.py
  5. 1
      run_mypy.py
  6. 2
      run_tests.py
  7. 2
      run_unittests.py

@ -2647,10 +2647,10 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
guessed_dependencies = []
# TODO The get_library_naming requirement currently excludes link targets that use d or fortran as their main linker
if hasattr(linker, 'get_library_naming'):
search_dirs = tuple(search_dirs) + tuple(linker.get_library_dirs(self.environment))
try:
static_patterns = linker.get_library_naming(self.environment, LibType.STATIC, strict=True)
shared_patterns = linker.get_library_naming(self.environment, LibType.SHARED, strict=True)
search_dirs = tuple(search_dirs) + tuple(linker.get_library_dirs(self.environment))
for libname in libs:
# be conservative and record most likely shared and static resolution, because we don't know exactly
# which one the linker will prefer
@ -2662,6 +2662,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
guessed_dependencies.append(staticlibs.resolve().as_posix())
if sharedlibs:
guessed_dependencies.append(sharedlibs.resolve().as_posix())
except (mesonlib.MesonException, AttributeError) as e:
if 'get_library_naming' not in str(e):
raise
return guessed_dependencies + absolute_libs

@ -512,27 +512,27 @@ class Compiler(metaclass=abc.ABCMeta):
return self.default_suffix
def get_define(self, dname: str, prefix: str, env: 'Environment',
extra_args: T.Sequence[str], dependencies: T.Sequence['Dependency'],
extra_args: T.List[str], dependencies: T.List['Dependency'],
disable_cache: bool = False) -> T.Tuple[str, bool]:
raise EnvironmentException('%s does not support get_define ' % self.get_id())
def compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
guess: T.Optional[int], prefix: str, env: 'Environment',
extra_args: T.Sequence[str], dependencies: T.Sequence['Dependency']) -> int:
extra_args: T.List[str], dependencies: T.List['Dependency']) -> int:
raise EnvironmentException('%s does not support compute_int ' % self.get_id())
def compute_parameters_with_absolute_paths(self, parameter_list: T.Sequence[str],
def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
build_dir: str) -> T.List[str]:
raise EnvironmentException('%s does not support compute_parameters_with_absolute_paths ' % self.get_id())
def has_members(self, typename: str, membernames: T.Sequence[str],
def has_members(self, typename: str, membernames: T.Iterable[str],
prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.Sequence[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
extra_args: T.Optional[T.Iterable[str]] = None,
dependencies: T.Optional[T.Iterable['Dependency']] = None) -> T.Tuple[bool, bool]:
raise EnvironmentException('%s does not support has_member(s) ' % self.get_id())
def has_type(self, typename: str, prefix: str, env: 'Environment',
extra_args: T.Sequence[str], *,
extra_args: T.List[str], *,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
raise EnvironmentException('%s does not support has_type ' % self.get_id())
@ -632,14 +632,14 @@ class Compiler(metaclass=abc.ABCMeta):
raise EnvironmentException('Language %s does not support header symbol checks.' % self.get_display_language())
def compiles(self, code: str, env: 'Environment', *,
extra_args: T.Sequence[T.Union[T.Sequence[str], str]] = None,
extra_args: T.Union[None, T.List[str], CompilerArgs] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
mode: str = 'compile',
disable_cache: bool = False) -> T.Tuple[bool, bool]:
raise EnvironmentException('Language %s does not support compile checks.' % self.get_display_language())
def links(self, code: str, env: 'Environment', *,
extra_args: T.Sequence[T.Union[T.Sequence[str], str]] = None,
extra_args: T.Union[None, T.List[str], CompilerArgs] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
mode: str = 'compile',
disable_cache: bool = False) -> T.Tuple[bool, bool]:
@ -686,8 +686,10 @@ class Compiler(metaclass=abc.ABCMeta):
raise EnvironmentException('Language {} does not support library finding.'.format(self.get_display_language()))
def get_library_naming(self, env: 'Environment', libtype: LibType,
strict: bool = False) -> T.Tuple[str, ...]:
return ()
strict: bool = False) -> T.Optional[T.Tuple[str, ...]]:
raise EnvironmentException(
'Language {} does not support get_library_naming.'.format(
self.get_display_language()))
def get_program_dirs(self, env: 'Environment') -> T.List[str]:
return []
@ -728,9 +730,11 @@ class Compiler(metaclass=abc.ABCMeta):
return CompilerArgs(self, args)
@contextlib.contextmanager
def compile(self, code: 'mesonlib.FileOrString', extra_args: T.Optional[T.List[str]] = None,
def compile(self, code: 'mesonlib.FileOrString',
extra_args: T.Union[None, CompilerArgs, T.List[str]] = None,
*, mode: str = 'link', want_output: bool = False,
temp_dir: T.Optional[str] = None) -> T.Iterator[T.Optional[CompileResult]]:
# TODO: there isn't really any reason for this to be a contextmanager
if extra_args is None:
extra_args = []
try:
@ -787,9 +791,11 @@ class Compiler(metaclass=abc.ABCMeta):
@contextlib.contextmanager
def cached_compile(self, code: str, cdata: coredata.CoreData, *,
extra_args: T.Optional[T.List[str]] = None,
extra_args: T.Union[None, T.List[str], CompilerArgs] = None,
mode: str = 'link',
temp_dir: T.Optional[str] = None) -> T.Iterator[T.Optional[CompileResult]]:
# TODO: There's isn't really any reason for this to be a context manager
# Calculate the key
textra_args = tuple(extra_args) if extra_args is not None else tuple() # type: T.Tuple[str, ...]
key = (tuple(self.exelist), self.version, code, textra_args, mode) # type: coredata.CompilerCheckCacheKey
@ -994,27 +1000,38 @@ class Compiler(metaclass=abc.ABCMeta):
def get_preprocess_only_args(self) -> T.List[str]:
raise EnvironmentError('This compiler does not have a preprocessor')
def get_default_include_dirs(self) -> T.List[str]:
return []
def get_largefile_args(self) -> T.List[str]:
'''Enable transparent large-file-support for 32-bit UNIX systems'''
if not (self.get_argument_syntax() == 'msvc' or self.info.is_darwin()):
# Enable large-file support unconditionally on all platforms other
# than macOS and MSVC. macOS is now 64-bit-only so it doesn't
# need anything special, and MSVC doesn't have automatic LFS.
# You must use the 64-bit counterparts explicitly.
# glibc, musl, and uclibc, and all BSD libcs support this. On Android,
# support for transparent LFS is available depending on the version of
# Bionic: https://github.com/android/platform_bionic#32-bit-abi-bugs
# https://code.google.com/p/android/issues/detail?id=64613
#
# If this breaks your code, fix it! It's been 20+ years!
return ['-D_FILE_OFFSET_BITS=64']
# We don't enable -D_LARGEFILE64_SOURCE since that enables
# transitionary features and must be enabled by programs that use
# those features explicitly.
return []
def get_library_dirs(self, env: 'Environment',
elf_class: T.Optional[int] = None) -> T.List[str]:
return []
def find_framework_paths(self, env: 'Environment') -> T.List[str]:
raise EnvironmentException('{} does not support find_framework_paths'.format(self.id))
def attribute_check_func(self, name: str) -> str:
raise EnvironmentException('{} does not support attribute checks'.format(self.id))
def get_largefile_args(compiler: Compiler) -> T.List[str]:
'''
Enable transparent large-file-support for 32-bit UNIX systems
'''
if not (compiler.get_argument_syntax() == 'msvc' or compiler.info.is_darwin()):
# Enable large-file support unconditionally on all platforms other
# than macOS and MSVC. macOS is now 64-bit-only so it doesn't
# need anything special, and MSVC doesn't have automatic LFS.
# You must use the 64-bit counterparts explicitly.
# glibc, musl, and uclibc, and all BSD libcs support this. On Android,
# support for transparent LFS is available depending on the version of
# Bionic: https://github.com/android/platform_bionic#32-bit-abi-bugs
# https://code.google.com/p/android/issues/detail?id=64613
#
# If this breaks your code, fix it! It's been 20+ years!
return ['-D_FILE_OFFSET_BITS=64']
# We don't enable -D_LARGEFILE64_SOURCE since that enables
# transitionary features and must be enabled by programs that use
# those features explicitly.
return []
def get_args_from_envvars(lang: str,

@ -143,7 +143,7 @@ class FortranCompiler(CLikeCompiler, Compiler):
def find_library(self, libname, env, extra_dirs, libtype: LibType = LibType.PREFER_SHARED):
code = 'stop; end program'
return self.find_library_impl(libname, env, extra_dirs, code, libtype)
return self._find_library_impl(libname, env, extra_dirs, code, libtype)
def has_multi_arguments(self, args: T.Sequence[str], env):
for arg in args[:]:

@ -20,6 +20,8 @@ of this is to have mixin's, which are classes that are designed *not* to be
standalone, they only work through inheritance.
"""
import contextlib
import collections
import functools
import glob
import itertools
@ -38,7 +40,10 @@ from .. import compilers
from .visualstudio import VisualStudioLikeCompiler
if T.TYPE_CHECKING:
from ...coredata import CoreData
from ...dependencies import Dependency, ExternalProgram
from ...environment import Environment
from ...linkers import DynamicLinker
GROUP_FLAGS = re.compile(r'''\.so (?:\.[0-9]+)? (?:\.[0-9]+)? (?:\.[0-9]+)?$ |
^(?:-Wl,)?-l |
@ -55,6 +60,9 @@ class CLikeCompilerArgs(arglist.CompilerArgs):
dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread')
def to_native(self, copy: bool = False) -> T.List[str]:
# This seems to be allowed, but could never work?
assert isinstance(self.compiler, compilers.Compiler), 'How did you get here'
# Check if we need to add --start/end-group for circular dependencies
# between static libraries, and for recursively searching for symbols
# needed by static libraries that are provided by object files or
@ -82,8 +90,8 @@ class CLikeCompilerArgs(arglist.CompilerArgs):
new.insert(group_end + 1, '-Wl,--end-group')
new.insert(group_start, '-Wl,--start-group')
# Remove system/default include paths added with -isystem
if hasattr(self.compiler, 'get_default_include_dirs'):
default_dirs = self.compiler.get_default_include_dirs()
default_dirs = self.compiler.get_default_include_dirs()
if default_dirs:
bad_idx_list = [] # type: T.List[int]
for i, each in enumerate(new):
if not each.startswith('-isystem'):
@ -111,14 +119,47 @@ class CLikeCompiler:
"""Shared bits for the C and CPP Compilers."""
if T.TYPE_CHECKING:
can_compile_suffixes = set() # type: T.Set[str]
exelist = [] # type: T.List[str]
for_machine = mesonlib.MachineChoice.HOST
id = ''
ignore_libs = () # type: T.Tuple[str, ...]
language = ''
linker = SolarisDynamicLinker([], mesonlib.MachineChoice.HOST, [], []) # type: DynamicLinker
warn_args = {} # type: T.Dict[str, T.List[str]]
@staticmethod
def attribute_check_func(name: str) -> str:...
def get_allow_undefined_link_args(self) -> T.List[str]: ...
def get_compiler_args_for_mode(self, mode: str) -> T.List[str]: ...
def get_display_language(self) -> str: ...
def get_largefile_args(self) -> T.List[str]: ...
def get_linker_always_args(self) -> T.List[str]: ...
def get_pch_suffix(self) -> str: ...
def get_id(self) -> str: ...
def remove_linkerlike_args(self, args: T.List[str]) -> T.List[str]: ...
@classmethod
def use_linker_args(cls, linker: str) -> T.List[str]: ...
@contextlib.contextmanager
def compile(self, code: 'mesonlib.FileOrString',
extra_args: T.Union[None, arglist.CompilerArgs, T.List[str]] = None,
*, mode: str = 'link', want_output: bool = False,
temp_dir: T.Optional[str] = None) -> T.Iterator[T.Optional[compilers.CompileResult]]: ...
@contextlib.contextmanager
def cached_compile(self, code: str, cdata: 'CoreData', *,
extra_args: T.Union[None, arglist.CompilerArgs, T.List[str]] = None,
mode: str = 'link',
temp_dir: T.Optional[str] = None) -> T.Iterator[T.Optional[compilers.CompileResult]]: ...
# TODO: Replace this manual cache with functools.lru_cache
library_dirs_cache = {}
program_dirs_cache = {}
find_library_cache = {}
find_framework_cache = {}
find_library_cache = {} # type: T.Dict[T.Tuple[T.Tuple[str, ...], str, T.Tuple[str, ...], str, LibType], T.Optional[T.List[str]]]
find_framework_cache = {} # type: T.Dict[T.Tuple[T.Tuple[str, ...], str, T.Tuple[str, ...], bool], T.Optional[T.List[str]]]
internal_libs = arglist.UNIXY_COMPILER_INTERNAL_LIBS
def __init__(self, is_cross: bool, exe_wrapper: T.Optional[str] = None):
def __init__(self, is_cross: bool, exe_wrapper: T.Optional['ExternalProgram'] = None):
# If a child ObjC or CPP class has already set it, don't set it ourselves
self.is_cross = is_cross
self.can_compile_suffixes.add('h')
@ -129,70 +170,72 @@ class CLikeCompiler:
else:
self.exe_wrapper = exe_wrapper.get_command()
def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CLikeCompilerArgs:
return CLikeCompilerArgs(self, args)
def compiler_args(self, args: T.Optional[T.List[str]] = None) -> CLikeCompilerArgs:
# This is correct, mypy just doesn't understand co-operative inheritance
return CLikeCompilerArgs(self, args) # type: ignore
def needs_static_linker(self):
def needs_static_linker(self) -> bool:
return True # When compiling static libraries, so yes.
def get_always_args(self):
def get_always_args(self) -> T.List[str]:
'''
Args that are always-on for all C compilers other than MSVC
'''
return ['-pipe'] + compilers.get_largefile_args(self)
return ['-pipe'] + self.get_largefile_args()
def get_no_stdinc_args(self):
def get_no_stdinc_args(self) -> T.List[str]:
return ['-nostdinc']
def get_no_stdlib_link_args(self):
def get_no_stdlib_link_args(self) -> T.List[str]:
return ['-nostdlib']
def get_warn_args(self, level):
def get_warn_args(self, level: str) -> T.List[str]:
# TODO: this should be an enum
return self.warn_args[level]
def get_no_warn_args(self):
def get_no_warn_args(self) -> T.List[str]:
# Almost every compiler uses this for disabling warnings
return ['-w']
def split_shlib_to_parts(self, fname):
def split_shlib_to_parts(self, fname: str) -> T.Tuple[T.Optional[str], str]:
return None, fname
def depfile_for_object(self, objfile):
def depfile_for_object(self, objfile: str) -> str:
return objfile + '.' + self.get_depfile_suffix()
def get_depfile_suffix(self):
def get_depfile_suffix(self) -> str:
return 'd'
def get_exelist(self):
return self.exelist[:]
def get_exelist(self) -> T.List[str]:
return self.exelist.copy()
def get_preprocess_only_args(self):
def get_preprocess_only_args(self) -> T.List[str]:
return ['-E', '-P']
def get_compile_only_args(self):
def get_compile_only_args(self) -> T.List[str]:
return ['-c']
def get_no_optimization_args(self):
def get_no_optimization_args(self) -> T.List[str]:
return ['-O0']
def get_compiler_check_args(self):
def get_compiler_check_args(self) -> T.List[str]:
'''
Get arguments useful for compiler checks such as being permissive in
the code quality and not doing any optimization.
'''
return self.get_no_optimization_args()
def get_output_args(self, target):
def get_output_args(self, target: str) -> T.List[str]:
return ['-o', target]
def get_werror_args(self):
def get_werror_args(self) -> T.List[str]:
return ['-Werror']
def get_std_exe_link_args(self):
def get_std_exe_link_args(self) -> T.List[str]:
# TODO: is this a linker property?
return []
def get_include_args(self, path, is_system):
def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
if path == '':
path = '.'
if is_system:
@ -206,7 +249,9 @@ class CLikeCompiler:
return []
@functools.lru_cache()
def get_library_dirs(self, env, elf_class = None):
def _get_library_dirs(self, env: 'Environment',
elf_class: T.Optional[int] = None) -> T.List[str]:
# TODO: replace elf_class with enum
dirs = self.get_compiler_dirs(env, 'libraries')
if elf_class is None or elf_class == 0:
return dirs
@ -241,10 +286,17 @@ class CLikeCompiler:
# Skip the file if we can't read it
pass
return tuple(retval)
return retval
def get_library_dirs(self, env: 'Environment',
elf_class: T.Optional[int] = None) -> T.List[str]:
"""Wrap the lru_cache so that we return a new copy and don't allow
mutation of the cached value.
"""
return self._get_library_dirs(env, elf_class).copy()
@functools.lru_cache()
def get_program_dirs(self, env):
def get_program_dirs(self, env: 'Environment') -> T.List[str]:
'''
Programs used by the compiler. Also where toolchain DLLs such as
libstdc++-6.dll are found with MinGW.
@ -266,7 +318,7 @@ class CLikeCompiler:
def get_linker_search_args(self, dirname: str) -> T.List[str]:
return self.linker.get_search_args(dirname)
def get_default_include_dirs(self):
def get_default_include_dirs(self) -> T.List[str]:
return []
def gen_export_dynamic_link_args(self, env: 'Environment') -> T.List[str]:
@ -275,7 +327,8 @@ class CLikeCompiler:
def gen_import_library_args(self, implibname: str) -> T.List[str]:
return self.linker.import_library_args(implibname)
def sanity_check_impl(self, work_dir, environment, sname, code):
def sanity_check_impl(self, work_dir: str, environment: 'Environment',
sname: str, code: str) -> None:
mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', ' '.join(self.exelist))
mlog.debug('Is cross compiler: %s.' % str(self.is_cross))
@ -330,18 +383,23 @@ class CLikeCompiler:
if pe.returncode != 0:
raise mesonlib.EnvironmentException('Executables created by {0} compiler {1} are not runnable.'.format(self.language, self.name_string()))
def sanity_check(self, work_dir, environment):
def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
code = 'int main(void) { int class=0; return class; }\n'
return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
def check_header(self, hname: str, prefix: str, env, *, extra_args=None, dependencies=None):
def check_header(self, hname: str, prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
fargs = {'prefix': prefix, 'header': hname}
code = '''{prefix}
#include <{header}>'''
return self.compiles(code.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
def has_header(self, hname: str, prefix: str, env, *, extra_args=None, dependencies=None, disable_cache: bool = False):
def has_header(self, hname: str, prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
disable_cache: bool = False) -> T.Tuple[bool, bool]:
fargs = {'prefix': prefix, 'header': hname}
code = '''{prefix}
#ifdef __has_include
@ -354,7 +412,10 @@ class CLikeCompiler:
return self.compiles(code.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies, mode='preprocess', disable_cache=disable_cache)
def has_header_symbol(self, hname: str, symbol: str, prefix: str, env, *, extra_args=None, dependencies=None):
def has_header_symbol(self, hname: str, symbol: str, prefix: str,
env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
t = '''{prefix}
#include <{header}>
@ -368,8 +429,9 @@ class CLikeCompiler:
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
def _get_basic_compiler_args(self, env, mode: str):
cargs, largs = [], []
def _get_basic_compiler_args(self, env: 'Environment', mode: str) -> T.Tuple[T.List[str], T.List[str]]:
cargs = [] # type: T.List[str]
largs = [] # type: T.List[str]
if mode == 'link':
# Sometimes we need to manually select the CRT to use with MSVC.
# One example is when trying to do a compiler check that involves
@ -405,20 +467,26 @@ class CLikeCompiler:
cargs += self.get_compiler_args_for_mode(mode)
return cargs, largs
def _get_compiler_check_args(self, env, extra_args: list, dependencies, mode: str = 'compile') -> T.List[str]:
def _get_compiler_check_args(self, env: 'Environment',
extra_args: T.Union[None, arglist.CompilerArgs, T.List[str]],
dependencies: T.Optional[T.List['Dependency']],
mode: str = 'compile') -> arglist.CompilerArgs:
# TODO: the caller should handle the listfing of these arguments
if extra_args is None:
extra_args = []
else:
# TODO: we want to do this in the caller
extra_args = mesonlib.listify(extra_args)
extra_args = mesonlib.listify([e(mode) if callable(e) else e for e in extra_args])
if dependencies is None:
dependencies = []
elif not isinstance(dependencies, list):
dependencies = [dependencies]
elif not isinstance(dependencies, collections.abc.Iterable):
# TODO: we want to ensure the front end does the listifing here
dependencies = [dependencies] # type: ignore
# Collect compiler arguments
cargs = self.compiler_args()
largs = []
cargs = self.compiler_args() # type: arglist.CompilerArgs
largs = [] # type: T.List[str]
for d in dependencies:
# Add compile flags needed by dependencies
cargs += d.get_compile_args()
@ -440,23 +508,40 @@ class CLikeCompiler:
args = cargs + extra_args + largs
return args
def compiles(self, code: str, env, *,
extra_args: T.Sequence[T.Union[T.Sequence[str], str]] = None,
dependencies=None, mode: str = 'compile', disable_cache: bool = False) -> T.Tuple[bool, bool]:
def compiles(self, code: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
mode: str = 'compile',
disable_cache: bool = False) -> T.Tuple[bool, bool]:
with self._build_wrapper(code, env, extra_args, dependencies, mode, disable_cache=disable_cache) as p:
return p.returncode == 0, p.cached
def _build_wrapper(self, code: str, env, extra_args, dependencies=None, mode: str = 'compile', want_output: bool = False, disable_cache: bool = False, temp_dir: str = None) -> T.Iterator[compilers.CompileResult]:
@contextlib.contextmanager
def _build_wrapper(self, code: str, env: 'Environment',
extra_args: T.Union[None, arglist.CompilerArgs, T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
mode: str = 'compile', want_output: bool = False,
disable_cache: bool = False,
temp_dir: str = None) -> T.Iterator[T.Optional[compilers.CompileResult]]:
args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
if disable_cache or want_output:
return self.compile(code, extra_args=args, mode=mode, want_output=want_output, temp_dir=env.scratch_dir)
return self.cached_compile(code, env.coredata, extra_args=args, mode=mode, temp_dir=env.scratch_dir)
def links(self, code, env, *, extra_args=None, dependencies=None, disable_cache=False):
with self.compile(code, extra_args=args, mode=mode, want_output=want_output, temp_dir=env.scratch_dir) as r:
yield r
else:
with self.cached_compile(code, env.coredata, extra_args=args, mode=mode, temp_dir=env.scratch_dir) as r:
yield r
def links(self, code: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None,
mode: str = 'compile',
disable_cache: bool = False) -> T.Tuple[bool, bool]:
return self.compiles(code, env, extra_args=extra_args,
dependencies=dependencies, mode='link', disable_cache=disable_cache)
def run(self, code: str, env, *, extra_args=None, dependencies=None):
def run(self, code: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> compilers.RunResult:
need_exe_wrapper = env.need_exe_wrapper(self.for_machine)
if need_exe_wrapper and self.exe_wrapper is None:
raise compilers.CrossNoRunException('Can not run test applications in this cross environment.')
@ -469,7 +554,7 @@ class CLikeCompiler:
if need_exe_wrapper:
cmdlist = self.exe_wrapper + [p.output_name]
else:
cmdlist = p.output_name
cmdlist = [p.output_name]
try:
pe, so, se = mesonlib.Popen_safe(cmdlist)
except Exception as e:
@ -482,7 +567,9 @@ class CLikeCompiler:
mlog.debug(se)
return compilers.RunResult(True, pe.returncode, so, se)
def _compile_int(self, expression, prefix, env, extra_args, dependencies):
def _compile_int(self, expression: str, prefix: str, env: 'Environment',
extra_args: T.Optional[T.List[str]],
dependencies: T.Optional[T.List['Dependency']]) -> bool:
fargs = {'prefix': prefix, 'expression': expression}
t = '''#include <stdio.h>
{prefix}
@ -490,7 +577,10 @@ class CLikeCompiler:
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)[0]
def cross_compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies):
def cross_compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
guess: T.Optional[int], prefix: str, env: 'Environment',
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
# Try user's guess first
if isinstance(guess, int):
if self._compile_int('%s == %d' % (expression, guess), prefix, env, extra_args, dependencies):
@ -538,7 +628,10 @@ class CLikeCompiler:
return low
def compute_int(self, expression, low, high, guess, prefix, env, *, extra_args=None, dependencies=None):
def compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
guess: T.Optional[int], prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
if self.is_cross:
@ -558,7 +651,9 @@ class CLikeCompiler:
raise mesonlib.EnvironmentException('Could not run compute_int test binary.')
return int(res.stdout)
def cross_sizeof(self, typename, prefix, env, *, extra_args=None, dependencies=None):
def cross_sizeof(self, typename: str, prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename}
@ -573,7 +668,9 @@ class CLikeCompiler:
return -1
return self.cross_compute_int('sizeof(%s)' % typename, None, None, None, prefix, env, extra_args, dependencies)
def sizeof(self, typename, prefix, env, *, extra_args=None, dependencies=None):
def sizeof(self, typename: str, prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename}
@ -594,7 +691,9 @@ class CLikeCompiler:
raise mesonlib.EnvironmentException('Could not run sizeof test binary.')
return int(res.stdout)
def cross_alignment(self, typename, prefix, env, *, extra_args=None, dependencies=None):
def cross_alignment(self, typename: str, prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename}
@ -615,7 +714,9 @@ class CLikeCompiler:
}};'''
return self.cross_compute_int('offsetof(struct tmp, target)', None, None, None, t.format(**fargs), env, extra_args, dependencies)
def alignment(self, typename, prefix, env, *, extra_args=None, dependencies=None):
def alignment(self, typename: str, prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
if self.is_cross:
@ -644,7 +745,10 @@ class CLikeCompiler:
raise mesonlib.EnvironmentException('Could not determine alignment of %s. Sorry. You might want to file a bug.' % typename)
return align
def get_define(self, dname, prefix, env, extra_args, dependencies, disable_cache=False):
def get_define(self, dname: str, prefix: str, env: 'Environment',
extra_args: T.Optional[T.List[str]],
dependencies: T.Optional[T.List['Dependency']],
disable_cache: bool = False) -> T.Tuple[str, bool]:
delim = '"MESON_GET_DEFINE_DELIMITER"'
fargs = {'prefix': prefix, 'define': dname, 'delim': delim}
code = '''
@ -655,9 +759,9 @@ class CLikeCompiler:
{delim}\n{define}'''
args = self._get_compiler_check_args(env, extra_args, dependencies,
mode='preprocess').to_native()
func = lambda: self.cached_compile(code.format(**fargs), env.coredata, extra_args=args, mode='preprocess')
func = functools.partial(self.cached_compile, code.format(**fargs), env.coredata, extra_args=args, mode='preprocess')
if disable_cache:
func = lambda: self.compile(code.format(**fargs), extra_args=args, mode='preprocess', temp_dir=env.scratch_dir)
func = functools.partial(self.compile, code.format(**fargs), extra_args=args, mode='preprocess', temp_dir=env.scratch_dir)
with func() as p:
cached = p.cached
if p.returncode != 0:
@ -665,9 +769,13 @@ class CLikeCompiler:
# Get the preprocessed value after the delimiter,
# minus the extra newline at the end and
# merge string literals.
return self.concatenate_string_literals(p.stdout.split(delim + '\n')[-1][:-1]), cached
return self._concatenate_string_literals(p.stdout.split(delim + '\n')[-1][:-1]), cached
def get_return_value(self, fname, rtype, prefix, env, extra_args, dependencies):
def get_return_value(self, fname: str, rtype: str, prefix: str,
env: 'Environment', extra_args: T.Optional[T.List[str]],
dependencies: T.Optional[T.List['Dependency']]) -> T.Union[str, int]:
# TODO: rtype should be an enum.
# TODO: maybe we can use overload to tell mypy when this will return int vs str?
if rtype == 'string':
fmt = '%s'
cast = '(char*)'
@ -695,9 +803,10 @@ class CLikeCompiler:
except ValueError:
m = 'Return value of {}() is not an int'
raise mesonlib.EnvironmentException(m.format(fname))
assert False, 'Unreachable'
@staticmethod
def _no_prototype_templ():
def _no_prototype_templ() -> T.Tuple[str, str]:
"""
Try to find the function without a prototype from a header by defining
our own dummy prototype and trying to link with the C library (and
@ -732,7 +841,7 @@ class CLikeCompiler:
return head, main
@staticmethod
def _have_prototype_templ():
def _have_prototype_templ() -> T.Tuple[str, str]:
"""
Returns a head-er and main() call that uses the headers listed by the
user for the function prototype while checking if a function exists.
@ -752,8 +861,11 @@ class CLikeCompiler:
}}'''
return head, main
def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None):
"""
def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
"""Determine if a function exists.
First, this function looks for the symbol in the default libraries
provided by the compiler (stdlib + a few others usually). If that
fails, it checks if any of the headers specified in the prefix provide
@ -773,7 +885,11 @@ class CLikeCompiler:
return val, False
raise mesonlib.EnvironmentException('Cross variable {0} is not a boolean.'.format(varname))
fargs = {'prefix': prefix, 'func': funcname}
# TODO: we really need a protocol for this,
#
# class StrProto(typing.Protocol):
# def __str__(self) -> str: ...
fargs = {'prefix': prefix, 'func': funcname} # type: T.Dict[str, T.Union[str, bool, int]]
# glibc defines functions that are not available on Linux as stubs that
# fail with ENOSYS (such as e.g. lchmod). In this case we want to fail
@ -845,7 +961,10 @@ class CLikeCompiler:
return self.links(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
def has_members(self, typename, membernames, prefix, env, *, extra_args=None, dependencies=None):
def has_members(self, typename: str, membernames: T.List[str],
prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename, 'name': 'foo'}
@ -862,7 +981,8 @@ class CLikeCompiler:
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
def has_type(self, typename, prefix, env, extra_args, dependencies=None):
def has_type(self, typename: str, prefix: str, env: 'Environment', extra_args: T.List[str],
dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
fargs = {'prefix': prefix, 'type': typename}
t = '''{prefix}
void bar(void) {{
@ -871,7 +991,7 @@ class CLikeCompiler:
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
def symbols_have_underscore_prefix(self, env):
def symbols_have_underscore_prefix(self, env: 'Environment') -> bool:
'''
Check if the compiler prefixes an underscore to global C symbols
'''
@ -906,8 +1026,8 @@ class CLikeCompiler:
return False
raise RuntimeError('BUG: {!r} check failed unexpectedly'.format(n))
def _get_patterns(self, env, prefixes, suffixes, shared=False):
patterns = []
def _get_patterns(self, env: 'Environment', prefixes: T.List[str], suffixes: T.List[str], shared: bool = False) -> T.List[str]:
patterns = [] # type: T.List[str]
for p in prefixes:
for s in suffixes:
patterns.append(p + '{}.' + s)
@ -923,7 +1043,7 @@ class CLikeCompiler:
patterns.append(p + '{}.so.[0-9]*.[0-9]*')
return patterns
def get_library_naming(self, env, libtype: LibType, strict=False):
def get_library_naming(self, env: 'Environment', libtype: LibType, strict: bool = False) -> T.Tuple[str, ...]:
'''
Get library prefixes and suffixes for the target platform ordered by
priority
@ -970,8 +1090,8 @@ class CLikeCompiler:
return tuple(patterns)
@staticmethod
def _sort_shlibs_openbsd(libs):
filtered = []
def _sort_shlibs_openbsd(libs: T.List[str]) -> T.List[str]:
filtered = [] # type: T.List[str]
for lib in libs:
# Validate file as a shared library of type libfoo.so.X.Y
ret = lib.rsplit('.so.', maxsplit=1)
@ -986,7 +1106,7 @@ class CLikeCompiler:
return sorted(filtered, key=float_cmp, reverse=True)
@classmethod
def _get_trials_from_pattern(cls, pattern, directory, libname):
def _get_trials_from_pattern(cls, pattern: str, directory: str, libname: str) -> T.List[Path]:
f = Path(directory) / pattern.format(libname)
# Globbing for OpenBSD
if '*' in pattern:
@ -996,7 +1116,7 @@ class CLikeCompiler:
return [f]
@staticmethod
def _get_file_from_list(env, files: T.List[str]) -> Path:
def _get_file_from_list(env: 'Environment', paths: T.List[Path]) -> Path:
'''
We just check whether the library exists. We can't do a link check
because the library might have unresolved symbols that require other
@ -1004,7 +1124,6 @@ class CLikeCompiler:
architecture.
'''
# If not building on macOS for Darwin, do a simple file check
paths = [Path(f) for f in files]
if not env.machines.host.is_darwin() or not env.machines.build.is_darwin():
for p in paths:
if p.is_file():
@ -1022,13 +1141,13 @@ class CLikeCompiler:
return None
@functools.lru_cache()
def output_is_64bit(self, env):
def output_is_64bit(self, env: 'Environment') -> bool:
'''
returns true if the output produced is 64-bit, false if 32-bit
'''
return self.sizeof('void *', '', env) == 8
def find_library_real(self, libname, env, extra_dirs, code, libtype: LibType):
def _find_library_real(self, libname: str, env: 'Environment', extra_dirs: T.List[str], code: str, libtype: LibType) -> T.Optional[T.List[str]]:
# First try if we can just add the library as -l.
# Gcc + co seem to prefer builtin lib dirs to -L dirs.
# Only try to find std libs if no extra dirs specified.
@ -1061,16 +1180,17 @@ class CLikeCompiler:
# Search in the specified dirs, and then in the system libraries
for d in itertools.chain(extra_dirs, self.get_library_dirs(env, elf_class)):
for p in patterns:
trial = self._get_trials_from_pattern(p, d, libname)
if not trial:
trials = self._get_trials_from_pattern(p, d, libname)
if not trials:
continue
trial = self._get_file_from_list(env, trial)
trial = self._get_file_from_list(env, trials)
if not trial:
continue
return [trial.as_posix()]
return None
def find_library_impl(self, libname, env, extra_dirs, code, libtype: LibType):
def _find_library_impl(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
code: str, libtype: LibType) -> T.Optional[T.List[str]]:
# These libraries are either built-in or invalid
if libname in self.ignore_libs:
return []
@ -1078,24 +1198,26 @@ class CLikeCompiler:
extra_dirs = [extra_dirs]
key = (tuple(self.exelist), libname, tuple(extra_dirs), code, libtype)
if key not in self.find_library_cache:
value = self.find_library_real(libname, env, extra_dirs, code, libtype)
value = self._find_library_real(libname, env, extra_dirs, code, libtype)
self.find_library_cache[key] = value
else:
value = self.find_library_cache[key]
if value is None:
return None
return value[:]
return value.copy()
def find_library(self, libname, env, extra_dirs, libtype: LibType = LibType.PREFER_SHARED):
def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
libtype: LibType = LibType.PREFER_SHARED) -> T.Optional[T.List[str]]:
code = 'int main(void) { return 0; }\n'
return self.find_library_impl(libname, env, extra_dirs, code, libtype)
return self._find_library_impl(libname, env, extra_dirs, code, libtype)
def find_framework_paths(self, env):
def find_framework_paths(self, env: 'Environment') -> T.List[str]:
'''
These are usually /Library/Frameworks and /System/Library/Frameworks,
unless you select a particular macOS SDK with the -isysroot flag.
You can also add to this by setting -F in CFLAGS.
'''
# TODO: this really needs to be *AppleClang*, not just any clang.
if self.id != 'clang':
raise mesonlib.MesonException('Cannot find framework path with non-clang compiler')
# Construct the compiler command-line
@ -1107,7 +1229,7 @@ class CLikeCompiler:
os_env = os.environ.copy()
os_env['LC_ALL'] = 'C'
_, _, stde = mesonlib.Popen_safe(commands, env=os_env, stdin=subprocess.PIPE)
paths = []
paths = [] # T.List[str]
for line in stde.split('\n'):
if '(framework directory)' not in line:
continue
@ -1116,7 +1238,7 @@ class CLikeCompiler:
paths.append(line[:-21].strip())
return paths
def find_framework_real(self, name, env, extra_dirs, allow_system):
def _find_framework_real(self, name: str, env: 'Environment', extra_dirs: T.List[str], allow_system: bool) -> T.Optional[T.List[str]]:
code = 'int main(void) { return 0; }'
link_args = []
for d in extra_dirs:
@ -1127,36 +1249,43 @@ class CLikeCompiler:
link_args += ['-framework', name]
if self.links(code, env, extra_args=(extra_args + link_args), disable_cache=True)[0]:
return link_args
return None
def find_framework_impl(self, name, env, extra_dirs, allow_system):
def _find_framework_impl(self, name: str, env: 'Environment', extra_dirs: T.List[str],
allow_system: bool) -> T.Optional[T.List[str]]:
if isinstance(extra_dirs, str):
extra_dirs = [extra_dirs]
key = (tuple(self.exelist), name, tuple(extra_dirs), allow_system)
if key in self.find_framework_cache:
value = self.find_framework_cache[key]
else:
value = self.find_framework_real(name, env, extra_dirs, allow_system)
value = self._find_framework_real(name, env, extra_dirs, allow_system)
self.find_framework_cache[key] = value
if value is None:
return None
return value[:]
return value.copy()
def find_framework(self, name, env, extra_dirs, allow_system=True):
def find_framework(self, name: str, env: 'Environment', extra_dirs: T.List[str],
allow_system: bool = True) -> T.Optional[T.List[str]]:
'''
Finds the framework with the specified name, and returns link args for
the same or returns None when the framework is not found.
'''
# TODO: maybe this belongs in clang? also, should probably check for macOS?
if self.id != 'clang':
raise mesonlib.MesonException('Cannot find frameworks with non-clang compiler')
return self.find_framework_impl(name, env, extra_dirs, allow_system)
return self._find_framework_impl(name, env, extra_dirs, allow_system)
def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
# TODO: does this belong here or in GnuLike or maybe PosixLike?
return []
def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]:
# TODO: does this belong here or in GnuLike or maybe PosixLike?
return []
def thread_flags(self, env):
def thread_flags(self, env: 'Environment') -> T.List[str]:
# TODO: does this belong here or in GnuLike or maybe PosixLike?
host_m = env.machines[self.for_machine]
if host_m.is_haiku() or host_m.is_darwin():
return []
@ -1165,19 +1294,21 @@ class CLikeCompiler:
def thread_link_flags(self, env: 'Environment') -> T.List[str]:
return self.linker.thread_flags(env)
def linker_to_compiler_args(self, args):
return args
def linker_to_compiler_args(self, args: T.List[str]) -> T.List[str]:
return args.copy()
def has_arguments(self, args: T.Sequence[str], env, code: str, mode: str) -> T.Tuple[bool, bool]:
def has_arguments(self, args: T.List[str], env: 'Environment', code: str,
mode: str) -> T.Tuple[bool, bool]:
return self.compiles(code, env, extra_args=args, mode=mode)
def has_multi_arguments(self, args, env):
for arg in args[:]:
def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
new_args = [] # type: T.List[str]
for arg in args:
# some compilers, e.g. GCC, don't warn for unsupported warning-disable
# flags, so when we are testing a flag like "-Wno-forgotten-towel", also
# check the equivalent enable flag too "-Wforgotten-towel"
if arg.startswith('-Wno-'):
args.append('-W' + arg[5:])
new_args.append('-W' + arg[5:])
if arg.startswith('-Wl,'):
mlog.warning('{} looks like a linker argument, '
'but has_argument and other similar methods only '
@ -1187,10 +1318,11 @@ class CLikeCompiler:
'the compiler you are using. has_link_argument or '
'other similar method can be used instead.'
.format(arg))
new_args.append(arg)
code = 'extern int i;\nint i;\n'
return self.has_arguments(args, env, code, mode='compile')
return self.has_arguments(new_args, env, code, mode='compile')
def has_multi_link_arguments(self, args, env):
def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
# First time we check for link flags we need to first check if we have
# --fatal-warnings, otherwise some linker checks could give some
# false positive.
@ -1200,7 +1332,7 @@ class CLikeCompiler:
return self.has_arguments(args, env, code, mode='link')
@staticmethod
def concatenate_string_literals(s):
def _concatenate_string_literals(s: str) -> str:
pattern = re.compile(r'(?P<pre>.*([^\\]")|^")(?P<str1>([^\\"]|\\.)*)"\s+"(?P<str2>([^\\"]|\\.)*)(?P<post>".*)')
ret = s
m = pattern.match(ret)
@ -1209,13 +1341,13 @@ class CLikeCompiler:
m = pattern.match(ret)
return ret
def get_has_func_attribute_extra_args(self, name):
def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
# Most compilers (such as GCC and Clang) only warn about unknown or
# ignored attributes, so force an error. Overriden in GCC and Clang
# mixins.
return ['-Werror']
def has_func_attribute(self, name, env):
def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]:
# Just assume that if we're not on windows that dllimport and dllexport
# don't work
m = env.machines[self.for_machine]

@ -18,6 +18,7 @@ modules = [
'mesonbuild/compilers/mixins/arm.py',
'mesonbuild/compilers/mixins/c2000.py',
'mesonbuild/compilers/mixins/ccrx.py',
'mesonbuild/compilers/mixins/clike.py',
# 'mesonbuild/compilers/mixins/intel.py',
# 'mesonbuild/coredata.py',
'mesonbuild/dependencies/boost.py',

@ -263,8 +263,6 @@ def run_mtest_inprocess(commandlist):
return returncode, stdout.getvalue(), stderr.getvalue()
def clear_meson_configure_class_caches():
compilers.CCompiler.library_dirs_cache = {}
compilers.CCompiler.program_dirs_cache = {}
compilers.CCompiler.find_library_cache = {}
compilers.CCompiler.find_framework_cache = {}
dependencies.PkgConfigDependency.pkgbin_cache = {}

@ -792,7 +792,7 @@ class InternalTests(unittest.TestCase):
f.write('')
with open(os.path.join(tmpdir, 'libfoo.so.70.0.so.1'), 'w') as f:
f.write('')
found = cc.find_library_real('foo', env, [tmpdir], '', LibType.PREFER_SHARED)
found = cc._find_library_real('foo', env, [tmpdir], '', LibType.PREFER_SHARED)
self.assertEqual(os.path.basename(found[0]), 'libfoo.so.54.0')
def test_find_library_patterns(self):

Loading…
Cancel
Save