aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/interpreter/compiler.py
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2021-07-26 14:16:31 -0700
committerDylan Baker <dylan@pnwbakers.com>2021-08-16 16:21:51 -0700
commitb30dddd4e5b4ae6e5e1e812085a00a47e3edfcf1 (patch)
tree0bfb4db2a7b544de3153b70a2f0d972c62d1b0f6 /mesonbuild/interpreter/compiler.py
parentbcb5400e344805cacb3d1dc8b765b0d07193e7a4 (diff)
downloadmeson-b30dddd4e5b4ae6e5e1e812085a00a47e3edfcf1.zip
meson-b30dddd4e5b4ae6e5e1e812085a00a47e3edfcf1.tar.gz
meson-b30dddd4e5b4ae6e5e1e812085a00a47e3edfcf1.tar.bz2
interpreter/compiler: Add type checking for the Compiler object
This adds a full set of `typed_pos_args` and `typed_kwarg` decorations, as well as fixing all of the typing errors reported by mypy.
Diffstat (limited to 'mesonbuild/interpreter/compiler.py')
-rw-r--r--mesonbuild/interpreter/compiler.py741
1 files changed, 347 insertions, 394 deletions
diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py
index 1050e26..927ac77 100644
--- a/mesonbuild/interpreter/compiler.py
+++ b/mesonbuild/interpreter/compiler.py
@@ -1,22 +1,90 @@
-import functools
-
-from ..interpreterbase.decorators import typed_kwargs, KwargInfo
+# SPDX-Licnese-Identifier: Apache-2.0
+# Copyright 2012-2021 The Meson development team
+# Copyright © 2021 Intel Corpration
-from .interpreterobjects import (extract_required_kwarg, extract_search_dirs)
+import enum
+import functools
+import typing as T
+from .. import build
+from .. import coredata
+from .. import dependencies
from .. import mesonlib
from .. import mlog
-from .. import dependencies
-from ..interpreterbase import (ObjectHolder, noPosargs, noKwargs, permittedKwargs,
- FeatureNew, FeatureNewKwargs, disablerIfNotFound,
- check_stringlist, InterpreterException, InvalidArguments)
-
-import typing as T
-import os
+from ..compilers.compilers import CompileCheckMode
+from ..interpreterbase import (ObjectHolder, noPosargs, noKwargs,
+ FeatureNew, disablerIfNotFound,
+ InterpreterException)
+from ..interpreterbase.decorators import ContainerTypeInfo, typed_kwargs, KwargInfo, typed_pos_args
+from .interpreterobjects import (extract_required_kwarg, extract_search_dirs)
+from .type_checking import REQUIRED_KW
if T.TYPE_CHECKING:
from ..interpreter import Interpreter
from ..compilers import Compiler, RunResult
+ from ..interpreterbase import TYPE_var, TYPE_kwargs
+ from .kwargs import ExtractRequired, ExtractSearchDirs
+
+ from typing_extensions import TypedDict, Literal
+
+ class GetSupportedArgumentKw(TypedDict):
+
+ checked: Literal['warn', 'require', 'off']
+
+ class AlignmentKw(TypedDict):
+
+ prefix: str
+ args: T.List[str]
+ dependencies: T.List[dependencies.Dependency]
+
+ class CompileKW(TypedDict):
+
+ name: str
+ no_builtin_args: bool
+ include_directories: T.List[build.IncludeDirs]
+ args: T.List[str]
+ dependencies: T.List[dependencies.Dependency]
+
+ class CommonKW(TypedDict):
+
+ prefix: str
+ no_builtin_args: bool
+ include_directories: T.List[build.IncludeDirs]
+ args: T.List[str]
+ dependencies: T.List[dependencies.Dependency]
+
+ class CompupteIntKW(CommonKW):
+
+ guess: T.Optional[int]
+ high: T.Optional[int]
+ low: T.Optional[int]
+
+ class HeaderKW(CommonKW, ExtractRequired):
+ pass
+
+ class FindLibraryKW(ExtractRequired, ExtractSearchDirs):
+
+ disabler: bool
+ has_headers: T.List[str]
+ static: bool
+
+ # This list must be all of the `HeaderKW` values with `header_`
+ # prepended to the key
+ header_args: T.List[str]
+ header_dependencies: T.List[dependencies.Dependency]
+ header_include_directories: T.List[build.IncludeDirs]
+ header_no_builtin_args: bool
+ header_prefix: str
+ header_required: T.Union[bool, coredata.UserFeatureOption]
+
+
+class _TestMode(enum.Enum):
+
+ """Whether we're doing a compiler or linker check."""
+
+ COMPILER = 0
+ LINKER = 1
+
class TryRunResultHolder(ObjectHolder['RunResult']):
def __init__(self, res: 'RunResult', interpreter: 'Interpreter'):
@@ -47,23 +115,37 @@ class TryRunResultHolder(ObjectHolder['RunResult']):
def stderr_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.held_object.stderr
-header_permitted_kwargs = {
- 'required',
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
+
+_ARGS_KW: KwargInfo[T.List[str]] = KwargInfo(
'args',
+ ContainerTypeInfo(list, str),
+ listify=True,
+ default=[],
+)
+_DEPENDENCIES_KW: KwargInfo[T.List['dependencies.Dependency']] = KwargInfo(
'dependencies',
-}
+ ContainerTypeInfo(list, dependencies.Dependency),
+ listify=True,
+ default=[],
+)
+_INCLUDE_DIRS_KW: KwargInfo[T.List[build.IncludeDirs]] = KwargInfo(
+ 'include_directories',
+ ContainerTypeInfo(list, build.IncludeDirs),
+ default=[],
+ listify=True,
+)
+_PREFIX_KW = KwargInfo('prefix', str, default='')
+_NO_BUILTIN_ARGS_KW = KwargInfo('no_builtin_args', bool, default=False)
+_NAME_KW = KwargInfo('name', str, default='')
+
+# Many of the compiler methods take this kwarg signature exactly, this allows
+# simplifying the `typed_kwargs` calls
+_COMMON_KWS: T.List[KwargInfo] = [_ARGS_KW, _DEPENDENCIES_KW, _INCLUDE_DIRS_KW, _PREFIX_KW, _NO_BUILTIN_ARGS_KW]
-find_library_permitted_kwargs = {
- 'has_headers',
- 'required',
- 'dirs',
- 'static',
-}
+# Common methods of compiles, links, runs, and similar
+_COMPILES_KWS: T.List[KwargInfo] = [_NAME_KW, _ARGS_KW, _DEPENDENCIES_KW, _INCLUDE_DIRS_KW, _NO_BUILTIN_ARGS_KW]
-find_library_permitted_kwargs |= {'header_' + k for k in header_permitted_kwargs}
+_HEADER_KWS: T.List[KwargInfo] = [REQUIRED_KW.evolve(since='0.50.0', default=False), *_COMMON_KWS]
class CompilerHolder(ObjectHolder['Compiler']):
def __init__(self, compiler: 'Compiler', interpreter: 'Interpreter'):
@@ -106,7 +188,8 @@ class CompilerHolder(ObjectHolder['Compiler']):
def compiler(self) -> 'Compiler':
return self.held_object
- def _dep_msg(self, deps, endl):
+ @staticmethod
+ def _dep_msg(deps: T.List['dependencies.Dependency'], endl: str) -> str:
msg_single = 'with dependency {}'
msg_many = 'with dependencies {}'
if not deps:
@@ -129,37 +212,32 @@ class CompilerHolder(ObjectHolder['Compiler']):
@noPosargs
@noKwargs
- def version_method(self, args, kwargs):
+ def version_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.compiler.version
@noPosargs
@noKwargs
- def cmd_array_method(self, args, kwargs):
+ def cmd_array_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> T.List[str]:
return self.compiler.exelist
- def determine_args(self, kwargs, mode='link'):
- nobuiltins = kwargs.get('no_builtin_args', False)
- if not isinstance(nobuiltins, bool):
- raise InterpreterException('Type of no_builtin_args not a boolean.')
- args = []
- incdirs = mesonlib.extract_as_list(kwargs, 'include_directories')
+ def determine_args(self, nobuiltins: bool,
+ incdirs: T.List[build.IncludeDirs],
+ extra_args: T.List[str],
+ mode: CompileCheckMode = CompileCheckMode.LINK) -> T.List[str]:
+ args: T.List[str] = []
for i in incdirs:
- from ..build import IncludeDirs
- if not isinstance(i, IncludeDirs):
- raise InterpreterException('Include directories argument must be an include_directories object.')
for idir in i.to_string_list(self.environment.get_source_dir()):
- args += self.compiler.get_include_args(idir, False)
+ args.extend(self.compiler.get_include_args(idir, False))
if not nobuiltins:
opts = self.environment.coredata.options
args += self.compiler.get_option_compile_args(opts)
- if mode == 'link':
- args += self.compiler.get_option_link_args(opts)
- args += mesonlib.stringlistify(kwargs.get('args', []))
+ if mode is CompileCheckMode.LINK:
+ args.extend(self.compiler.get_option_link_args(opts))
+ args.extend(extra_args)
return args
- def determine_dependencies(self, kwargs, endl=':'):
- deps = kwargs.get('dependencies', None)
- if deps is not None:
+ def determine_dependencies(self, deps: T.List['dependencies.Dependency'], endl: str = ':') -> T.Tuple[T.List['dependencies.Dependency'], str]:
+ if deps:
final_deps = []
while deps:
next_deps = []
@@ -170,53 +248,40 @@ class CompilerHolder(ObjectHolder['Compiler']):
next_deps.extend(d.ext_deps)
deps = next_deps
deps = final_deps
+ else:
+ # Ensure that we alway return a new instance
+ deps = deps.copy()
return deps, self._dep_msg(deps, endl)
- @permittedKwargs({
- 'prefix',
- 'args',
- 'dependencies',
- })
- def alignment_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Alignment method takes exactly one positional argument.')
- check_stringlist(args)
+ @typed_pos_args('compiler.alignment', str)
+ @typed_kwargs(
+ 'compiler.alignment',
+ _PREFIX_KW,
+ _ARGS_KW,
+ _DEPENDENCIES_KW,
+ )
+ def alignment_method(self, args: T.Tuple[str], kwargs: 'AlignmentKw') -> int:
typename = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of alignment must be a string.')
- extra_args = mesonlib.stringlistify(kwargs.get('args', []))
- deps, msg = self.determine_dependencies(kwargs)
- result = self.compiler.alignment(typename, prefix, self.environment,
- extra_args=extra_args,
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
+ result = self.compiler.alignment(typename, kwargs['prefix'], self.environment,
+ extra_args=kwargs['args'],
dependencies=deps)
mlog.log('Checking for alignment of', mlog.bold(typename, True), msg, result)
return result
- @permittedKwargs({
- 'name',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def run_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Run method takes exactly one positional argument.')
+ @typed_pos_args('compiler.run', (str, mesonlib.File))
+ @typed_kwargs('compiler.run', *_COMPILES_KWS)
+ def run_method(self, args: T.Tuple['mesonlib.FileOrString'], kwargs: 'CompileKW') -> 'RunResult':
code = args[0]
if isinstance(code, mesonlib.File):
code = mesonlib.File.from_absolute_file(
code.rel_to_builddir(self.environment.source_dir))
- elif not isinstance(code, str):
- raise InvalidArguments('Argument must be string or file.')
- testname = kwargs.get('name', '')
- if not isinstance(testname, str):
- raise InterpreterException('Testname argument must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs, endl=None)
+ testname = kwargs['name']
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'], endl=None)
result = self.compiler.run(code, self.environment, extra_args=extra_args,
dependencies=deps)
- if len(testname) > 0:
+ if testname:
if not result.compiled:
h = mlog.red('DID NOT COMPILE')
elif result.returncode == 0:
@@ -246,346 +311,226 @@ class CompilerHolder(ObjectHolder['Compiler']):
'''
return self.compiler.symbols_have_underscore_prefix(self.environment)
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_member_method(self, args, kwargs):
- if len(args) != 2:
- raise InterpreterException('Has_member takes exactly two arguments.')
- check_stringlist(args)
+ @typed_pos_args('compiler.has_member', str, str)
+ @typed_kwargs('compiler.has_member', *_COMMON_KWS)
+ def has_member_method(self, args: T.Tuple[str, str], kwargs: 'CommonKW') -> bool:
typename, membername = args
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_member must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had, cached = self.compiler.has_members(typename, [membername], prefix,
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
+ had, cached = self.compiler.has_members(typename, [membername], kwargs['prefix'],
self.environment,
extra_args=extra_args,
dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
+ cached_msg = mlog.blue('(cached)') if cached else ''
if had:
hadtxt = mlog.green('YES')
else:
hadtxt = mlog.red('NO')
mlog.log('Checking whether type', mlog.bold(typename, True),
- 'has member', mlog.bold(membername, True), msg, hadtxt, cached)
+ 'has member', mlog.bold(membername, True), msg, hadtxt, cached_msg)
return had
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_members_method(self, args, kwargs):
- if len(args) < 2:
- raise InterpreterException('Has_members needs at least two arguments.')
- check_stringlist(args)
- typename, *membernames = args
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_members must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had, cached = self.compiler.has_members(typename, membernames, prefix,
+ @typed_pos_args('compiler.has_members', str, varargs=str, min_varargs=1)
+ @typed_kwargs('compiler.has_members', *_COMMON_KWS)
+ def has_members_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'CommonKW') -> bool:
+ typename, membernames = args
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
+ had, cached = self.compiler.has_members(typename, membernames, kwargs['prefix'],
self.environment,
extra_args=extra_args,
dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
+ cached_msg = mlog.blue('(cached)') if cached else ''
if had:
hadtxt = mlog.green('YES')
else:
hadtxt = mlog.red('NO')
members = mlog.bold(', '.join([f'"{m}"' for m in membernames]))
mlog.log('Checking whether type', mlog.bold(typename, True),
- 'has members', members, msg, hadtxt, cached)
+ 'has members', members, msg, hadtxt, cached_msg)
return had
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_function_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Has_function takes exactly one argument.')
- check_stringlist(args)
+ @typed_pos_args('compiler.has_function', str)
+ @typed_kwargs('compiler.has_type', *_COMMON_KWS)
+ def has_function_method(self, args: T.Tuple[str], kwargs: 'CommonKW') -> bool:
funcname = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_function must be a string.')
- extra_args = self.determine_args(kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had, cached = self.compiler.has_function(funcname, prefix, self.environment,
+ extra_args = self.determine_args(kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
+ had, cached = self.compiler.has_function(funcname, kwargs['prefix'], self.environment,
extra_args=extra_args,
dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
+ cached_msg = mlog.blue('(cached)') if cached else ''
if had:
hadtxt = mlog.green('YES')
else:
hadtxt = mlog.red('NO')
- mlog.log('Checking for function', mlog.bold(funcname, True), msg, hadtxt, cached)
+ mlog.log('Checking for function', mlog.bold(funcname, True), msg, hadtxt, cached_msg)
return had
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_type_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Has_type takes exactly one argument.')
- check_stringlist(args)
+ @typed_pos_args('compiler.has_type', str)
+ @typed_kwargs('compiler.has_type', *_COMMON_KWS)
+ def has_type_method(self, args: T.Tuple[str], kwargs: 'CommonKW') -> bool:
typename = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_type must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had, cached = self.compiler.has_type(typename, prefix, self.environment,
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
+ had, cached = self.compiler.has_type(typename, kwargs['prefix'], self.environment,
extra_args=extra_args, dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
+ cached_msg = mlog.blue('(cached)') if cached else ''
if had:
hadtxt = mlog.green('YES')
else:
hadtxt = mlog.red('NO')
- mlog.log('Checking for type', mlog.bold(typename, True), msg, hadtxt, cached)
+ mlog.log('Checking for type', mlog.bold(typename, True), msg, hadtxt, cached_msg)
return had
@FeatureNew('compiler.compute_int', '0.40.0')
- @permittedKwargs({
- 'prefix',
- 'low',
- 'high',
- 'guess',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def compute_int_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Compute_int takes exactly one argument.')
- check_stringlist(args)
+ @typed_pos_args('compiler.compute_int', str)
+ @typed_kwargs(
+ 'compiler.compute_int',
+ KwargInfo('low', (int, None)),
+ KwargInfo('high', (int, None)),
+ KwargInfo('guess', (int, None)),
+ *_COMMON_KWS,
+ )
+ def compute_int_method(self, args: T.Tuple[str], kwargs: 'CompupteIntKW') -> int:
expression = args[0]
- prefix = kwargs.get('prefix', '')
- low = kwargs.get('low', None)
- high = kwargs.get('high', None)
- guess = kwargs.get('guess', None)
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of compute_int must be a string.')
- if low is not None and not isinstance(low, int):
- raise InterpreterException('Low argument of compute_int must be an int.')
- if high is not None and not isinstance(high, int):
- raise InterpreterException('High argument of compute_int must be an int.')
- if guess is not None and not isinstance(guess, int):
- raise InterpreterException('Guess argument of compute_int must be an int.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- res = self.compiler.compute_int(expression, low, high, guess, prefix,
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
+ res = self.compiler.compute_int(expression, kwargs['low'], kwargs['high'],
+ kwargs['guess'], kwargs['prefix'],
self.environment, extra_args=extra_args,
dependencies=deps)
mlog.log('Computing int of', mlog.bold(expression, True), msg, res)
return res
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def sizeof_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Sizeof takes exactly one argument.')
- check_stringlist(args)
+ @typed_pos_args('compiler.sizeof', str)
+ @typed_kwargs('compiler.sizeof', *_COMMON_KWS)
+ def sizeof_method(self, args: T.Tuple[str], kwargs: 'CommonKW') -> int:
element = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of sizeof must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- esize = self.compiler.sizeof(element, prefix, self.environment,
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
+ esize = self.compiler.sizeof(element, kwargs['prefix'], self.environment,
extra_args=extra_args, dependencies=deps)
mlog.log('Checking for size of', mlog.bold(element, True), msg, esize)
return esize
@FeatureNew('compiler.get_define', '0.40.0')
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def get_define_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('get_define() takes exactly one argument.')
- check_stringlist(args)
+ @typed_pos_args('compiler.get_define', str)
+ @typed_kwargs('compiler.get_define', *_COMMON_KWS)
+ def get_define_method(self, args: T.Tuple[str], kwargs: 'CommonKW') -> str:
element = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of get_define() must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- value, cached = self.compiler.get_define(element, prefix, self.environment,
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
+ value, cached = self.compiler.get_define(element, kwargs['prefix'], self.environment,
extra_args=extra_args,
dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log('Fetching value of define', mlog.bold(element, True), msg, value, cached)
+ cached_msg = mlog.blue('(cached)') if cached else ''
+ mlog.log('Fetching value of define', mlog.bold(element, True), msg, value, cached_msg)
return value
- @permittedKwargs({
- 'name',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def compiles_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('compiles method takes exactly one argument.')
+ @typed_pos_args('compiler.compiles', (str, mesonlib.File))
+ @typed_kwargs('compiler.compiles', *_COMPILES_KWS)
+ def compiles_method(self, args: T.Tuple['mesonlib.FileOrString'], kwargs: 'CompileKW') -> bool:
code = args[0]
if isinstance(code, mesonlib.File):
code = mesonlib.File.from_absolute_file(
code.rel_to_builddir(self.environment.source_dir))
- elif not isinstance(code, str):
- raise InvalidArguments('Argument must be string or file.')
- testname = kwargs.get('name', '')
- if not isinstance(testname, str):
- raise InterpreterException('Testname argument must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs, endl=None)
+ testname = kwargs['name']
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
result, cached = self.compiler.compiles(code, self.environment,
extra_args=extra_args,
dependencies=deps)
- if len(testname) > 0:
+ if testname:
if result:
h = mlog.green('YES')
else:
h = mlog.red('NO')
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log('Checking if', mlog.bold(testname, True), msg, 'compiles:', h, cached)
+ cached_msg = mlog.blue('(cached)') if cached else ''
+ mlog.log('Checking if', mlog.bold(testname, True), msg, 'compiles:', h, cached_msg)
return result
- @permittedKwargs({
- 'name',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def links_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('links method takes exactly one argument.')
+ @typed_pos_args('compiler.links', (str, mesonlib.File))
+ @typed_kwargs('compiler.links', *_COMPILES_KWS)
+ def links_method(self, args: T.Tuple['mesonlib.FileOrString'], kwargs: 'CompileKW') -> bool:
code = args[0]
if isinstance(code, mesonlib.File):
code = mesonlib.File.from_absolute_file(
code.rel_to_builddir(self.environment.source_dir))
- elif not isinstance(code, str):
- raise InvalidArguments('Argument must be string or file.')
- testname = kwargs.get('name', '')
- if not isinstance(testname, str):
- raise InterpreterException('Testname argument must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs, endl=None)
+ testname = kwargs['name']
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
result, cached = self.compiler.links(code, self.environment,
extra_args=extra_args,
dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- if len(testname) > 0:
+ cached_msg = mlog.blue('(cached)') if cached else ''
+ if testname:
if result:
h = mlog.green('YES')
else:
h = mlog.red('NO')
- mlog.log('Checking if', mlog.bold(testname, True), msg, 'links:', h, cached)
+ mlog.log('Checking if', mlog.bold(testname, True), msg, 'links:', h, cached_msg)
return result
@FeatureNew('compiler.check_header', '0.47.0')
- @FeatureNewKwargs('compiler.check_header', '0.50.0', ['required'])
- @permittedKwargs(header_permitted_kwargs)
- def check_header_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('check_header method takes exactly one argument.')
- check_stringlist(args)
+ @typed_pos_args('compiler.check_header', str)
+ @typed_kwargs('compiler.check_header', *_HEADER_KWS)
+ def check_header_method(self, args: T.Tuple[str], kwargs: 'HeaderKW') -> bool:
hname = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_header must be a string.')
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
mlog.log('Check usable header', mlog.bold(hname, True), 'skipped: feature', mlog.bold(feature), 'disabled')
return False
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- haz, cached = self.compiler.check_header(hname, prefix, self.environment,
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
+ haz, cached = self.compiler.check_header(hname, kwargs['prefix'], self.environment,
extra_args=extra_args,
dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
+ cached_msg = mlog.blue('(cached)') if cached else ''
if required and not haz:
raise InterpreterException(f'{self.compiler.get_display_language()} header {hname!r} not usable')
elif haz:
h = mlog.green('YES')
else:
h = mlog.red('NO')
- mlog.log('Check usable header', mlog.bold(hname, True), msg, h, cached)
+ mlog.log('Check usable header', mlog.bold(hname, True), msg, h, cached_msg)
return haz
- @FeatureNewKwargs('compiler.has_header', '0.50.0', ['required'])
- @permittedKwargs(header_permitted_kwargs)
- def has_header_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('has_header method takes exactly one argument.')
- check_stringlist(args)
- hname = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_header must be a string.')
+ def _has_header_impl(self, hname: str, kwargs: 'HeaderKW') -> bool:
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
mlog.log('Has header', mlog.bold(hname, True), 'skipped: feature', mlog.bold(feature), 'disabled')
return False
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- haz, cached = self.compiler.has_header(hname, prefix, self.environment,
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
+ haz, cached = self.compiler.has_header(hname, kwargs['prefix'], self.environment,
extra_args=extra_args, dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
+ cached_msg = mlog.blue('(cached)') if cached else ''
if required and not haz:
raise InterpreterException(f'{self.compiler.get_display_language()} header {hname!r} not found')
elif haz:
h = mlog.green('YES')
else:
h = mlog.red('NO')
- mlog.log('Has header', mlog.bold(hname, True), msg, h, cached)
+ mlog.log('Has header', mlog.bold(hname, True), msg, h, cached_msg)
return haz
- @FeatureNewKwargs('compiler.has_header_symbol', '0.50.0', ['required'])
- @permittedKwargs(header_permitted_kwargs)
- def has_header_symbol_method(self, args, kwargs):
- if len(args) != 2:
- raise InterpreterException('has_header_symbol method takes exactly two arguments.')
- check_stringlist(args)
+ @typed_pos_args('compiler.has_header', str)
+ @typed_kwargs('compiler.has_header', *_HEADER_KWS)
+ def has_header_method(self, args: T.Tuple[str], kwargs: 'HeaderKW') -> bool:
+ return self._has_header_impl(args[0], kwargs)
+
+ @typed_pos_args('compiler.has_header_symbol', str, str)
+ @typed_kwargs('compiler.has_header_symbol', *_HEADER_KWS)
+ def has_header_symbol_method(self, args: T.Tuple[str, str], kwargs: 'HeaderKW') -> bool:
hname, symbol = args
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_header_symbol must be a string.')
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
mlog.log(f'Header <{hname}> has symbol', mlog.bold(symbol, True), 'skipped: feature', mlog.bold(feature), 'disabled')
return False
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- haz, cached = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment,
+ extra_args = functools.partial(self.determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
+ deps, msg = self.determine_dependencies(kwargs['dependencies'])
+ haz, cached = self.compiler.has_header_symbol(hname, symbol, kwargs['prefix'], self.environment,
extra_args=extra_args,
dependencies=deps)
if required and not haz:
@@ -594,97 +539,113 @@ class CompilerHolder(ObjectHolder['Compiler']):
h = mlog.green('YES')
else:
h = mlog.red('NO')
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log(f'Header <{hname}> has symbol', mlog.bold(symbol, True), msg, h, cached)
+ cached_msg = mlog.blue('(cached)') if cached else ''
+ mlog.log(f'Header <{hname}> has symbol', mlog.bold(symbol, True), msg, h, cached_msg)
return haz
- def notfound_library(self, libname):
+ def notfound_library(self, libname: str) -> 'dependencies.ExternalLibrary':
lib = dependencies.ExternalLibrary(libname, None,
self.environment,
self.compiler.language,
silent=True)
return lib
- @FeatureNewKwargs('compiler.find_library', '0.51.0', ['static'])
- @FeatureNewKwargs('compiler.find_library', '0.50.0', ['has_headers'])
- @FeatureNewKwargs('compiler.find_library', '0.49.0', ['disabler'])
@disablerIfNotFound
- @permittedKwargs(find_library_permitted_kwargs)
- def find_library_method(self, args, kwargs):
+ @typed_pos_args('compiler.find_library', str)
+ @typed_kwargs(
+ 'compiler.find_library',
+ KwargInfo('required', (bool, coredata.UserFeatureOption), default=True),
+ KwargInfo('has_headers', ContainerTypeInfo(list, str), listify=True, default=[], since='0.50.0'),
+ KwargInfo('static', (bool, None), since='0.51.0'),
+ KwargInfo('disabler', bool, default=False, since='0.49.0'),
+ KwargInfo('dirs', ContainerTypeInfo(list, str), listify=True, default=[]),
+ *[k.evolve(name=f'header_{k.name}') for k in _HEADER_KWS]
+ )
+ def find_library_method(self, args: T.Tuple[str], kwargs: 'FindLibraryKW') -> 'dependencies.ExternalLibrary':
# TODO add dependencies support?
- if len(args) != 1:
- raise InterpreterException('find_library method takes one argument.')
libname = args[0]
- if not isinstance(libname, str):
- raise InterpreterException('Library name not a string.')
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
mlog.log('Library', mlog.bold(libname), 'skipped: feature', mlog.bold(feature), 'disabled')
return self.notfound_library(libname)
- has_header_kwargs = {k[7:]: v for k, v in kwargs.items() if k.startswith('header_')}
- has_header_kwargs['required'] = required
- headers = mesonlib.stringlistify(kwargs.get('has_headers', []))
- for h in headers:
- if not self.has_header_method([h], has_header_kwargs):
+ # This could be done with a comprehension, but that confuses the type
+ # checker, and having it check this seems valuable
+ has_header_kwargs: 'HeaderKW' = {
+ 'required': required,
+ 'args': kwargs['header_args'],
+ 'dependencies': kwargs['header_dependencies'],
+ 'include_directories': kwargs['header_include_directories'],
+ 'prefix': kwargs['header_prefix'],
+ 'no_builtin_args': kwargs['header_no_builtin_args'],
+ }
+ for h in kwargs['has_headers']:
+ if not self._has_header_impl(h, has_header_kwargs):
return self.notfound_library(libname)
search_dirs = extract_search_dirs(kwargs)
- libtype = mesonlib.LibType.PREFER_SHARED
- if 'static' in kwargs:
- if not isinstance(kwargs['static'], bool):
- raise InterpreterException('static must be a boolean')
- libtype = mesonlib.LibType.STATIC if kwargs['static'] else mesonlib.LibType.SHARED
+ if kwargs['static'] is True:
+ libtype = mesonlib.LibType.STATIC
+ elif kwargs['static'] is False:
+ libtype = mesonlib.LibType.SHARED
+ else:
+ libtype = mesonlib.LibType.PREFER_SHARED
linkargs = self.compiler.find_library(libname, self.environment, search_dirs, libtype)
if required and not linkargs:
if libtype == mesonlib.LibType.PREFER_SHARED:
- libtype = 'shared or static'
+ libtype_s = 'shared or static'
else:
- libtype = libtype.name.lower()
+ libtype_s = libtype.name.lower()
raise InterpreterException('{} {} library {!r} not found'
.format(self.compiler.get_display_language(),
- libtype, libname))
+ libtype_s, libname))
lib = dependencies.ExternalLibrary(libname, linkargs, self.environment,
self.compiler.language)
return lib
+ def _has_argument_impl(self, arguments: T.Union[str, T.List[str]],
+ mode: _TestMode = _TestMode.COMPILER) -> bool:
+ """Shared implementaiton for methods checking compiler and linker arguments."""
+ # This simplifies the callers
+ if isinstance(arguments, str):
+ arguments = [arguments]
+ test = self.compiler.has_multi_link_arguments if mode is _TestMode.LINKER else self.compiler.has_multi_arguments
+ result, cached = test(arguments, self.environment)
+ cached_msg = mlog.blue('(cached)') if cached else ''
+ mlog.log(
+ 'Compiler for',
+ self.compiler.get_display_language(),
+ 'supports{}'.format(' link' if mode is _TestMode.LINKER else ''),
+ 'arguments {}:'.format(' '.join(arguments)),
+ mlog.green('YES') if result else mlog.red('NO'),
+ cached_msg)
+ return result
+
@noKwargs
- def has_argument_method(self, args: T.Sequence[str], kwargs) -> bool:
- args = mesonlib.stringlistify(args)
- if len(args) != 1:
- raise InterpreterException('has_argument takes exactly one argument.')
- return self.has_multi_arguments_method(args, kwargs)
+ @typed_pos_args('compiler.has_argument', str)
+ def has_argument_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> bool:
+ return self._has_argument_impl([args[0]])
@noKwargs
- def has_multi_arguments_method(self, args: T.Sequence[str], kwargs: dict):
- args = mesonlib.stringlistify(args)
- result, cached = self.compiler.has_multi_arguments(args, self.environment)
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log(
- 'Compiler for {} supports arguments {}:'.format(
- self.compiler.get_display_language(), ' '.join(args)),
- h, cached)
- return result
+ @typed_pos_args('compiler.has_multi_arguments', varargs=str)
+ def has_multi_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> bool:
+ return self._has_argument_impl(args[0])
@FeatureNew('compiler.get_supported_arguments', '0.43.0')
+ @typed_pos_args('compiler.get_supported_arguments', varargs=str)
@typed_kwargs(
'compiler.get_supported_arguments',
KwargInfo('checked', str, default='off', since='0.59.0',
validator=lambda s: 'must be one of "warn", "require" or "off"' if s not in ['warn', 'require', 'off'] else None)
)
- def get_supported_arguments_method(self, args: T.Sequence[str], kwargs: T.Dict[str, T.Any]):
- args = mesonlib.stringlistify(args)
- supported_args = []
- checked = kwargs.pop('checked')
+ def get_supported_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'GetSupportedArgumentKw') -> T.List[str]:
+ supported_args: T.List[str] = []
+ checked = kwargs['checked']
- for arg in args:
- if not self.has_argument_method(arg, kwargs):
+ for arg in args[0]:
+ if not self._has_argument_impl([arg]):
msg = f'Compiler for {self.compiler.get_display_language()} does not support "{arg}"'
if checked == 'warn':
mlog.warning(msg)
@@ -695,9 +656,10 @@ class CompilerHolder(ObjectHolder['Compiler']):
return supported_args
@noKwargs
- def first_supported_argument_method(self, args: T.Sequence[str], kwargs: dict) -> T.List[str]:
- for arg in mesonlib.stringlistify(args):
- if self.has_argument_method(arg, kwargs):
+ @typed_pos_args('compiler.first_supported_argument', varargs=str)
+ def first_supported_argument_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> T.List[str]:
+ for arg in args[0]:
+ if self._has_argument_impl([arg]):
mlog.log('First supported argument:', mlog.bold(arg))
return [arg]
mlog.log('First supported argument:', mlog.red('None'))
@@ -705,68 +667,59 @@ class CompilerHolder(ObjectHolder['Compiler']):
@FeatureNew('compiler.has_link_argument', '0.46.0')
@noKwargs
- def has_link_argument_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- if len(args) != 1:
- raise InterpreterException('has_link_argument takes exactly one argument.')
- return self.has_multi_link_arguments_method(args, kwargs)
+ @typed_pos_args('compiler.has_link_argument', str)
+ def has_link_argument_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> bool:
+ return self._has_argument_impl([args[0]], mode=_TestMode.LINKER)
@FeatureNew('compiler.has_multi_link_argument', '0.46.0')
@noKwargs
- def has_multi_link_arguments_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- result, cached = self.compiler.has_multi_link_arguments(args, self.environment)
- cached = mlog.blue('(cached)') if cached else ''
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log(
- 'Compiler for {} supports link arguments {}:'.format(
- self.compiler.get_display_language(), ' '.join(args)),
- h, cached)
- return result
+ @typed_pos_args('compiler.has_multi_link_argument', varargs=str)
+ def has_multi_link_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> bool:
+ return self._has_argument_impl(args[0], mode=_TestMode.LINKER)
- @FeatureNew('compiler.get_supported_link_arguments_method', '0.46.0')
+ @FeatureNew('compiler.get_supported_link_arguments', '0.46.0')
@noKwargs
- def get_supported_link_arguments_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- supported_args = []
- for arg in args:
- if self.has_link_argument_method(arg, kwargs):
+ @typed_pos_args('compiler.get_supported_link_arguments', varargs=str)
+ def get_supported_link_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> T.List[str]:
+ supported_args: T.List[str] = []
+ for arg in args[0]:
+ if self._has_argument_impl([arg], mode=_TestMode.LINKER):
supported_args.append(arg)
return supported_args
@FeatureNew('compiler.first_supported_link_argument_method', '0.46.0')
@noKwargs
- def first_supported_link_argument_method(self, args, kwargs):
- for i in mesonlib.stringlistify(args):
- if self.has_link_argument_method(i, kwargs):
- mlog.log('First supported link argument:', mlog.bold(i))
- return [i]
+ @typed_pos_args('compiler.first_supported_link_argument', varargs=str)
+ def first_supported_link_argument_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> T.List[str]:
+ for arg in args[0]:
+ if self._has_argument_impl([arg], mode=_TestMode.LINKER):
+ mlog.log('First supported link argument:', mlog.bold(arg))
+ return [arg]
mlog.log('First supported link argument:', mlog.red('None'))
return []
- @FeatureNew('compiler.has_function_attribute', '0.48.0')
- @noKwargs
- def has_func_attribute_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- if len(args) != 1:
- raise InterpreterException('has_func_attribute takes exactly one argument.')
- result, cached = self.compiler.has_func_attribute(args[0], self.environment)
- cached = mlog.blue('(cached)') if cached else ''
+ def _has_function_attribute_impl(self, attr: str) -> bool:
+ """Common helper for function attribute testing."""
+ result, cached = self.compiler.has_func_attribute(attr, self.environment)
+ cached_msg = mlog.blue('(cached)') if cached else ''
h = mlog.green('YES') if result else mlog.red('NO')
- mlog.log('Compiler for {} supports function attribute {}:'.format(self.compiler.get_display_language(), args[0]), h, cached)
+ mlog.log('Compiler for {} supports function attribute {}:'.format(self.compiler.get_display_language(), attr), h, cached_msg)
return result
+ @FeatureNew('compiler.has_function_attribute', '0.48.0')
+ @noKwargs
+ @typed_pos_args('compiler.has_function_attribute', str)
+ def has_func_attribute_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> bool:
+ return self._has_function_attribute_impl(args[0])
+
@FeatureNew('compiler.get_supported_function_attributes', '0.48.0')
@noKwargs
- def get_supported_function_attributes_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- return [a for a in args if self.has_func_attribute_method(a, kwargs)]
+ @typed_pos_args('compiler.get_supported_function_attributes', varargs=str)
+ def get_supported_function_attributes_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> T.List[str]:
+ return [a for a in args[0] if self._has_function_attribute_impl(a)]
@FeatureNew('compiler.get_argument_syntax_method', '0.49.0')
@noPosargs
@noKwargs
- def get_argument_syntax_method(self, args, kwargs):
+ def get_argument_syntax_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:
return self.compiler.get_argument_syntax()