diff options
Diffstat (limited to 'mesonbuild/interpreter/compiler.py')
-rw-r--r-- | mesonbuild/interpreter/compiler.py | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py new file mode 100644 index 0000000..95126cf --- /dev/null +++ b/mesonbuild/interpreter/compiler.py @@ -0,0 +1,781 @@ +# SPDX-Licnese-Identifier: Apache-2.0 +# Copyright 2012-2021 The Meson development team +# Copyright © 2021 Intel Corporation +from __future__ import annotations + +import enum +import functools +import os +import typing as T + +from .. import build +from .. import coredata +from .. import dependencies +from .. import mesonlib +from .. import mlog +from ..compilers import SUFFIX_TO_LANG +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 ..mesonlib import OptionKey +from .interpreterobjects import (extract_required_kwarg, extract_search_dirs) +from .type_checking import REQUIRED_KW, in_set_validator, NoneType + +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 PreprocessKW(TypedDict): + output: str + compile_args: T.List[str] + include_directories: T.List[build.IncludeDirs] + + +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'): + super().__init__(res, interpreter) + self.methods.update({'returncode': self.returncode_method, + 'compiled': self.compiled_method, + 'stdout': self.stdout_method, + 'stderr': self.stderr_method, + }) + + @noPosargs + @noKwargs + def returncode_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> int: + return self.held_object.returncode + + @noPosargs + @noKwargs + def compiled_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: + return self.held_object.compiled + + @noPosargs + @noKwargs + def stdout_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.held_object.stdout + + @noPosargs + @noKwargs + def stderr_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.held_object.stderr + + +_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[str] = KwargInfo( + 'prefix', + (str, ContainerTypeInfo(list, str)), + default='', + since_values={list: '1.0.0'}, + convertor=lambda x: '\n'.join(x) if isinstance(x, list) else x) + +_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] + +# 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] + +_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'): + super().__init__(compiler, interpreter) + self.environment = self.env + self.methods.update({'compiles': self.compiles_method, + 'links': self.links_method, + 'get_id': self.get_id_method, + 'get_linker_id': self.get_linker_id_method, + 'compute_int': self.compute_int_method, + 'sizeof': self.sizeof_method, + 'get_define': self.get_define_method, + 'check_header': self.check_header_method, + 'has_header': self.has_header_method, + 'has_header_symbol': self.has_header_symbol_method, + 'run': self.run_method, + 'has_function': self.has_function_method, + 'has_member': self.has_member_method, + 'has_members': self.has_members_method, + 'has_type': self.has_type_method, + 'alignment': self.alignment_method, + 'version': self.version_method, + 'cmd_array': self.cmd_array_method, + 'find_library': self.find_library_method, + 'has_argument': self.has_argument_method, + 'has_function_attribute': self.has_func_attribute_method, + 'get_supported_function_attributes': self.get_supported_function_attributes_method, + 'has_multi_arguments': self.has_multi_arguments_method, + 'get_supported_arguments': self.get_supported_arguments_method, + 'first_supported_argument': self.first_supported_argument_method, + 'has_link_argument': self.has_link_argument_method, + 'has_multi_link_arguments': self.has_multi_link_arguments_method, + 'get_supported_link_arguments': self.get_supported_link_arguments_method, + 'first_supported_link_argument': self.first_supported_link_argument_method, + 'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method, + 'get_argument_syntax': self.get_argument_syntax_method, + 'preprocess': self.preprocess_method, + }) + + @property + def compiler(self) -> 'Compiler': + return self.held_object + + def _dep_msg(self, deps: T.List['dependencies.Dependency'], compile_only: bool, endl: str) -> str: + msg_single = 'with dependency {}' + msg_many = 'with dependencies {}' + names = [] + for d in deps: + if isinstance(d, dependencies.InternalDependency): + FeatureNew.single_use('compiler method "dependencies" kwarg with internal dep', '0.57.0', self.subproject, + location=self.current_node) + continue + if isinstance(d, dependencies.ExternalLibrary): + if compile_only: + continue + name = '-l' + d.name + else: + name = d.name + names.append(name) + if not names: + return endl + tpl = msg_many if len(names) > 1 else msg_single + if endl is None: + endl = '' + return tpl.format(', '.join(names)) + endl + + @noPosargs + @noKwargs + 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: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> T.List[str]: + return self.compiler.exelist + + 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: + for idir in i.to_string_list(self.environment.get_source_dir()): + 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 is CompileCheckMode.LINK: + args.extend(self.compiler.get_option_link_args(opts)) + args.extend(extra_args) + return args + + def _determine_dependencies(self, deps: T.List['dependencies.Dependency'], compile_only: bool = False, endl: str = ':') -> T.Tuple[T.List['dependencies.Dependency'], str]: + deps = dependencies.get_leaf_external_dependencies(deps) + return deps, self._dep_msg(deps, compile_only, endl) + + @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] + deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=self.compiler.is_cross) + 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 + + @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): + self.interpreter.add_build_def_file(code) + code = mesonlib.File.from_absolute_file( + code.rel_to_builddir(self.environment.source_dir)) + 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'], compile_only=False, endl=None) + result = self.compiler.run(code, self.environment, extra_args=extra_args, + dependencies=deps) + if testname: + if not result.compiled: + h = mlog.red('DID NOT COMPILE') + elif result.returncode == 0: + h = mlog.green('YES') + else: + h = mlog.red(f'NO ({result.returncode})') + mlog.log('Checking if', mlog.bold(testname, True), msg, 'runs:', h) + return result + + @noPosargs + @noKwargs + def get_id_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.compiler.get_id() + + @noPosargs + @noKwargs + @FeatureNew('compiler.get_linker_id', '0.53.0') + def get_linker_id_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.compiler.get_linker_id() + + @noPosargs + @noKwargs + def symbols_have_underscore_prefix_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> bool: + ''' + Check if the compiler prefixes _ (underscore) to global C symbols + See: https://en.wikipedia.org/wiki/Name_mangling#C + ''' + return self.compiler.symbols_have_underscore_prefix(self.environment) + + @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 + 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_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_msg) + return had + + @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_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_msg) + return had + + @typed_pos_args('compiler.has_function', str) + @typed_kwargs('compiler.has_function', *_COMMON_KWS) + def has_function_method(self, args: T.Tuple[str], kwargs: 'CommonKW') -> bool: + funcname = args[0] + extra_args = self._determine_args(kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args']) + deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=False) + had, cached = self.compiler.has_function(funcname, kwargs['prefix'], self.environment, + extra_args=extra_args, + dependencies=deps) + 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_msg) + return had + + @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] + 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_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_msg) + return had + + @FeatureNew('compiler.compute_int', '0.40.0') + @typed_pos_args('compiler.compute_int', str) + @typed_kwargs( + 'compiler.compute_int', + KwargInfo('low', (int, NoneType)), + KwargInfo('high', (int, NoneType)), + KwargInfo('guess', (int, NoneType)), + *_COMMON_KWS, + ) + def compute_int_method(self, args: T.Tuple[str], kwargs: 'CompupteIntKW') -> int: + expression = args[0] + extra_args = functools.partial(self._determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args']) + deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=self.compiler.is_cross) + 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 + + @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] + extra_args = functools.partial(self._determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args']) + deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=self.compiler.is_cross) + 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') + @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] + 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_msg = mlog.blue('(cached)') if cached else '' + mlog.log('Fetching value of define', mlog.bold(element, True), msg, value, cached_msg) + return value + + @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): + self.interpreter.add_build_def_file(code) + code = mesonlib.File.from_absolute_file( + code.rel_to_builddir(self.environment.source_dir)) + 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, cached = self.compiler.compiles(code, self.environment, + extra_args=extra_args, + dependencies=deps) + if testname: + if result: + h = mlog.green('YES') + else: + h = mlog.red('NO') + cached_msg = mlog.blue('(cached)') if cached else '' + mlog.log('Checking if', mlog.bold(testname, True), msg, 'compiles:', h, cached_msg) + return result + + @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] + compiler = None + if isinstance(code, mesonlib.File): + self.interpreter.add_build_def_file(code) + code = mesonlib.File.from_absolute_file( + code.rel_to_builddir(self.environment.source_dir)) + suffix = code.suffix + if suffix not in self.compiler.file_suffixes: + for_machine = self.compiler.for_machine + clist = self.interpreter.coredata.compilers[for_machine] + if suffix not in SUFFIX_TO_LANG: + # just pass it to the compiler driver + mlog.warning(f'Unknown suffix for test file {code}') + elif SUFFIX_TO_LANG[suffix] not in clist: + mlog.warning(f'Passed {SUFFIX_TO_LANG[suffix]} source to links method, not specified for {for_machine.get_lower_case_name()} machine.') + else: + compiler = clist[SUFFIX_TO_LANG[suffix]] + + 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'], compile_only=False) + result, cached = self.compiler.links(code, self.environment, + compiler=compiler, + extra_args=extra_args, + dependencies=deps) + 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_msg) + return result + + @FeatureNew('compiler.check_header', '0.47.0') + @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] + 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['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_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_msg) + return haz + + 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['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_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_msg) + return haz + + @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 + disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False) + if disabled: + mlog.log('Header', mlog.bold(hname, True), 'has symbol', mlog.bold(symbol, True), 'skipped: feature', mlog.bold(feature), 'disabled') + return False + 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: + raise InterpreterException(f'{self.compiler.get_display_language()} symbol {symbol} not found in header {hname}') + elif haz: + h = mlog.green('YES') + else: + h = mlog.red('NO') + cached_msg = mlog.blue('(cached)') if cached else '' + mlog.log('Header', mlog.bold(hname, True), 'has symbol', mlog.bold(symbol, True), msg, h, cached_msg) + return haz + + def notfound_library(self, libname: str) -> 'dependencies.ExternalLibrary': + lib = dependencies.ExternalLibrary(libname, None, + self.environment, + self.compiler.language, + silent=True) + return lib + + @disablerIfNotFound + @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, NoneType), 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? + libname = args[0] + + 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) + + # 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) + + prefer_static = self.environment.coredata.get_option(OptionKey('prefer_static')) + if kwargs['static'] is True: + libtype = mesonlib.LibType.STATIC + elif kwargs['static'] is False: + libtype = mesonlib.LibType.SHARED + elif prefer_static: + libtype = mesonlib.LibType.PREFER_STATIC + 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_s = 'shared or static' + else: + libtype_s = libtype.name.lower() + raise InterpreterException('{} {} library {!r} not found' + .format(self.compiler.get_display_language(), + 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 implementation 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 + @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 + @typed_pos_args('compiler.has_multi_arguments', varargs=str) + @FeatureNew('compiler.has_multi_arguments', '0.37.0') + 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=in_set_validator({'warn', 'require', 'off'})), + ) + 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[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) + elif checked == 'require': + raise mesonlib.MesonException(msg) + else: + supported_args.append(arg) + return supported_args + + @noKwargs + @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')) + return [] + + @FeatureNew('compiler.has_link_argument', '0.46.0') + @noKwargs + @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 + @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', '0.46.0') + @noKwargs + @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 + @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 [] + + 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(f'Compiler for {self.compiler.get_display_language()} supports function attribute {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 + @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: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: + return self.compiler.get_argument_syntax() + + @FeatureNew('compiler.preprocess', '0.64.0') + @typed_pos_args('compiler.preprocess', varargs=(mesonlib.File, str), min_varargs=1) + @typed_kwargs( + 'compiler.preprocess', + KwargInfo('output', str, default='@PLAINNAME@.i'), + KwargInfo('compile_args', ContainerTypeInfo(list, str), listify=True, default=[]), + _INCLUDE_DIRS_KW, + ) + def preprocess_method(self, args: T.Tuple[T.List['mesonlib.FileOrString']], kwargs: 'PreprocessKW') -> T.List[build.CustomTargetIndex]: + compiler = self.compiler.get_preprocessor() + sources = self.interpreter.source_strings_to_files(args[0]) + tg_kwargs = { + f'{self.compiler.language}_args': kwargs['compile_args'], + 'build_by_default': False, + 'include_directories': kwargs['include_directories'], + } + tg = build.CompileTarget( + 'preprocessor', + self.interpreter.subdir, + self.subproject, + self.environment, + sources, + kwargs['output'], + compiler, + tg_kwargs) + self.interpreter.add_target(tg.name, tg) + # Expose this target as list of its outputs, so user can pass them to + # other targets, list outputs, etc. + private_dir = os.path.relpath(self.interpreter.backend.get_target_private_dir(tg), self.interpreter.subdir) + return [build.CustomTargetIndex(tg, os.path.join(private_dir, o)) for o in tg.outputs] |