summaryrefslogtreecommitdiffstats
path: root/mesonbuild/compilers/compilers.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-29 04:41:38 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-29 04:41:38 +0000
commit7b6e527f440cd7e6f8be2b07cee320ee6ca18786 (patch)
tree4a2738d69fa2814659fdadddf5826282e73d81f4 /mesonbuild/compilers/compilers.py
parentInitial commit. (diff)
downloadmeson-7b6e527f440cd7e6f8be2b07cee320ee6ca18786.tar.xz
meson-7b6e527f440cd7e6f8be2b07cee320ee6ca18786.zip
Adding upstream version 1.0.1.upstream/1.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--mesonbuild/compilers/compilers.py1342
1 files changed, 1342 insertions, 0 deletions
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
new file mode 100644
index 0000000..c5a51cb
--- /dev/null
+++ b/mesonbuild/compilers/compilers.py
@@ -0,0 +1,1342 @@
+# Copyright 2012-2022 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from __future__ import annotations
+
+import abc
+import contextlib, os.path, re
+import enum
+import itertools
+import typing as T
+from functools import lru_cache
+
+from .. import coredata
+from .. import mlog
+from .. import mesonlib
+from ..mesonlib import (
+ HoldableObject,
+ EnvironmentException, MesonException,
+ Popen_safe, LibType, TemporaryDirectoryWinProof, OptionKey,
+)
+
+from ..arglist import CompilerArgs
+
+if T.TYPE_CHECKING:
+ from ..build import BuildTarget
+ from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..linkers import DynamicLinker, RSPFileSyntax
+ from ..mesonlib import MachineChoice
+ from ..dependencies import Dependency
+
+ CompilerType = T.TypeVar('CompilerType', bound='Compiler')
+ _T = T.TypeVar('_T')
+
+"""This file contains the data files of all compilers Meson knows
+about. To support a new compiler, add its information below.
+Also add corresponding autodetection code in environment.py."""
+
+header_suffixes = {'h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi', 'di'}
+obj_suffixes = {'o', 'obj', 'res'}
+# To the emscripten compiler, .js files are libraries
+lib_suffixes = {'a', 'lib', 'dll', 'dll.a', 'dylib', 'so', 'js'}
+# Mapping of language to suffixes of files that should always be in that language
+# This means we can't include .h headers here since they could be C, C++, ObjC, etc.
+# First suffix is the language's default.
+lang_suffixes = {
+ 'c': ('c',),
+ 'cpp': ('cpp', 'cc', 'cxx', 'c++', 'hh', 'hpp', 'ipp', 'hxx', 'ino', 'ixx', 'C'),
+ 'cuda': ('cu',),
+ # f90, f95, f03, f08 are for free-form fortran ('f90' recommended)
+ # f, for, ftn, fpp are for fixed-form fortran ('f' or 'for' recommended)
+ 'fortran': ('f90', 'f95', 'f03', 'f08', 'f', 'for', 'ftn', 'fpp'),
+ 'd': ('d', 'di'),
+ 'objc': ('m',),
+ 'objcpp': ('mm',),
+ 'rust': ('rs',),
+ 'vala': ('vala', 'vapi', 'gs'),
+ 'cs': ('cs',),
+ 'swift': ('swift',),
+ 'java': ('java',),
+ 'cython': ('pyx', ),
+ 'nasm': ('asm',),
+ 'masm': ('masm',),
+}
+all_languages = lang_suffixes.keys()
+c_cpp_suffixes = {'h'}
+cpp_suffixes = set(lang_suffixes['cpp']) | c_cpp_suffixes
+c_suffixes = set(lang_suffixes['c']) | c_cpp_suffixes
+assembler_suffixes = {'s', 'S', 'asm', 'masm'}
+llvm_ir_suffixes = {'ll'}
+all_suffixes = set(itertools.chain(*lang_suffixes.values(), assembler_suffixes, llvm_ir_suffixes, c_cpp_suffixes))
+source_suffixes = all_suffixes - header_suffixes
+# List of languages that by default consume and output libraries following the
+# C ABI; these can generally be used interchangeably
+# This must be sorted, see sort_clink().
+clib_langs = ('objcpp', 'cpp', 'objc', 'c', 'fortran')
+# List of languages that can be linked with C code directly by the linker
+# used in build.py:process_compilers() and build.py:get_dynamic_linker()
+# This must be sorted, see sort_clink().
+clink_langs = ('d', 'cuda') + clib_langs
+
+SUFFIX_TO_LANG = dict(itertools.chain(*(
+ [(suffix, lang) for suffix in v] for lang, v in lang_suffixes.items()))) # type: T.Dict[str, str]
+
+# Languages that should use LDFLAGS arguments when linking.
+LANGUAGES_USING_LDFLAGS = {'objcpp', 'cpp', 'objc', 'c', 'fortran', 'd', 'cuda'} # type: T.Set[str]
+# Languages that should use CPPFLAGS arguments when linking.
+LANGUAGES_USING_CPPFLAGS = {'c', 'cpp', 'objc', 'objcpp'} # type: T.Set[str]
+soregex = re.compile(r'.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$')
+
+# Environment variables that each lang uses.
+CFLAGS_MAPPING: T.Mapping[str, str] = {
+ 'c': 'CFLAGS',
+ 'cpp': 'CXXFLAGS',
+ 'cuda': 'CUFLAGS',
+ 'objc': 'OBJCFLAGS',
+ 'objcpp': 'OBJCXXFLAGS',
+ 'fortran': 'FFLAGS',
+ 'd': 'DFLAGS',
+ 'vala': 'VALAFLAGS',
+ 'rust': 'RUSTFLAGS',
+ 'cython': 'CYTHONFLAGS',
+ 'cs': 'CSFLAGS', # This one might not be standard.
+}
+
+# All these are only for C-linkable languages; see `clink_langs` above.
+
+def sort_clink(lang: str) -> int:
+ '''
+ Sorting function to sort the list of languages according to
+ reversed(compilers.clink_langs) and append the unknown langs in the end.
+ The purpose is to prefer C over C++ for files that can be compiled by
+ both such as assembly, C, etc. Also applies to ObjC, ObjC++, etc.
+ '''
+ if lang not in clink_langs:
+ return 1
+ return -clink_langs.index(lang)
+
+def is_header(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
+ fname = fname.fname
+ suffix = fname.split('.')[-1]
+ return suffix in header_suffixes
+
+def is_source(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
+ fname = fname.fname
+ suffix = fname.split('.')[-1].lower()
+ return suffix in source_suffixes
+
+def is_assembly(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
+ fname = fname.fname
+ suffix = fname.split('.')[-1]
+ return suffix in assembler_suffixes
+
+def is_llvm_ir(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
+ fname = fname.fname
+ suffix = fname.split('.')[-1]
+ return suffix in llvm_ir_suffixes
+
+@lru_cache(maxsize=None)
+def cached_by_name(fname: 'mesonlib.FileOrString') -> bool:
+ suffix = fname.split('.')[-1]
+ return suffix in obj_suffixes
+
+def is_object(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
+ fname = fname.fname
+ return cached_by_name(fname)
+
+def is_library(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
+ fname = fname.fname
+
+ if soregex.match(fname):
+ return True
+
+ suffix = fname.split('.')[-1]
+ return suffix in lib_suffixes
+
+def is_known_suffix(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
+ fname = fname.fname
+ suffix = fname.split('.')[-1]
+
+ return suffix in all_suffixes
+
+
+class CompileCheckMode(enum.Enum):
+
+ PREPROCESS = 'preprocess'
+ COMPILE = 'compile'
+ LINK = 'link'
+
+
+cuda_buildtype_args = {'plain': [],
+ 'debug': ['-g', '-G'],
+ 'debugoptimized': ['-g', '-lineinfo'],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+ } # type: T.Dict[str, T.List[str]]
+java_buildtype_args = {'plain': [],
+ 'debug': ['-g'],
+ 'debugoptimized': ['-g'],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+ } # type: T.Dict[str, T.List[str]]
+
+rust_buildtype_args = {'plain': [],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+ } # type: T.Dict[str, T.List[str]]
+
+d_gdc_buildtype_args = {'plain': [],
+ 'debug': [],
+ 'debugoptimized': ['-finline-functions'],
+ 'release': ['-finline-functions'],
+ 'minsize': [],
+ 'custom': [],
+ } # type: T.Dict[str, T.List[str]]
+
+d_ldc_buildtype_args = {'plain': [],
+ 'debug': [],
+ 'debugoptimized': ['-enable-inlining', '-Hkeep-all-bodies'],
+ 'release': ['-enable-inlining', '-Hkeep-all-bodies'],
+ 'minsize': [],
+ 'custom': [],
+ } # type: T.Dict[str, T.List[str]]
+
+d_dmd_buildtype_args = {'plain': [],
+ 'debug': [],
+ 'debugoptimized': ['-inline'],
+ 'release': ['-inline'],
+ 'minsize': [],
+ 'custom': [],
+ } # type: T.Dict[str, T.List[str]]
+
+mono_buildtype_args = {'plain': [],
+ 'debug': [],
+ 'debugoptimized': ['-optimize+'],
+ 'release': ['-optimize+'],
+ 'minsize': [],
+ 'custom': [],
+ } # type: T.Dict[str, T.List[str]]
+
+swift_buildtype_args = {'plain': [],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+ } # type: T.Dict[str, T.List[str]]
+
+gnu_winlibs = ['-lkernel32', '-luser32', '-lgdi32', '-lwinspool', '-lshell32',
+ '-lole32', '-loleaut32', '-luuid', '-lcomdlg32', '-ladvapi32'] # type: T.List[str]
+
+msvc_winlibs = ['kernel32.lib', 'user32.lib', 'gdi32.lib',
+ 'winspool.lib', 'shell32.lib', 'ole32.lib', 'oleaut32.lib',
+ 'uuid.lib', 'comdlg32.lib', 'advapi32.lib'] # type: T.List[str]
+
+clike_optimization_args = {'plain': [],
+ '0': [],
+ 'g': [],
+ '1': ['-O1'],
+ '2': ['-O2'],
+ '3': ['-O3'],
+ 's': ['-Os'],
+ } # type: T.Dict[str, T.List[str]]
+
+cuda_optimization_args = {'plain': [],
+ '0': [],
+ 'g': ['-O0'],
+ '1': ['-O1'],
+ '2': ['-O2'],
+ '3': ['-O3'],
+ 's': ['-O3']
+ } # type: T.Dict[str, T.List[str]]
+
+cuda_debug_args = {False: [],
+ True: ['-g']} # type: T.Dict[bool, T.List[str]]
+
+clike_debug_args = {False: [],
+ True: ['-g']} # type: T.Dict[bool, T.List[str]]
+
+base_options: 'KeyedOptionDictType' = {
+ OptionKey('b_pch'): coredata.UserBooleanOption('Use precompiled headers', True),
+ OptionKey('b_lto'): coredata.UserBooleanOption('Use link time optimization', False),
+ OptionKey('b_lto_threads'): coredata.UserIntegerOption('Use multiple threads for Link Time Optimization', (None, None, 0)),
+ OptionKey('b_lto_mode'): coredata.UserComboOption('Select between different LTO modes.',
+ ['default', 'thin'],
+ 'default'),
+ OptionKey('b_thinlto_cache'): coredata.UserBooleanOption('Use LLVM ThinLTO caching for faster incremental builds', False),
+ OptionKey('b_thinlto_cache_dir'): coredata.UserStringOption('Directory to store ThinLTO cache objects', ''),
+ OptionKey('b_sanitize'): coredata.UserComboOption('Code sanitizer to use',
+ ['none', 'address', 'thread', 'undefined', 'memory', 'leak', 'address,undefined'],
+ 'none'),
+ OptionKey('b_lundef'): coredata.UserBooleanOption('Use -Wl,--no-undefined when linking', True),
+ OptionKey('b_asneeded'): coredata.UserBooleanOption('Use -Wl,--as-needed when linking', True),
+ OptionKey('b_pgo'): coredata.UserComboOption('Use profile guided optimization',
+ ['off', 'generate', 'use'],
+ 'off'),
+ OptionKey('b_coverage'): coredata.UserBooleanOption('Enable coverage tracking.', False),
+ OptionKey('b_colorout'): coredata.UserComboOption('Use colored output',
+ ['auto', 'always', 'never'],
+ 'always'),
+ OptionKey('b_ndebug'): coredata.UserComboOption('Disable asserts', ['true', 'false', 'if-release'], 'false'),
+ OptionKey('b_staticpic'): coredata.UserBooleanOption('Build static libraries as position independent', True),
+ OptionKey('b_pie'): coredata.UserBooleanOption('Build executables as position independent', False),
+ OptionKey('b_bitcode'): coredata.UserBooleanOption('Generate and embed bitcode (only macOS/iOS/tvOS)', False),
+ OptionKey('b_vscrt'): coredata.UserComboOption('VS run-time library type to use.',
+ ['none', 'md', 'mdd', 'mt', 'mtd', 'from_buildtype', 'static_from_buildtype'],
+ 'from_buildtype'),
+}
+
+def option_enabled(boptions: T.Set[OptionKey], options: 'KeyedOptionDictType',
+ option: OptionKey) -> bool:
+ try:
+ if option not in boptions:
+ return False
+ ret = options[option].value
+ assert isinstance(ret, bool), 'must return bool' # could also be str
+ return ret
+ except KeyError:
+ return False
+
+
+def get_option_value(options: 'KeyedOptionDictType', opt: OptionKey, fallback: '_T') -> '_T':
+ """Get the value of an option, or the fallback value."""
+ try:
+ v: '_T' = options[opt].value
+ except KeyError:
+ return fallback
+
+ assert isinstance(v, type(fallback)), f'Should have {type(fallback)!r} but was {type(v)!r}'
+ # Mypy doesn't understand that the above assert ensures that v is type _T
+ return v
+
+
+def get_base_compile_args(options: 'KeyedOptionDictType', compiler: 'Compiler') -> T.List[str]:
+ args = [] # type T.List[str]
+ try:
+ if options[OptionKey('b_lto')].value:
+ args.extend(compiler.get_lto_compile_args(
+ threads=get_option_value(options, OptionKey('b_lto_threads'), 0),
+ mode=get_option_value(options, OptionKey('b_lto_mode'), 'default')))
+ except KeyError:
+ pass
+ try:
+ args += compiler.get_colorout_args(options[OptionKey('b_colorout')].value)
+ except KeyError:
+ pass
+ try:
+ args += compiler.sanitizer_compile_args(options[OptionKey('b_sanitize')].value)
+ except KeyError:
+ pass
+ try:
+ pgo_val = options[OptionKey('b_pgo')].value
+ if pgo_val == 'generate':
+ args.extend(compiler.get_profile_generate_args())
+ elif pgo_val == 'use':
+ args.extend(compiler.get_profile_use_args())
+ except KeyError:
+ pass
+ try:
+ if options[OptionKey('b_coverage')].value:
+ args += compiler.get_coverage_args()
+ except KeyError:
+ pass
+ try:
+ if (options[OptionKey('b_ndebug')].value == 'true' or
+ (options[OptionKey('b_ndebug')].value == 'if-release' and
+ options[OptionKey('buildtype')].value in {'release', 'plain'})):
+ args += compiler.get_disable_assert_args()
+ except KeyError:
+ pass
+ # This does not need a try...except
+ if option_enabled(compiler.base_options, options, OptionKey('b_bitcode')):
+ args.append('-fembed-bitcode')
+ try:
+ crt_val = options[OptionKey('b_vscrt')].value
+ buildtype = options[OptionKey('buildtype')].value
+ try:
+ args += compiler.get_crt_compile_args(crt_val, buildtype)
+ except AttributeError:
+ pass
+ except KeyError:
+ pass
+ return args
+
+def get_base_link_args(options: 'KeyedOptionDictType', linker: 'Compiler',
+ is_shared_module: bool, build_dir: str) -> T.List[str]:
+ args = [] # type: T.List[str]
+ try:
+ if options[OptionKey('b_lto')].value:
+ thinlto_cache_dir = None
+ if get_option_value(options, OptionKey('b_thinlto_cache'), False):
+ thinlto_cache_dir = get_option_value(options, OptionKey('b_thinlto_cache_dir'), '')
+ if thinlto_cache_dir == '':
+ thinlto_cache_dir = os.path.join(build_dir, 'meson-private', 'thinlto-cache')
+ args.extend(linker.get_lto_link_args(
+ threads=get_option_value(options, OptionKey('b_lto_threads'), 0),
+ mode=get_option_value(options, OptionKey('b_lto_mode'), 'default'),
+ thinlto_cache_dir=thinlto_cache_dir))
+ except KeyError:
+ pass
+ try:
+ args += linker.sanitizer_link_args(options[OptionKey('b_sanitize')].value)
+ except KeyError:
+ pass
+ try:
+ pgo_val = options[OptionKey('b_pgo')].value
+ if pgo_val == 'generate':
+ args.extend(linker.get_profile_generate_args())
+ elif pgo_val == 'use':
+ args.extend(linker.get_profile_use_args())
+ except KeyError:
+ pass
+ try:
+ if options[OptionKey('b_coverage')].value:
+ args += linker.get_coverage_link_args()
+ except KeyError:
+ pass
+
+ as_needed = option_enabled(linker.base_options, options, OptionKey('b_asneeded'))
+ bitcode = option_enabled(linker.base_options, options, OptionKey('b_bitcode'))
+ # Shared modules cannot be built with bitcode_bundle because
+ # -bitcode_bundle is incompatible with -undefined and -bundle
+ if bitcode and not is_shared_module:
+ args.extend(linker.bitcode_args())
+ elif as_needed:
+ # -Wl,-dead_strip_dylibs is incompatible with bitcode
+ args.extend(linker.get_asneeded_args())
+
+ # Apple's ld (the only one that supports bitcode) does not like -undefined
+ # arguments or -headerpad_max_install_names when bitcode is enabled
+ if not bitcode:
+ args.extend(linker.headerpad_args())
+ if (not is_shared_module and
+ option_enabled(linker.base_options, options, OptionKey('b_lundef'))):
+ args.extend(linker.no_undefined_link_args())
+ else:
+ args.extend(linker.get_allow_undefined_link_args())
+
+ try:
+ crt_val = options[OptionKey('b_vscrt')].value
+ buildtype = options[OptionKey('buildtype')].value
+ try:
+ args += linker.get_crt_link_args(crt_val, buildtype)
+ except AttributeError:
+ pass
+ except KeyError:
+ pass
+ return args
+
+
+class CrossNoRunException(MesonException):
+ pass
+
+class RunResult(HoldableObject):
+ def __init__(self, compiled: bool, returncode: int = 999,
+ stdout: str = 'UNDEFINED', stderr: str = 'UNDEFINED'):
+ self.compiled = compiled
+ self.returncode = returncode
+ self.stdout = stdout
+ self.stderr = stderr
+
+
+class CompileResult(HoldableObject):
+
+ """The result of Compiler.compiles (and friends)."""
+
+ def __init__(self, stdo: T.Optional[str] = None, stde: T.Optional[str] = None,
+ command: T.Optional[T.List[str]] = None,
+ returncode: int = 999,
+ input_name: T.Optional[str] = None,
+ output_name: T.Optional[str] = None,
+ cached: bool = False):
+ self.stdout = stdo
+ self.stderr = stde
+ self.input_name = input_name
+ self.output_name = output_name
+ self.command = command or []
+ self.cached = cached
+ self.returncode = returncode
+
+
+class Compiler(HoldableObject, metaclass=abc.ABCMeta):
+ # Libraries to ignore in find_library() since they are provided by the
+ # compiler or the C library. Currently only used for MSVC.
+ ignore_libs = [] # type: T.List[str]
+ # Libraries that are internal compiler implementations, and must not be
+ # manually searched.
+ internal_libs = [] # type: T.List[str]
+
+ LINKER_PREFIX = None # type: T.Union[None, str, T.List[str]]
+ INVOKES_LINKER = True
+
+ language: str
+ id: str
+ warn_args: T.Dict[str, T.List[str]]
+ mode: str = 'COMPILER'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str,
+ for_machine: MachineChoice, info: 'MachineInfo',
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None, is_cross: bool = False):
+ self.exelist = ccache + exelist
+ self.exelist_no_ccache = exelist
+ # In case it's been overridden by a child class already
+ if not hasattr(self, 'file_suffixes'):
+ self.file_suffixes = lang_suffixes[self.language]
+ if not hasattr(self, 'can_compile_suffixes'):
+ self.can_compile_suffixes = set(self.file_suffixes)
+ self.default_suffix = self.file_suffixes[0]
+ self.version = version
+ self.full_version = full_version
+ self.for_machine = for_machine
+ self.base_options: T.Set[OptionKey] = set()
+ self.linker = linker
+ self.info = info
+ self.is_cross = is_cross
+ self.modes: T.List[Compiler] = []
+
+ def __repr__(self) -> str:
+ repr_str = "<{0}: v{1} `{2}`>"
+ return repr_str.format(self.__class__.__name__, self.version,
+ ' '.join(self.exelist))
+
+ @lru_cache(maxsize=None)
+ def can_compile(self, src: 'mesonlib.FileOrString') -> bool:
+ if isinstance(src, mesonlib.File):
+ src = src.fname
+ suffix = os.path.splitext(src)[1]
+ if suffix != '.C':
+ suffix = suffix.lower()
+ return bool(suffix) and suffix[1:] in self.can_compile_suffixes
+
+ def get_id(self) -> str:
+ return self.id
+
+ def get_modes(self) -> T.List[Compiler]:
+ return self.modes
+
+ def get_linker_id(self) -> str:
+ # There is not guarantee that we have a dynamic linker instance, as
+ # some languages don't have separate linkers and compilers. In those
+ # cases return the compiler id
+ try:
+ return self.linker.id
+ except AttributeError:
+ return self.id
+
+ def get_version_string(self) -> str:
+ details = [self.id, self.version]
+ if self.full_version:
+ details += ['"%s"' % (self.full_version)]
+ return '(%s)' % (' '.join(details))
+
+ def get_language(self) -> str:
+ return self.language
+
+ @classmethod
+ def get_display_language(cls) -> str:
+ return cls.language.capitalize()
+
+ def get_default_suffix(self) -> str:
+ return self.default_suffix
+
+ def get_define(self, dname: str, prefix: str, env: 'Environment',
+ extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], 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.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
+ dependencies: T.Optional[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.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.List[str],
+ prefix: str, env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
+ dependencies: T.Optional[T.List['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.Union[T.List[str], T.Callable[[CompileCheckMode], 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())
+
+ def symbols_have_underscore_prefix(self, env: 'Environment') -> bool:
+ raise EnvironmentException('%s does not support symbols_have_underscore_prefix ' % self.get_id())
+
+ def get_exelist(self, ccache: bool = True) -> T.List[str]:
+ return self.exelist.copy() if ccache else self.exelist_no_ccache.copy()
+
+ def get_linker_exelist(self) -> T.List[str]:
+ return self.linker.get_exelist()
+
+ @abc.abstractmethod
+ def get_output_args(self, outputname: str) -> T.List[str]:
+ pass
+
+ def get_linker_output_args(self, outputname: str) -> T.List[str]:
+ return self.linker.get_output_args(outputname)
+
+ def get_linker_search_args(self, dirname: str) -> T.List[str]:
+ return self.linker.get_search_args(dirname)
+
+ def get_builtin_define(self, define: str) -> T.Optional[str]:
+ raise EnvironmentException('%s does not support get_builtin_define.' % self.id)
+
+ def has_builtin_define(self, define: str) -> bool:
+ raise EnvironmentException('%s does not support has_builtin_define.' % self.id)
+
+ def get_always_args(self) -> T.List[str]:
+ return []
+
+ def can_linker_accept_rsp(self) -> bool:
+ """
+ Determines whether the linker can accept arguments using the @rsp syntax.
+ """
+ return self.linker.get_accepts_rsp()
+
+ def get_linker_always_args(self) -> T.List[str]:
+ return self.linker.get_always_args()
+
+ def get_linker_lib_prefix(self) -> str:
+ return self.linker.get_lib_prefix()
+
+ def gen_import_library_args(self, implibname: str) -> T.List[str]:
+ """
+ Used only on Windows for libraries that need an import library.
+ This currently means C, C++, Fortran.
+ """
+ return []
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ return {}
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return []
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return self.linker.get_option_args(options)
+
+ def check_header(self, hname: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
+ """Check that header is usable.
+
+ Returns a two item tuple of bools. The first bool is whether the
+ check succeeded, the second is whether the result was cached (True)
+ or run fresh (False).
+ """
+ raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language())
+
+ def has_header(self, hname: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None,
+ disable_cache: bool = False) -> T.Tuple[bool, bool]:
+ """Check that header is exists.
+
+ This check will return true if the file exists, even if it contains:
+
+ ```c
+ # error "You thought you could use this, LOLZ!"
+ ```
+
+ Use check_header if your header only works in some cases.
+
+ Returns a two item tuple of bools. The first bool is whether the
+ check succeeded, the second is whether the result was cached (True)
+ or run fresh (False).
+ """
+ raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language())
+
+ def has_header_symbol(self, hname: str, symbol: str, prefix: str,
+ env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
+ raise EnvironmentException('Language %s does not support header symbol checks.' % self.get_display_language())
+
+ def run(self, code: 'mesonlib.FileOrString', env: 'Environment', *,
+ extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]], None] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> RunResult:
+ raise EnvironmentException('Language %s does not support run checks.' % self.get_display_language())
+
+ def sizeof(self, typename: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
+ raise EnvironmentException('Language %s does not support sizeof checks.' % self.get_display_language())
+
+ 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:
+ raise EnvironmentException('Language %s does not support alignment checks.' % self.get_display_language())
+
+ 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]:
+ """See if a function exists.
+
+ Returns a two item tuple of bools. The first bool is whether the
+ check succeeded, the second is whether the result was cached (True)
+ or run fresh (False).
+ """
+ raise EnvironmentException('Language %s does not support function checks.' % self.get_display_language())
+
+ @classmethod
+ def _unix_args_to_native(cls, args: T.List[str], info: MachineInfo) -> T.List[str]:
+ "Always returns a copy that can be independently mutated"
+ return args.copy()
+
+ def unix_args_to_native(self, args: T.List[str]) -> T.List[str]:
+ return self._unix_args_to_native(args, self.info)
+
+ @classmethod
+ def native_args_to_unix(cls, args: T.List[str]) -> T.List[str]:
+ "Always returns a copy that can be independently mutated"
+ return args.copy()
+
+ def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
+ libtype: LibType = LibType.PREFER_SHARED) -> T.Optional[T.List[str]]:
+ raise EnvironmentException(f'Language {self.get_display_language()} does not support library finding.')
+
+ def get_library_naming(self, env: 'Environment', libtype: LibType,
+ 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 []
+
+ def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ raise EnvironmentException(
+ 'Language {} does not support has_multi_arguments.'.format(
+ self.get_display_language()))
+
+ def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ return self.linker.has_multi_arguments(args, env)
+
+ def _get_compile_output(self, dirname: str, mode: str) -> str:
+ # TODO: mode should really be an enum
+ # In pre-processor mode, the output is sent to stdout and discarded
+ if mode == 'preprocess':
+ return None
+ # Extension only matters if running results; '.exe' is
+ # guaranteed to be executable on every platform.
+ if mode == 'link':
+ suffix = 'exe'
+ else:
+ suffix = 'obj'
+ return os.path.join(dirname, 'output.' + suffix)
+
+ def get_compiler_args_for_mode(self, mode: CompileCheckMode) -> T.List[str]:
+ # TODO: mode should really be an enum
+ args = [] # type: T.List[str]
+ args += self.get_always_args()
+ if mode is CompileCheckMode.COMPILE:
+ args += self.get_compile_only_args()
+ elif mode is CompileCheckMode.PREPROCESS:
+ args += self.get_preprocess_only_args()
+ else:
+ assert mode is CompileCheckMode.LINK
+ return args
+
+ def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CompilerArgs:
+ """Return an appropriate CompilerArgs instance for this class."""
+ return CompilerArgs(self, args)
+
+ @contextlib.contextmanager
+ 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 = []
+
+ with TemporaryDirectoryWinProof(dir=temp_dir) as tmpdirname:
+ no_ccache = False
+ if isinstance(code, str):
+ srcname = os.path.join(tmpdirname,
+ 'testfile.' + self.default_suffix)
+ with open(srcname, 'w', encoding='utf-8') as ofile:
+ ofile.write(code)
+ # ccache would result in a cache miss
+ no_ccache = True
+ contents = code
+ else:
+ srcname = code.fname
+ if not is_object(code.fname):
+ with open(code.fname, encoding='utf-8') as f:
+ contents = f.read()
+ else:
+ contents = '<binary>'
+
+ # Construct the compiler command-line
+ commands = self.compiler_args()
+ commands.append(srcname)
+
+ # Preprocess mode outputs to stdout, so no output args
+ output = self._get_compile_output(tmpdirname, mode)
+ if mode != 'preprocess':
+ commands += self.get_output_args(output)
+ commands.extend(self.get_compiler_args_for_mode(CompileCheckMode(mode)))
+
+ # extra_args must be last because it could contain '/link' to
+ # pass args to VisualStudio's linker. In that case everything
+ # in the command line after '/link' is given to the linker.
+ if extra_args:
+ commands += extra_args
+ # Generate full command-line with the exelist
+ command_list = self.get_exelist(ccache=not no_ccache) + commands.to_native()
+ mlog.debug('Running compile:')
+ mlog.debug('Working directory: ', tmpdirname)
+ mlog.debug('Command line: ', ' '.join(command_list), '\n')
+ mlog.debug('Code:\n', contents)
+ os_env = os.environ.copy()
+ os_env['LC_ALL'] = 'C'
+ if no_ccache:
+ os_env['CCACHE_DISABLE'] = '1'
+ p, stdo, stde = Popen_safe(command_list, cwd=tmpdirname, env=os_env)
+ mlog.debug('Compiler stdout:\n', stdo)
+ mlog.debug('Compiler stderr:\n', stde)
+
+ result = CompileResult(stdo, stde, command_list, p.returncode, input_name=srcname)
+ if want_output:
+ result.output_name = output
+ yield result
+
+ @contextlib.contextmanager
+ def cached_compile(self, code: 'mesonlib.FileOrString', cdata: coredata.CoreData, *,
+ 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
+
+ # Check if not cached, and generate, otherwise get from the cache
+ if key in cdata.compiler_check_cache:
+ p = cdata.compiler_check_cache[key]
+ p.cached = True
+ mlog.debug('Using cached compile:')
+ mlog.debug('Cached command line: ', ' '.join(p.command), '\n')
+ mlog.debug('Code:\n', code)
+ mlog.debug('Cached compiler stdout:\n', p.stdout)
+ mlog.debug('Cached compiler stderr:\n', p.stderr)
+ yield p
+ else:
+ with self.compile(code, extra_args=extra_args, mode=mode, want_output=False, temp_dir=temp_dir) as p:
+ cdata.compiler_check_cache[key] = p
+ yield p
+
+ def get_colorout_args(self, colortype: str) -> T.List[str]:
+ # TODO: colortype can probably be an emum
+ return []
+
+ # Some compilers (msvc) write debug info to a separate file.
+ # These args specify where it should be written.
+ def get_compile_debugfile_args(self, rel_obj: str, pch: bool = False) -> T.List[str]:
+ return []
+
+ def get_link_debugfile_name(self, targetfile: str) -> str:
+ return self.linker.get_debugfile_name(targetfile)
+
+ def get_link_debugfile_args(self, targetfile: str) -> T.List[str]:
+ return self.linker.get_debugfile_args(targetfile)
+
+ def get_std_shared_lib_link_args(self) -> T.List[str]:
+ return self.linker.get_std_shared_lib_args()
+
+ def get_std_shared_module_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return self.linker.get_std_shared_module_args(options)
+
+ def get_link_whole_for(self, args: T.List[str]) -> T.List[str]:
+ return self.linker.get_link_whole_for(args)
+
+ def get_allow_undefined_link_args(self) -> T.List[str]:
+ return self.linker.get_allow_undefined_args()
+
+ def no_undefined_link_args(self) -> T.List[str]:
+ return self.linker.no_undefined_args()
+
+ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
+ """Compiler arguments needed to enable the given instruction set.
+
+ Return type ay be an empty list meaning nothing needed or None
+ meaning the given set is not supported.
+ """
+ return None
+
+ def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
+ rpath_paths: T.Tuple[str, ...], build_rpath: str,
+ install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ return self.linker.build_rpath_args(
+ env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ return []
+
+ def thread_link_flags(self, env: 'Environment') -> T.List[str]:
+ return self.linker.thread_flags(env)
+
+ def openmp_flags(self) -> T.List[str]:
+ raise EnvironmentException('Language %s does not support OpenMP flags.' % self.get_display_language())
+
+ def openmp_link_flags(self) -> T.List[str]:
+ return self.openmp_flags()
+
+ def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ return []
+
+ def gnu_symbol_visibility_args(self, vistype: str) -> T.List[str]:
+ return []
+
+ def get_gui_app_args(self, value: bool) -> T.List[str]:
+ # Only used on Windows
+ return self.linker.get_gui_app_args(value)
+
+ def get_win_subsystem_args(self, value: str) -> T.List[str]:
+ # By default the dynamic linker is going to return an empty
+ # array in case it either doesn't support Windows subsystems
+ # or does not target Windows
+ return self.linker.get_win_subsystem_args(value)
+
+ def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]:
+ raise EnvironmentException(
+ f'Language {self.get_display_language()} does not support function attributes.')
+
+ def get_pic_args(self) -> T.List[str]:
+ m = 'Language {} does not support position-independent code'
+ raise EnvironmentException(m.format(self.get_display_language()))
+
+ def get_pie_args(self) -> T.List[str]:
+ m = 'Language {} does not support position-independent executable'
+ raise EnvironmentException(m.format(self.get_display_language()))
+
+ def get_pie_link_args(self) -> T.List[str]:
+ return self.linker.get_pie_args()
+
+ def get_argument_syntax(self) -> str:
+ """Returns the argument family type.
+
+ Compilers fall into families if they try to emulate the command line
+ interface of another compiler. For example, clang is in the GCC family
+ since it accepts most of the same arguments as GCC. ICL (ICC on
+ windows) is in the MSVC family since it accepts most of the same
+ arguments as MSVC.
+ """
+ return 'other'
+
+ def get_profile_generate_args(self) -> T.List[str]:
+ raise EnvironmentException(
+ '%s does not support get_profile_generate_args ' % self.get_id())
+
+ def get_profile_use_args(self) -> T.List[str]:
+ raise EnvironmentException(
+ '%s does not support get_profile_use_args ' % self.get_id())
+
+ def remove_linkerlike_args(self, args: T.List[str]) -> T.List[str]:
+ rm_exact = ('-headerpad_max_install_names',)
+ rm_prefixes = ('-Wl,', '-L',)
+ rm_next = ('-L', '-framework',)
+ ret = [] # T.List[str]
+ iargs = iter(args)
+ for arg in iargs:
+ # Remove this argument
+ if arg in rm_exact:
+ continue
+ # If the argument starts with this, but is not *exactly* this
+ # f.ex., '-L' should match ['-Lfoo'] but not ['-L', 'foo']
+ if arg.startswith(rm_prefixes) and arg not in rm_prefixes:
+ continue
+ # Ignore this argument and the one after it
+ if arg in rm_next:
+ next(iargs)
+ continue
+ ret.append(arg)
+ return ret
+
+ def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T.List[str]:
+ return []
+
+ def get_lto_link_args(self, *, threads: int = 0, mode: str = 'default',
+ thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
+ return self.linker.get_lto_args()
+
+ def sanitizer_compile_args(self, value: str) -> T.List[str]:
+ return []
+
+ def sanitizer_link_args(self, value: str) -> T.List[str]:
+ return self.linker.sanitizer_args(value)
+
+ def get_asneeded_args(self) -> T.List[str]:
+ return self.linker.get_asneeded_args()
+
+ def headerpad_args(self) -> T.List[str]:
+ return self.linker.headerpad_args()
+
+ def bitcode_args(self) -> T.List[str]:
+ return self.linker.bitcode_args()
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ raise EnvironmentException(f'{self.id} does not implement get_buildtype_args')
+
+ def get_buildtype_linker_args(self, buildtype: str) -> T.List[str]:
+ return self.linker.get_buildtype_args(buildtype)
+
+ def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
+ suffix: str, soversion: str,
+ darwin_versions: T.Tuple[str, str]) -> T.List[str]:
+ return self.linker.get_soname_args(
+ env, prefix, shlib_name, suffix, soversion,
+ darwin_versions)
+
+ def get_target_link_args(self, target: 'BuildTarget') -> T.List[str]:
+ return target.link_args
+
+ def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]:
+ return dep.get_compile_args()
+
+ def get_dependency_link_args(self, dep: 'Dependency') -> T.List[str]:
+ return dep.get_link_args()
+
+ @classmethod
+ def use_linker_args(cls, linker: str, version: str) -> T.List[str]:
+ """Get a list of arguments to pass to the compiler to set the linker.
+ """
+ return []
+
+ def get_coverage_args(self) -> T.List[str]:
+ return []
+
+ def get_coverage_link_args(self) -> T.List[str]:
+ return self.linker.get_coverage_args()
+
+ def get_disable_assert_args(self) -> T.List[str]:
+ return []
+
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ raise EnvironmentException('This compiler does not support Windows CRT selection')
+
+ def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ raise EnvironmentException('This compiler does not support Windows CRT selection')
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return []
+
+ def get_preprocess_only_args(self) -> T.List[str]:
+ raise EnvironmentException('This compiler does not have a preprocessor')
+
+ def get_preprocess_to_file_args(self) -> T.List[str]:
+ return self.get_preprocess_only_args()
+
+ def get_default_include_dirs(self) -> T.List[str]:
+ # TODO: This is a candidate for returning an immutable list
+ 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 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]:
+ raise EnvironmentException(f'{self.id} does not support get_return_value')
+
+ def find_framework(self,
+ name: str,
+ env: 'Environment',
+ extra_dirs: T.List[str],
+ allow_system: bool = True) -> T.Optional[T.List[str]]:
+ raise EnvironmentException(f'{self.id} does not support find_framework')
+
+ def find_framework_paths(self, env: 'Environment') -> T.List[str]:
+ raise EnvironmentException(f'{self.id} does not support find_framework_paths')
+
+ def attribute_check_func(self, name: str) -> str:
+ raise EnvironmentException(f'{self.id} does not support attribute checks')
+
+ def get_pch_suffix(self) -> str:
+ raise EnvironmentException(f'{self.id} does not support pre compiled headers')
+
+ def get_pch_name(self, name: str) -> str:
+ raise EnvironmentException(f'{self.id} does not support pre compiled headers')
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ raise EnvironmentException(f'{self.id} does not support pre compiled headers')
+
+ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
+ raise EnvironmentException(f'{self.id} does not support function attributes')
+
+ def name_string(self) -> str:
+ return ' '.join(self.exelist)
+
+ @abc.abstractmethod
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ """Check that this compiler actually works.
+
+ This should provide a simple compile/link test. Something as simple as:
+ ```python
+ main(): return 0
+ ```
+ is good enough here.
+ """
+
+ def split_shlib_to_parts(self, fname: str) -> T.Tuple[T.Optional[str], str]:
+ return None, fname
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return []
+
+ def get_std_exe_link_args(self) -> T.List[str]:
+ # TODO: is this a linker property?
+ return []
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ return []
+
+ def depfile_for_object(self, objfile: str) -> T.Optional[str]:
+ return objfile + '.' + self.get_depfile_suffix()
+
+ def get_depfile_suffix(self) -> str:
+ raise EnvironmentException(f'{self.id} does not implement get_depfile_suffix')
+
+ def get_no_stdinc_args(self) -> T.List[str]:
+ """Arguments to turn off default inclusion of standard libraries."""
+ return []
+
+ def get_warn_args(self, level: str) -> T.List[str]:
+ return []
+
+ def get_werror_args(self) -> T.List[str]:
+ return []
+
+ @abc.abstractmethod
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ pass
+
+ def get_module_incdir_args(self) -> T.Tuple[str, ...]:
+ raise EnvironmentException(f'{self.id} does not implement get_module_incdir_args')
+
+ def get_module_outdir_args(self, path: str) -> T.List[str]:
+ raise EnvironmentException(f'{self.id} does not implement get_module_outdir_args')
+
+ def module_name_to_filename(self, module_name: str) -> str:
+ raise EnvironmentException(f'{self.id} does not implement module_name_to_filename')
+
+ def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
+ """Arguments to pass the compiler and/or linker for checks.
+
+ The default implementation turns off optimizations.
+
+ Examples of things that go here:
+ - extra arguments for error checking
+ - Arguments required to make the compiler exit with a non-zero status
+ when something is wrong.
+ """
+ return self.get_no_optimization_args()
+
+ def get_no_optimization_args(self) -> T.List[str]:
+ """Arguments to the compiler to turn off all optimizations."""
+ return []
+
+ def build_wrapper_args(self, env: 'Environment',
+ extra_args: T.Union[None, CompilerArgs, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
+ dependencies: T.Optional[T.List['Dependency']],
+ mode: CompileCheckMode = CompileCheckMode.COMPILE) -> CompilerArgs:
+ """Arguments to pass the build_wrapper helper.
+
+ This generally needs to be set on a per-language baises. It provides
+ a hook for languages to handle dependencies and extra args. The base
+ implementation handles the most common cases, namely adding the
+ check_arguments, unwrapping dependencies, and appending extra args.
+ """
+ if callable(extra_args):
+ extra_args = extra_args(mode)
+ if extra_args is None:
+ extra_args = []
+ if dependencies is None:
+ dependencies = []
+
+ # Collect compiler arguments
+ args = self.compiler_args(self.get_compiler_check_args(mode))
+ for d in dependencies:
+ # Add compile flags needed by dependencies
+ args += d.get_compile_args()
+ if mode is CompileCheckMode.LINK:
+ # Add link flags needed to find dependencies
+ args += d.get_link_args()
+
+ if mode is CompileCheckMode.COMPILE:
+ # Add DFLAGS from the env
+ args += env.coredata.get_external_args(self.for_machine, self.language)
+ elif mode is CompileCheckMode.LINK:
+ # Add LDFLAGS from the env
+ args += env.coredata.get_external_link_args(self.for_machine, self.language)
+ # extra_args must override all other arguments, so we add them last
+ args += extra_args
+ return args
+
+ @contextlib.contextmanager
+ def _build_wrapper(self, code: 'mesonlib.FileOrString', env: 'Environment',
+ extra_args: T.Union[None, CompilerArgs, T.List[str], T.Callable[[CompileCheckMode], 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[CompileResult]]:
+ """Helper for getting a cacched value when possible.
+
+ This method isn't meant to be called externally, it's mean to be
+ wrapped by other methods like compiles() and links().
+ """
+ args = self.build_wrapper_args(env, extra_args, dependencies, CompileCheckMode(mode))
+ if disable_cache or want_output:
+ 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 compiles(self, code: 'mesonlib.FileOrString', env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], CompilerArgs, T.Callable[[CompileCheckMode], 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 links(self, code: 'mesonlib.FileOrString', env: 'Environment', *,
+ compiler: T.Optional['Compiler'] = None,
+ extra_args: T.Union[None, T.List[str], CompilerArgs, T.Callable[[CompileCheckMode], T.List[str]]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None,
+ mode: str = 'compile',
+ disable_cache: bool = False) -> T.Tuple[bool, bool]:
+ if compiler:
+ with compiler._build_wrapper(code, env, dependencies=dependencies, want_output=True) as r:
+ objfile = mesonlib.File.from_absolute_file(r.output_name)
+ return self.compiles(objfile, env, extra_args=extra_args,
+ dependencies=dependencies, mode='link', disable_cache=True)
+
+ return self.compiles(code, env, extra_args=extra_args,
+ dependencies=dependencies, mode='link', disable_cache=disable_cache)
+
+ def get_feature_args(self, kwargs: T.Dict[str, T.Any], build_to_src: str) -> T.List[str]:
+ """Used by D for extra language features."""
+ # TODO: using a TypeDict here would improve this
+ raise EnvironmentException(f'{self.id} does not implement get_feature_args')
+
+ def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.List[str]:
+ raise EnvironmentException(f'{self.id} does not know how to do prelinking.')
+
+ def rsp_file_syntax(self) -> 'RSPFileSyntax':
+ """The format of the RSP file that this compiler supports.
+
+ If `self.can_linker_accept_rsp()` returns True, then this needs to
+ be implemented
+ """
+ return self.linker.rsp_file_syntax()
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ """Arguments required for a debug build."""
+ return []
+
+ def get_no_warn_args(self) -> T.List[str]:
+ """Arguments to completely disable warnings."""
+ return []
+
+ def needs_static_linker(self) -> bool:
+ raise NotImplementedError(f'There is no static linker for {self.language}')
+
+ def get_preprocessor(self) -> Compiler:
+ """Get compiler's preprocessor.
+ """
+ raise EnvironmentException(f'{self.get_id()} does not support preprocessor')
+
+def get_global_options(lang: str,
+ comp: T.Type[Compiler],
+ for_machine: MachineChoice,
+ env: 'Environment') -> 'KeyedOptionDictType':
+ """Retrieve options that apply to all compilers for a given language."""
+ description = f'Extra arguments passed to the {lang}'
+ argkey = OptionKey('args', lang=lang, machine=for_machine)
+ largkey = argkey.evolve('link_args')
+ envkey = argkey.evolve('env_args')
+
+ comp_key = argkey if argkey in env.options else envkey
+
+ comp_options = env.options.get(comp_key, [])
+ link_options = env.options.get(largkey, [])
+
+ cargs = coredata.UserArrayOption(
+ description + ' compiler',
+ comp_options, split_args=True, user_input=True, allow_dups=True)
+
+ largs = coredata.UserArrayOption(
+ description + ' linker',
+ link_options, split_args=True, user_input=True, allow_dups=True)
+
+ if comp.INVOKES_LINKER and comp_key == envkey:
+ # If the compiler acts as a linker driver, and we're using the
+ # environment variable flags for both the compiler and linker
+ # arguments, then put the compiler flags in the linker flags as well.
+ # This is how autotools works, and the env vars freature is for
+ # autotools compatibility.
+ largs.extend_value(comp_options)
+
+ opts: 'KeyedOptionDictType' = {argkey: cargs, largkey: largs}
+
+ return opts