summaryrefslogtreecommitdiffstats
path: root/mesonbuild/compilers
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
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 'mesonbuild/compilers')
-rw-r--r--mesonbuild/compilers/__init__.py97
-rw-r--r--mesonbuild/compilers/asm.py231
-rw-r--r--mesonbuild/compilers/c.py740
-rw-r--r--mesonbuild/compilers/c_function_attributes.py141
-rw-r--r--mesonbuild/compilers/compilers.py1342
-rw-r--r--mesonbuild/compilers/cpp.py890
-rw-r--r--mesonbuild/compilers/cs.py154
-rw-r--r--mesonbuild/compilers/cuda.py791
-rw-r--r--mesonbuild/compilers/cython.py96
-rw-r--r--mesonbuild/compilers/d.py1022
-rw-r--r--mesonbuild/compilers/detect.py1317
-rw-r--r--mesonbuild/compilers/fortran.py546
-rw-r--r--mesonbuild/compilers/java.py125
-rw-r--r--mesonbuild/compilers/mixins/__init__.py0
-rw-r--r--mesonbuild/compilers/mixins/arm.py199
-rw-r--r--mesonbuild/compilers/mixins/ccrx.py134
-rw-r--r--mesonbuild/compilers/mixins/clang.py180
-rw-r--r--mesonbuild/compilers/mixins/clike.py1349
-rw-r--r--mesonbuild/compilers/mixins/compcert.py137
-rw-r--r--mesonbuild/compilers/mixins/elbrus.py101
-rw-r--r--mesonbuild/compilers/mixins/emscripten.py101
-rw-r--r--mesonbuild/compilers/mixins/gnu.py649
-rw-r--r--mesonbuild/compilers/mixins/intel.py185
-rw-r--r--mesonbuild/compilers/mixins/islinker.py130
-rw-r--r--mesonbuild/compilers/mixins/pgi.py113
-rw-r--r--mesonbuild/compilers/mixins/ti.py151
-rw-r--r--mesonbuild/compilers/mixins/visualstudio.py500
-rw-r--r--mesonbuild/compilers/mixins/xc16.py132
-rw-r--r--mesonbuild/compilers/objc.py114
-rw-r--r--mesonbuild/compilers/objcpp.py115
-rw-r--r--mesonbuild/compilers/rust.py208
-rw-r--r--mesonbuild/compilers/swift.py130
-rw-r--r--mesonbuild/compilers/vala.py140
33 files changed, 12260 insertions, 0 deletions
diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py
new file mode 100644
index 0000000..c516aab
--- /dev/null
+++ b/mesonbuild/compilers/__init__.py
@@ -0,0 +1,97 @@
+# Copyright 2017 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.
+
+# Public symbols for compilers sub-package when using 'from . import compilers'
+__all__ = [
+ 'Compiler',
+ 'RunResult',
+
+ 'all_languages',
+ 'base_options',
+ 'clib_langs',
+ 'clink_langs',
+ 'c_suffixes',
+ 'cpp_suffixes',
+ 'get_base_compile_args',
+ 'get_base_link_args',
+ 'is_assembly',
+ 'is_header',
+ 'is_library',
+ 'is_llvm_ir',
+ 'is_object',
+ 'is_source',
+ 'is_known_suffix',
+ 'lang_suffixes',
+ 'LANGUAGES_USING_LDFLAGS',
+ 'sort_clink',
+ 'SUFFIX_TO_LANG',
+
+ 'compiler_from_language',
+ 'detect_compiler_for',
+ 'detect_static_linker',
+ 'detect_c_compiler',
+ 'detect_cpp_compiler',
+ 'detect_cuda_compiler',
+ 'detect_fortran_compiler',
+ 'detect_objc_compiler',
+ 'detect_objcpp_compiler',
+ 'detect_java_compiler',
+ 'detect_cs_compiler',
+ 'detect_vala_compiler',
+ 'detect_rust_compiler',
+ 'detect_d_compiler',
+ 'detect_swift_compiler',
+]
+
+# Bring symbols from each module into compilers sub-package namespace
+from .compilers import (
+ Compiler,
+ RunResult,
+ all_languages,
+ base_options,
+ clib_langs,
+ clink_langs,
+ c_suffixes,
+ cpp_suffixes,
+ get_base_compile_args,
+ get_base_link_args,
+ is_header,
+ is_source,
+ is_assembly,
+ is_llvm_ir,
+ is_object,
+ is_library,
+ is_known_suffix,
+ lang_suffixes,
+ LANGUAGES_USING_LDFLAGS,
+ sort_clink,
+ SUFFIX_TO_LANG,
+)
+from .detect import (
+ compiler_from_language,
+ detect_compiler_for,
+ detect_static_linker,
+ detect_c_compiler,
+ detect_cpp_compiler,
+ detect_cuda_compiler,
+ detect_objc_compiler,
+ detect_objcpp_compiler,
+ detect_fortran_compiler,
+ detect_java_compiler,
+ detect_cs_compiler,
+ detect_vala_compiler,
+ detect_rust_compiler,
+ detect_d_compiler,
+ detect_swift_compiler,
+)
diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py
new file mode 100644
index 0000000..9a95599
--- /dev/null
+++ b/mesonbuild/compilers/asm.py
@@ -0,0 +1,231 @@
+import os
+import typing as T
+
+from ..mesonlib import EnvironmentException, get_meson_command
+from .compilers import Compiler
+
+if T.TYPE_CHECKING:
+ from ..environment import Environment
+
+nasm_optimization_args = {
+ 'plain': [],
+ '0': ['-O0'],
+ 'g': ['-O0'],
+ '1': ['-O1'],
+ '2': ['-Ox'],
+ '3': ['-Ox'],
+ 's': ['-Ox'],
+} # type: T.Dict[str, T.List[str]]
+
+
+class NasmCompiler(Compiler):
+ language = 'nasm'
+ id = 'nasm'
+
+ def needs_static_linker(self) -> bool:
+ return True
+
+ def get_always_args(self) -> T.List[str]:
+ cpu = '64' if self.info.is_64_bit else '32'
+ if self.info.is_windows() or self.info.is_cygwin():
+ plat = 'win'
+ define = f'WIN{cpu}'
+ elif self.info.is_darwin():
+ plat = 'macho'
+ define = 'MACHO'
+ else:
+ plat = 'elf'
+ define = 'ELF'
+ args = ['-f', f'{plat}{cpu}', f'-D{define}']
+ if self.info.is_64_bit:
+ args.append('-D__x86_64__')
+ return args
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['-Werror']
+
+ def get_output_args(self, outputname: str) -> T.List[str]:
+ return ['-o', outputname]
+
+ def unix_args_to_native(self, args: T.List[str]) -> T.List[str]:
+ outargs = []
+ for arg in args:
+ if arg == '-pthread':
+ continue
+ outargs.append(arg)
+ return outargs
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return nasm_optimization_args[optimization_level]
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ if is_debug:
+ if self.info.is_windows():
+ return []
+ return ['-g', '-F', 'dwarf']
+ return []
+
+ def get_depfile_suffix(self) -> str:
+ return 'd'
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return ['-MD', outfile, '-MQ', outtarget]
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ if self.info.cpu_family not in {'x86', 'x86_64'}:
+ raise EnvironmentException(f'ASM compiler {self.id!r} does not support {self.info.cpu_family} CPU family')
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ # FIXME: Not implemented
+ return []
+
+ def get_pic_args(self) -> T.List[str]:
+ return []
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if not path:
+ path = '.'
+ return ['-I' + path]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-I':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+ return parameter_list
+
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ return []
+
+class YasmCompiler(NasmCompiler):
+ id = 'yasm'
+
+ def get_exelist(self, ccache: bool = True) -> T.List[str]:
+ # Wrap yasm executable with an internal script that will write depfile.
+ exelist = super().get_exelist(ccache)
+ return get_meson_command() + ['--internal', 'yasm'] + exelist
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ if is_debug:
+ if self.info.is_windows():
+ return ['-g', 'null']
+ return ['-g', 'dwarf2']
+ return []
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return ['--depfile', outfile]
+
+# https://learn.microsoft.com/en-us/cpp/assembler/masm/ml-and-ml64-command-line-reference
+class MasmCompiler(Compiler):
+ language = 'masm'
+ id = 'ml'
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return ['/c']
+
+ def get_argument_syntax(self) -> str:
+ return 'msvc'
+
+ def needs_static_linker(self) -> bool:
+ return True
+
+ def get_always_args(self) -> T.List[str]:
+ return ['/nologo']
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['/WX']
+
+ def get_output_args(self, outputname: str) -> T.List[str]:
+ return ['/Fo', outputname]
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return []
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ if is_debug:
+ return ['/Zi']
+ return []
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ if self.info.cpu_family not in {'x86', 'x86_64'}:
+ raise EnvironmentException(f'ASM compiler {self.id!r} does not support {self.info.cpu_family} CPU family')
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ # FIXME: Not implemented
+ return []
+
+ def get_pic_args(self) -> T.List[str]:
+ return []
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if not path:
+ path = '.'
+ return ['-I' + path]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-I' or i[:2] == '/I':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+ return parameter_list
+
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ return []
+
+ def depfile_for_object(self, objfile: str) -> T.Optional[str]:
+ return None
+
+
+# https://learn.microsoft.com/en-us/cpp/assembler/arm/arm-assembler-command-line-reference
+class MasmARMCompiler(Compiler):
+ language = 'masm'
+ id = 'armasm'
+
+ def needs_static_linker(self) -> bool:
+ return True
+
+ def get_always_args(self) -> T.List[str]:
+ return ['-nologo']
+
+ def get_werror_args(self) -> T.List[str]:
+ return []
+
+ def get_output_args(self, outputname: str) -> T.List[str]:
+ return ['-o', outputname]
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return []
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ if is_debug:
+ return ['-g']
+ return []
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ if self.info.cpu_family not in {'arm', 'aarch64'}:
+ raise EnvironmentException(f'ASM compiler {self.id!r} does not support {self.info.cpu_family} CPU family')
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ # FIXME: Not implemented
+ return []
+
+ def get_pic_args(self) -> T.List[str]:
+ return []
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if not path:
+ path = '.'
+ return ['-i' + path]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-I':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+ return parameter_list
+
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ return []
+
+ def depfile_for_object(self, objfile: str) -> T.Optional[str]:
+ return None
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
new file mode 100644
index 0000000..4d9283a
--- /dev/null
+++ b/mesonbuild/compilers/c.py
@@ -0,0 +1,740 @@
+# Copyright 2012-2020 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 os.path
+import typing as T
+
+from .. import coredata
+from .. import mlog
+from ..mesonlib import MesonException, version_compare, OptionKey
+from .c_function_attributes import C_FUNC_ATTRIBUTES
+from .mixins.clike import CLikeCompiler
+from .mixins.ccrx import CcrxCompiler
+from .mixins.xc16 import Xc16Compiler
+from .mixins.compcert import CompCertCompiler
+from .mixins.ti import TICompiler
+from .mixins.arm import ArmCompiler, ArmclangCompiler
+from .mixins.visualstudio import MSVCCompiler, ClangClCompiler
+from .mixins.gnu import GnuCompiler
+from .mixins.gnu import gnu_common_warning_args, gnu_c_warning_args
+from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler
+from .mixins.clang import ClangCompiler
+from .mixins.elbrus import ElbrusCompiler
+from .mixins.pgi import PGICompiler
+from .mixins.emscripten import EmscriptenMixin
+from .compilers import (
+ gnu_winlibs,
+ msvc_winlibs,
+ Compiler,
+)
+
+if T.TYPE_CHECKING:
+ from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType
+ from ..dependencies import Dependency
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..linkers import DynamicLinker
+ from ..mesonlib import MachineChoice
+ from ..programs import ExternalProgram
+ from .compilers import CompileCheckMode
+
+ CompilerMixinBase = Compiler
+else:
+ CompilerMixinBase = object
+
+
+class CCompiler(CLikeCompiler, Compiler):
+ def attribute_check_func(self, name: str) -> str:
+ try:
+ return C_FUNC_ATTRIBUTES[name]
+ except KeyError:
+ raise MesonException(f'Unknown function attribute "{name}"')
+
+ language = 'c'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ # If a child ObjC or CPP class has already set it, don't set it ourselves
+ Compiler.__init__(self, ccache, exelist, version, for_machine, info,
+ is_cross=is_cross, full_version=full_version, linker=linker)
+ CLikeCompiler.__init__(self, exe_wrapper)
+
+ def get_no_stdinc_args(self) -> T.List[str]:
+ return ['-nostdinc']
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ code = 'int main(void) { int class=0; return class; }\n'
+ return self._sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
+
+ def 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]:
+ fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
+ t = '''{prefix}
+ #include <{header}>
+ int main(void) {{
+ /* If it's not defined as a macro, try to use as a symbol */
+ #ifndef {symbol}
+ {symbol};
+ #endif
+ return 0;
+ }}'''
+ return self.compiles(t.format(**fargs), env, extra_args=extra_args,
+ dependencies=dependencies)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = super().get_options()
+ opts.update({
+ OptionKey('std', machine=self.for_machine, lang=self.language): coredata.UserComboOption(
+ 'C language standard to use',
+ ['none'],
+ 'none',
+ )
+ })
+ return opts
+
+
+class _ClangCStds(CompilerMixinBase):
+
+ """Mixin class for clang based compilers for setting C standards.
+
+ This is used by both ClangCCompiler and ClangClCompiler, as they share
+ the same versions
+ """
+
+ _C17_VERSION = '>=6.0.0'
+ _C18_VERSION = '>=8.0.0'
+ _C2X_VERSION = '>=9.0.0'
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = super().get_options()
+ c_stds = ['c89', 'c99', 'c11']
+ g_stds = ['gnu89', 'gnu99', 'gnu11']
+ # https://releases.llvm.org/6.0.0/tools/clang/docs/ReleaseNotes.html
+ # https://en.wikipedia.org/wiki/Xcode#Latest_versions
+ if version_compare(self.version, self._C17_VERSION):
+ c_stds += ['c17']
+ g_stds += ['gnu17']
+ if version_compare(self.version, self._C18_VERSION):
+ c_stds += ['c18']
+ g_stds += ['gnu18']
+ if version_compare(self.version, self._C2X_VERSION):
+ c_stds += ['c2x']
+ g_stds += ['gnu2x']
+ opts[OptionKey('std', machine=self.for_machine, lang=self.language)].choices = ['none'] + c_stds + g_stds
+ return opts
+
+
+class ClangCCompiler(_ClangCStds, ClangCompiler, CCompiler):
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version)
+ ClangCompiler.__init__(self, defines)
+ default_warn_args = ['-Wall', '-Winvalid-pch']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': ['-Weverything']}
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = super().get_options()
+ if self.info.is_windows() or self.info.is_cygwin():
+ opts.update({
+ OptionKey('winlibs', machine=self.for_machine, lang=self.language): coredata.UserArrayOption(
+ 'Standard Win libraries to link against',
+ gnu_winlibs,
+ ),
+ })
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ std = options[OptionKey('std', machine=self.for_machine, lang=self.language)]
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ if self.info.is_windows() or self.info.is_cygwin():
+ # without a typedict mypy can't understand this.
+ libs = options[OptionKey('winlibs', machine=self.for_machine, lang=self.language)].value.copy()
+ assert isinstance(libs, list)
+ for l in libs:
+ assert isinstance(l, str)
+ return libs
+ return []
+
+
+class ArmLtdClangCCompiler(ClangCCompiler):
+
+ id = 'armltdclang'
+
+
+class AppleClangCCompiler(ClangCCompiler):
+
+ """Handle the differences between Apple Clang and Vanilla Clang.
+
+ Right now this just handles the differences between the versions that new
+ C standards were added.
+ """
+
+ _C17_VERSION = '>=10.0.0'
+ _C18_VERSION = '>=11.0.0'
+ _C2X_VERSION = '>=11.0.0'
+
+
+class EmscriptenCCompiler(EmscriptenMixin, ClangCCompiler):
+
+ id = 'emscripten'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ full_version: T.Optional[str] = None):
+ if not is_cross:
+ raise MesonException('Emscripten compiler can only be used for cross compilation.')
+ if not version_compare(version, '>=1.39.19'):
+ raise MesonException('Meson requires Emscripten >= 1.39.19')
+ ClangCCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper=exe_wrapper, linker=linker,
+ defines=defines, full_version=full_version)
+
+
+class ArmclangCCompiler(ArmclangCompiler, CCompiler):
+ '''
+ Keil armclang
+ '''
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ArmclangCompiler.__init__(self)
+ default_warn_args = ['-Wall', '-Winvalid-pch']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': ['-Weverything']}
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c90', 'c99', 'c11', 'gnu90', 'gnu99', 'gnu11']
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ std = options[OptionKey('std', machine=self.for_machine, lang=self.language)]
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return []
+
+
+class GnuCCompiler(GnuCompiler, CCompiler):
+
+ _C18_VERSION = '>=8.0.0'
+ _C2X_VERSION = '>=9.0.0'
+ _INVALID_PCH_VERSION = ">=3.4.0"
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross, info, exe_wrapper, linker=linker, full_version=full_version)
+ GnuCompiler.__init__(self, defines)
+ default_warn_args = ['-Wall']
+ if version_compare(self.version, self._INVALID_PCH_VERSION):
+ default_warn_args += ['-Winvalid-pch']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': (default_warn_args + ['-Wextra', '-Wpedantic'] +
+ self.supported_warn_args(gnu_common_warning_args) +
+ self.supported_warn_args(gnu_c_warning_args))}
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CCompiler.get_options(self)
+ c_stds = ['c89', 'c99', 'c11']
+ g_stds = ['gnu89', 'gnu99', 'gnu11']
+ if version_compare(self.version, self._C18_VERSION):
+ c_stds += ['c17', 'c18']
+ g_stds += ['gnu17', 'gnu18']
+ if version_compare(self.version, self._C2X_VERSION):
+ c_stds += ['c2x']
+ g_stds += ['gnu2x']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none'] + c_stds + g_stds
+ if self.info.is_windows() or self.info.is_cygwin():
+ opts.update({
+ key.evolve('winlibs'): coredata.UserArrayOption(
+ 'Standard Win libraries to link against',
+ gnu_winlibs,
+ ),
+ })
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ std = options[OptionKey('std', lang=self.language, machine=self.for_machine)]
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ if self.info.is_windows() or self.info.is_cygwin():
+ # without a typeddict mypy can't figure this out
+ libs: T.List[str] = options[OptionKey('winlibs', lang=self.language, machine=self.for_machine)].value.copy()
+ assert isinstance(libs, list)
+ for l in libs:
+ assert isinstance(l, str)
+ return libs
+ return []
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ return ['-fpch-preprocess', '-include', os.path.basename(header)]
+
+
+class PGICCompiler(PGICompiler, CCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ PGICompiler.__init__(self)
+
+
+class NvidiaHPC_CCompiler(PGICompiler, CCompiler):
+
+ id = 'nvidia_hpc'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ PGICompiler.__init__(self)
+
+
+class ElbrusCCompiler(ElbrusCompiler, CCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ElbrusCompiler.__init__(self)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CCompiler.get_options(self)
+ stds = ['c89', 'c9x', 'c99', 'gnu89', 'gnu9x', 'gnu99']
+ stds += ['iso9899:1990', 'iso9899:199409', 'iso9899:1999']
+ if version_compare(self.version, '>=1.20.00'):
+ stds += ['c11', 'gnu11']
+ if version_compare(self.version, '>=1.21.00') and version_compare(self.version, '<1.22.00'):
+ stds += ['c90', 'c1x', 'gnu90', 'gnu1x', 'iso9899:2011']
+ if version_compare(self.version, '>=1.23.00'):
+ stds += ['c90', 'c1x', 'gnu90', 'gnu1x', 'iso9899:2011']
+ if version_compare(self.version, '>=1.26.00'):
+ stds += ['c17', 'c18', 'iso9899:2017', 'iso9899:2018', 'gnu17', 'gnu18']
+ opts[OptionKey('std', machine=self.for_machine, lang=self.language)].choices = ['none'] + stds
+ return opts
+
+ # Elbrus C compiler does not have lchmod, but there is only linker warning, not compiler error.
+ # So we should explicitly fail at this case.
+ 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]:
+ if funcname == 'lchmod':
+ return False, False
+ else:
+ return super().has_function(funcname, prefix, env,
+ extra_args=extra_args,
+ dependencies=dependencies)
+
+
+class IntelCCompiler(IntelGnuLikeCompiler, CCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ IntelGnuLikeCompiler.__init__(self)
+ self.lang_header = 'c-header'
+ default_warn_args = ['-Wall', '-w3']
+ self.warn_args = {'0': [],
+ '1': default_warn_args + ['-diag-disable:remark'],
+ '2': default_warn_args + ['-Wextra', '-diag-disable:remark'],
+ '3': default_warn_args + ['-Wextra', '-diag-disable:remark'],
+ 'everything': default_warn_args + ['-Wextra']}
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CCompiler.get_options(self)
+ c_stds = ['c89', 'c99']
+ g_stds = ['gnu89', 'gnu99']
+ if version_compare(self.version, '>=16.0.0'):
+ c_stds += ['c11']
+ opts[OptionKey('std', machine=self.for_machine, lang=self.language)].choices = ['none'] + c_stds + g_stds
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ std = options[OptionKey('std', machine=self.for_machine, lang=self.language)]
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+
+class IntelLLVMCCompiler(ClangCCompiler):
+
+ id = 'intel-llvm'
+
+
+class VisualStudioLikeCCompilerMixin(CompilerMixinBase):
+
+ """Shared methods that apply to MSVC-like C compilers."""
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = super().get_options()
+ opts.update({
+ OptionKey('winlibs', machine=self.for_machine, lang=self.language): coredata.UserArrayOption(
+ 'Windows libs to link against.',
+ msvc_winlibs,
+ ),
+ })
+ return opts
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ # need a TypeDict to make this work
+ key = OptionKey('winlibs', machine=self.for_machine, lang=self.language)
+ libs = options[key].value.copy()
+ assert isinstance(libs, list)
+ for l in libs:
+ assert isinstance(l, str)
+ return libs
+
+
+class VisualStudioCCompiler(MSVCCompiler, VisualStudioLikeCCompilerMixin, CCompiler):
+
+ _C11_VERSION = '>=19.28'
+ _C17_VERSION = '>=19.28'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo', target: str,
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ MSVCCompiler.__init__(self, target)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = super().get_options()
+ c_stds = ['c89', 'c99']
+ # Need to have these to be compatible with projects
+ # that set c_std to e.g. gnu99.
+ # https://github.com/mesonbuild/meson/issues/7611
+ g_stds = ['gnu89', 'gnu90', 'gnu9x', 'gnu99']
+ if version_compare(self.version, self._C11_VERSION):
+ c_stds += ['c11']
+ g_stds += ['gnu1x', 'gnu11']
+ if version_compare(self.version, self._C17_VERSION):
+ c_stds += ['c17', 'c18']
+ g_stds += ['gnu17', 'gnu18']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none'] + c_stds + g_stds
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ std = options[OptionKey('std', machine=self.for_machine, lang=self.language)]
+ if std.value.startswith('gnu'):
+ mlog.log_once(
+ 'cl.exe does not actually support gnu standards, and meson '
+ 'will instead demote to the nearest ISO C standard. This '
+ 'may cause compilation to fail.')
+ # As of MVSC 16.8, /std:c11 and /std:c17 are the only valid C standard options.
+ if std.value in {'c11', 'gnu1x', 'gnu11'}:
+ args.append('/std:c11')
+ elif std.value in {'c17', 'c18', 'gnu17', 'gnu18'}:
+ args.append('/std:c17')
+ return args
+
+
+class ClangClCCompiler(_ClangCStds, ClangClCompiler, VisualStudioLikeCCompilerMixin, CCompiler):
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo', target: str,
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ ClangClCompiler.__init__(self, target)
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key].value
+ if std != "none":
+ return [f'/clang:-std={std}']
+ return []
+
+
+class IntelClCCompiler(IntelVisualStudioLikeCompiler, VisualStudioLikeCCompilerMixin, CCompiler):
+
+ """Intel "ICL" compiler abstraction."""
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo', target: str,
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ IntelVisualStudioLikeCompiler.__init__(self, target)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = super().get_options()
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99', 'c11']
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value == 'c89':
+ mlog.log_once("ICL doesn't explicitly implement c89, setting the standard to 'none', which is close.")
+ elif std.value != 'none':
+ args.append('/Qstd:' + std.value)
+ return args
+
+
+class IntelLLVMClCCompiler(IntelClCCompiler):
+
+ id = 'intel-llvm-cl'
+
+
+class ArmCCompiler(ArmCompiler, CCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ ArmCompiler.__init__(self)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99', 'c11']
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append('--' + std.value)
+ return args
+
+
+class CcrxCCompiler(CcrxCompiler, CCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ CcrxCompiler.__init__(self)
+
+ # Override CCompiler.get_always_args
+ def get_always_args(self) -> T.List[str]:
+ return ['-nologo']
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99']
+ return opts
+
+ def get_no_stdinc_args(self) -> T.List[str]:
+ return []
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value == 'c89':
+ args.append('-lang=c')
+ elif std.value == 'c99':
+ args.append('-lang=c99')
+ return args
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return []
+
+ def get_no_optimization_args(self) -> T.List[str]:
+ return ['-optimize=0']
+
+ def get_output_args(self, target: str) -> T.List[str]:
+ return [f'-output=obj={target}']
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['-change_message=error']
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if path == '':
+ path = '.'
+ return ['-include=' + path]
+
+
+class Xc16CCompiler(Xc16Compiler, CCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ Xc16Compiler.__init__(self)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99', 'gnu89', 'gnu99']
+ return opts
+
+ def get_no_stdinc_args(self) -> T.List[str]:
+ return []
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append('-ansi')
+ args.append('-std=' + std.value)
+ return args
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return []
+
+ def get_no_optimization_args(self) -> T.List[str]:
+ return ['-O0']
+
+ def get_output_args(self, target: str) -> T.List[str]:
+ return [f'-o{target}']
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['-change_message=error']
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if path == '':
+ path = '.'
+ return ['-I' + path]
+
+class CompCertCCompiler(CompCertCompiler, CCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ CompCertCompiler.__init__(self)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99']
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return []
+
+ def get_no_optimization_args(self) -> T.List[str]:
+ return ['-O0']
+
+ def get_output_args(self, target: str) -> T.List[str]:
+ return [f'-o{target}']
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['-Werror']
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if path == '':
+ path = '.'
+ return ['-I' + path]
+
+class TICCompiler(TICompiler, CCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ TICompiler.__init__(self)
+
+ # Override CCompiler.get_always_args
+ def get_always_args(self) -> T.List[str]:
+ return []
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c89', 'c99', 'c11']
+ return opts
+
+ def get_no_stdinc_args(self) -> T.List[str]:
+ return []
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append('--' + std.value)
+ return args
+
+class C2000CCompiler(TICCompiler):
+ # Required for backwards compat with projects created before ti-cgt support existed
+ id = 'c2000'
diff --git a/mesonbuild/compilers/c_function_attributes.py b/mesonbuild/compilers/c_function_attributes.py
new file mode 100644
index 0000000..f663bfc
--- /dev/null
+++ b/mesonbuild/compilers/c_function_attributes.py
@@ -0,0 +1,141 @@
+# These functions are based on the following code:
+# https://git.savannah.gnu.org/gitweb/?p=autoconf-archive.git;a=blob_plain;f=m4/ax_gcc_func_attribute.m4,
+# which is licensed under the following terms:
+#
+# Copyright (c) 2013 Gabriele Svelto <gabriele.svelto@gmail.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+#
+
+C_FUNC_ATTRIBUTES = {
+ 'alias': '''
+ int foo(void) { return 0; }
+ int bar(void) __attribute__((alias("foo")));''',
+ 'aligned':
+ 'int foo(void) __attribute__((aligned(32)));',
+ 'alloc_size':
+ 'void *foo(int a) __attribute__((alloc_size(1)));',
+ 'always_inline':
+ 'inline __attribute__((always_inline)) int foo(void) { return 0; }',
+ 'artificial':
+ 'inline __attribute__((artificial)) int foo(void) { return 0; }',
+ 'cold':
+ 'int foo(void) __attribute__((cold));',
+ 'const':
+ 'int foo(void) __attribute__((const));',
+ 'constructor':
+ 'int foo(void) __attribute__((constructor));',
+ 'constructor_priority':
+ 'int foo( void ) __attribute__((__constructor__(65535/2)));',
+ 'deprecated':
+ 'int foo(void) __attribute__((deprecated("")));',
+ 'destructor':
+ 'int foo(void) __attribute__((destructor));',
+ 'dllexport':
+ '__declspec(dllexport) int foo(void) { return 0; }',
+ 'dllimport':
+ '__declspec(dllimport) int foo(void);',
+ 'error':
+ 'int foo(void) __attribute__((error("")));',
+ 'externally_visible':
+ 'int foo(void) __attribute__((externally_visible));',
+ 'fallthrough': '''
+ int foo( void ) {
+ switch (0) {
+ case 1: __attribute__((fallthrough));
+ case 2: break;
+ }
+ return 0;
+ };''',
+ 'flatten':
+ 'int foo(void) __attribute__((flatten));',
+ 'format':
+ 'int foo(const char * p, ...) __attribute__((format(printf, 1, 2)));',
+ 'format_arg':
+ 'char * foo(const char * p) __attribute__((format_arg(1)));',
+ 'force_align_arg_pointer':
+ '__attribute__((force_align_arg_pointer)) int foo(void) { return 0; }',
+ 'gnu_inline':
+ 'inline __attribute__((gnu_inline)) int foo(void) { return 0; }',
+ 'hot':
+ 'int foo(void) __attribute__((hot));',
+ 'ifunc':
+ ('int my_foo(void) { return 0; }'
+ 'static int (*resolve_foo(void))(void) { return my_foo; }'
+ 'int foo(void) __attribute__((ifunc("resolve_foo")));'),
+ 'leaf':
+ '__attribute__((leaf)) int foo(void) { return 0; }',
+ 'malloc':
+ 'int *foo(void) __attribute__((malloc));',
+ 'noclone':
+ 'int foo(void) __attribute__((noclone));',
+ 'noinline':
+ '__attribute__((noinline)) int foo(void) { return 0; }',
+ 'nonnull':
+ 'int foo(char * p) __attribute__((nonnull(1)));',
+ 'noreturn':
+ 'int foo(void) __attribute__((noreturn));',
+ 'nothrow':
+ 'int foo(void) __attribute__((nothrow));',
+ 'optimize':
+ '__attribute__((optimize(3))) int foo(void) { return 0; }',
+ 'packed':
+ 'struct __attribute__((packed)) foo { int bar; };',
+ 'pure':
+ 'int foo(void) __attribute__((pure));',
+ 'returns_nonnull':
+ 'int *foo(void) __attribute__((returns_nonnull));',
+ 'section': '''
+ #if defined(__APPLE__) && defined(__MACH__)
+ extern int foo __attribute__((section("__BAR,__bar")));
+ #else
+ extern int foo __attribute__((section(".bar")));
+ #endif''',
+ 'sentinel':
+ 'int foo(const char *bar, ...) __attribute__((sentinel));',
+ 'unused':
+ 'int foo(void) __attribute__((unused));',
+ 'used':
+ 'int foo(void) __attribute__((used));',
+ 'visibility': '''
+ int foo_def(void) __attribute__((visibility("default")));
+ int foo_hid(void) __attribute__((visibility("hidden")));
+ int foo_int(void) __attribute__((visibility("internal")));''',
+ 'visibility:default':
+ 'int foo(void) __attribute__((visibility("default")));',
+ 'visibility:hidden':
+ 'int foo(void) __attribute__((visibility("hidden")));',
+ 'visibility:internal':
+ 'int foo(void) __attribute__((visibility("internal")));',
+ 'visibility:protected':
+ 'int foo(void) __attribute__((visibility("protected")));',
+ 'warning':
+ 'int foo(void) __attribute__((warning("")));',
+ 'warn_unused_result':
+ 'int foo(void) __attribute__((warn_unused_result));',
+ 'weak':
+ 'int foo(void) __attribute__((weak));',
+ 'weakref': '''
+ static int foo(void) { return 0; }
+ static int var(void) __attribute__((weakref("foo")));''',
+ 'retain': '__attribute__((retain)) int x;',
+}
+
+CXX_FUNC_ATTRIBUTES = {
+ # Alias must be applied to the mangled name in C++
+ 'alias':
+ ('extern "C" {'
+ 'int foo(void) { return 0; }'
+ '}'
+ 'int bar(void) __attribute__((alias("foo")));'
+ ),
+ 'ifunc':
+ ('extern "C" {'
+ 'int my_foo(void) { return 0; }'
+ 'static int (*resolve_foo(void))(void) { return my_foo; }'
+ '}'
+ 'int foo(void) __attribute__((ifunc("resolve_foo")));'),
+}
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
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
new file mode 100644
index 0000000..4466a1b
--- /dev/null
+++ b/mesonbuild/compilers/cpp.py
@@ -0,0 +1,890 @@
+# Copyright 2012-2017 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 copy
+import functools
+import os.path
+import typing as T
+
+from .. import coredata
+from .. import mlog
+from ..mesonlib import MesonException, version_compare, OptionKey
+
+from .compilers import (
+ gnu_winlibs,
+ msvc_winlibs,
+ Compiler,
+)
+from .c_function_attributes import CXX_FUNC_ATTRIBUTES, C_FUNC_ATTRIBUTES
+from .mixins.clike import CLikeCompiler
+from .mixins.ccrx import CcrxCompiler
+from .mixins.ti import TICompiler
+from .mixins.arm import ArmCompiler, ArmclangCompiler
+from .mixins.visualstudio import MSVCCompiler, ClangClCompiler
+from .mixins.gnu import GnuCompiler, gnu_common_warning_args, gnu_cpp_warning_args
+from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler
+from .mixins.clang import ClangCompiler
+from .mixins.elbrus import ElbrusCompiler
+from .mixins.pgi import PGICompiler
+from .mixins.emscripten import EmscriptenMixin
+
+if T.TYPE_CHECKING:
+ from .compilers import CompileCheckMode
+ from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType
+ from ..dependencies import Dependency
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..linkers import DynamicLinker
+ from ..mesonlib import MachineChoice
+ from ..programs import ExternalProgram
+ CompilerMixinBase = CLikeCompiler
+else:
+ CompilerMixinBase = object
+
+
+def non_msvc_eh_options(eh: str, args: T.List[str]) -> None:
+ if eh == 'none':
+ args.append('-fno-exceptions')
+ elif eh in {'s', 'c'}:
+ mlog.warning('non-MSVC compilers do not support ' + eh + ' exception handling.' +
+ 'You may want to set eh to \'default\'.')
+
+class CPPCompiler(CLikeCompiler, Compiler):
+ def attribute_check_func(self, name: str) -> str:
+ try:
+ return CXX_FUNC_ATTRIBUTES.get(name, C_FUNC_ATTRIBUTES[name])
+ except KeyError:
+ raise MesonException(f'Unknown function attribute "{name}"')
+
+ language = 'cpp'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ # If a child ObjCPP class has already set it, don't set it ourselves
+ Compiler.__init__(self, ccache, exelist, version, for_machine, info,
+ is_cross=is_cross, linker=linker,
+ full_version=full_version)
+ CLikeCompiler.__init__(self, exe_wrapper)
+
+ @staticmethod
+ def get_display_language() -> str:
+ return 'C++'
+
+ def get_no_stdinc_args(self) -> T.List[str]:
+ return ['-nostdinc++']
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ code = 'class breakCCompiler;int main(void) { return 0; }\n'
+ return self._sanity_check_impl(work_dir, environment, 'sanitycheckcpp.cc', code)
+
+ def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
+ # -fpermissive allows non-conforming code to compile which is necessary
+ # for many C++ checks. Particularly, the has_header_symbol check is
+ # too strict without this and always fails.
+ return super().get_compiler_check_args(mode) + ['-fpermissive']
+
+ 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]:
+ # Check if it's a C-like symbol
+ found, cached = super().has_header_symbol(hname, symbol, prefix, env,
+ extra_args=extra_args,
+ dependencies=dependencies)
+ if found:
+ return True, cached
+ # Check if it's a class or a template
+ if extra_args is None:
+ extra_args = []
+ t = f'''{prefix}
+ #include <{hname}>
+ using {symbol};
+ int main(void) {{ return 0; }}'''
+ return self.compiles(t, env, extra_args=extra_args,
+ dependencies=dependencies)
+
+ def _test_cpp_std_arg(self, cpp_std_value: str) -> bool:
+ # Test whether the compiler understands a -std=XY argument
+ assert cpp_std_value.startswith('-std=')
+
+ # This test does not use has_multi_arguments() for two reasons:
+ # 1. has_multi_arguments() requires an env argument, which the compiler
+ # object does not have at this point.
+ # 2. even if it did have an env object, that might contain another more
+ # recent -std= argument, which might lead to a cascaded failure.
+ CPP_TEST = 'int i = static_cast<int>(0);'
+ with self.compile(CPP_TEST, extra_args=[cpp_std_value], mode='compile') as p:
+ if p.returncode == 0:
+ mlog.debug(f'Compiler accepts {cpp_std_value}:', 'YES')
+ return True
+ else:
+ mlog.debug(f'Compiler accepts {cpp_std_value}:', 'NO')
+ return False
+
+ @functools.lru_cache()
+ def _find_best_cpp_std(self, cpp_std: str) -> str:
+ # The initial version mapping approach to make falling back
+ # from '-std=c++14' to '-std=c++1y' was too brittle. For instance,
+ # Apple's Clang uses a different versioning scheme to upstream LLVM,
+ # making the whole detection logic awfully brittle. Instead, let's
+ # just see if feeding GCC or Clang our '-std=' setting works, and
+ # if not, try the fallback argument.
+ CPP_FALLBACKS = {
+ 'c++11': 'c++0x',
+ 'gnu++11': 'gnu++0x',
+ 'c++14': 'c++1y',
+ 'gnu++14': 'gnu++1y',
+ 'c++17': 'c++1z',
+ 'gnu++17': 'gnu++1z',
+ 'c++20': 'c++2a',
+ 'gnu++20': 'gnu++2a',
+ }
+
+ # Currently, remapping is only supported for Clang, Elbrus and GCC
+ assert self.id in frozenset(['clang', 'lcc', 'gcc', 'emscripten', 'armltdclang', 'intel-llvm'])
+
+ if cpp_std not in CPP_FALLBACKS:
+ # 'c++03' and 'c++98' don't have fallback types
+ return '-std=' + cpp_std
+
+ for i in (cpp_std, CPP_FALLBACKS[cpp_std]):
+ cpp_std_value = '-std=' + i
+ if self._test_cpp_std_arg(cpp_std_value):
+ return cpp_std_value
+
+ raise MesonException(f'C++ Compiler does not support -std={cpp_std}')
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = super().get_options()
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts.update({
+ key: coredata.UserComboOption(
+ 'C++ language standard to use',
+ ['none'],
+ 'none',
+ ),
+ })
+ return opts
+
+
+class ClangCPPCompiler(ClangCompiler, CPPCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ClangCompiler.__init__(self, defines)
+ default_warn_args = ['-Wall', '-Winvalid-pch']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': ['-Weverything']}
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CPPCompiler.get_options(self)
+ key = OptionKey('key', machine=self.for_machine, lang=self.language)
+ opts.update({
+ key.evolve('eh'): coredata.UserComboOption(
+ 'C++ exception handling type.',
+ ['none', 'default', 'a', 's', 'sc'],
+ 'default',
+ ),
+ key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True),
+ })
+ opts[key.evolve('std')].choices = [
+ 'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z',
+ 'c++2a', 'c++20', 'gnu++11', 'gnu++14', 'gnu++17', 'gnu++1z',
+ 'gnu++2a', 'gnu++20',
+ ]
+ if self.info.is_windows() or self.info.is_cygwin():
+ opts.update({
+ key.evolve('winlibs'): coredata.UserArrayOption(
+ 'Standard Win libraries to link against',
+ gnu_winlibs,
+ ),
+ })
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append(self._find_best_cpp_std(std.value))
+
+ non_msvc_eh_options(options[key.evolve('eh')].value, args)
+
+ if not options[key.evolve('rtti')].value:
+ args.append('-fno-rtti')
+
+ return args
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ if self.info.is_windows() or self.info.is_cygwin():
+ # without a typedict mypy can't understand this.
+ key = OptionKey('winlibs', machine=self.for_machine, lang=self.language)
+ libs = options[key].value.copy()
+ assert isinstance(libs, list)
+ for l in libs:
+ assert isinstance(l, str)
+ return libs
+ return []
+
+ def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ # We need to apply the search prefix here, as these link arguments may
+ # be passed to a different compiler with a different set of default
+ # search paths, such as when using Clang for C/C++ and gfortran for
+ # fortran,
+ search_dirs: T.List[str] = []
+ for d in self.get_compiler_dirs(env, 'libraries'):
+ search_dirs.append(f'-L{d}')
+ return search_dirs + ['-lstdc++']
+
+
+class ArmLtdClangCPPCompiler(ClangCPPCompiler):
+
+ id = 'armltdclang'
+
+
+class AppleClangCPPCompiler(ClangCPPCompiler):
+ def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ # We need to apply the search prefix here, as these link arguments may
+ # be passed to a different compiler with a different set of default
+ # search paths, such as when using Clang for C/C++ and gfortran for
+ # fortran,
+ search_dirs: T.List[str] = []
+ for d in self.get_compiler_dirs(env, 'libraries'):
+ search_dirs.append(f'-L{d}')
+ return search_dirs + ['-lc++']
+
+
+class EmscriptenCPPCompiler(EmscriptenMixin, ClangCPPCompiler):
+
+ id = 'emscripten'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ full_version: T.Optional[str] = None):
+ if not is_cross:
+ raise MesonException('Emscripten compiler can only be used for cross compilation.')
+ if not version_compare(version, '>=1.39.19'):
+ raise MesonException('Meson requires Emscripten >= 1.39.19')
+ ClangCPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper=exe_wrapper, linker=linker,
+ defines=defines, full_version=full_version)
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append(self._find_best_cpp_std(std.value))
+ return args
+
+
+class ArmclangCPPCompiler(ArmclangCompiler, CPPCompiler):
+ '''
+ Keil armclang
+ '''
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ArmclangCompiler.__init__(self)
+ default_warn_args = ['-Wall', '-Winvalid-pch']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': ['-Weverything']}
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CPPCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts.update({
+ key.evolve('eh'): coredata.UserComboOption(
+ 'C++ exception handling type.',
+ ['none', 'default', 'a', 's', 'sc'],
+ 'default',
+ ),
+ })
+ opts[key].choices = [
+ 'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'gnu++98',
+ 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17',
+ ]
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+
+ non_msvc_eh_options(options[key.evolve('eh')].value, args)
+
+ return args
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return []
+
+
+class GnuCPPCompiler(GnuCompiler, CPPCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ GnuCompiler.__init__(self, defines)
+ default_warn_args = ['-Wall', '-Winvalid-pch']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': (default_warn_args + ['-Wextra', '-Wpedantic'] +
+ self.supported_warn_args(gnu_common_warning_args) +
+ self.supported_warn_args(gnu_cpp_warning_args))}
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts = CPPCompiler.get_options(self)
+ opts.update({
+ key.evolve('eh'): coredata.UserComboOption(
+ 'C++ exception handling type.',
+ ['none', 'default', 'a', 's', 'sc'],
+ 'default',
+ ),
+ key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True),
+ key.evolve('debugstl'): coredata.UserBooleanOption(
+ 'STL debug mode',
+ False,
+ )
+ })
+ opts[key].choices = [
+ 'none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++17', 'c++1z',
+ 'c++2a', 'c++20', 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++17',
+ 'gnu++1z', 'gnu++2a', 'gnu++20',
+ ]
+ if self.info.is_windows() or self.info.is_cygwin():
+ opts.update({
+ key.evolve('winlibs'): coredata.UserArrayOption(
+ 'Standard Win libraries to link against',
+ gnu_winlibs,
+ ),
+ })
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append(self._find_best_cpp_std(std.value))
+
+ non_msvc_eh_options(options[key.evolve('eh')].value, args)
+
+ if not options[key.evolve('rtti')].value:
+ args.append('-fno-rtti')
+
+ if options[key.evolve('debugstl')].value:
+ args.append('-D_GLIBCXX_DEBUG=1')
+ return args
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ if self.info.is_windows() or self.info.is_cygwin():
+ # without a typedict mypy can't understand this.
+ key = OptionKey('winlibs', machine=self.for_machine, lang=self.language)
+ libs = options[key].value.copy()
+ assert isinstance(libs, list)
+ for l in libs:
+ assert isinstance(l, str)
+ return libs
+ return []
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ return ['-fpch-preprocess', '-include', os.path.basename(header)]
+
+ def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ # We need to apply the search prefix here, as these link arguments may
+ # be passed to a different compiler with a different set of default
+ # search paths, such as when using Clang for C/C++ and gfortran for
+ # fortran,
+ search_dirs: T.List[str] = []
+ for d in self.get_compiler_dirs(env, 'libraries'):
+ search_dirs.append(f'-L{d}')
+ return ['-lstdc++']
+
+
+class PGICPPCompiler(PGICompiler, CPPCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ PGICompiler.__init__(self)
+
+
+class NvidiaHPC_CPPCompiler(PGICompiler, CPPCompiler):
+
+ id = 'nvidia_hpc'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ PGICompiler.__init__(self)
+
+
+class ElbrusCPPCompiler(ElbrusCompiler, CPPCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ElbrusCompiler.__init__(self)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CPPCompiler.get_options(self)
+
+ cpp_stds = ['none', 'c++98', 'gnu++98']
+ if version_compare(self.version, '>=1.20.00'):
+ cpp_stds += ['c++03', 'c++0x', 'c++11', 'gnu++03', 'gnu++0x', 'gnu++11']
+ if version_compare(self.version, '>=1.21.00') and version_compare(self.version, '<1.22.00'):
+ cpp_stds += ['c++14', 'gnu++14', 'c++1y', 'gnu++1y']
+ if version_compare(self.version, '>=1.22.00'):
+ cpp_stds += ['c++14', 'gnu++14']
+ if version_compare(self.version, '>=1.23.00'):
+ cpp_stds += ['c++1y', 'gnu++1y']
+ if version_compare(self.version, '>=1.24.00'):
+ cpp_stds += ['c++1z', 'c++17', 'gnu++1z', 'gnu++17']
+ if version_compare(self.version, '>=1.25.00'):
+ cpp_stds += ['c++2a', 'gnu++2a']
+ if version_compare(self.version, '>=1.26.00'):
+ cpp_stds += ['c++20', 'gnu++20']
+
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts.update({
+ key.evolve('eh'): coredata.UserComboOption(
+ 'C++ exception handling type.',
+ ['none', 'default', 'a', 's', 'sc'],
+ 'default',
+ ),
+ key.evolve('debugstl'): coredata.UserBooleanOption(
+ 'STL debug mode',
+ False,
+ ),
+ })
+ opts[key].choices = cpp_stds
+ return opts
+
+ # Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error.
+ # So we should explicitly fail at this case.
+ 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]:
+ if funcname == 'lchmod':
+ return False, False
+ else:
+ return super().has_function(funcname, prefix, env,
+ extra_args=extra_args,
+ dependencies=dependencies)
+
+ # Elbrus C++ compiler does not support RTTI, so don't check for it.
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append(self._find_best_cpp_std(std.value))
+
+ non_msvc_eh_options(options[key.evolve('eh')].value, args)
+
+ if options[key.evolve('debugstl')].value:
+ args.append('-D_GLIBCXX_DEBUG=1')
+ return args
+
+
+class IntelCPPCompiler(IntelGnuLikeCompiler, CPPCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ IntelGnuLikeCompiler.__init__(self)
+ self.lang_header = 'c++-header'
+ default_warn_args = ['-Wall', '-w3', '-Wpch-messages']
+ self.warn_args = {'0': [],
+ '1': default_warn_args + ['-diag-disable:remark'],
+ '2': default_warn_args + ['-Wextra', '-diag-disable:remark'],
+ '3': default_warn_args + ['-Wextra', '-diag-disable:remark'],
+ 'everything': default_warn_args + ['-Wextra']}
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CPPCompiler.get_options(self)
+ # Every Unix compiler under the sun seems to accept -std=c++03,
+ # with the exception of ICC. Instead of preventing the user from
+ # globally requesting C++03, we transparently remap it to C++98
+ c_stds = ['c++98', 'c++03']
+ g_stds = ['gnu++98', 'gnu++03']
+ if version_compare(self.version, '>=15.0.0'):
+ c_stds += ['c++11', 'c++14']
+ g_stds += ['gnu++11']
+ if version_compare(self.version, '>=16.0.0'):
+ c_stds += ['c++17']
+ if version_compare(self.version, '>=17.0.0'):
+ g_stds += ['gnu++14']
+ if version_compare(self.version, '>=19.1.0'):
+ c_stds += ['c++2a']
+ g_stds += ['gnu++2a']
+
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts.update({
+ key.evolve('eh'): coredata.UserComboOption(
+ 'C++ exception handling type.',
+ ['none', 'default', 'a', 's', 'sc'],
+ 'default',
+ ),
+ key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True),
+ key.evolve('debugstl'): coredata.UserBooleanOption('STL debug mode', False),
+ })
+ opts[key].choices = ['none'] + c_stds + g_stds
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ remap_cpp03 = {
+ 'c++03': 'c++98',
+ 'gnu++03': 'gnu++98'
+ }
+ args.append('-std=' + remap_cpp03.get(std.value, std.value))
+ if options[key.evolve('eh')].value == 'none':
+ args.append('-fno-exceptions')
+ if not options[key.evolve('rtti')].value:
+ args.append('-fno-rtti')
+ if options[key.evolve('debugstl')].value:
+ args.append('-D_GLIBCXX_DEBUG=1')
+ return args
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return []
+
+
+class IntelLLVMCPPCompiler(ClangCPPCompiler):
+
+ id = 'intel-llvm'
+
+
+class VisualStudioLikeCPPCompilerMixin(CompilerMixinBase):
+
+ """Mixin for C++ specific method overrides in MSVC-like compilers."""
+
+ VC_VERSION_MAP = {
+ 'none': (True, None),
+ 'vc++11': (True, 11),
+ 'vc++14': (True, 14),
+ 'vc++17': (True, 17),
+ 'vc++20': (True, 20),
+ 'vc++latest': (True, "latest"),
+ 'c++11': (False, 11),
+ 'c++14': (False, 14),
+ 'c++17': (False, 17),
+ 'c++20': (False, 20),
+ 'c++latest': (False, "latest"),
+ }
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ # need a typeddict for this
+ key = OptionKey('winlibs', machine=self.for_machine, lang=self.language)
+ return T.cast('T.List[str]', options[key].value[:])
+
+ def _get_options_impl(self, opts: 'MutableKeyedOptionDictType', cpp_stds: T.List[str]) -> 'MutableKeyedOptionDictType':
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts.update({
+ key.evolve('eh'): coredata.UserComboOption(
+ 'C++ exception handling type.',
+ ['none', 'default', 'a', 's', 'sc'],
+ 'default',
+ ),
+ key.evolve('rtti'): coredata.UserBooleanOption('Enable RTTI', True),
+ key.evolve('winlibs'): coredata.UserArrayOption(
+ 'Windows libs to link against.',
+ msvc_winlibs,
+ ),
+ })
+ opts[key.evolve('std')].choices = cpp_stds
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+
+ eh = options[key.evolve('eh')]
+ if eh.value == 'default':
+ args.append('/EHsc')
+ elif eh.value == 'none':
+ args.append('/EHs-c-')
+ else:
+ args.append('/EH' + eh.value)
+
+ if not options[key.evolve('rtti')].value:
+ args.append('/GR-')
+
+ permissive, ver = self.VC_VERSION_MAP[options[key].value]
+
+ if ver is not None:
+ args.append(f'/std:c++{ver}')
+
+ if not permissive:
+ args.append('/permissive-')
+
+ return args
+
+ def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
+ # XXX: this is a hack because so much GnuLike stuff is in the base CPPCompiler class.
+ return Compiler.get_compiler_check_args(self, mode)
+
+
+class CPP11AsCPP14Mixin(CompilerMixinBase):
+
+ """Mixin class for VisualStudio and ClangCl to replace C++11 std with C++14.
+
+ This is a limitation of Clang and MSVC that ICL doesn't share.
+ """
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ # Note: there is no explicit flag for supporting C++11; we attempt to do the best we can
+ # which means setting the C++ standard version to C++14, in compilers that support it
+ # (i.e., after VS2015U3)
+ # if one is using anything before that point, one cannot set the standard.
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ if options[key].value in {'vc++11', 'c++11'}:
+ mlog.warning(self.id, 'does not support C++11;',
+ 'attempting best effort; setting the standard to C++14', once=True)
+ # Don't mutate anything we're going to change, we need to use
+ # deepcopy since we're messing with members, and we can't simply
+ # copy the members because the option proxy doesn't support it.
+ options = copy.deepcopy(options)
+ if options[key].value == 'vc++11':
+ options[key].value = 'vc++14'
+ else:
+ options[key].value = 'c++14'
+ return super().get_option_compile_args(options)
+
+
+class VisualStudioCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, MSVCCompiler, CPPCompiler):
+
+ id = 'msvc'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo', target: str,
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ MSVCCompiler.__init__(self, target)
+
+ # By default, MSVC has a broken __cplusplus define that pretends to be c++98:
+ # https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-160
+ # Pass the flag to enable a truthful define, if possible.
+ if version_compare(self.version, '>= 19.14.26428'):
+ self.always_args = self.always_args + ['/Zc:__cplusplus']
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ cpp_stds = ['none', 'c++11', 'vc++11']
+ # Visual Studio 2015 and later
+ if version_compare(self.version, '>=19'):
+ cpp_stds.extend(['c++14', 'c++latest', 'vc++latest'])
+ # Visual Studio 2017 and later
+ if version_compare(self.version, '>=19.11'):
+ cpp_stds.extend(['vc++14', 'c++17', 'vc++17'])
+ if version_compare(self.version, '>=19.29'):
+ cpp_stds.extend(['c++20', 'vc++20'])
+ return self._get_options_impl(super().get_options(), cpp_stds)
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ if options[key].value != 'none' and version_compare(self.version, '<19.00.24210'):
+ mlog.warning('This version of MSVC does not support cpp_std arguments')
+ options = copy.copy(options)
+ options[key].value = 'none'
+
+ args = super().get_option_compile_args(options)
+
+ if version_compare(self.version, '<19.11'):
+ try:
+ i = args.index('/permissive-')
+ except ValueError:
+ return args
+ del args[i]
+ return args
+
+class ClangClCPPCompiler(CPP11AsCPP14Mixin, VisualStudioLikeCPPCompilerMixin, ClangClCompiler, CPPCompiler):
+
+ id = 'clang-cl'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo', target: str,
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ClangClCompiler.__init__(self, target)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++20', 'vc++20', 'c++latest']
+ return self._get_options_impl(super().get_options(), cpp_stds)
+
+
+class IntelClCPPCompiler(VisualStudioLikeCPPCompilerMixin, IntelVisualStudioLikeCompiler, CPPCompiler):
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo', target: str,
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, [], exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ IntelVisualStudioLikeCompiler.__init__(self, target)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ # This has only been tested with version 19.0,
+ cpp_stds = ['none', 'c++11', 'vc++11', 'c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest']
+ return self._get_options_impl(super().get_options(), cpp_stds)
+
+ def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
+ # XXX: this is a hack because so much GnuLike stuff is in the base CPPCompiler class.
+ return IntelVisualStudioLikeCompiler.get_compiler_check_args(self, mode)
+
+
+class IntelLLVMClCPPCompiler(IntelClCPPCompiler):
+
+ id = 'intel-llvm-cl'
+
+
+class ArmCPPCompiler(ArmCompiler, CPPCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ArmCompiler.__init__(self)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CPPCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c++03', 'c++11']
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value == 'c++11':
+ args.append('--cpp11')
+ elif std.value == 'c++03':
+ args.append('--cpp')
+ return args
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return []
+
+ def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
+ return []
+
+
+class CcrxCPPCompiler(CcrxCompiler, CPPCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ CcrxCompiler.__init__(self)
+
+ # Override CCompiler.get_always_args
+ def get_always_args(self) -> T.List[str]:
+ return ['-nologo', '-lang=cpp']
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return []
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return []
+
+ def get_output_args(self, target: str) -> T.List[str]:
+ return [f'-output=obj={target}']
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return []
+
+ def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
+ return []
+
+class TICPPCompiler(TICompiler, CPPCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ CPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ TICompiler.__init__(self)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = CPPCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'c++03']
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append('--' + std.value)
+ return args
+
+ def get_always_args(self) -> T.List[str]:
+ return []
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return []
+
+class C2000CPPCompiler(TICPPCompiler):
+ # Required for backwards compat with projects created before ti-cgt support existed
+ id = 'c2000'
diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py
new file mode 100644
index 0000000..14fcfd7
--- /dev/null
+++ b/mesonbuild/compilers/cs.py
@@ -0,0 +1,154 @@
+# Copyright 2012-2017 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 os.path, subprocess
+import textwrap
+import typing as T
+
+from ..mesonlib import EnvironmentException
+from ..linkers import RSPFileSyntax
+
+from .compilers import Compiler, mono_buildtype_args
+from .mixins.islinker import BasicLinkerIsCompilerMixin
+
+if T.TYPE_CHECKING:
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..mesonlib import MachineChoice
+
+cs_optimization_args = {
+ 'plain': [],
+ '0': [],
+ 'g': [],
+ '1': ['-optimize+'],
+ '2': ['-optimize+'],
+ '3': ['-optimize+'],
+ 's': ['-optimize+'],
+ } # type: T.Dict[str, T.List[str]]
+
+
+class CsCompiler(BasicLinkerIsCompilerMixin, Compiler):
+
+ language = 'cs'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ info: 'MachineInfo', runner: T.Optional[str] = None):
+ super().__init__([], exelist, version, for_machine, info)
+ self.runner = runner
+
+ @classmethod
+ def get_display_language(cls) -> str:
+ return 'C sharp'
+
+ def get_always_args(self) -> T.List[str]:
+ return ['/nologo']
+
+ def get_linker_always_args(self) -> T.List[str]:
+ return ['/nologo']
+
+ def get_output_args(self, fname: str) -> T.List[str]:
+ return ['-out:' + fname]
+
+ def get_link_args(self, fname: str) -> T.List[str]:
+ return ['-r:' + fname]
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['-warnaserror']
+
+ def get_pic_args(self) -> T.List[str]:
+ return []
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-L':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+ if i[:5] == '-lib:':
+ parameter_list[idx] = i[:5] + os.path.normpath(os.path.join(build_dir, i[5:]))
+
+ return parameter_list
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ return []
+
+ def get_pch_name(self, header_name: str) -> str:
+ return ''
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ src = 'sanity.cs'
+ obj = 'sanity.exe'
+ source_name = os.path.join(work_dir, src)
+ with open(source_name, 'w', encoding='utf-8') as ofile:
+ ofile.write(textwrap.dedent('''
+ public class Sanity {
+ static public void Main () {
+ }
+ }
+ '''))
+ pc = subprocess.Popen(self.exelist + self.get_always_args() + [src], cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('C# compiler %s can not compile programs.' % self.name_string())
+ if self.runner:
+ cmdlist = [self.runner, obj]
+ else:
+ cmdlist = [os.path.join(work_dir, obj)]
+ pe = subprocess.Popen(cmdlist, cwd=work_dir)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by Mono compiler %s are not runnable.' % self.name_string())
+
+ def needs_static_linker(self) -> bool:
+ return False
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return mono_buildtype_args[buildtype]
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return ['-debug'] if is_debug else []
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return cs_optimization_args[optimization_level]
+
+
+class MonoCompiler(CsCompiler):
+
+ id = 'mono'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ info: 'MachineInfo'):
+ super().__init__(exelist, version, for_machine, info, runner='mono')
+
+ def rsp_file_syntax(self) -> 'RSPFileSyntax':
+ return RSPFileSyntax.GCC
+
+
+class VisualStudioCsCompiler(CsCompiler):
+
+ id = 'csc'
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ res = mono_buildtype_args[buildtype]
+ if not self.info.is_windows():
+ tmp = []
+ for flag in res:
+ if flag == '-debug':
+ flag = '-debug:portable'
+ tmp.append(flag)
+ res = tmp
+ return res
+
+ def rsp_file_syntax(self) -> 'RSPFileSyntax':
+ return RSPFileSyntax.MSVC
diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py
new file mode 100644
index 0000000..d008d0c
--- /dev/null
+++ b/mesonbuild/compilers/cuda.py
@@ -0,0 +1,791 @@
+# Copyright 2012-2017 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 enum
+import os.path
+import string
+import typing as T
+
+from .. import coredata
+from .. import mlog
+from ..mesonlib import (
+ EnvironmentException, Popen_safe, OptionOverrideProxy,
+ is_windows, LibType, OptionKey, version_compare,
+)
+from .compilers import (Compiler, cuda_buildtype_args, cuda_optimization_args,
+ cuda_debug_args)
+
+if T.TYPE_CHECKING:
+ from .compilers import CompileCheckMode
+ from ..build import BuildTarget
+ from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType
+ from ..dependencies import Dependency
+ from ..environment import Environment # noqa: F401
+ from ..envconfig import MachineInfo
+ from ..linkers import DynamicLinker
+ from ..mesonlib import MachineChoice
+ from ..programs import ExternalProgram
+
+
+class _Phase(enum.Enum):
+
+ COMPILER = 'compiler'
+ LINKER = 'linker'
+
+
+class CudaCompiler(Compiler):
+
+ LINKER_PREFIX = '-Xlinker='
+ language = 'cuda'
+
+ # NVCC flags taking no arguments.
+ _FLAG_PASSTHRU_NOARGS = {
+ # NVCC --long-option, NVCC -short-option CUDA Toolkit 11.2.1 Reference
+ '--objdir-as-tempdir', '-objtemp', # 4.2.1.2
+ '--generate-dependency-targets', '-MP', # 4.2.1.12
+ '--allow-unsupported-compiler', '-allow-unsupported-compiler', # 4.2.1.14
+ '--link', # 4.2.2.1
+ '--lib', '-lib', # 4.2.2.2
+ '--device-link', '-dlink', # 4.2.2.3
+ '--device-c', '-dc', # 4.2.2.4
+ '--device-w', '-dw', # 4.2.2.5
+ '--cuda', '-cuda', # 4.2.2.6
+ '--compile', '-c', # 4.2.2.7
+ '--fatbin', '-fatbin', # 4.2.2.8
+ '--cubin', '-cubin', # 4.2.2.9
+ '--ptx', '-ptx', # 4.2.2.10
+ '--preprocess', '-E', # 4.2.2.11
+ '--generate-dependencies', '-M', # 4.2.2.12
+ '--generate-nonsystem-dependencies', '-MM', # 4.2.2.13
+ '--generate-dependencies-with-compile', '-MD', # 4.2.2.14
+ '--generate-nonsystem-dependencies-with-compile', '-MMD', # 4.2.2.15
+ '--run', # 4.2.2.16
+ '--profile', '-pg', # 4.2.3.1
+ '--debug', '-g', # 4.2.3.2
+ '--device-debug', '-G', # 4.2.3.3
+ '--extensible-whole-program', '-ewp', # 4.2.3.4
+ '--generate-line-info', '-lineinfo', # 4.2.3.5
+ '--dlink-time-opt', '-dlto', # 4.2.3.8
+ '--no-exceptions', '-noeh', # 4.2.3.11
+ '--shared', '-shared', # 4.2.3.12
+ '--no-host-device-initializer-list', '-nohdinitlist', # 4.2.3.15
+ '--expt-relaxed-constexpr', '-expt-relaxed-constexpr', # 4.2.3.16
+ '--extended-lambda', '-extended-lambda', # 4.2.3.17
+ '--expt-extended-lambda', '-expt-extended-lambda', # 4.2.3.18
+ '--m32', '-m32', # 4.2.3.20
+ '--m64', '-m64', # 4.2.3.21
+ '--forward-unknown-to-host-compiler', '-forward-unknown-to-host-compiler', # 4.2.5.1
+ '--forward-unknown-to-host-linker', '-forward-unknown-to-host-linker', # 4.2.5.2
+ '--dont-use-profile', '-noprof', # 4.2.5.3
+ '--dryrun', '-dryrun', # 4.2.5.5
+ '--verbose', '-v', # 4.2.5.6
+ '--keep', '-keep', # 4.2.5.7
+ '--save-temps', '-save-temps', # 4.2.5.9
+ '--clean-targets', '-clean', # 4.2.5.10
+ '--no-align-double', # 4.2.5.16
+ '--no-device-link', '-nodlink', # 4.2.5.17
+ '--allow-unsupported-compiler', '-allow-unsupported-compiler', # 4.2.5.18
+ '--use_fast_math', '-use_fast_math', # 4.2.7.7
+ '--extra-device-vectorization', '-extra-device-vectorization', # 4.2.7.12
+ '--compile-as-tools-patch', '-astoolspatch', # 4.2.7.13
+ '--keep-device-functions', '-keep-device-functions', # 4.2.7.14
+ '--disable-warnings', '-w', # 4.2.8.1
+ '--source-in-ptx', '-src-in-ptx', # 4.2.8.2
+ '--restrict', '-restrict', # 4.2.8.3
+ '--Wno-deprecated-gpu-targets', '-Wno-deprecated-gpu-targets', # 4.2.8.4
+ '--Wno-deprecated-declarations', '-Wno-deprecated-declarations', # 4.2.8.5
+ '--Wreorder', '-Wreorder', # 4.2.8.6
+ '--Wdefault-stream-launch', '-Wdefault-stream-launch', # 4.2.8.7
+ '--Wext-lambda-captures-this', '-Wext-lambda-captures-this', # 4.2.8.8
+ '--display-error-number', '-err-no', # 4.2.8.10
+ '--resource-usage', '-res-usage', # 4.2.8.14
+ '--help', '-h', # 4.2.8.15
+ '--version', '-V', # 4.2.8.16
+ '--list-gpu-code', '-code-ls', # 4.2.8.20
+ '--list-gpu-arch', '-arch-ls', # 4.2.8.21
+ }
+ # Dictionary of NVCC flags taking either one argument or a comma-separated list.
+ # Maps --long to -short options, because the short options are more GCC-like.
+ _FLAG_LONG2SHORT_WITHARGS = {
+ '--output-file': '-o', # 4.2.1.1
+ '--pre-include': '-include', # 4.2.1.3
+ '--library': '-l', # 4.2.1.4
+ '--define-macro': '-D', # 4.2.1.5
+ '--undefine-macro': '-U', # 4.2.1.6
+ '--include-path': '-I', # 4.2.1.7
+ '--system-include': '-isystem', # 4.2.1.8
+ '--library-path': '-L', # 4.2.1.9
+ '--output-directory': '-odir', # 4.2.1.10
+ '--dependency-output': '-MF', # 4.2.1.11
+ '--compiler-bindir': '-ccbin', # 4.2.1.13
+ '--archiver-binary': '-arbin', # 4.2.1.15
+ '--cudart': '-cudart', # 4.2.1.16
+ '--cudadevrt': '-cudadevrt', # 4.2.1.17
+ '--libdevice-directory': '-ldir', # 4.2.1.18
+ '--target-directory': '-target-dir', # 4.2.1.19
+ '--optimization-info': '-opt-info', # 4.2.3.6
+ '--optimize': '-O', # 4.2.3.7
+ '--ftemplate-backtrace-limit': '-ftemplate-backtrace-limit', # 4.2.3.9
+ '--ftemplate-depth': '-ftemplate-depth', # 4.2.3.10
+ '--x': '-x', # 4.2.3.13
+ '--std': '-std', # 4.2.3.14
+ '--machine': '-m', # 4.2.3.19
+ '--compiler-options': '-Xcompiler', # 4.2.4.1
+ '--linker-options': '-Xlinker', # 4.2.4.2
+ '--archive-options': '-Xarchive', # 4.2.4.3
+ '--ptxas-options': '-Xptxas', # 4.2.4.4
+ '--nvlink-options': '-Xnvlink', # 4.2.4.5
+ '--threads': '-t', # 4.2.5.4
+ '--keep-dir': '-keep-dir', # 4.2.5.8
+ '--run-args': '-run-args', # 4.2.5.11
+ '--input-drive-prefix': '-idp', # 4.2.5.12
+ '--dependency-drive-prefix': '-ddp', # 4.2.5.13
+ '--drive-prefix': '-dp', # 4.2.5.14
+ '--dependency-target-name': '-MT', # 4.2.5.15
+ '--default-stream': '-default-stream', # 4.2.6.1
+ '--gpu-architecture': '-arch', # 4.2.7.1
+ '--gpu-code': '-code', # 4.2.7.2
+ '--generate-code': '-gencode', # 4.2.7.3
+ '--relocatable-device-code': '-rdc', # 4.2.7.4
+ '--entries': '-e', # 4.2.7.5
+ '--maxrregcount': '-maxrregcount', # 4.2.7.6
+ '--ftz': '-ftz', # 4.2.7.8
+ '--prec-div': '-prec-div', # 4.2.7.9
+ '--prec-sqrt': '-prec-sqrt', # 4.2.7.10
+ '--fmad': '-fmad', # 4.2.7.11
+ '--Werror': '-Werror', # 4.2.8.9
+ '--diag-error': '-diag-error', # 4.2.8.11
+ '--diag-suppress': '-diag-suppress', # 4.2.8.12
+ '--diag-warn': '-diag-warn', # 4.2.8.13
+ '--options-file': '-optf', # 4.2.8.17
+ '--time': '-time', # 4.2.8.18
+ '--qpp-config': '-qpp-config', # 4.2.8.19
+ }
+ # Reverse map -short to --long options.
+ _FLAG_SHORT2LONG_WITHARGS = {v: k for k, v in _FLAG_LONG2SHORT_WITHARGS.items()}
+
+ id = 'nvcc'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, exe_wrapper: T.Optional['ExternalProgram'],
+ host_compiler: Compiler, info: 'MachineInfo',
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ super().__init__(ccache, exelist, version, for_machine, info, linker=linker, full_version=full_version, is_cross=is_cross)
+ self.exe_wrapper = exe_wrapper
+ self.host_compiler = host_compiler
+ self.base_options = host_compiler.base_options
+ self.warn_args = {level: self._to_host_flags(flags) for level, flags in host_compiler.warn_args.items()}
+
+ @classmethod
+ def _shield_nvcc_list_arg(cls, arg: str, listmode: bool = True) -> str:
+ r"""
+ Shield an argument against both splitting by NVCC's list-argument
+ parse logic, and interpretation by any shell.
+
+ NVCC seems to consider every comma , that is neither escaped by \ nor inside
+ a double-quoted string a split-point. Single-quotes do not provide protection
+ against splitting; In fact, after splitting they are \-escaped. Unfortunately,
+ double-quotes don't protect against shell expansion. What follows is a
+ complex dance to accommodate everybody.
+ """
+
+ SQ = "'"
+ DQ = '"'
+ CM = ","
+ BS = "\\"
+ DQSQ = DQ+SQ+DQ
+ quotable = set(string.whitespace+'"$`\\')
+
+ if CM not in arg or not listmode:
+ if SQ not in arg:
+ # If any of the special characters "$`\ or whitespace are present, single-quote.
+ # Otherwise return bare.
+ if set(arg).intersection(quotable):
+ return SQ+arg+SQ
+ else:
+ return arg # Easy case: no splits, no quoting.
+ else:
+ # There are single quotes. Double-quote them, and single-quote the
+ # strings between them.
+ l = [cls._shield_nvcc_list_arg(s) for s in arg.split(SQ)]
+ l = sum([[s, DQSQ] for s in l][:-1], []) # Interleave l with DQSQs
+ return ''.join(l)
+ else:
+ # A comma is present, and list mode was active.
+ # We apply (what we guess is) the (primitive) NVCC splitting rule:
+ l = ['']
+ instring = False
+ argit = iter(arg)
+ for c in argit:
+ if c == CM and not instring:
+ l.append('')
+ elif c == DQ:
+ l[-1] += c
+ instring = not instring
+ elif c == BS:
+ try:
+ l[-1] += next(argit)
+ except StopIteration:
+ break
+ else:
+ l[-1] += c
+
+ # Shield individual strings, without listmode, then return them with
+ # escaped commas between them.
+ l = [cls._shield_nvcc_list_arg(s, listmode=False) for s in l]
+ return r'\,'.join(l)
+
+ @classmethod
+ def _merge_flags(cls, flags: T.List[str]) -> T.List[str]:
+ r"""
+ The flags to NVCC gets exceedingly verbose and unreadable when too many of them
+ are shielded with -Xcompiler. Merge consecutive -Xcompiler-wrapped arguments
+ into one.
+ """
+ if len(flags) <= 1:
+ return flags
+ flagit = iter(flags)
+ xflags = []
+
+ def is_xcompiler_flag_isolated(flag: str) -> bool:
+ return flag == '-Xcompiler'
+
+ def is_xcompiler_flag_glued(flag: str) -> bool:
+ return flag.startswith('-Xcompiler=')
+
+ def is_xcompiler_flag(flag: str) -> bool:
+ return is_xcompiler_flag_isolated(flag) or is_xcompiler_flag_glued(flag)
+
+ def get_xcompiler_val(flag: str, flagit: T.Iterator[str]) -> str:
+ if is_xcompiler_flag_glued(flag):
+ return flag[len('-Xcompiler='):]
+ else:
+ try:
+ return next(flagit)
+ except StopIteration:
+ return ""
+
+ ingroup = False
+ for flag in flagit:
+ if not is_xcompiler_flag(flag):
+ ingroup = False
+ xflags.append(flag)
+ elif ingroup:
+ xflags[-1] += ','
+ xflags[-1] += get_xcompiler_val(flag, flagit)
+ elif is_xcompiler_flag_isolated(flag):
+ ingroup = True
+ xflags.append(flag)
+ xflags.append(get_xcompiler_val(flag, flagit))
+ elif is_xcompiler_flag_glued(flag):
+ ingroup = True
+ xflags.append(flag)
+ else:
+ raise ValueError("-Xcompiler flag merging failed, unknown argument form!")
+ return xflags
+
+ def _to_host_flags(self, flags: T.List[str], phase: _Phase = _Phase.COMPILER) -> T.List[str]:
+ """
+ Translate generic "GCC-speak" plus particular "NVCC-speak" flags to NVCC flags.
+
+ NVCC's "short" flags have broad similarities to the GCC standard, but have
+ gratuitous, irritating differences.
+ """
+
+ xflags = []
+ flagit = iter(flags)
+
+ for flag in flagit:
+ # The CUDA Toolkit Documentation, in 4.1. Command Option Types and Notation,
+ # specifies that NVCC does not parse the standard flags as GCC does. It has
+ # its own strategy, to wit:
+ #
+ # nvcc recognizes three types of command options: boolean options, single
+ # value options, and list options.
+ #
+ # Boolean options do not have an argument; they are either specified on a
+ # command line or not. Single value options must be specified at most once,
+ # and list options may be repeated. Examples of each of these option types
+ # are, respectively: --verbose (switch to verbose mode), --output-file
+ # (specify output file), and --include-path (specify include path).
+ #
+ # Single value options and list options must have arguments, which must
+ # follow the name of the option itself by either one of more spaces or an
+ # equals character. When a one-character short name such as -I, -l, and -L
+ # is used, the value of the option may also immediately follow the option
+ # itself without being separated by spaces or an equal character. The
+ # individual values of list options may be separated by commas in a single
+ # instance of the option, or the option may be repeated, or any
+ # combination of these two cases.
+ #
+ # One strange consequence of this choice is that directory and filenames that
+ # contain commas (',') cannot be passed to NVCC (at least, not as easily as
+ # in GCC). Another strange consequence is that it is legal to supply flags
+ # such as
+ #
+ # -lpthread,rt,dl,util
+ # -l pthread,rt,dl,util
+ # -l=pthread,rt,dl,util
+ #
+ # and each of the above alternatives is equivalent to GCC-speak
+ #
+ # -lpthread -lrt -ldl -lutil
+ # -l pthread -l rt -l dl -l util
+ # -l=pthread -l=rt -l=dl -l=util
+ #
+ # *With the exception of commas in the name*, GCC-speak for these list flags
+ # is a strict subset of NVCC-speak, so we passthrough those flags.
+ #
+ # The -D macro-define flag is documented as somehow shielding commas from
+ # splitting a definition. Balanced parentheses, braces and single-quotes
+ # around the comma are not sufficient, but balanced double-quotes are. The
+ # shielding appears to work with -l, -I, -L flags as well, for instance.
+ #
+ # Since our goal is to replicate GCC-speak as much as possible, we check for
+ # commas in all list-arguments and shield them with double-quotes. We make
+ # an exception for -D (where this would be value-changing) and -U (because
+ # it isn't possible to define a macro with a comma in the name).
+
+ if flag in self._FLAG_PASSTHRU_NOARGS:
+ xflags.append(flag)
+ continue
+
+ # Handle breakup of flag-values into a flag-part and value-part.
+ if flag[:1] not in '-/':
+ # This is not a flag. It's probably a file input. Pass it through.
+ xflags.append(flag)
+ continue
+ elif flag[:1] == '/':
+ # This is ambiguously either an MVSC-style /switch or an absolute path
+ # to a file. For some magical reason the following works acceptably in
+ # both cases.
+ wrap = '"' if ',' in flag else ''
+ xflags.append(f'-X{phase.value}={wrap}{flag}{wrap}')
+ continue
+ elif len(flag) >= 2 and flag[0] == '-' and flag[1] in 'IDULlmOxmte':
+ # This is a single-letter short option. These options (with the
+ # exception of -o) are allowed to receive their argument with neither
+ # space nor = sign before them. Detect and separate them in that event.
+ if flag[2:3] == '': # -I something
+ try:
+ val = next(flagit)
+ except StopIteration:
+ pass
+ elif flag[2:3] == '=': # -I=something
+ val = flag[3:]
+ else: # -Isomething
+ val = flag[2:]
+ flag = flag[:2] # -I
+ elif flag in self._FLAG_LONG2SHORT_WITHARGS or \
+ flag in self._FLAG_SHORT2LONG_WITHARGS:
+ # This is either -o or a multi-letter flag, and it is receiving its
+ # value isolated.
+ try:
+ val = next(flagit) # -o something
+ except StopIteration:
+ pass
+ elif flag.split('=', 1)[0] in self._FLAG_LONG2SHORT_WITHARGS or \
+ flag.split('=', 1)[0] in self._FLAG_SHORT2LONG_WITHARGS:
+ # This is either -o or a multi-letter flag, and it is receiving its
+ # value after an = sign.
+ flag, val = flag.split('=', 1) # -o=something
+ # Some dependencies (e.g., BoostDependency) add unspaced "-isystem/usr/include" arguments
+ elif flag.startswith('-isystem'):
+ val = flag[8:].strip()
+ flag = flag[:8]
+ else:
+ # This is a flag, and it's foreign to NVCC.
+ #
+ # We do not know whether this GCC-speak flag takes an isolated
+ # argument. Assuming it does not (the vast majority indeed don't),
+ # wrap this argument in an -Xcompiler flag and send it down to NVCC.
+ if flag == '-ffast-math':
+ xflags.append('-use_fast_math')
+ xflags.append('-Xcompiler='+flag)
+ elif flag == '-fno-fast-math':
+ xflags.append('-ftz=false')
+ xflags.append('-prec-div=true')
+ xflags.append('-prec-sqrt=true')
+ xflags.append('-Xcompiler='+flag)
+ elif flag == '-freciprocal-math':
+ xflags.append('-prec-div=false')
+ xflags.append('-Xcompiler='+flag)
+ elif flag == '-fno-reciprocal-math':
+ xflags.append('-prec-div=true')
+ xflags.append('-Xcompiler='+flag)
+ else:
+ xflags.append('-Xcompiler='+self._shield_nvcc_list_arg(flag))
+ # The above should securely handle GCC's -Wl, -Wa, -Wp, arguments.
+ continue
+
+ assert val is not None # Should only trip if there is a missing argument.
+
+ # Take care of the various NVCC-supported flags that need special handling.
+ flag = self._FLAG_LONG2SHORT_WITHARGS.get(flag, flag)
+
+ if flag in {'-include', '-isystem', '-I', '-L', '-l'}:
+ # These flags are known to GCC, but list-valued in NVCC. They potentially
+ # require double-quoting to prevent NVCC interpreting the flags as lists
+ # when GCC would not have done so.
+ #
+ # We avoid doing this quoting for -D to avoid redefining macros and for
+ # -U because it isn't possible to define a macro with a comma in the name.
+ # -U with comma arguments is impossible in GCC-speak (and thus unambiguous
+ #in NVCC-speak, albeit unportable).
+ if len(flag) == 2:
+ xflags.append(flag+self._shield_nvcc_list_arg(val))
+ elif flag == '-isystem' and val in self.host_compiler.get_default_include_dirs():
+ # like GnuLikeCompiler, we have to filter out include directories specified
+ # with -isystem that overlap with the host compiler's search path
+ pass
+ else:
+ xflags.append(flag)
+ xflags.append(self._shield_nvcc_list_arg(val))
+ elif flag == '-O':
+ # Handle optimization levels GCC knows about that NVCC does not.
+ if val == 'fast':
+ xflags.append('-O3')
+ xflags.append('-use_fast_math')
+ xflags.append('-Xcompiler')
+ xflags.append(flag+val)
+ elif val in {'s', 'g', 'z'}:
+ xflags.append('-Xcompiler')
+ xflags.append(flag+val)
+ else:
+ xflags.append(flag+val)
+ elif flag in {'-D', '-U', '-m', '-t'}:
+ xflags.append(flag+val) # For style, keep glued.
+ elif flag in {'-std'}:
+ xflags.append(flag+'='+val) # For style, keep glued.
+ else:
+ xflags.append(flag)
+ xflags.append(val)
+
+ return self._merge_flags(xflags)
+
+ def needs_static_linker(self) -> bool:
+ return False
+
+ def thread_link_flags(self, environment: 'Environment') -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.thread_link_flags(environment), _Phase.LINKER)
+
+ def sanity_check(self, work_dir: str, env: 'Environment') -> None:
+ mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', ' '.join(self.exelist))
+ mlog.debug('Is cross compiler: %s.' % str(self.is_cross))
+
+ sname = 'sanitycheckcuda.cu'
+ code = r'''
+ #include <cuda_runtime.h>
+ #include <stdio.h>
+
+ __global__ void kernel (void) {}
+
+ int main(void){
+ struct cudaDeviceProp prop;
+ int count, i;
+ cudaError_t ret = cudaGetDeviceCount(&count);
+ if(ret != cudaSuccess){
+ fprintf(stderr, "%d\n", (int)ret);
+ }else{
+ for(i=0;i<count;i++){
+ if(cudaGetDeviceProperties(&prop, i) == cudaSuccess){
+ fprintf(stdout, "%d.%d\n", prop.major, prop.minor);
+ }
+ }
+ }
+ fflush(stderr);
+ fflush(stdout);
+ return 0;
+ }
+ '''
+ binname = sname.rsplit('.', 1)[0]
+ binname += '_cross' if self.is_cross else ''
+ source_name = os.path.join(work_dir, sname)
+ binary_name = os.path.join(work_dir, binname + '.exe')
+ with open(source_name, 'w', encoding='utf-8') as ofile:
+ ofile.write(code)
+
+ # The Sanity Test for CUDA language will serve as both a sanity test
+ # and a native-build GPU architecture detection test, useful later.
+ #
+ # For this second purpose, NVCC has very handy flags, --run and
+ # --run-args, that allow one to run an application with the
+ # environment set up properly. Of course, this only works for native
+ # builds; For cross builds we must still use the exe_wrapper (if any).
+ self.detected_cc = ''
+ flags = []
+
+ # Disable warnings, compile with statically-linked runtime for minimum
+ # reliance on the system.
+ flags += ['-w', '-cudart', 'static', source_name]
+
+ # Use the -ccbin option, if available, even during sanity checking.
+ # Otherwise, on systems where CUDA does not support the default compiler,
+ # NVCC becomes unusable.
+ flags += self.get_ccbin_args(env.coredata.options)
+
+ # If cross-compiling, we can't run the sanity check, only compile it.
+ if self.is_cross and self.exe_wrapper is None:
+ # Linking cross built apps is painful. You can't really
+ # tell if you should use -nostdlib or not and for example
+ # on OSX the compiler binary is the same but you need
+ # a ton of compiler flags to differentiate between
+ # arm and x86_64. So just compile.
+ flags += self.get_compile_only_args()
+ flags += self.get_output_args(binary_name)
+
+ # Compile sanity check
+ cmdlist = self.exelist + flags
+ mlog.debug('Sanity check compiler command line: ', ' '.join(cmdlist))
+ pc, stdo, stde = Popen_safe(cmdlist, cwd=work_dir)
+ mlog.debug('Sanity check compile stdout: ')
+ mlog.debug(stdo)
+ mlog.debug('-----\nSanity check compile stderr:')
+ mlog.debug(stde)
+ mlog.debug('-----')
+ if pc.returncode != 0:
+ raise EnvironmentException(f'Compiler {self.name_string()} can not compile programs.')
+
+ # Run sanity check (if possible)
+ if self.is_cross:
+ if self.exe_wrapper is None:
+ return
+ else:
+ cmdlist = self.exe_wrapper.get_command() + [binary_name]
+ else:
+ cmdlist = self.exelist + ['--run', '"' + binary_name + '"']
+ mlog.debug('Sanity check run command line: ', ' '.join(cmdlist))
+ pe, stdo, stde = Popen_safe(cmdlist, cwd=work_dir)
+ mlog.debug('Sanity check run stdout: ')
+ mlog.debug(stdo)
+ mlog.debug('-----\nSanity check run stderr:')
+ mlog.debug(stde)
+ mlog.debug('-----')
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException(f'Executables created by {self.language} compiler {self.name_string()} are not runnable.')
+
+ # Interpret the result of the sanity test.
+ # As mentioned above, it is not only a sanity test but also a GPU
+ # architecture detection test.
+ if stde == '':
+ self.detected_cc = stdo
+ else:
+ mlog.debug('cudaGetDeviceCount() returned ' + stde)
+
+ 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]:
+ if extra_args is None:
+ extra_args = []
+ fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
+ # Check if it's a C-like symbol
+ t = '''{prefix}
+ #include <{header}>
+ int main(void) {{
+ /* If it's not defined as a macro, try to use as a symbol */
+ #ifndef {symbol}
+ {symbol};
+ #endif
+ return 0;
+ }}'''
+ found, cached = self.compiles(t.format_map(fargs), env, extra_args=extra_args, dependencies=dependencies)
+ if found:
+ return True, cached
+ # Check if it's a class or a template
+ t = '''{prefix}
+ #include <{header}>
+ using {symbol};
+ int main(void) {{
+ return 0;
+ }}'''
+ return self.compiles(t.format_map(fargs), env, extra_args=extra_args, dependencies=dependencies)
+
+ _CPP14_VERSION = '>=9.0'
+ _CPP17_VERSION = '>=11.0'
+ _CPP20_VERSION = '>=12.0'
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = super().get_options()
+ std_key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ ccbindir_key = OptionKey('ccbindir', machine=self.for_machine, lang=self.language)
+
+ cpp_stds = ['none', 'c++03', 'c++11']
+ if version_compare(self.version, self._CPP14_VERSION):
+ cpp_stds += ['c++14']
+ if version_compare(self.version, self._CPP17_VERSION):
+ cpp_stds += ['c++17']
+ if version_compare(self.version, self._CPP20_VERSION):
+ cpp_stds += ['c++20']
+
+ opts.update({
+ std_key: coredata.UserComboOption('C++ language standard to use with CUDA',
+ cpp_stds, 'none'),
+ ccbindir_key: coredata.UserStringOption('CUDA non-default toolchain directory to use (-ccbin)',
+ ''),
+ })
+ return opts
+
+ def _to_host_compiler_options(self, options: 'KeyedOptionDictType') -> 'KeyedOptionDictType':
+ """
+ Convert an NVCC Option set to a host compiler's option set.
+ """
+
+ # We must strip the -std option from the host compiler option set, as NVCC has
+ # its own -std flag that may not agree with the host compiler's.
+ host_options = {key: options.get(key, opt) for key, opt in self.host_compiler.get_options().items()}
+ std_key = OptionKey('std', machine=self.for_machine, lang=self.host_compiler.language)
+ overrides = {std_key: 'none'}
+ return OptionOverrideProxy(overrides, host_options)
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = self.get_ccbin_args(options)
+ # On Windows, the version of the C++ standard used by nvcc is dictated by
+ # the combination of CUDA version and MSVC version; the --std= is thus ignored
+ # and attempting to use it will result in a warning: https://stackoverflow.com/a/51272091/741027
+ if not is_windows():
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append('--std=' + std.value)
+
+ return args + self._to_host_flags(self.host_compiler.get_option_compile_args(self._to_host_compiler_options(options)))
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = self.get_ccbin_args(options)
+ return args + self._to_host_flags(self.host_compiler.get_option_link_args(self._to_host_compiler_options(options)), _Phase.LINKER)
+
+ 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._to_host_flags(self.host_compiler.get_soname_args(
+ env, prefix, shlib_name, suffix, soversion, darwin_versions), _Phase.LINKER)
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return ['-c']
+
+ def get_no_optimization_args(self) -> T.List[str]:
+ return ['-O0']
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ # alternatively, consider simply redirecting this to the host compiler, which would
+ # give us more control over options like "optimize for space" (which nvcc doesn't support):
+ # return self._to_host_flags(self.host_compiler.get_optimization_args(optimization_level))
+ return cuda_optimization_args[optimization_level]
+
+ def sanitizer_compile_args(self, value: str) -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.sanitizer_compile_args(value))
+
+ def sanitizer_link_args(self, value: str) -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.sanitizer_link_args(value))
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return cuda_debug_args[is_debug]
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['-Werror=cross-execution-space-call,deprecated-declarations,reorder']
+
+ def get_warn_args(self, level: str) -> T.List[str]:
+ return self.warn_args[level]
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ # nvcc doesn't support msvc's "Edit and Continue" PDB format; "downgrade" to
+ # a regular PDB to avoid cl's warning to that effect (D9025 : overriding '/ZI' with '/Zi')
+ host_args = ['/Zi' if arg == '/ZI' else arg for arg in self.host_compiler.get_buildtype_args(buildtype)]
+ return cuda_buildtype_args[buildtype] + self._to_host_flags(host_args)
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if path == '':
+ path = '.'
+ return ['-isystem=' + path] if is_system else ['-I' + path]
+
+ def get_compile_debugfile_args(self, rel_obj: str, pch: bool = False) -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.get_compile_debugfile_args(rel_obj, pch))
+
+ def get_link_debugfile_args(self, targetfile: str) -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.get_link_debugfile_args(targetfile), _Phase.LINKER)
+
+ def get_depfile_suffix(self) -> str:
+ return 'd'
+
+ def get_buildtype_linker_args(self, buildtype: str) -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.get_buildtype_linker_args(buildtype), _Phase.LINKER)
+
+ 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]]:
+ (rpath_args, rpath_dirs_to_remove) = self.host_compiler.build_rpath_args(
+ env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
+ return (self._to_host_flags(rpath_args, _Phase.LINKER), rpath_dirs_to_remove)
+
+ def linker_to_compiler_args(self, args: T.List[str]) -> T.List[str]:
+ return args
+
+ def get_pic_args(self) -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.get_pic_args())
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ return []
+
+ def get_output_args(self, target: str) -> T.List[str]:
+ return ['-o', target]
+
+ def get_std_exe_link_args(self) -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.get_std_exe_link_args(), _Phase.LINKER)
+
+ def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
+ libtype: LibType = LibType.PREFER_SHARED) -> T.Optional[T.List[str]]:
+ return ['-l' + libname] # FIXME
+
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ return self._to_host_flags(self.host_compiler.get_crt_compile_args(crt_val, buildtype))
+
+ def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ # nvcc defaults to static, release version of msvc runtime and provides no
+ # native option to override it; override it with /NODEFAULTLIB
+ host_link_arg_overrides = []
+ host_crt_compile_args = self.host_compiler.get_crt_compile_args(crt_val, buildtype)
+ if any(arg in {'/MDd', '/MD', '/MTd'} for arg in host_crt_compile_args):
+ host_link_arg_overrides += ['/NODEFAULTLIB:LIBCMT.lib']
+ return self._to_host_flags(host_link_arg_overrides + self.host_compiler.get_crt_link_args(crt_val, buildtype), _Phase.LINKER)
+
+ def get_target_link_args(self, target: 'BuildTarget') -> T.List[str]:
+ return self._to_host_flags(super().get_target_link_args(target), _Phase.LINKER)
+
+ def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]:
+ return self._to_host_flags(super().get_dependency_compile_args(dep))
+
+ def get_dependency_link_args(self, dep: 'Dependency') -> T.List[str]:
+ return self._to_host_flags(super().get_dependency_link_args(dep), _Phase.LINKER)
+
+ def get_ccbin_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ key = OptionKey('ccbindir', machine=self.for_machine, lang=self.language)
+ ccbindir = options[key].value
+ if isinstance(ccbindir, str) and ccbindir != '':
+ return [self._shield_nvcc_list_arg('-ccbin='+ccbindir, False)]
+ else:
+ return []
+
+ def get_profile_generate_args(self) -> T.List[str]:
+ return ['-Xcompiler=' + x for x in self.host_compiler.get_profile_generate_args()]
+
+ def get_profile_use_args(self) -> T.List[str]:
+ return ['-Xcompiler=' + x for x in self.host_compiler.get_profile_use_args()]
+
+ def get_disable_assert_args(self) -> T.List[str]:
+ return self.host_compiler.get_disable_assert_args()
diff --git a/mesonbuild/compilers/cython.py b/mesonbuild/compilers/cython.py
new file mode 100644
index 0000000..9bbfebe
--- /dev/null
+++ b/mesonbuild/compilers/cython.py
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: Apache-2.0
+# Copyright © 2021 Intel Corporation
+from __future__ import annotations
+
+"""Abstraction for Cython language compilers."""
+
+import typing as T
+
+from .. import coredata
+from ..mesonlib import EnvironmentException, OptionKey, version_compare
+from .compilers import Compiler
+
+if T.TYPE_CHECKING:
+ from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType
+ from ..environment import Environment
+
+
+class CythonCompiler(Compiler):
+
+ """Cython Compiler."""
+
+ language = 'cython'
+ id = 'cython'
+
+ def needs_static_linker(self) -> bool:
+ # We transpile into C, so we don't need any linker
+ return False
+
+ def get_always_args(self) -> T.List[str]:
+ return ['--fast-fail']
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['-Werror']
+
+ def get_output_args(self, outputname: str) -> T.List[str]:
+ return ['-o', outputname]
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ # Cython doesn't have optimization levels itself, the underlying
+ # compiler might though
+ return []
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ if version_compare(self.version, '>=0.29.33'):
+ return ['-M']
+ return []
+
+ def get_depfile_suffix(self) -> str:
+ return 'dep'
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ code = 'print("hello world")'
+ with self.cached_compile(code, environment.coredata) as p:
+ if p.returncode != 0:
+ raise EnvironmentException(f'Cython compiler {self.id!r} cannot compile programs')
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ # Cython doesn't implement this, but Meson requires an implementation
+ return []
+
+ def get_pic_args(self) -> T.List[str]:
+ # We can lie here, it's fine
+ return []
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ new: T.List[str] = []
+ for i in parameter_list:
+ new.append(i)
+
+ return new
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = super().get_options()
+ opts.update({
+ OptionKey('version', machine=self.for_machine, lang=self.language): coredata.UserComboOption(
+ 'Python version to target',
+ ['2', '3'],
+ '3',
+ ),
+ OptionKey('language', machine=self.for_machine, lang=self.language): coredata.UserComboOption(
+ 'Output C or C++ files',
+ ['c', 'cpp'],
+ 'c',
+ )
+ })
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args: T.List[str] = []
+ key = options[OptionKey('version', machine=self.for_machine, lang=self.language)]
+ args.append(f'-{key.value}')
+ lang = options[OptionKey('language', machine=self.for_machine, lang=self.language)]
+ if lang.value == 'cpp':
+ args.append('--cplus')
+ return args
diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py
new file mode 100644
index 0000000..90c0498
--- /dev/null
+++ b/mesonbuild/compilers/d.py
@@ -0,0 +1,1022 @@
+# 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 os.path
+import re
+import subprocess
+import typing as T
+
+from .. import mesonlib
+from .. import mlog
+from ..arglist import CompilerArgs
+from ..linkers import RSPFileSyntax
+from ..mesonlib import (
+ EnvironmentException, version_compare, OptionKey, is_windows
+)
+
+from . import compilers
+from .compilers import (
+ d_dmd_buildtype_args,
+ d_gdc_buildtype_args,
+ d_ldc_buildtype_args,
+ clike_debug_args,
+ Compiler,
+ CompileCheckMode,
+)
+from .mixins.gnu import GnuCompiler
+from .mixins.gnu import gnu_common_warning_args
+
+if T.TYPE_CHECKING:
+ from ..dependencies import Dependency
+ from ..programs import ExternalProgram
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..linkers import DynamicLinker
+ from ..mesonlib import MachineChoice
+
+ CompilerMixinBase = Compiler
+else:
+ CompilerMixinBase = object
+
+d_feature_args = {'gcc': {'unittest': '-funittest',
+ 'debug': '-fdebug',
+ 'version': '-fversion',
+ 'import_dir': '-J'
+ },
+ 'llvm': {'unittest': '-unittest',
+ 'debug': '-d-debug',
+ 'version': '-d-version',
+ 'import_dir': '-J'
+ },
+ 'dmd': {'unittest': '-unittest',
+ 'debug': '-debug',
+ 'version': '-version',
+ 'import_dir': '-J'
+ }
+ } # type: T.Dict[str, T.Dict[str, str]]
+
+ldc_optimization_args = {'plain': [],
+ '0': [],
+ 'g': [],
+ '1': ['-O1'],
+ '2': ['-O2'],
+ '3': ['-O3'],
+ 's': ['-Oz'],
+ } # type: T.Dict[str, T.List[str]]
+
+dmd_optimization_args = {'plain': [],
+ '0': [],
+ 'g': [],
+ '1': ['-O'],
+ '2': ['-O'],
+ '3': ['-O'],
+ 's': ['-O'],
+ } # type: T.Dict[str, T.List[str]]
+
+
+class DmdLikeCompilerMixin(CompilerMixinBase):
+
+ """Mixin class for DMD and LDC.
+
+ LDC has a number of DMD like arguments, and this class allows for code
+ sharing between them as makes sense.
+ """
+
+ def __init__(self, dmd_frontend_version: T.Optional[str]):
+ if dmd_frontend_version is None:
+ self._dmd_has_depfile = False
+ else:
+ # -makedeps switch introduced in 2.095 frontend
+ self._dmd_has_depfile = version_compare(dmd_frontend_version, ">=2.095.0")
+
+ if T.TYPE_CHECKING:
+ mscrt_args = {} # type: T.Dict[str, T.List[str]]
+
+ def _get_target_arch_args(self) -> T.List[str]: ...
+
+ LINKER_PREFIX = '-L='
+
+ def get_output_args(self, outputname: str) -> T.List[str]:
+ return ['-of=' + outputname]
+
+ def get_linker_output_args(self, outputname: str) -> T.List[str]:
+ return ['-of=' + outputname]
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if path == "":
+ path = "."
+ return ['-I=' + path]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:3] == '-I=':
+ parameter_list[idx] = i[:3] + os.path.normpath(os.path.join(build_dir, i[3:]))
+ if i[:4] == '-L-L':
+ parameter_list[idx] = i[:4] + os.path.normpath(os.path.join(build_dir, i[4:]))
+ if i[:5] == '-L=-L':
+ parameter_list[idx] = i[:5] + os.path.normpath(os.path.join(build_dir, i[5:]))
+ if i[:6] == '-Wl,-L':
+ parameter_list[idx] = i[:6] + os.path.normpath(os.path.join(build_dir, i[6:]))
+
+ return parameter_list
+
+ def get_warn_args(self, level: str) -> T.List[str]:
+ return ['-wi']
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['-w']
+
+ def get_coverage_args(self) -> T.List[str]:
+ return ['-cov']
+
+ def get_coverage_link_args(self) -> T.List[str]:
+ return []
+
+ def get_preprocess_only_args(self) -> T.List[str]:
+ return ['-E']
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return ['-c']
+
+ def get_depfile_suffix(self) -> str:
+ return 'deps'
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ if self._dmd_has_depfile:
+ return [f'-makedeps={outfile}']
+ return []
+
+ def get_pic_args(self) -> T.List[str]:
+ if self.info.is_windows():
+ return []
+ return ['-fPIC']
+
+ def get_feature_args(self, kwargs: T.Dict[str, T.Any], build_to_src: str) -> T.List[str]:
+ # TODO: using a TypeDict here would improve this
+ res = []
+ # get_feature_args can be called multiple times for the same target when there is generated source
+ # so we have to copy the kwargs (target.d_features) dict before popping from it
+ kwargs = kwargs.copy()
+ if 'unittest' in kwargs:
+ unittest = kwargs.pop('unittest')
+ unittest_arg = d_feature_args[self.id]['unittest']
+ if not unittest_arg:
+ raise EnvironmentException('D compiler %s does not support the "unittest" feature.' % self.name_string())
+ if unittest:
+ res.append(unittest_arg)
+
+ if 'debug' in kwargs:
+ debug_level = -1
+ debugs = kwargs.pop('debug')
+ if not isinstance(debugs, list):
+ debugs = [debugs]
+
+ debug_arg = d_feature_args[self.id]['debug']
+ if not debug_arg:
+ raise EnvironmentException('D compiler %s does not support conditional debug identifiers.' % self.name_string())
+
+ # Parse all debug identifiers and the largest debug level identifier
+ for d in debugs:
+ if isinstance(d, int):
+ if d > debug_level:
+ debug_level = d
+ elif isinstance(d, str) and d.isdigit():
+ if int(d) > debug_level:
+ debug_level = int(d)
+ else:
+ res.append(f'{debug_arg}={d}')
+
+ if debug_level >= 0:
+ res.append(f'{debug_arg}={debug_level}')
+
+ if 'versions' in kwargs:
+ version_level = -1
+ versions = kwargs.pop('versions')
+ if not isinstance(versions, list):
+ versions = [versions]
+
+ version_arg = d_feature_args[self.id]['version']
+ if not version_arg:
+ raise EnvironmentException('D compiler %s does not support conditional version identifiers.' % self.name_string())
+
+ # Parse all version identifiers and the largest version level identifier
+ for v in versions:
+ if isinstance(v, int):
+ if v > version_level:
+ version_level = v
+ elif isinstance(v, str) and v.isdigit():
+ if int(v) > version_level:
+ version_level = int(v)
+ else:
+ res.append(f'{version_arg}={v}')
+
+ if version_level >= 0:
+ res.append(f'{version_arg}={version_level}')
+
+ if 'import_dirs' in kwargs:
+ import_dirs = kwargs.pop('import_dirs')
+ if not isinstance(import_dirs, list):
+ import_dirs = [import_dirs]
+
+ import_dir_arg = d_feature_args[self.id]['import_dir']
+ if not import_dir_arg:
+ raise EnvironmentException('D compiler %s does not support the "string import directories" feature.' % self.name_string())
+ for idir_obj in import_dirs:
+ basedir = idir_obj.get_curdir()
+ for idir in idir_obj.get_incdirs():
+ bldtreedir = os.path.join(basedir, idir)
+ # Avoid superfluous '/.' at the end of paths when d is '.'
+ if idir not in ('', '.'):
+ expdir = bldtreedir
+ else:
+ expdir = basedir
+ srctreedir = os.path.join(build_to_src, expdir)
+ res.append(f'{import_dir_arg}{srctreedir}')
+ res.append(f'{import_dir_arg}{bldtreedir}')
+
+ if kwargs:
+ raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys()))
+
+ return res
+
+ def get_buildtype_linker_args(self, buildtype: str) -> T.List[str]:
+ if buildtype != 'plain':
+ return self._get_target_arch_args()
+ return []
+
+ def gen_import_library_args(self, implibname: str) -> T.List[str]:
+ return self.linker.import_library_args(implibname)
+
+ 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]]:
+ if self.info.is_windows():
+ return ([], set())
+
+ # GNU ld, solaris ld, and lld acting like GNU ld
+ if self.linker.id.startswith('ld'):
+ # The way that dmd and ldc pass rpath to gcc is different than we would
+ # do directly, each argument -rpath and the value to rpath, need to be
+ # split into two separate arguments both prefaced with the -L=.
+ args = []
+ (rpath_args, rpath_dirs_to_remove) = super().build_rpath_args(
+ env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
+ for r in rpath_args:
+ if ',' in r:
+ a, b = r.split(',', maxsplit=1)
+ args.append(a)
+ args.append(self.LINKER_PREFIX + b)
+ else:
+ args.append(r)
+ return (args, rpath_dirs_to_remove)
+
+ return super().build_rpath_args(
+ env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
+
+ @classmethod
+ def _translate_args_to_nongnu(cls, args: T.List[str], info: MachineInfo, link_id: str) -> T.List[str]:
+ # Translate common arguments to flags the LDC/DMD compilers
+ # can understand.
+ # The flags might have been added by pkg-config files,
+ # and are therefore out of the user's control.
+ dcargs = []
+ # whether we hit a linker argument that expect another arg
+ # see the comment in the "-L" section
+ link_expect_arg = False
+ link_flags_with_arg = [
+ '-rpath', '-rpath-link', '-soname', '-compatibility_version', '-current_version',
+ ]
+ for arg in args:
+ # Translate OS specific arguments first.
+ osargs = [] # type: T.List[str]
+ if info.is_windows():
+ osargs = cls.translate_arg_to_windows(arg)
+ elif info.is_darwin():
+ osargs = cls._translate_arg_to_osx(arg)
+ if osargs:
+ dcargs.extend(osargs)
+ continue
+
+ # Translate common D arguments here.
+ if arg == '-pthread':
+ continue
+ if arg.startswith('-fstack-protector'):
+ continue
+ if arg.startswith('-D') and not (arg == '-D' or arg.startswith(('-Dd', '-Df'))):
+ # ignore all '-D*' flags (like '-D_THREAD_SAFE')
+ # unless they are related to documentation
+ continue
+ if arg.startswith('-Wl,'):
+ # Translate linker arguments here.
+ linkargs = arg[arg.index(',') + 1:].split(',')
+ for la in linkargs:
+ dcargs.append('-L=' + la.strip())
+ continue
+ elif arg.startswith(('-link-defaultlib', '-linker', '-link-internally', '-linkonce-templates', '-lib')):
+ # these are special arguments to the LDC linker call,
+ # arguments like "-link-defaultlib-shared" do *not*
+ # denote a library to be linked, but change the default
+ # Phobos/DRuntime linking behavior, while "-linker" sets the
+ # default linker.
+ dcargs.append(arg)
+ continue
+ elif arg.startswith('-l'):
+ # translate library link flag
+ dcargs.append('-L=' + arg)
+ continue
+ elif arg.startswith('-isystem'):
+ # translate -isystem system include path
+ # this flag might sometimes be added by C library Cflags via
+ # pkg-config.
+ # NOTE: -isystem and -I are not 100% equivalent, so this is just
+ # a workaround for the most common cases.
+ if arg.startswith('-isystem='):
+ dcargs.append('-I=' + arg[9:])
+ else:
+ dcargs.append('-I' + arg[8:])
+ continue
+ elif arg.startswith('-idirafter'):
+ # same as -isystem, but appends the path instead
+ if arg.startswith('-idirafter='):
+ dcargs.append('-I=' + arg[11:])
+ else:
+ dcargs.append('-I' + arg[10:])
+ continue
+ elif arg.startswith('-L'):
+ # The D linker expect library search paths in the form of -L=-L/path (the '=' is optional).
+ #
+ # This function receives a mix of arguments already prepended
+ # with -L for the D linker driver and other linker arguments.
+ # The arguments starting with -L can be:
+ # - library search path (with or without a second -L)
+ # - it can come from pkg-config (a single -L)
+ # - or from the user passing linker flags (-L-L would be expected)
+ # - arguments like "-L=-rpath" that expect a second argument (also prepended with -L)
+ # - arguments like "-L=@rpath/xxx" without a second argument (on Apple platform)
+ # - arguments like "-L=/SUBSYSTEM:CONSOLE (for Windows linker)
+ #
+ # The logic that follows tries to detect all these cases (some may be missing)
+ # in order to prepend a -L only for the library search paths with a single -L
+
+ if arg.startswith('-L='):
+ suffix = arg[3:]
+ else:
+ suffix = arg[2:]
+
+ if link_expect_arg:
+ # flags like rpath and soname expect a path or filename respectively,
+ # we must not alter it (i.e. prefixing with -L for a lib search path)
+ dcargs.append(arg)
+ link_expect_arg = False
+ continue
+
+ if suffix in link_flags_with_arg:
+ link_expect_arg = True
+
+ if suffix.startswith('-') or suffix.startswith('@'):
+ # this is not search path
+ dcargs.append(arg)
+ continue
+
+ # linker flag such as -L=/DEBUG must pass through
+ if info.is_windows() and link_id == 'link' and suffix.startswith('/'):
+ dcargs.append(arg)
+ continue
+
+ # Make sure static library files are passed properly to the linker.
+ if arg.endswith('.a') or arg.endswith('.lib'):
+ if len(suffix) > 0 and not suffix.startswith('-'):
+ dcargs.append('-L=' + suffix)
+ continue
+
+ dcargs.append('-L=' + arg)
+ continue
+ elif not arg.startswith('-') and arg.endswith(('.a', '.lib')):
+ # ensure static libraries are passed through to the linker
+ dcargs.append('-L=' + arg)
+ continue
+ else:
+ dcargs.append(arg)
+
+ return dcargs
+
+ @classmethod
+ def translate_arg_to_windows(cls, arg: str) -> T.List[str]:
+ args = []
+ if arg.startswith('-Wl,'):
+ # Translate linker arguments here.
+ linkargs = arg[arg.index(',') + 1:].split(',')
+ for la in linkargs:
+ if la.startswith('--out-implib='):
+ # Import library name
+ args.append('-L=/IMPLIB:' + la[13:].strip())
+ elif arg.startswith('-mscrtlib='):
+ args.append(arg)
+ mscrtlib = arg[10:].lower()
+ if cls is LLVMDCompiler:
+ # Default crt libraries for LDC2 must be excluded for other
+ # selected crt options.
+ if mscrtlib != 'libcmt':
+ args.append('-L=/NODEFAULTLIB:libcmt')
+ args.append('-L=/NODEFAULTLIB:libvcruntime')
+
+ # Fixes missing definitions for printf-functions in VS2017
+ if mscrtlib.startswith('msvcrt'):
+ args.append('-L=/DEFAULTLIB:legacy_stdio_definitions.lib')
+
+ return args
+
+ @classmethod
+ def _translate_arg_to_osx(cls, arg: str) -> T.List[str]:
+ args = []
+ if arg.startswith('-install_name'):
+ args.append('-L=' + arg)
+ return args
+
+ @classmethod
+ def _unix_args_to_native(cls, args: T.List[str], info: MachineInfo, link_id: str = '') -> T.List[str]:
+ return cls._translate_args_to_nongnu(args, info, link_id)
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ ddebug_args = []
+ if is_debug:
+ ddebug_args = [d_feature_args[self.id]['debug']]
+
+ return clike_debug_args[is_debug] + ddebug_args
+
+ def _get_crt_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ if not self.info.is_windows():
+ return []
+
+ if crt_val in self.mscrt_args:
+ return self.mscrt_args[crt_val]
+ assert crt_val in {'from_buildtype', 'static_from_buildtype'}
+
+ dbg = 'mdd'
+ rel = 'md'
+ if crt_val == 'static_from_buildtype':
+ dbg = 'mtd'
+ rel = 'mt'
+
+ # Match what build type flags used to do.
+ if buildtype == 'plain':
+ return []
+ elif buildtype == 'debug':
+ return self.mscrt_args[dbg]
+ elif buildtype == 'debugoptimized':
+ return self.mscrt_args[rel]
+ elif buildtype == 'release':
+ return self.mscrt_args[rel]
+ elif buildtype == 'minsize':
+ return self.mscrt_args[rel]
+ else:
+ assert buildtype == 'custom'
+ raise EnvironmentException('Requested C runtime based on buildtype, but buildtype is "custom".')
+
+ 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]:
+ sargs = super().get_soname_args(env, prefix, shlib_name, suffix,
+ soversion, darwin_versions)
+
+ # LDC and DMD actually do use a linker, but they proxy all of that with
+ # their own arguments
+ if self.linker.id.startswith('ld.'):
+ soargs = []
+ for arg in sargs:
+ a, b = arg.split(',', maxsplit=1)
+ soargs.append(a)
+ soargs.append(self.LINKER_PREFIX + b)
+ return soargs
+ elif self.linker.id.startswith('ld64'):
+ soargs = []
+ for arg in sargs:
+ if not arg.startswith(self.LINKER_PREFIX):
+ soargs.append(self.LINKER_PREFIX + arg)
+ else:
+ soargs.append(arg)
+ return soargs
+ else:
+ return sargs
+
+ def get_allow_undefined_link_args(self) -> T.List[str]:
+ args = self.linker.get_allow_undefined_args()
+ if self.info.is_darwin():
+ # On macOS we're passing these options to the C compiler, but
+ # they're linker options and need -Wl, so clang/gcc knows what to
+ # do with them. I'm assuming, but don't know for certain, that
+ # ldc/dmd do some kind of mapping internally for arguments they
+ # understand, but pass arguments they don't understand directly.
+ args = [a.replace('-L=', '-Xcc=-Wl,') for a in args]
+ return args
+
+
+class DCompilerArgs(CompilerArgs):
+ prepend_prefixes = ('-I', '-L')
+ dedup2_prefixes = ('-I', )
+
+
+class DCompiler(Compiler):
+ mscrt_args = {
+ 'none': ['-mscrtlib='],
+ 'md': ['-mscrtlib=msvcrt'],
+ 'mdd': ['-mscrtlib=msvcrtd'],
+ 'mt': ['-mscrtlib=libcmt'],
+ 'mtd': ['-mscrtlib=libcmtd'],
+ }
+
+ language = 'd'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ info: 'MachineInfo', arch: str, *,
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None,
+ is_cross: bool = False):
+ super().__init__([], exelist, version, for_machine, info, linker=linker,
+ full_version=full_version, is_cross=is_cross)
+ self.arch = arch
+ self.exe_wrapper = exe_wrapper
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ source_name = os.path.join(work_dir, 'sanity.d')
+ output_name = os.path.join(work_dir, 'dtest')
+ with open(source_name, 'w', encoding='utf-8') as ofile:
+ ofile.write('''void main() { }''')
+ pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + self._get_target_arch_args() + [source_name], cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('D compiler %s can not compile programs.' % self.name_string())
+ if self.is_cross:
+ if self.exe_wrapper is None:
+ # Can't check if the binaries run so we have to assume they do
+ return
+ cmdlist = self.exe_wrapper.get_command() + [output_name]
+ else:
+ cmdlist = [output_name]
+ if subprocess.call(cmdlist) != 0:
+ raise EnvironmentException('Executables created by D compiler %s are not runnable.' % self.name_string())
+
+ def needs_static_linker(self) -> bool:
+ return True
+
+ def get_depfile_suffix(self) -> str:
+ return 'deps'
+
+ def get_pic_args(self) -> T.List[str]:
+ if self.info.is_windows():
+ return []
+ return ['-fPIC']
+
+ def get_feature_args(self, kwargs: T.Dict[str, T.Any], build_to_src: str) -> T.List[str]:
+ # TODO: using a TypeDict here would improve this
+ res = []
+ # get_feature_args can be called multiple times for the same target when there is generated source
+ # so we have to copy the kwargs (target.d_features) dict before popping from it
+ kwargs = kwargs.copy()
+ if 'unittest' in kwargs:
+ unittest = kwargs.pop('unittest')
+ unittest_arg = d_feature_args[self.id]['unittest']
+ if not unittest_arg:
+ raise EnvironmentException('D compiler %s does not support the "unittest" feature.' % self.name_string())
+ if unittest:
+ res.append(unittest_arg)
+
+ if 'debug' in kwargs:
+ debug_level = -1
+ debugs = kwargs.pop('debug')
+ if not isinstance(debugs, list):
+ debugs = [debugs]
+
+ debug_arg = d_feature_args[self.id]['debug']
+ if not debug_arg:
+ raise EnvironmentException('D compiler %s does not support conditional debug identifiers.' % self.name_string())
+
+ # Parse all debug identifiers and the largest debug level identifier
+ for d in debugs:
+ if isinstance(d, int):
+ if d > debug_level:
+ debug_level = d
+ elif isinstance(d, str) and d.isdigit():
+ if int(d) > debug_level:
+ debug_level = int(d)
+ else:
+ res.append(f'{debug_arg}={d}')
+
+ if debug_level >= 0:
+ res.append(f'{debug_arg}={debug_level}')
+
+ if 'versions' in kwargs:
+ version_level = -1
+ versions = kwargs.pop('versions')
+ if not isinstance(versions, list):
+ versions = [versions]
+
+ version_arg = d_feature_args[self.id]['version']
+ if not version_arg:
+ raise EnvironmentException('D compiler %s does not support conditional version identifiers.' % self.name_string())
+
+ # Parse all version identifiers and the largest version level identifier
+ for v in versions:
+ if isinstance(v, int):
+ if v > version_level:
+ version_level = v
+ elif isinstance(v, str) and v.isdigit():
+ if int(v) > version_level:
+ version_level = int(v)
+ else:
+ res.append(f'{version_arg}={v}')
+
+ if version_level >= 0:
+ res.append(f'{version_arg}={version_level}')
+
+ if 'import_dirs' in kwargs:
+ import_dirs = kwargs.pop('import_dirs')
+ if not isinstance(import_dirs, list):
+ import_dirs = [import_dirs]
+
+ import_dir_arg = d_feature_args[self.id]['import_dir']
+ if not import_dir_arg:
+ raise EnvironmentException('D compiler %s does not support the "string import directories" feature.' % self.name_string())
+ for idir_obj in import_dirs:
+ basedir = idir_obj.get_curdir()
+ for idir in idir_obj.get_incdirs():
+ bldtreedir = os.path.join(basedir, idir)
+ # Avoid superfluous '/.' at the end of paths when d is '.'
+ if idir not in ('', '.'):
+ expdir = bldtreedir
+ else:
+ expdir = basedir
+ srctreedir = os.path.join(build_to_src, expdir)
+ res.append(f'{import_dir_arg}{srctreedir}')
+ res.append(f'{import_dir_arg}{bldtreedir}')
+
+ if kwargs:
+ raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys()))
+
+ return res
+
+ def get_buildtype_linker_args(self, buildtype: str) -> T.List[str]:
+ if buildtype != 'plain':
+ return self._get_target_arch_args()
+ return []
+
+ def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> DCompilerArgs:
+ return DCompilerArgs(self, args)
+
+ def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ return self.compiles('int i;\n', env, extra_args=args)
+
+ def _get_target_arch_args(self) -> T.List[str]:
+ # LDC2 on Windows targets to current OS architecture, but
+ # it should follow the target specified by the MSVC toolchain.
+ if self.info.is_windows():
+ if self.arch == 'x86_64':
+ return ['-m64']
+ return ['-m32']
+ return []
+
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ return []
+
+ def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ return []
+
+ def _get_compile_extra_args(self, extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]], None] = None) -> T.List[str]:
+ args = self._get_target_arch_args()
+ if extra_args:
+ if callable(extra_args):
+ extra_args = extra_args(CompileCheckMode.COMPILE)
+ if isinstance(extra_args, list):
+ args.extend(extra_args)
+ elif isinstance(extra_args, str):
+ args.append(extra_args)
+ return args
+
+ 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) -> compilers.RunResult:
+ need_exe_wrapper = env.need_exe_wrapper(self.for_machine)
+ if need_exe_wrapper and self.exe_wrapper is None:
+ raise compilers.CrossNoRunException('Can not run test applications in this cross environment.')
+ extra_args = self._get_compile_extra_args(extra_args)
+ with self._build_wrapper(code, env, extra_args, dependencies, mode='link', want_output=True) as p:
+ if p.returncode != 0:
+ mlog.debug(f'Could not compile test file {p.input_name}: {p.returncode}\n')
+ return compilers.RunResult(False)
+ if need_exe_wrapper:
+ cmdlist = self.exe_wrapper.get_command() + [p.output_name]
+ else:
+ cmdlist = [p.output_name]
+ try:
+ pe, so, se = mesonlib.Popen_safe(cmdlist)
+ except Exception as e:
+ mlog.debug(f'Could not run: {cmdlist} (error: {e})\n')
+ return compilers.RunResult(False)
+
+ mlog.debug('Program stdout:\n')
+ mlog.debug(so)
+ mlog.debug('Program stderr:\n')
+ mlog.debug(se)
+ return compilers.RunResult(True, pe.returncode, so, se)
+
+ 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:
+ if extra_args is None:
+ extra_args = []
+ t = f'''
+ import std.stdio : writeln;
+ {prefix}
+ void main() {{
+ writeln(({typename}).sizeof);
+ }}
+ '''
+ res = self.run(t, env, extra_args=extra_args,
+ dependencies=dependencies)
+ if not res.compiled:
+ return -1
+ if res.returncode != 0:
+ raise mesonlib.EnvironmentException('Could not run sizeof test binary.')
+ return int(res.stdout)
+
+ def alignment(self, typename: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
+ if extra_args is None:
+ extra_args = []
+ t = f'''
+ import std.stdio : writeln;
+ {prefix}
+ void main() {{
+ writeln(({typename}).alignof);
+ }}
+ '''
+ res = self.run(t, env, extra_args=extra_args,
+ dependencies=dependencies)
+ if not res.compiled:
+ raise mesonlib.EnvironmentException('Could not compile alignment test.')
+ if res.returncode != 0:
+ raise mesonlib.EnvironmentException('Could not run alignment test binary.')
+ align = int(res.stdout)
+ if align == 0:
+ raise mesonlib.EnvironmentException(f'Could not determine alignment of {typename}. Sorry. You might want to file a bug.')
+ return align
+
+ 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]:
+
+ extra_args = self._get_compile_extra_args(extra_args)
+ code = f'''{prefix}
+ import {hname};
+ '''
+ return self.compiles(code, env, extra_args=extra_args,
+ dependencies=dependencies, mode='compile', disable_cache=disable_cache)
+
+class GnuDCompiler(GnuCompiler, DCompiler):
+
+ # we mostly want DCompiler, but that gives us the Compiler.LINKER_PREFIX instead
+ LINKER_PREFIX = GnuCompiler.LINKER_PREFIX
+ id = 'gcc'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ info: 'MachineInfo', arch: str, *,
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None,
+ is_cross: bool = False):
+ DCompiler.__init__(self, exelist, version, for_machine, info, arch,
+ exe_wrapper=exe_wrapper, linker=linker,
+ full_version=full_version, is_cross=is_cross)
+ GnuCompiler.__init__(self, {})
+ default_warn_args = ['-Wall', '-Wdeprecated']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': (default_warn_args + ['-Wextra', '-Wpedantic'] +
+ self.supported_warn_args(gnu_common_warning_args))}
+
+ self.base_options = {
+ OptionKey(o) for o in [
+ 'b_colorout', 'b_sanitize', 'b_staticpic', 'b_vscrt',
+ 'b_coverage', 'b_pgo', 'b_ndebug']}
+
+ self._has_color_support = version_compare(self.version, '>=4.9')
+ # dependencies were implemented before, but broken - support was fixed in GCC 7.1+
+ # (and some backported versions)
+ self._has_deps_support = version_compare(self.version, '>=7.1')
+
+ def get_colorout_args(self, colortype: str) -> T.List[str]:
+ if self._has_color_support:
+ super().get_colorout_args(colortype)
+ return []
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ if self._has_deps_support:
+ return super().get_dependency_gen_args(outtarget, outfile)
+ return []
+
+ def get_warn_args(self, level: str) -> T.List[str]:
+ return self.warn_args[level]
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return d_gdc_buildtype_args[buildtype]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-I' or i[:2] == '-L':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+
+ return parameter_list
+
+ def get_allow_undefined_link_args(self) -> T.List[str]:
+ return self.linker.get_allow_undefined_args()
+
+ def get_linker_always_args(self) -> T.List[str]:
+ args = super().get_linker_always_args()
+ if self.info.is_windows():
+ return args
+ return args + ['-shared-libphobos']
+
+ def get_disable_assert_args(self) -> T.List[str]:
+ return ['-frelease']
+
+# LDC uses the DMD frontend code to parse and analyse the code.
+# It then uses LLVM for the binary code generation and optimizations.
+# This function retrieves the dmd frontend version, which determines
+# the common features between LDC and DMD.
+# We need the complete version text because the match is not on first line
+# of version_output
+def find_ldc_dmd_frontend_version(version_output: T.Optional[str]) -> T.Optional[str]:
+ if version_output is None:
+ return None
+ version_regex = re.search(r'DMD v(\d+\.\d+\.\d+)', version_output)
+ if version_regex:
+ return version_regex.group(1)
+ return None
+
+class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler):
+
+ id = 'llvm'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ info: 'MachineInfo', arch: str, *,
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None,
+ is_cross: bool = False, version_output: T.Optional[str] = None):
+ DCompiler.__init__(self, exelist, version, for_machine, info, arch,
+ exe_wrapper=exe_wrapper, linker=linker,
+ full_version=full_version, is_cross=is_cross)
+ DmdLikeCompilerMixin.__init__(self, dmd_frontend_version=find_ldc_dmd_frontend_version(version_output))
+ self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']}
+
+ def get_colorout_args(self, colortype: str) -> T.List[str]:
+ if colortype == 'always':
+ return ['-enable-color']
+ return []
+
+ def get_warn_args(self, level: str) -> T.List[str]:
+ if level in {'2', '3'}:
+ return ['-wi', '-dw']
+ elif level == '1':
+ return ['-wi']
+ return []
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ if buildtype != 'plain':
+ return self._get_target_arch_args() + d_ldc_buildtype_args[buildtype]
+ return d_ldc_buildtype_args[buildtype]
+
+ def get_pic_args(self) -> T.List[str]:
+ return ['-relocation-model=pic']
+
+ def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ return self._get_crt_args(crt_val, buildtype)
+
+ def unix_args_to_native(self, args: T.List[str]) -> T.List[str]:
+ return self._unix_args_to_native(args, self.info, self.linker.id)
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return ldc_optimization_args[optimization_level]
+
+ @classmethod
+ def use_linker_args(cls, linker: str, version: str) -> T.List[str]:
+ return [f'-linker={linker}']
+
+ def get_linker_always_args(self) -> T.List[str]:
+ args = super().get_linker_always_args()
+ if self.info.is_windows():
+ return args
+ return args + ['-link-defaultlib-shared']
+
+ def get_disable_assert_args(self) -> T.List[str]:
+ return ['--release']
+
+ def rsp_file_syntax(self) -> RSPFileSyntax:
+ # We use `mesonlib.is_windows` here because we want to know what the
+ # build machine is, not the host machine. This really means we would
+ # have the Environment not the MachineInfo in the compiler.
+ return RSPFileSyntax.MSVC if is_windows() else RSPFileSyntax.GCC
+
+
+class DmdDCompiler(DmdLikeCompilerMixin, DCompiler):
+
+ id = 'dmd'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ info: 'MachineInfo', arch: str, *,
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None,
+ is_cross: bool = False):
+ DCompiler.__init__(self, exelist, version, for_machine, info, arch,
+ exe_wrapper=exe_wrapper, linker=linker,
+ full_version=full_version, is_cross=is_cross)
+ DmdLikeCompilerMixin.__init__(self, version)
+ self.base_options = {OptionKey(o) for o in ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']}
+
+ def get_colorout_args(self, colortype: str) -> T.List[str]:
+ if colortype == 'always':
+ return ['-color=on']
+ return []
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ if buildtype != 'plain':
+ return self._get_target_arch_args() + d_dmd_buildtype_args[buildtype]
+ return d_dmd_buildtype_args[buildtype]
+
+ def get_std_exe_link_args(self) -> T.List[str]:
+ if self.info.is_windows():
+ # DMD links against D runtime only when main symbol is found,
+ # so these needs to be inserted when linking static D libraries.
+ if self.arch == 'x86_64':
+ return ['phobos64.lib']
+ elif self.arch == 'x86_mscoff':
+ return ['phobos32mscoff.lib']
+ return ['phobos.lib']
+ return []
+
+ def get_std_shared_lib_link_args(self) -> T.List[str]:
+ libname = 'libphobos2.so'
+ if self.info.is_windows():
+ if self.arch == 'x86_64':
+ libname = 'phobos64.lib'
+ elif self.arch == 'x86_mscoff':
+ libname = 'phobos32mscoff.lib'
+ else:
+ libname = 'phobos.lib'
+ return ['-shared', '-defaultlib=' + libname]
+
+ def _get_target_arch_args(self) -> T.List[str]:
+ # DMD32 and DMD64 on 64-bit Windows defaults to 32-bit (OMF).
+ # Force the target to 64-bit in order to stay consistent
+ # across the different platforms.
+ if self.info.is_windows():
+ if self.arch == 'x86_64':
+ return ['-m64']
+ elif self.arch == 'x86_mscoff':
+ return ['-m32mscoff']
+ return ['-m32']
+ return []
+
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ return self._get_crt_args(crt_val, buildtype)
+
+ def unix_args_to_native(self, args: T.List[str]) -> T.List[str]:
+ return self._unix_args_to_native(args, self.info, self.linker.id)
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return dmd_optimization_args[optimization_level]
+
+ def can_linker_accept_rsp(self) -> bool:
+ return False
+
+ def get_linker_always_args(self) -> T.List[str]:
+ args = super().get_linker_always_args()
+ if self.info.is_windows():
+ return args
+ return args + ['-defaultlib=phobos2', '-debuglib=phobos2']
+
+ def get_disable_assert_args(self) -> T.List[str]:
+ return ['-release']
+
+ def rsp_file_syntax(self) -> RSPFileSyntax:
+ return RSPFileSyntax.MSVC
diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py
new file mode 100644
index 0000000..1f37833
--- /dev/null
+++ b/mesonbuild/compilers/detect.py
@@ -0,0 +1,1317 @@
+# 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
+
+from ..mesonlib import (
+ MesonException, EnvironmentException, MachineChoice, join_args,
+ search_version, is_windows, Popen_safe, windows_proof_rm,
+)
+from ..envconfig import BinaryTable
+from .. import mlog
+
+from ..linkers import guess_win_linker, guess_nix_linker
+
+import subprocess
+import platform
+import re
+import shutil
+import tempfile
+import os
+import typing as T
+
+if T.TYPE_CHECKING:
+ from .compilers import Compiler
+ from .c import CCompiler
+ from .cpp import CPPCompiler
+ from .fortran import FortranCompiler
+ from .rust import RustCompiler
+ from ..linkers import StaticLinker
+ from ..environment import Environment
+ from ..programs import ExternalProgram
+
+
+# Default compilers and linkers
+# =============================
+
+defaults: T.Dict[str, T.List[str]] = {}
+
+# List of potential compilers.
+if is_windows():
+ # Intel C and C++ compiler is icl on Windows, but icc and icpc elsewhere.
+ # Search for icl before cl, since Intel "helpfully" provides a
+ # cl.exe that returns *exactly the same thing* that microsofts
+ # cl.exe does, and if icl is present, it's almost certainly what
+ # you want.
+ defaults['c'] = ['icl', 'cl', 'cc', 'gcc', 'clang', 'clang-cl', 'pgcc']
+ # There is currently no pgc++ for Windows, only for Mac and Linux.
+ defaults['cpp'] = ['icl', 'cl', 'c++', 'g++', 'clang++', 'clang-cl']
+ defaults['fortran'] = ['ifort', 'gfortran', 'flang', 'pgfortran', 'g95']
+ # Clang and clang++ are valid, but currently unsupported.
+ defaults['objc'] = ['cc', 'gcc']
+ defaults['objcpp'] = ['c++', 'g++']
+ defaults['cs'] = ['csc', 'mcs']
+else:
+ if platform.machine().lower() == 'e2k':
+ defaults['c'] = ['cc', 'gcc', 'lcc', 'clang']
+ defaults['cpp'] = ['c++', 'g++', 'l++', 'clang++']
+ defaults['objc'] = ['clang']
+ defaults['objcpp'] = ['clang++']
+ else:
+ defaults['c'] = ['cc', 'gcc', 'clang', 'nvc', 'pgcc', 'icc', 'icx']
+ defaults['cpp'] = ['c++', 'g++', 'clang++', 'nvc++', 'pgc++', 'icpc', 'icpx']
+ defaults['objc'] = ['cc', 'gcc', 'clang']
+ defaults['objcpp'] = ['c++', 'g++', 'clang++']
+ defaults['fortran'] = ['gfortran', 'flang', 'nvfortran', 'pgfortran', 'ifort', 'ifx', 'g95']
+ defaults['cs'] = ['mcs', 'csc']
+defaults['d'] = ['ldc2', 'ldc', 'gdc', 'dmd']
+defaults['java'] = ['javac']
+defaults['cuda'] = ['nvcc']
+defaults['rust'] = ['rustc']
+defaults['swift'] = ['swiftc']
+defaults['vala'] = ['valac']
+defaults['cython'] = ['cython', 'cython3'] # Official name is cython, but Debian renamed it to cython3.
+defaults['static_linker'] = ['ar', 'gar']
+defaults['strip'] = ['strip']
+defaults['vs_static_linker'] = ['lib']
+defaults['clang_cl_static_linker'] = ['llvm-lib']
+defaults['cuda_static_linker'] = ['nvlink']
+defaults['gcc_static_linker'] = ['gcc-ar']
+defaults['clang_static_linker'] = ['llvm-ar']
+defaults['nasm'] = ['nasm', 'yasm']
+
+
+def compiler_from_language(env: 'Environment', lang: str, for_machine: MachineChoice) -> T.Optional[Compiler]:
+ lang_map: T.Dict[str, T.Callable[['Environment', MachineChoice], Compiler]] = {
+ 'c': detect_c_compiler,
+ 'cpp': detect_cpp_compiler,
+ 'objc': detect_objc_compiler,
+ 'cuda': detect_cuda_compiler,
+ 'objcpp': detect_objcpp_compiler,
+ 'java': detect_java_compiler,
+ 'cs': detect_cs_compiler,
+ 'vala': detect_vala_compiler,
+ 'd': detect_d_compiler,
+ 'rust': detect_rust_compiler,
+ 'fortran': detect_fortran_compiler,
+ 'swift': detect_swift_compiler,
+ 'cython': detect_cython_compiler,
+ 'nasm': detect_nasm_compiler,
+ 'masm': detect_masm_compiler,
+ }
+ return lang_map[lang](env, for_machine) if lang in lang_map else None
+
+def detect_compiler_for(env: 'Environment', lang: str, for_machine: MachineChoice) -> T.Optional[Compiler]:
+ comp = compiler_from_language(env, lang, for_machine)
+ if comp is not None:
+ assert comp.for_machine == for_machine
+ env.coredata.process_new_compiler(lang, comp, env)
+ return comp
+
+
+# Helpers
+# =======
+
+def _get_compilers(env: 'Environment', lang: str, for_machine: MachineChoice) -> T.Tuple[T.List[T.List[str]], T.List[str], T.Optional['ExternalProgram']]:
+ '''
+ The list of compilers is detected in the exact same way for
+ C, C++, ObjC, ObjC++, Fortran, CS so consolidate it here.
+ '''
+ value = env.lookup_binary_entry(for_machine, lang)
+ if value is not None:
+ comp, ccache = BinaryTable.parse_entry(value)
+ # Return value has to be a list of compiler 'choices'
+ compilers = [comp]
+ else:
+ if not env.machines.matches_build_machine(for_machine):
+ raise EnvironmentException(f'{lang!r} compiler binary not defined in cross or native file')
+ compilers = [[x] for x in defaults[lang]]
+ ccache = BinaryTable.detect_compiler_cache()
+
+ if env.machines.matches_build_machine(for_machine):
+ exe_wrap: T.Optional[ExternalProgram] = None
+ else:
+ exe_wrap = env.get_exe_wrapper()
+
+ return compilers, ccache, exe_wrap
+
+def _handle_exceptions(
+ exceptions: T.Mapping[str, T.Union[Exception, str]],
+ binaries: T.List[T.List[str]],
+ bintype: str = 'compiler') -> T.NoReturn:
+ errmsg = f'Unknown {bintype}(s): {binaries}'
+ if exceptions:
+ errmsg += '\nThe following exception(s) were encountered:'
+ for c, e in exceptions.items():
+ errmsg += f'\nRunning `{c}` gave "{e}"'
+ raise EnvironmentException(errmsg)
+
+
+# Linker specific
+# ===============
+
+def detect_static_linker(env: 'Environment', compiler: Compiler) -> StaticLinker:
+ from . import d
+ from ..linkers import linkers
+ linker = env.lookup_binary_entry(compiler.for_machine, 'ar')
+ if linker is not None:
+ trials = [linker]
+ else:
+ default_linkers = [[l] for l in defaults['static_linker']]
+ if compiler.language == 'cuda':
+ trials = [defaults['cuda_static_linker']] + default_linkers
+ elif compiler.get_argument_syntax() == 'msvc':
+ trials = [defaults['vs_static_linker'], defaults['clang_cl_static_linker']]
+ elif compiler.id == 'gcc':
+ # Use gcc-ar if available; needed for LTO
+ trials = [defaults['gcc_static_linker']] + default_linkers
+ elif compiler.id == 'clang':
+ # Use llvm-ar if available; needed for LTO
+ trials = [defaults['clang_static_linker']] + default_linkers
+ elif compiler.language == 'd':
+ # Prefer static linkers over linkers used by D compilers
+ if is_windows():
+ trials = [defaults['vs_static_linker'], defaults['clang_cl_static_linker'], compiler.get_linker_exelist()]
+ else:
+ trials = default_linkers
+ elif compiler.id == 'intel-cl' and compiler.language == 'c': # why not cpp? Is this a bug?
+ # Intel has it's own linker that acts like microsoft's lib
+ trials = [['xilib']]
+ elif is_windows() and compiler.id == 'pgi': # this handles cpp / nvidia HPC, in addition to just c/fortran
+ trials = [['ar']] # For PGI on Windows, "ar" is just a wrapper calling link/lib.
+ else:
+ trials = default_linkers
+ popen_exceptions = {}
+ for linker in trials:
+ linker_name = os.path.basename(linker[0])
+
+ if any(os.path.basename(x) in {'lib', 'lib.exe', 'llvm-lib', 'llvm-lib.exe', 'xilib', 'xilib.exe'} for x in linker):
+ arg = '/?'
+ elif linker_name in {'ar2000', 'ar2000.exe', 'ar430', 'ar430.exe', 'armar', 'armar.exe'}:
+ arg = '?'
+ else:
+ arg = '--version'
+ try:
+ p, out, err = Popen_safe(linker + [arg])
+ except OSError as e:
+ popen_exceptions[join_args(linker + [arg])] = e
+ continue
+ if "xilib: executing 'lib'" in err:
+ return linkers.IntelVisualStudioLinker(linker, getattr(compiler, 'machine', None))
+ if '/OUT:' in out.upper() or '/OUT:' in err.upper():
+ return linkers.VisualStudioLinker(linker, getattr(compiler, 'machine', None))
+ if 'ar-Error-Unknown switch: --version' in err:
+ return linkers.PGIStaticLinker(linker)
+ if p.returncode == 0 and 'armar' in linker_name:
+ return linkers.ArmarLinker(linker)
+ if 'DMD32 D Compiler' in out or 'DMD64 D Compiler' in out:
+ assert isinstance(compiler, d.DCompiler)
+ return linkers.DLinker(linker, compiler.arch)
+ if 'LDC - the LLVM D compiler' in out:
+ assert isinstance(compiler, d.DCompiler)
+ return linkers.DLinker(linker, compiler.arch, rsp_syntax=compiler.rsp_file_syntax())
+ if 'GDC' in out and ' based on D ' in out:
+ assert isinstance(compiler, d.DCompiler)
+ return linkers.DLinker(linker, compiler.arch)
+ if err.startswith('Renesas') and 'rlink' in linker_name:
+ return linkers.CcrxLinker(linker)
+ if out.startswith('GNU ar') and 'xc16-ar' in linker_name:
+ return linkers.Xc16Linker(linker)
+ if 'Texas Instruments Incorporated' in out:
+ if 'ar2000' in linker_name:
+ return linkers.C2000Linker(linker)
+ else:
+ return linkers.TILinker(linker)
+ if out.startswith('The CompCert'):
+ return linkers.CompCertLinker(linker)
+ if p.returncode == 0:
+ return linkers.ArLinker(compiler.for_machine, linker)
+ if p.returncode == 1 and err.startswith('usage'): # OSX
+ return linkers.AppleArLinker(compiler.for_machine, linker)
+ if p.returncode == 1 and err.startswith('Usage'): # AIX
+ return linkers.AIXArLinker(linker)
+ if p.returncode == 1 and err.startswith('ar: bad option: --'): # Solaris
+ return linkers.ArLinker(compiler.for_machine, linker)
+ _handle_exceptions(popen_exceptions, trials, 'linker')
+ raise EnvironmentException('Unreachable code (exception to make mypy happy)')
+
+
+# Compilers
+# =========
+
+
+def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: MachineChoice, *, override_compiler: T.Optional[T.List[str]] = None) -> Compiler:
+ """Shared implementation for finding the C or C++ compiler to use.
+
+ the override_compiler option is provided to allow compilers which use
+ the compiler (GCC or Clang usually) as their shared linker, to find
+ the linker they need.
+ """
+ from . import c, cpp
+ from ..linkers import linkers
+ popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
+ compilers, ccache, exe_wrap = _get_compilers(env, lang, for_machine)
+ if override_compiler is not None:
+ compilers = [override_compiler]
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+ cls: T.Union[T.Type[CCompiler], T.Type[CPPCompiler]]
+
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ compiler_name = os.path.basename(compiler[0])
+
+ if any(os.path.basename(x) in {'cl', 'cl.exe', 'clang-cl', 'clang-cl.exe'} for x in compiler):
+ # Watcom C provides it's own cl.exe clone that mimics an older
+ # version of Microsoft's compiler. Since Watcom's cl.exe is
+ # just a wrapper, we skip using it if we detect its presence
+ # so as not to confuse Meson when configuring for MSVC.
+ #
+ # Additionally the help text of Watcom's cl.exe is paged, and
+ # the binary will not exit without human intervention. In
+ # practice, Meson will block waiting for Watcom's cl.exe to
+ # exit, which requires user input and thus will never exit.
+ if 'WATCOM' in os.environ:
+ def sanitize(p: str) -> str:
+ return os.path.normcase(os.path.abspath(p))
+
+ watcom_cls = [sanitize(os.path.join(os.environ['WATCOM'], 'BINNT', 'cl')),
+ sanitize(os.path.join(os.environ['WATCOM'], 'BINNT', 'cl.exe')),
+ sanitize(os.path.join(os.environ['WATCOM'], 'BINNT64', 'cl')),
+ sanitize(os.path.join(os.environ['WATCOM'], 'BINNT64', 'cl.exe'))]
+ found_cl = sanitize(shutil.which('cl'))
+ if found_cl in watcom_cls:
+ mlog.debug('Skipping unsupported cl.exe clone at:', found_cl)
+ continue
+ arg = '/?'
+ elif 'armcc' in compiler_name:
+ arg = '--vsn'
+ elif 'ccrx' in compiler_name:
+ arg = '-v'
+ elif 'xc16' in compiler_name:
+ arg = '--version'
+ elif 'ccomp' in compiler_name:
+ arg = '-version'
+ elif compiler_name in {'cl2000', 'cl2000.exe', 'cl430', 'cl430.exe', 'armcl', 'armcl.exe'}:
+ # TI compiler
+ arg = '-version'
+ elif compiler_name in {'icl', 'icl.exe'}:
+ # if you pass anything to icl you get stuck in a pager
+ arg = ''
+ else:
+ arg = '--version'
+
+ cmd = compiler + [arg]
+ try:
+ mlog.debug('-----')
+ mlog.debug(f'Detecting compiler via: {join_args(cmd)}')
+ p, out, err = Popen_safe(cmd)
+ mlog.debug(f'compiler returned {p}')
+ mlog.debug(f'compiler stdout:\n{out}')
+ mlog.debug(f'compiler stderr:\n{err}')
+ except OSError as e:
+ popen_exceptions[join_args(cmd)] = e
+ continue
+
+ if 'ccrx' in compiler_name:
+ out = err
+
+ full_version = out.split('\n', 1)[0]
+ version = search_version(out)
+
+ guess_gcc_or_lcc: T.Optional[str] = None
+ if 'Free Software Foundation' in out or 'xt-' in out:
+ guess_gcc_or_lcc = 'gcc'
+ if 'e2k' in out and 'lcc' in out:
+ guess_gcc_or_lcc = 'lcc'
+ if 'Microchip Technology' in out:
+ # this output has "Free Software Foundation" in its version
+ guess_gcc_or_lcc = None
+
+ if guess_gcc_or_lcc:
+ defines = _get_gnu_compiler_defines(compiler)
+ if not defines:
+ popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
+ continue
+
+ if guess_gcc_or_lcc == 'lcc':
+ version = _get_lcc_version_from_defines(defines)
+ cls = c.ElbrusCCompiler if lang == 'c' else cpp.ElbrusCPPCompiler
+ else:
+ version = _get_gnu_version_from_defines(defines)
+ cls = c.GnuCCompiler if lang == 'c' else cpp.GnuCPPCompiler
+
+ linker = guess_nix_linker(env, compiler, cls, version, for_machine)
+
+ return cls(
+ ccache, compiler, version, for_machine, is_cross,
+ info, exe_wrap, defines=defines, full_version=full_version,
+ linker=linker)
+
+ if 'Emscripten' in out:
+ cls = c.EmscriptenCCompiler if lang == 'c' else cpp.EmscriptenCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+
+ # emcc requires a file input in order to pass arguments to the
+ # linker. It'll exit with an error code, but still print the
+ # linker version.
+ with tempfile.NamedTemporaryFile(suffix='.c') as f:
+ cmd = compiler + [cls.LINKER_PREFIX + "--version", f.name]
+ _, o, _ = Popen_safe(cmd)
+
+ linker = linkers.WASMDynamicLinker(
+ compiler, for_machine, cls.LINKER_PREFIX,
+ [], version=search_version(o))
+ return cls(
+ ccache, compiler, version, for_machine, is_cross, info,
+ exe_wrap, linker=linker, full_version=full_version)
+
+ if 'Arm C/C++/Fortran Compiler' in out:
+ arm_ver_match = re.search(r'version (\d+)\.(\d+)\.?(\d+)? \(build number (\d+)\)', out)
+ assert arm_ver_match is not None, 'for mypy' # because mypy *should* be complaning that this could be None
+ version = '.'.join([x for x in arm_ver_match.groups() if x is not None])
+ if lang == 'c':
+ cls = c.ArmLtdClangCCompiler
+ elif lang == 'cpp':
+ cls = cpp.ArmLtdClangCPPCompiler
+ linker = guess_nix_linker(env, compiler, cls, version, for_machine)
+ return cls(
+ ccache, compiler, version, for_machine, is_cross, info,
+ exe_wrap, linker=linker)
+ if 'armclang' in out:
+ # The compiler version is not present in the first line of output,
+ # instead it is present in second line, startswith 'Component:'.
+ # So, searching for the 'Component' in out although we know it is
+ # present in second line, as we are not sure about the
+ # output format in future versions
+ arm_ver_match = re.search('.*Component.*', out)
+ if arm_ver_match is None:
+ popen_exceptions[join_args(compiler)] = 'version string not found'
+ continue
+ arm_ver_str = arm_ver_match.group(0)
+ # Override previous values
+ version = search_version(arm_ver_str)
+ full_version = arm_ver_str
+ cls = c.ArmclangCCompiler if lang == 'c' else cpp.ArmclangCPPCompiler
+ linker = linkers.ArmClangDynamicLinker(for_machine, version=version)
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ return cls(
+ ccache, compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+ if 'CL.EXE COMPATIBILITY' in out:
+ # if this is clang-cl masquerading as cl, detect it as cl, not
+ # clang
+ arg = '--version'
+ try:
+ p, out, err = Popen_safe(compiler + [arg])
+ except OSError as e:
+ popen_exceptions[join_args(compiler + [arg])] = e
+ version = search_version(out)
+ match = re.search('^Target: (.*?)-', out, re.MULTILINE)
+ if match:
+ target = match.group(1)
+ else:
+ target = 'unknown target'
+ cls = c.ClangClCCompiler if lang == 'c' else cpp.ClangClCPPCompiler
+ linker = guess_win_linker(env, ['lld-link'], cls, version, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info, target,
+ exe_wrap, linker=linker)
+ if 'clang' in out or 'Clang' in out:
+ linker = None
+
+ defines = _get_clang_compiler_defines(compiler)
+
+ # Even if the for_machine is darwin, we could be using vanilla
+ # clang.
+ if 'Apple' in out:
+ cls = c.AppleClangCCompiler if lang == 'c' else cpp.AppleClangCPPCompiler
+ else:
+ cls = c.ClangCCompiler if lang == 'c' else cpp.ClangCPPCompiler
+
+ if 'windows' in out or env.machines[for_machine].is_windows():
+ # If we're in a MINGW context this actually will use a gnu
+ # style ld, but for clang on "real" windows we'll use
+ # either link.exe or lld-link.exe
+ try:
+ linker = guess_win_linker(env, compiler, cls, version, for_machine, invoked_directly=False)
+ except MesonException:
+ pass
+ if linker is None:
+ linker = guess_nix_linker(env, compiler, cls, version, for_machine)
+
+ return cls(
+ ccache, compiler, version, for_machine, is_cross, info,
+ exe_wrap, defines=defines, full_version=full_version, linker=linker)
+
+ if 'Intel(R) C++ Intel(R)' in err:
+ version = search_version(err)
+ target = 'x86' if 'IA-32' in err else 'x86_64'
+ cls = c.IntelClCCompiler if lang == 'c' else cpp.IntelClCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info, target,
+ exe_wrap, linker=linker)
+ if 'Intel(R) oneAPI DPC++/C++ Compiler for applications' in err:
+ version = search_version(err)
+ target = 'x86' if 'IA-32' in err else 'x86_64'
+ cls = c.IntelLLVMClCCompiler if lang == 'c' else cpp.IntelLLVMClCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info, target,
+ exe_wrap, linker=linker)
+ if 'Microsoft' in out or 'Microsoft' in err:
+ # Latest versions of Visual Studio print version
+ # number to stderr but earlier ones print version
+ # on stdout. Why? Lord only knows.
+ # Check both outputs to figure out version.
+ for lookat in [err, out]:
+ version = search_version(lookat)
+ if version != 'unknown version':
+ break
+ else:
+ raise EnvironmentException(f'Failed to detect MSVC compiler version: stderr was\n{err!r}')
+ cl_signature = lookat.split('\n', maxsplit=1)[0]
+ match = re.search(r'.*(x86|x64|ARM|ARM64)([^_A-Za-z0-9]|$)', cl_signature)
+ if match:
+ target = match.group(1)
+ else:
+ m = f'Failed to detect MSVC compiler target architecture: \'cl /?\' output is\n{cl_signature}'
+ raise EnvironmentException(m)
+ cls = c.VisualStudioCCompiler if lang == 'c' else cpp.VisualStudioCPPCompiler
+ linker = guess_win_linker(env, ['link'], cls, version, for_machine)
+ # As of this writing, CCache does not support MSVC but sccache does.
+ if 'sccache' not in ccache:
+ ccache = []
+ return cls(
+ ccache, compiler, version, for_machine, is_cross, info, target,
+ exe_wrap, full_version=cl_signature, linker=linker)
+ if 'PGI Compilers' in out:
+ cls = c.PGICCompiler if lang == 'c' else cpp.PGICPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.PGIDynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
+ return cls(
+ ccache, compiler, version, for_machine, is_cross,
+ info, exe_wrap, linker=linker)
+ if 'NVIDIA Compilers and Tools' in out:
+ cls = c.NvidiaHPC_CCompiler if lang == 'c' else cpp.NvidiaHPC_CPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.NvidiaHPC_DynamicLinker(compiler, for_machine, cls.LINKER_PREFIX, [], version=version)
+ return cls(
+ ccache, compiler, version, for_machine, is_cross,
+ info, exe_wrap, linker=linker)
+ if '(ICC)' in out:
+ cls = c.IntelCCompiler if lang == 'c' else cpp.IntelCPPCompiler
+ l = guess_nix_linker(env, compiler, cls, version, for_machine)
+ return cls(
+ ccache, compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=l)
+ if 'Intel(R) oneAPI' in out:
+ cls = c.IntelLLVMCCompiler if lang == 'c' else cpp.IntelLLVMCPPCompiler
+ l = guess_nix_linker(env, compiler, cls, version, for_machine)
+ return cls(
+ ccache, compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=l)
+ if 'TMS320C2000 C/C++' in out or 'MSP430 C/C++' in out or 'TI ARM C/C++ Compiler' in out:
+ lnk: T.Union[T.Type[linkers.C2000DynamicLinker], T.Type[linkers.TIDynamicLinker]]
+ if 'TMS320C2000 C/C++' in out:
+ cls = c.C2000CCompiler if lang == 'c' else cpp.C2000CPPCompiler
+ lnk = linkers.C2000DynamicLinker
+ else:
+ cls = c.TICCompiler if lang == 'c' else cpp.TICPPCompiler
+ lnk = linkers.TIDynamicLinker
+
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = lnk(compiler, for_machine, version=version)
+ return cls(
+ ccache, compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+ if 'ARM' in out:
+ cls = c.ArmCCompiler if lang == 'c' else cpp.ArmCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.ArmDynamicLinker(for_machine, version=version)
+ return cls(
+ ccache, compiler, version, for_machine, is_cross,
+ info, exe_wrap, full_version=full_version, linker=linker)
+ if 'RX Family' in out:
+ cls = c.CcrxCCompiler if lang == 'c' else cpp.CcrxCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.CcrxDynamicLinker(for_machine, version=version)
+ return cls(
+ ccache, compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'Microchip Technology' in out:
+ cls = c.Xc16CCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.Xc16DynamicLinker(for_machine, version=version)
+ return cls(
+ ccache, compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'CompCert' in out:
+ cls = c.CompCertCCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.CompCertDynamicLinker(for_machine, version=version)
+ return cls(
+ ccache, compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ _handle_exceptions(popen_exceptions, compilers)
+ raise EnvironmentException(f'Unknown compiler {compilers}')
+
+def detect_c_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ return _detect_c_or_cpp_compiler(env, 'c', for_machine)
+
+def detect_cpp_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ return _detect_c_or_cpp_compiler(env, 'cpp', for_machine)
+
+def detect_cuda_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ from .cuda import CudaCompiler
+ from ..linkers.linkers import CudaLinker
+ popen_exceptions = {}
+ is_cross = env.is_cross_build(for_machine)
+ compilers, ccache, exe_wrap = _get_compilers(env, 'cuda', for_machine)
+ info = env.machines[for_machine]
+ for compiler in compilers:
+ arg = '--version'
+ try:
+ p, out, err = Popen_safe(compiler + [arg])
+ except OSError as e:
+ popen_exceptions[join_args(compiler + [arg])] = e
+ continue
+ # Example nvcc printout:
+ #
+ # nvcc: NVIDIA (R) Cuda compiler driver
+ # Copyright (c) 2005-2018 NVIDIA Corporation
+ # Built on Sat_Aug_25_21:08:01_CDT_2018
+ # Cuda compilation tools, release 10.0, V10.0.130
+ #
+ # search_version() first finds the "10.0" after "release",
+ # rather than the more precise "10.0.130" after "V".
+ # The patch version number is occasionally important; For
+ # instance, on Linux,
+ # - CUDA Toolkit 8.0.44 requires NVIDIA Driver 367.48
+ # - CUDA Toolkit 8.0.61 requires NVIDIA Driver 375.26
+ # Luckily, the "V" also makes it very simple to extract
+ # the full version:
+ version = out.strip().rsplit('V', maxsplit=1)[-1]
+ cpp_compiler = detect_cpp_compiler(env, for_machine)
+ cls = CudaCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = CudaLinker(compiler, for_machine, CudaCompiler.LINKER_PREFIX, [], version=CudaLinker.parse_version())
+ return cls(ccache, compiler, version, for_machine, is_cross, exe_wrap, host_compiler=cpp_compiler, info=info, linker=linker)
+ raise EnvironmentException(f'Could not find suitable CUDA compiler: "{"; ".join([" ".join(c) for c in compilers])}"')
+
+def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ from . import fortran
+ from ..linkers import linkers
+ popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
+ compilers, ccache, exe_wrap = _get_compilers(env, 'fortran', for_machine)
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+ cls: T.Type[FortranCompiler]
+ for compiler in compilers:
+ for arg in ['--version', '-V']:
+ try:
+ p, out, err = Popen_safe(compiler + [arg])
+ except OSError as e:
+ popen_exceptions[join_args(compiler + [arg])] = e
+ continue
+
+ version = search_version(out)
+ full_version = out.split('\n', 1)[0]
+
+ guess_gcc_or_lcc: T.Optional[str] = None
+ if 'GNU Fortran' in out:
+ guess_gcc_or_lcc = 'gcc'
+ if 'e2k' in out and 'lcc' in out:
+ guess_gcc_or_lcc = 'lcc'
+
+ if guess_gcc_or_lcc:
+ defines = _get_gnu_compiler_defines(compiler)
+ if not defines:
+ popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
+ continue
+ if guess_gcc_or_lcc == 'lcc':
+ version = _get_lcc_version_from_defines(defines)
+ cls = fortran.ElbrusFortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, version, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, defines, full_version=full_version, linker=linker)
+ else:
+ version = _get_gnu_version_from_defines(defines)
+ cls = fortran.GnuFortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, version, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, defines, full_version=full_version, linker=linker)
+
+ if 'Arm C/C++/Fortran Compiler' in out:
+ cls = fortran.ArmLtdFlangFortranCompiler
+ arm_ver_match = re.search(r'version (\d+)\.(\d+)\.?(\d+)? \(build number (\d+)\)', out)
+ assert arm_ver_match is not None, 'for mypy' # because mypy *should* be complaning that this could be None
+ version = '.'.join([x for x in arm_ver_match.groups() if x is not None])
+ linker = guess_nix_linker(env, compiler, cls, version, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, linker=linker)
+ if 'G95' in out:
+ cls = fortran.G95FortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, version, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'Sun Fortran' in err:
+ version = search_version(err)
+ cls = fortran.SunFortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, version, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'Intel(R) Fortran Compiler for applications' in err:
+ version = search_version(err)
+ target = 'x86' if 'IA-32' in err else 'x86_64'
+ cls = fortran.IntelLLVMClFortranCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ target, exe_wrap, linker=linker)
+
+ if 'Intel(R) Visual Fortran' in err or 'Intel(R) Fortran' in err:
+ version = search_version(err)
+ target = 'x86' if 'IA-32' in err else 'x86_64'
+ cls = fortran.IntelClFortranCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.XilinkDynamicLinker(for_machine, [], version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ target, exe_wrap, linker=linker)
+
+ if 'ifort (IFORT)' in out:
+ cls = fortran.IntelFortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, version, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'ifx (IFORT)' in out:
+ cls = fortran.IntelLLVMFortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, version, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'PathScale EKOPath(tm)' in err:
+ return fortran.PathScaleFortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version)
+
+ if 'PGI Compilers' in out:
+ cls = fortran.PGIFortranCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.PGIDynamicLinker(compiler, for_machine,
+ cls.LINKER_PREFIX, [], version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info, exe_wrap,
+ full_version=full_version, linker=linker)
+
+ if 'NVIDIA Compilers and Tools' in out:
+ cls = fortran.NvidiaHPC_FortranCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.PGIDynamicLinker(compiler, for_machine,
+ cls.LINKER_PREFIX, [], version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info, exe_wrap,
+ full_version=full_version, linker=linker)
+
+ if 'flang' in out or 'clang' in out:
+ cls = fortran.FlangFortranCompiler
+ linker = guess_nix_linker(env,
+ compiler, cls, version, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'Open64 Compiler Suite' in err:
+ cls = fortran.Open64FortranCompiler
+ linker = guess_nix_linker(env,
+ compiler, cls, version, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'NAG Fortran' in err:
+ full_version = err.split('\n', 1)[0]
+ version = full_version.split()[-1]
+ cls = fortran.NAGFortranCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = linkers.NAGDynamicLinker(
+ compiler, for_machine, cls.LINKER_PREFIX, [],
+ version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ _handle_exceptions(popen_exceptions, compilers)
+ raise EnvironmentException('Unreachable code (exception to make mypy happy)')
+
+def detect_objc_compiler(env: 'Environment', for_machine: MachineChoice) -> 'Compiler':
+ return _detect_objc_or_objcpp_compiler(env, 'objc', for_machine)
+
+def detect_objcpp_compiler(env: 'Environment', for_machine: MachineChoice) -> 'Compiler':
+ return _detect_objc_or_objcpp_compiler(env, 'objcpp', for_machine)
+
+def _detect_objc_or_objcpp_compiler(env: 'Environment', lang: str, for_machine: MachineChoice) -> 'Compiler':
+ from . import objc, objcpp
+ popen_exceptions: T.Dict[str, T.Union[Exception, str]] = {}
+ compilers, ccache, exe_wrap = _get_compilers(env, lang, for_machine)
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+ comp: T.Union[T.Type[objc.ObjCCompiler], T.Type[objcpp.ObjCPPCompiler]]
+
+ for compiler in compilers:
+ arg = ['--version']
+ try:
+ p, out, err = Popen_safe(compiler + arg)
+ except OSError as e:
+ popen_exceptions[join_args(compiler + arg)] = e
+ continue
+ version = search_version(out)
+ if 'Free Software Foundation' in out:
+ defines = _get_gnu_compiler_defines(compiler)
+ if not defines:
+ popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
+ continue
+ version = _get_gnu_version_from_defines(defines)
+ comp = objc.GnuObjCCompiler if lang == 'objc' else objcpp.GnuObjCPPCompiler
+ linker = guess_nix_linker(env, compiler, comp, version, for_machine)
+ return comp(
+ ccache, compiler, version, for_machine, is_cross, info,
+ exe_wrap, defines, linker=linker)
+ if 'clang' in out:
+ linker = None
+ defines = _get_clang_compiler_defines(compiler)
+ if not defines:
+ popen_exceptions[join_args(compiler)] = 'no pre-processor defines'
+ continue
+ if 'Apple' in out:
+ comp = objc.AppleClangObjCCompiler if lang == 'objc' else objcpp.AppleClangObjCPPCompiler
+ else:
+ comp = objc.ClangObjCCompiler if lang == 'objc' else objcpp.ClangObjCPPCompiler
+ if 'windows' in out or env.machines[for_machine].is_windows():
+ # If we're in a MINGW context this actually will use a gnu style ld
+ try:
+ linker = guess_win_linker(env, compiler, comp, version, for_machine)
+ except MesonException:
+ pass
+
+ if not linker:
+ linker = guess_nix_linker(env, compiler, comp, version, for_machine)
+ return comp(
+ ccache, compiler, version, for_machine,
+ is_cross, info, exe_wrap, linker=linker, defines=defines)
+ _handle_exceptions(popen_exceptions, compilers)
+ raise EnvironmentException('Unreachable code (exception to make mypy happy)')
+
+def detect_java_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ from .java import JavaCompiler
+ exelist = env.lookup_binary_entry(for_machine, 'java')
+ info = env.machines[for_machine]
+ if exelist is None:
+ # TODO support fallback
+ exelist = [defaults['java'][0]]
+
+ try:
+ p, out, err = Popen_safe(exelist + ['-version'])
+ except OSError:
+ raise EnvironmentException('Could not execute Java compiler: {}'.format(join_args(exelist)))
+ if 'javac' in out or 'javac' in err:
+ version = search_version(err if 'javac' in err else out)
+ if not version or version == 'unknown version':
+ parts = (err if 'javac' in err else out).split()
+ if len(parts) > 1:
+ version = parts[1]
+ comp_class = JavaCompiler
+ env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ return comp_class(exelist, version, for_machine, info)
+ raise EnvironmentException('Unknown compiler: ' + join_args(exelist))
+
+def detect_cs_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ from . import cs
+ compilers, ccache, exe_wrap = _get_compilers(env, 'cs', for_machine)
+ popen_exceptions = {}
+ info = env.machines[for_machine]
+ for comp in compilers:
+ try:
+ p, out, err = Popen_safe(comp + ['--version'])
+ except OSError as e:
+ popen_exceptions[join_args(comp + ['--version'])] = e
+ continue
+
+ version = search_version(out)
+ cls: T.Type[cs.CsCompiler]
+ if 'Mono' in out:
+ cls = cs.MonoCompiler
+ elif "Visual C#" in out:
+ cls = cs.VisualStudioCsCompiler
+ else:
+ continue
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ return cls(comp, version, for_machine, info)
+
+ _handle_exceptions(popen_exceptions, compilers)
+ raise EnvironmentException('Unreachable code (exception to make mypy happy)')
+
+def detect_cython_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ """Search for a cython compiler."""
+ from .cython import CythonCompiler
+ compilers, _, _ = _get_compilers(env, 'cython', MachineChoice.BUILD)
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+
+ popen_exceptions: T.Dict[str, Exception] = {}
+ for comp in compilers:
+ try:
+ err = Popen_safe(comp + ['-V'])[2]
+ except OSError as e:
+ popen_exceptions[join_args(comp + ['-V'])] = e
+ continue
+
+ version = search_version(err)
+ if 'Cython' in err:
+ comp_class = CythonCompiler
+ env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ return comp_class([], comp, version, for_machine, info, is_cross=is_cross)
+ _handle_exceptions(popen_exceptions, compilers)
+ raise EnvironmentException('Unreachable code (exception to make mypy happy)')
+
+def detect_vala_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ from .vala import ValaCompiler
+ exelist = env.lookup_binary_entry(MachineChoice.BUILD, 'vala')
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+ if exelist is None:
+ # TODO support fallback
+ exelist = [defaults['vala'][0]]
+
+ try:
+ p, out = Popen_safe(exelist + ['--version'])[0:2]
+ except OSError:
+ raise EnvironmentException('Could not execute Vala compiler: {}'.format(join_args(exelist)))
+ version = search_version(out)
+ if 'Vala' in out:
+ comp_class = ValaCompiler
+ env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ return comp_class(exelist, version, for_machine, is_cross, info)
+ raise EnvironmentException('Unknown compiler: ' + join_args(exelist))
+
+def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> RustCompiler:
+ from . import rust
+ from ..linkers import linkers
+ popen_exceptions = {} # type: T.Dict[str, Exception]
+ compilers, _, exe_wrap = _get_compilers(env, 'rust', for_machine)
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+
+ cc = detect_c_compiler(env, for_machine)
+ is_link_exe = isinstance(cc.linker, linkers.VisualStudioLikeLinkerMixin)
+ override = env.lookup_binary_entry(for_machine, 'rust_ld')
+
+ for compiler in compilers:
+ arg = ['--version']
+ try:
+ out = Popen_safe(compiler + arg)[1]
+ except OSError as e:
+ popen_exceptions[join_args(compiler + arg)] = e
+ continue
+
+ version = search_version(out)
+ cls: T.Type[RustCompiler] = rust.RustCompiler
+
+ # Clippy is a wrapper around rustc, but it doesn't have rustc in it's
+ # output. We can otherwise treat it as rustc.
+ if 'clippy' in out:
+ out = 'rustc'
+ cls = rust.ClippyRustCompiler
+
+ if 'rustc' in out:
+ # On Linux and mac rustc will invoke gcc (clang for mac
+ # presumably) and it can do this windows, for dynamic linking.
+ # this means the easiest way to C compiler for dynamic linking.
+ # figure out what linker to use is to just get the value of the
+ # C compiler and use that as the basis of the rust linker.
+ # However, there are two things we need to change, if CC is not
+ # the default use that, and second add the necessary arguments
+ # to rust to use -fuse-ld
+
+ if any(a.startswith('linker=') for a in compiler):
+ mlog.warning(
+ 'Please do not put -C linker= in your compiler '
+ 'command, set rust_ld=command in your cross file '
+ 'or use the RUST_LD environment variable, otherwise meson '
+ 'will override your selection.')
+
+ compiler = compiler.copy() # avoid mutating the original list
+
+ if override is None:
+ extra_args: T.Dict[str, T.Union[str, bool]] = {}
+ always_args: T.List[str] = []
+ if is_link_exe:
+ compiler.extend(cls.use_linker_args(cc.linker.exelist[0], ''))
+ extra_args['direct'] = True
+ extra_args['machine'] = cc.linker.machine
+ else:
+ exelist = cc.linker.exelist + cc.linker.get_always_args()
+ if 'ccache' in exelist[0]:
+ del exelist[0]
+ c = exelist.pop(0)
+ compiler.extend(cls.use_linker_args(c, ''))
+
+ # Also ensure that we pass any extra arguments to the linker
+ for l in exelist:
+ compiler.extend(['-C', f'link-arg={l}'])
+
+ # This trickery with type() gets us the class of the linker
+ # so we can initialize a new copy for the Rust Compiler
+ # TODO rewrite this without type: ignore
+ assert cc.linker is not None, 'for mypy'
+ if is_link_exe:
+ linker = type(cc.linker)(for_machine, always_args, exelist=cc.linker.exelist, # type: ignore
+ version=cc.linker.version, **extra_args) # type: ignore
+ else:
+ linker = type(cc.linker)(compiler, for_machine, cc.LINKER_PREFIX,
+ always_args=always_args, version=cc.linker.version,
+ **extra_args)
+ elif 'link' in override[0]:
+ linker = guess_win_linker(env,
+ override, cls, version, for_machine, use_linker_prefix=False)
+ # rustc takes linker arguments without a prefix, and
+ # inserts the correct prefix itself.
+ assert isinstance(linker, linkers.VisualStudioLikeLinkerMixin)
+ linker.direct = True
+ compiler.extend(cls.use_linker_args(linker.exelist[0], ''))
+ else:
+ # On linux and macos rust will invoke the c compiler for
+ # linking, on windows it will use lld-link or link.exe.
+ # we will simply ask for the C compiler that corresponds to
+ # it, and use that.
+ cc = _detect_c_or_cpp_compiler(env, 'c', for_machine, override_compiler=override)
+ linker = cc.linker
+
+ # Of course, we're not going to use any of that, we just
+ # need it to get the proper arguments to pass to rustc
+ c = linker.exelist[1] if linker.exelist[0].endswith('ccache') else linker.exelist[0]
+ compiler.extend(cls.use_linker_args(c, ''))
+
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ return cls(
+ compiler, version, for_machine, is_cross, info, exe_wrap,
+ linker=linker)
+
+ _handle_exceptions(popen_exceptions, compilers)
+ raise EnvironmentException('Unreachable code (exception to make mypy happy)')
+
+def detect_d_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ from . import c, d
+ info = env.machines[for_machine]
+
+ # Detect the target architecture, required for proper architecture handling on Windows.
+ # MSVC compiler is required for correct platform detection.
+ c_compiler = {'c': detect_c_compiler(env, for_machine)}
+ is_msvc = isinstance(c_compiler['c'], c.VisualStudioCCompiler)
+ if not is_msvc:
+ c_compiler = {}
+
+ # Import here to avoid circular imports
+ from ..environment import detect_cpu_family
+ arch = detect_cpu_family(c_compiler)
+ if is_msvc and arch == 'x86':
+ arch = 'x86_mscoff'
+
+ popen_exceptions = {}
+ is_cross = env.is_cross_build(for_machine)
+ compilers, ccache, exe_wrap = _get_compilers(env, 'd', for_machine)
+ cls: T.Type[d.DCompiler]
+ for exelist in compilers:
+ # Search for a D compiler.
+ # We prefer LDC over GDC unless overridden with the DC
+ # environment variable because LDC has a much more
+ # up to date language version at time (2016).
+ if os.path.basename(exelist[-1]).startswith(('ldmd', 'gdmd')):
+ raise EnvironmentException(
+ f'Meson does not support {exelist[-1]} as it is only a DMD frontend for another compiler.'
+ 'Please provide a valid value for DC or unset it so that Meson can resolve the compiler by itself.')
+ try:
+ p, out = Popen_safe(exelist + ['--version'])[0:2]
+ except OSError as e:
+ popen_exceptions[join_args(exelist + ['--version'])] = e
+ continue
+ version = search_version(out)
+ full_version = out.split('\n', 1)[0]
+
+ if 'LLVM D compiler' in out:
+ cls = d.LLVMDCompiler
+ # LDC seems to require a file
+ # We cannot use NamedTemproraryFile on windows, its documented
+ # to not work for our uses. So, just use mkstemp and only have
+ # one path for simplicity.
+ o, f = tempfile.mkstemp('.d')
+ os.close(o)
+
+ try:
+ if info.is_windows() or info.is_cygwin():
+ objfile = os.path.basename(f)[:-1] + 'obj'
+ linker = guess_win_linker(env,
+ exelist,
+ cls, full_version, for_machine,
+ use_linker_prefix=True, invoked_directly=False,
+ extra_args=[f])
+ else:
+ # LDC writes an object file to the current working directory.
+ # Clean it up.
+ objfile = os.path.basename(f)[:-1] + 'o'
+ linker = guess_nix_linker(env,
+ exelist, cls, full_version, for_machine,
+ extra_args=[f])
+ finally:
+ windows_proof_rm(f)
+ windows_proof_rm(objfile)
+
+ return cls(
+ exelist, version, for_machine, info, arch,
+ full_version=full_version, linker=linker, version_output=out)
+ elif 'gdc' in out:
+ cls = d.GnuDCompiler
+ linker = guess_nix_linker(env, exelist, cls, version, for_machine)
+ return cls(
+ exelist, version, for_machine, info, arch,
+ exe_wrapper=exe_wrap, is_cross=is_cross,
+ full_version=full_version, linker=linker)
+ elif 'The D Language Foundation' in out or 'Digital Mars' in out:
+ cls = d.DmdDCompiler
+ # DMD seems to require a file
+ # We cannot use NamedTemproraryFile on windows, its documented
+ # to not work for our uses. So, just use mkstemp and only have
+ # one path for simplicity.
+ o, f = tempfile.mkstemp('.d')
+ os.close(o)
+
+ # DMD as different detection logic for x86 and x86_64
+ arch_arg = '-m64' if arch == 'x86_64' else '-m32'
+
+ try:
+ if info.is_windows() or info.is_cygwin():
+ objfile = os.path.basename(f)[:-1] + 'obj'
+ linker = guess_win_linker(env,
+ exelist, cls, full_version, for_machine,
+ invoked_directly=False, extra_args=[f, arch_arg])
+ else:
+ objfile = os.path.basename(f)[:-1] + 'o'
+ linker = guess_nix_linker(env,
+ exelist, cls, full_version, for_machine,
+ extra_args=[f, arch_arg])
+ finally:
+ windows_proof_rm(f)
+ windows_proof_rm(objfile)
+
+ return cls(
+ exelist, version, for_machine, info, arch,
+ full_version=full_version, linker=linker)
+ raise EnvironmentException('Unknown compiler: ' + join_args(exelist))
+
+ _handle_exceptions(popen_exceptions, compilers)
+ raise EnvironmentException('Unreachable code (exception to make mypy happy)')
+
+def detect_swift_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ from .swift import SwiftCompiler
+ exelist = env.lookup_binary_entry(for_machine, 'swift')
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+ if exelist is None:
+ # TODO support fallback
+ exelist = [defaults['swift'][0]]
+
+ try:
+ p, _, err = Popen_safe(exelist + ['-v'])
+ except OSError:
+ raise EnvironmentException('Could not execute Swift compiler: {}'.format(join_args(exelist)))
+ version = search_version(err)
+ if 'Swift' in err:
+ # As for 5.0.1 swiftc *requires* a file to check the linker:
+ with tempfile.NamedTemporaryFile(suffix='.swift') as f:
+ cls = SwiftCompiler
+ linker = guess_nix_linker(env,
+ exelist, cls, version, for_machine,
+ extra_args=[f.name])
+ return cls(
+ exelist, version, for_machine, is_cross, info, linker=linker)
+
+ raise EnvironmentException('Unknown compiler: ' + join_args(exelist))
+
+def detect_nasm_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ from .asm import NasmCompiler, YasmCompiler
+ compilers, _, _ = _get_compilers(env, 'nasm', for_machine)
+ is_cross = env.is_cross_build(for_machine)
+
+ # We need a C compiler to properly detect the machine info and linker
+ cc = detect_c_compiler(env, for_machine)
+ if not is_cross:
+ from ..environment import detect_machine_info
+ info = detect_machine_info({'c': cc})
+ else:
+ info = env.machines[for_machine]
+
+ popen_exceptions: T.Dict[str, Exception] = {}
+ for comp in compilers:
+ if comp == ['nasm'] and is_windows() and not shutil.which(comp[0]):
+ # nasm is not in PATH on Windows by default
+ default_path = os.path.join(os.environ['ProgramFiles'], 'NASM')
+ comp[0] = shutil.which(comp[0], path=default_path) or comp[0]
+ try:
+ output = Popen_safe(comp + ['--version'])[1]
+ except OSError as e:
+ popen_exceptions[' '.join(comp + ['--version'])] = e
+ continue
+
+ version = search_version(output)
+ if 'NASM' in output:
+ comp_class = NasmCompiler
+ env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ return comp_class([], comp, version, for_machine, info, cc.linker, is_cross=is_cross)
+ elif 'yasm' in output:
+ comp_class = YasmCompiler
+ env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ return comp_class([], comp, version, for_machine, info, cc.linker, is_cross=is_cross)
+ _handle_exceptions(popen_exceptions, compilers)
+ raise EnvironmentException('Unreachable code (exception to make mypy happy)')
+
+def detect_masm_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler:
+ # We need a C compiler to properly detect the machine info and linker
+ is_cross = env.is_cross_build(for_machine)
+ cc = detect_c_compiler(env, for_machine)
+ if not is_cross:
+ from ..environment import detect_machine_info
+ info = detect_machine_info({'c': cc})
+ else:
+ info = env.machines[for_machine]
+
+ from .asm import MasmCompiler, MasmARMCompiler
+ comp_class: T.Type[Compiler]
+ if info.cpu_family == 'x86':
+ comp = ['ml']
+ comp_class = MasmCompiler
+ arg = '/?'
+ elif info.cpu_family == 'x86_64':
+ comp = ['ml64']
+ comp_class = MasmCompiler
+ arg = '/?'
+ elif info.cpu_family == 'arm':
+ comp = ['armasm']
+ comp_class = MasmARMCompiler
+ arg = '-h'
+ elif info.cpu_family == 'aarch64':
+ comp = ['armasm64']
+ comp_class = MasmARMCompiler
+ arg = '-h'
+ else:
+ raise EnvironmentException(f'Platform {info.cpu_family} not supported by MASM')
+
+ popen_exceptions: T.Dict[str, Exception] = {}
+ try:
+ output = Popen_safe(comp + [arg])[2]
+ version = search_version(output)
+ env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ return comp_class([], comp, version, for_machine, info, cc.linker, is_cross=is_cross)
+ except OSError as e:
+ popen_exceptions[' '.join(comp + [arg])] = e
+ _handle_exceptions(popen_exceptions, [comp])
+ raise EnvironmentException('Unreachable code (exception to make mypy happy)')
+
+# GNU/Clang defines and version
+# =============================
+
+def _get_gnu_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]:
+ """
+ Detect GNU compiler platform type (Apple, MinGW, Unix)
+ """
+ # Arguments to output compiler pre-processor defines to stdout
+ # gcc, g++, and gfortran all support these arguments
+ args = compiler + ['-E', '-dM', '-']
+ mlog.debug(f'Running command: {join_args(args)}')
+ p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
+ if p.returncode != 0:
+ raise EnvironmentException('Unable to detect GNU compiler type:\n'
+ f'Compiler stdout:\n{output}\n-----\n'
+ f'Compiler stderr:\n{error}\n-----\n')
+ # Parse several lines of the type:
+ # `#define ___SOME_DEF some_value`
+ # and extract `___SOME_DEF`
+ defines: T.Dict[str, str] = {}
+ for line in output.split('\n'):
+ if not line:
+ continue
+ d, *rest = line.split(' ', 2)
+ if d != '#define':
+ continue
+ if len(rest) == 1:
+ defines[rest[0]] = ''
+ if len(rest) == 2:
+ defines[rest[0]] = rest[1]
+ return defines
+
+def _get_clang_compiler_defines(compiler: T.List[str]) -> T.Dict[str, str]:
+ """
+ Get the list of Clang pre-processor defines
+ """
+ args = compiler + ['-E', '-dM', '-']
+ mlog.debug(f'Running command: {join_args(args)}')
+ p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
+ if p.returncode != 0:
+ raise EnvironmentException('Unable to get clang pre-processor defines:\n'
+ f'Compiler stdout:\n{output}\n-----\n'
+ f'Compiler stderr:\n{error}\n-----\n')
+ defines: T.Dict[str, str] = {}
+ for line in output.split('\n'):
+ if not line:
+ continue
+ d, *rest = line.split(' ', 2)
+ if d != '#define':
+ continue
+ if len(rest) == 1:
+ defines[rest[0]] = ''
+ if len(rest) == 2:
+ defines[rest[0]] = rest[1]
+ return defines
+
+def _get_gnu_version_from_defines(defines: T.Dict[str, str]) -> str:
+ dot = '.'
+ major = defines.get('__GNUC__', '0')
+ minor = defines.get('__GNUC_MINOR__', '0')
+ patch = defines.get('__GNUC_PATCHLEVEL__', '0')
+ return dot.join((major, minor, patch))
+
+def _get_lcc_version_from_defines(defines: T.Dict[str, str]) -> str:
+ dot = '.'
+ generation_and_major = defines.get('__LCC__', '100')
+ generation = generation_and_major[:1]
+ major = generation_and_major[1:]
+ minor = defines.get('__LCC_MINOR__', '0')
+ return dot.join((generation, major, minor))
diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py
new file mode 100644
index 0000000..90ca010
--- /dev/null
+++ b/mesonbuild/compilers/fortran.py
@@ -0,0 +1,546 @@
+# Copyright 2012-2017 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 typing as T
+import os
+
+from .. import coredata
+from .compilers import (
+ clike_debug_args,
+ Compiler,
+)
+from .mixins.clike import CLikeCompiler
+from .mixins.gnu import (
+ GnuCompiler, gnulike_buildtype_args, gnu_optimization_args
+)
+from .mixins.intel import IntelGnuLikeCompiler, IntelVisualStudioLikeCompiler
+from .mixins.clang import ClangCompiler
+from .mixins.elbrus import ElbrusCompiler
+from .mixins.pgi import PGICompiler
+
+from mesonbuild.mesonlib import (
+ version_compare, MesonException,
+ LibType, OptionKey,
+)
+
+if T.TYPE_CHECKING:
+ from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType
+ from ..dependencies import Dependency
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..linkers import DynamicLinker
+ from ..mesonlib import MachineChoice
+ from ..programs import ExternalProgram
+ from .compilers import CompileCheckMode
+
+
+class FortranCompiler(CLikeCompiler, Compiler):
+
+ language = 'fortran'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ Compiler.__init__(self, [], exelist, version, for_machine, info,
+ is_cross=is_cross, full_version=full_version, linker=linker)
+ CLikeCompiler.__init__(self, exe_wrapper)
+
+ 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]:
+ raise MesonException('Fortran does not have "has_function" capability.\n'
+ 'It is better to test if a Fortran capability is working like:\n\n'
+ "meson.get_compiler('fortran').links('block; end block; end program')\n\n"
+ 'that example is to see if the compiler has Fortran 2008 Block element.')
+
+ def _get_basic_compiler_args(self, env: 'Environment', mode: CompileCheckMode) -> T.Tuple[T.List[str], T.List[str]]:
+ cargs = env.coredata.get_external_args(self.for_machine, self.language)
+ largs = env.coredata.get_external_link_args(self.for_machine, self.language)
+ return cargs, largs
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ source_name = 'sanitycheckf.f90'
+ code = 'program main; print *, "Fortran compilation is working."; end program\n'
+ return self._sanity_check_impl(work_dir, environment, source_name, code)
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return gnulike_buildtype_args[buildtype]
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return gnu_optimization_args[optimization_level]
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return clike_debug_args[is_debug]
+
+ def get_preprocess_only_args(self) -> T.List[str]:
+ return ['-cpp'] + super().get_preprocess_only_args()
+
+ def get_module_incdir_args(self) -> T.Tuple[str, ...]:
+ return ('-I', )
+
+ def get_module_outdir_args(self, path: str) -> T.List[str]:
+ return ['-module', path]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-I' or i[:2] == '-L':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+
+ return parameter_list
+
+ def module_name_to_filename(self, module_name: str) -> str:
+ if '_' in module_name: # submodule
+ s = module_name.lower()
+ if self.id in {'gcc', 'intel', 'intel-cl'}:
+ filename = s.replace('_', '@') + '.smod'
+ elif self.id in {'pgi', 'flang'}:
+ filename = s.replace('_', '-') + '.mod'
+ else:
+ filename = s + '.mod'
+ else: # module
+ filename = module_name.lower() + '.mod'
+
+ return filename
+
+ def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
+ libtype: LibType = LibType.PREFER_SHARED) -> T.Optional[T.List[str]]:
+ code = 'stop; end program'
+ return self._find_library_impl(libname, env, extra_dirs, code, libtype)
+
+ def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ return self._has_multi_arguments(args, env, 'stop; end program')
+
+ def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ return self._has_multi_link_arguments(args, env, 'stop; end program')
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = super().get_options()
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts.update({
+ key: coredata.UserComboOption(
+ 'Fortran language standard to use',
+ ['none'],
+ 'none',
+ ),
+ })
+ return opts
+
+
+class GnuFortranCompiler(GnuCompiler, FortranCompiler):
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ FortranCompiler.__init__(self, exelist, version, for_machine,
+ is_cross, info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ GnuCompiler.__init__(self, defines)
+ default_warn_args = ['-Wall']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic', '-fimplicit-none'],
+ 'everything': default_warn_args + ['-Wextra', '-Wpedantic', '-fimplicit-none']}
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = FortranCompiler.get_options(self)
+ fortran_stds = ['legacy', 'f95', 'f2003']
+ if version_compare(self.version, '>=4.4.0'):
+ fortran_stds += ['f2008']
+ if version_compare(self.version, '>=8.0.0'):
+ fortran_stds += ['f2018']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none'] + fortran_stds
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ # Disabled until this is fixed:
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62162
+ # return ['-cpp', '-MD', '-MQ', outtarget]
+ return []
+
+ def get_module_outdir_args(self, path: str) -> T.List[str]:
+ return ['-J' + path]
+
+ def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ # We need to apply the search prefix here, as these link arguments may
+ # be passed to a different compiler with a different set of default
+ # search paths, such as when using Clang for C/C++ and gfortran for
+ # fortran,
+ search_dirs: T.List[str] = []
+ for d in self.get_compiler_dirs(env, 'libraries'):
+ search_dirs.append(f'-L{d}')
+ return search_dirs + ['-lgfortran', '-lm']
+
+ 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]:
+ '''
+ Derived from mixins/clike.py:has_header, but without C-style usage of
+ __has_include which breaks with GCC-Fortran 10:
+ https://github.com/mesonbuild/meson/issues/7017
+ '''
+ code = f'{prefix}\n#include <{hname}>'
+ return self.compiles(code, env, extra_args=extra_args,
+ dependencies=dependencies, mode='preprocess', disable_cache=disable_cache)
+
+
+class ElbrusFortranCompiler(ElbrusCompiler, FortranCompiler):
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ FortranCompiler.__init__(self, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ElbrusCompiler.__init__(self)
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = FortranCompiler.get_options(self)
+ fortran_stds = ['f95', 'f2003', 'f2008', 'gnu', 'legacy', 'f2008ts']
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none'] + fortran_stds
+ return opts
+
+ def get_module_outdir_args(self, path: str) -> T.List[str]:
+ return ['-J' + path]
+
+
+class G95FortranCompiler(FortranCompiler):
+
+ LINKER_PREFIX = '-Wl,'
+ id = 'g95'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ FortranCompiler.__init__(self, exelist, version, for_machine,
+ is_cross, info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ default_warn_args = ['-Wall']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-pedantic'],
+ 'everything': default_warn_args + ['-Wextra', '-pedantic']}
+
+ def get_module_outdir_args(self, path: str) -> T.List[str]:
+ return ['-fmod=' + path]
+
+ def get_no_warn_args(self) -> T.List[str]:
+ # FIXME: Confirm that there's no compiler option to disable all warnings
+ return []
+
+
+class SunFortranCompiler(FortranCompiler):
+
+ LINKER_PREFIX = '-Wl,'
+ id = 'sun'
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return ['-fpp']
+
+ def get_always_args(self) -> T.List[str]:
+ return []
+
+ def get_warn_args(self, level: str) -> T.List[str]:
+ return []
+
+ def get_module_incdir_args(self) -> T.Tuple[str, ...]:
+ return ('-M', )
+
+ def get_module_outdir_args(self, path: str) -> T.List[str]:
+ return ['-moddir=' + path]
+
+ def openmp_flags(self) -> T.List[str]:
+ return ['-xopenmp']
+
+
+class IntelFortranCompiler(IntelGnuLikeCompiler, FortranCompiler):
+
+ file_suffixes = ('f90', 'f', 'for', 'ftn', 'fpp', )
+ id = 'intel'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ FortranCompiler.__init__(self, exelist, version, for_machine,
+ is_cross, info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ # FIXME: Add support for OS X and Windows in detect_fortran_compiler so
+ # we are sent the type of compiler
+ IntelGnuLikeCompiler.__init__(self)
+ default_warn_args = ['-warn', 'general', '-warn', 'truncated_source']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-warn', 'unused'],
+ '3': ['-warn', 'all'],
+ 'everything': ['-warn', 'all']}
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = FortranCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'}
+ if std.value != 'none':
+ args.append('-stand=' + stds[std.value])
+ return args
+
+ def get_preprocess_only_args(self) -> T.List[str]:
+ return ['-cpp', '-EP']
+
+ def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ # TODO: needs default search path added
+ return ['-lifcore', '-limf']
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return ['-gen-dep=' + outtarget, '-gen-depformat=make']
+
+
+class IntelLLVMFortranCompiler(IntelFortranCompiler):
+
+ id = 'intel-llvm'
+
+
+class IntelClFortranCompiler(IntelVisualStudioLikeCompiler, FortranCompiler):
+
+ file_suffixes = ('f90', 'f', 'for', 'ftn', 'fpp', )
+ always_args = ['/nologo']
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo', target: str,
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ FortranCompiler.__init__(self, exelist, version, for_machine,
+ is_cross, info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ IntelVisualStudioLikeCompiler.__init__(self, target)
+
+ default_warn_args = ['/warn:general', '/warn:truncated_source']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['/warn:unused'],
+ '3': ['/warn:all'],
+ 'everything': ['/warn:all']}
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ opts = FortranCompiler.get_options(self)
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ opts[key].choices = ['none', 'legacy', 'f95', 'f2003', 'f2008', 'f2018']
+ return opts
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ stds = {'legacy': 'none', 'f95': 'f95', 'f2003': 'f03', 'f2008': 'f08', 'f2018': 'f18'}
+ if std.value != 'none':
+ args.append('/stand:' + stds[std.value])
+ return args
+
+ def get_module_outdir_args(self, path: str) -> T.List[str]:
+ return ['/module:' + path]
+
+
+class IntelLLVMClFortranCompiler(IntelClFortranCompiler):
+
+ id = 'intel-llvm-cl'
+
+class PathScaleFortranCompiler(FortranCompiler):
+
+ id = 'pathscale'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ FortranCompiler.__init__(self, exelist, version, for_machine,
+ is_cross, info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ default_warn_args = ['-fullwarn']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args,
+ '3': default_warn_args,
+ 'everything': default_warn_args}
+
+ def openmp_flags(self) -> T.List[str]:
+ return ['-mp']
+
+
+class PGIFortranCompiler(PGICompiler, FortranCompiler):
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ FortranCompiler.__init__(self, exelist, version, for_machine,
+ is_cross, info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ PGICompiler.__init__(self)
+
+ default_warn_args = ['-Minform=inform']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args,
+ '3': default_warn_args + ['-Mdclchk'],
+ 'everything': default_warn_args + ['-Mdclchk']}
+
+ def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ # TODO: needs default search path added
+ return ['-lpgf90rtl', '-lpgf90', '-lpgf90_rpm1', '-lpgf902',
+ '-lpgf90rtl', '-lpgftnrtl', '-lrt']
+
+
+class NvidiaHPC_FortranCompiler(PGICompiler, FortranCompiler):
+
+ id = 'nvidia_hpc'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ FortranCompiler.__init__(self, exelist, version, for_machine,
+ is_cross, info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ PGICompiler.__init__(self)
+
+ default_warn_args = ['-Minform=inform']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args,
+ '3': default_warn_args + ['-Mdclchk'],
+ 'everything': default_warn_args + ['-Mdclchk']}
+
+
+class FlangFortranCompiler(ClangCompiler, FortranCompiler):
+
+ id = 'flang'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ FortranCompiler.__init__(self, exelist, version, for_machine,
+ is_cross, info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ ClangCompiler.__init__(self, {})
+ default_warn_args = ['-Minform=inform']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args,
+ '3': default_warn_args,
+ 'everything': default_warn_args}
+
+ def language_stdlib_only_link_flags(self, env: 'Environment') -> T.List[str]:
+ # We need to apply the search prefix here, as these link arguments may
+ # be passed to a different compiler with a different set of default
+ # search paths, such as when using Clang for C/C++ and gfortran for
+ # fortran,
+ # XXX: Untested....
+ search_dirs: T.List[str] = []
+ for d in self.get_compiler_dirs(env, 'libraries'):
+ search_dirs.append(f'-L{d}')
+ return search_dirs + ['-lflang', '-lpgmath']
+
+class ArmLtdFlangFortranCompiler(FlangFortranCompiler):
+
+ id = 'armltdflang'
+
+class Open64FortranCompiler(FortranCompiler):
+
+ id = 'open64'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ FortranCompiler.__init__(self, exelist, version, for_machine,
+ is_cross, info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ default_warn_args = ['-fullwarn']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args,
+ '3': default_warn_args,
+ 'everything': default_warn_args}
+
+ def openmp_flags(self) -> T.List[str]:
+ return ['-mp']
+
+
+class NAGFortranCompiler(FortranCompiler):
+
+ id = 'nagfor'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, is_cross: bool,
+ info: 'MachineInfo', exe_wrapper: T.Optional['ExternalProgram'] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ FortranCompiler.__init__(self, exelist, version, for_machine,
+ is_cross, info, exe_wrapper, linker=linker,
+ full_version=full_version)
+ # Warnings are on by default; -w disables (by category):
+ self.warn_args = {
+ '0': ['-w=all'],
+ '1': [],
+ '2': [],
+ '3': [],
+ 'everything': [],
+ }
+
+ def get_always_args(self) -> T.List[str]:
+ return self.get_nagfor_quiet(self.version)
+
+ def get_module_outdir_args(self, path: str) -> T.List[str]:
+ return ['-mdir', path]
+
+ @staticmethod
+ def get_nagfor_quiet(version: str) -> T.List[str]:
+ return ['-quiet'] if version_compare(version, '>=7100') else []
+
+ def get_pic_args(self) -> T.List[str]:
+ return ['-PIC']
+
+ def get_preprocess_only_args(self) -> T.List[str]:
+ return ['-fpp']
+
+ def get_std_exe_link_args(self) -> T.List[str]:
+ return self.get_always_args()
+
+ def openmp_flags(self) -> T.List[str]:
+ return ['-openmp']
diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py
new file mode 100644
index 0000000..ebae509
--- /dev/null
+++ b/mesonbuild/compilers/java.py
@@ -0,0 +1,125 @@
+# Copyright 2012-2017 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 os
+import os.path
+import shutil
+import subprocess
+import textwrap
+import typing as T
+
+from ..mesonlib import EnvironmentException
+from .compilers import Compiler, java_buildtype_args
+from .mixins.islinker import BasicLinkerIsCompilerMixin
+
+if T.TYPE_CHECKING:
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..mesonlib import MachineChoice
+
+class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler):
+
+ language = 'java'
+ id = 'unknown'
+
+ _WARNING_LEVELS: T.Dict[str, T.List[str]] = {
+ '0': ['-nowarn'],
+ '1': ['-Xlint:all'],
+ '2': ['-Xlint:all', '-Xdoclint:all'],
+ '3': ['-Xlint:all', '-Xdoclint:all'],
+ }
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ info: 'MachineInfo', full_version: T.Optional[str] = None):
+ super().__init__([], exelist, version, for_machine, info, full_version=full_version)
+ self.javarunner = 'java'
+
+ def get_warn_args(self, level: str) -> T.List[str]:
+ return self._WARNING_LEVELS[level]
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['-Werror']
+
+ def get_no_warn_args(self) -> T.List[str]:
+ return ['-nowarn']
+
+ def get_output_args(self, outputname: str) -> T.List[str]:
+ if outputname == '':
+ outputname = './'
+ return ['-d', outputname, '-s', outputname]
+
+ def get_pic_args(self) -> T.List[str]:
+ return []
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ return []
+
+ def get_pch_name(self, name: str) -> str:
+ return ''
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return java_buildtype_args[buildtype]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i in {'-cp', '-classpath', '-sourcepath'} and idx + 1 < len(parameter_list):
+ path_list = parameter_list[idx + 1].split(os.pathsep)
+ path_list = [os.path.normpath(os.path.join(build_dir, x)) for x in path_list]
+ parameter_list[idx + 1] = os.pathsep.join(path_list)
+
+ return parameter_list
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ src = 'SanityCheck.java'
+ obj = 'SanityCheck'
+ source_name = os.path.join(work_dir, src)
+ with open(source_name, 'w', encoding='utf-8') as ofile:
+ ofile.write(textwrap.dedent(
+ '''class SanityCheck {
+ public static void main(String[] args) {
+ int i;
+ }
+ }
+ '''))
+ pc = subprocess.Popen(self.exelist + [src], cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException(f'Java compiler {self.name_string()} can not compile programs.')
+ runner = shutil.which(self.javarunner)
+ if runner:
+ cmdlist = [runner, obj]
+ pe = subprocess.Popen(cmdlist, cwd=work_dir)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException(f'Executables created by Java compiler {self.name_string()} are not runnable.')
+ else:
+ m = "Java Virtual Machine wasn't found, but it's needed by Meson. " \
+ "Please install a JRE.\nIf you have specific needs where this " \
+ "requirement doesn't make sense, please open a bug at " \
+ "https://github.com/mesonbuild/meson/issues/new and tell us " \
+ "all about it."
+ raise EnvironmentException(m)
+
+ def needs_static_linker(self) -> bool:
+ return False
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return []
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ if is_debug:
+ return ['-g']
+ return ['-g:none']
diff --git a/mesonbuild/compilers/mixins/__init__.py b/mesonbuild/compilers/mixins/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mesonbuild/compilers/mixins/__init__.py
diff --git a/mesonbuild/compilers/mixins/arm.py b/mesonbuild/compilers/mixins/arm.py
new file mode 100644
index 0000000..5d0fb87
--- /dev/null
+++ b/mesonbuild/compilers/mixins/arm.py
@@ -0,0 +1,199 @@
+# Copyright 2012-2020 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
+
+"""Representations specific to the arm family of compilers."""
+
+import os
+import typing as T
+
+from ... import mesonlib
+from ...linkers import ArmClangDynamicLinker
+from ...mesonlib import OptionKey
+from ..compilers import clike_debug_args
+from .clang import clang_color_args
+
+if T.TYPE_CHECKING:
+ from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
+arm_buildtype_args = {
+ 'plain': [],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+} # type: T.Dict[str, T.List[str]]
+
+arm_optimization_args = {
+ 'plain': [],
+ '0': ['-O0'],
+ 'g': ['-g'],
+ '1': ['-O1'],
+ '2': [], # Compiler defaults to -O2
+ '3': ['-O3', '-Otime'],
+ 's': ['-O3'], # Compiler defaults to -Ospace
+} # type: T.Dict[str, T.List[str]]
+
+armclang_buildtype_args = {
+ 'plain': [],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+} # type: T.Dict[str, T.List[str]]
+
+armclang_optimization_args = {
+ 'plain': [],
+ '0': [], # Compiler defaults to -O0
+ 'g': ['-g'],
+ '1': ['-O1'],
+ '2': ['-O2'],
+ '3': ['-O3'],
+ 's': ['-Oz']
+} # type: T.Dict[str, T.List[str]]
+
+
+class ArmCompiler(Compiler):
+
+ """Functionality that is common to all ARM family compilers."""
+
+ id = 'arm'
+
+ def __init__(self) -> None:
+ if not self.is_cross:
+ raise mesonlib.EnvironmentException('armcc supports only cross-compilation.')
+ default_warn_args = [] # type: T.List[str]
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + [],
+ '3': default_warn_args + [],
+ 'everything': default_warn_args + []} # type: T.Dict[str, T.List[str]]
+ # Assembly
+ self.can_compile_suffixes.add('s')
+
+ def get_pic_args(self) -> T.List[str]:
+ # FIXME: Add /ropi, /rwpi, /fpic etc. qualifiers to --apcs
+ return []
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return arm_buildtype_args[buildtype]
+
+ # Override CCompiler.get_always_args
+ def get_always_args(self) -> T.List[str]:
+ return []
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return ['--depend_target', outtarget, '--depend', outfile, '--depend_single_line']
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ # FIXME: Add required arguments
+ # NOTE from armcc user guide:
+ # "Support for Precompiled Header (PCH) files is deprecated from ARM Compiler 5.05
+ # onwards on all platforms. Note that ARM Compiler on Windows 8 never supported
+ # PCH files."
+ return []
+
+ def get_pch_suffix(self) -> str:
+ # NOTE from armcc user guide:
+ # "Support for Precompiled Header (PCH) files is deprecated from ARM Compiler 5.05
+ # onwards on all platforms. Note that ARM Compiler on Windows 8 never supported
+ # PCH files."
+ return 'pch'
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ return []
+
+ def get_coverage_args(self) -> T.List[str]:
+ return []
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return arm_optimization_args[optimization_level]
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return clike_debug_args[is_debug]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-I' or i[:2] == '-L':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+
+ return parameter_list
+
+
+class ArmclangCompiler(Compiler):
+ '''
+ This is the Keil armclang.
+ '''
+
+ id = 'armclang'
+
+ def __init__(self) -> None:
+ if not self.is_cross:
+ raise mesonlib.EnvironmentException('armclang supports only cross-compilation.')
+ # Check whether 'armlink' is available in path
+ if not isinstance(self.linker, ArmClangDynamicLinker):
+ raise mesonlib.EnvironmentException(f'Unsupported Linker {self.linker.exelist}, must be armlink')
+ if not mesonlib.version_compare(self.version, '==' + self.linker.version):
+ raise mesonlib.EnvironmentException('armlink version does not match with compiler version')
+ self.base_options = {
+ OptionKey(o) for o in
+ ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage',
+ 'b_ndebug', 'b_staticpic', 'b_colorout']}
+ # Assembly
+ self.can_compile_suffixes.add('s')
+
+ def get_pic_args(self) -> T.List[str]:
+ # PIC support is not enabled by default for ARM,
+ # if users want to use it, they need to add the required arguments explicitly
+ return []
+
+ def get_colorout_args(self, colortype: str) -> T.List[str]:
+ return clang_color_args[colortype][:]
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return armclang_buildtype_args[buildtype]
+
+ def get_pch_suffix(self) -> str:
+ return 'gch'
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ # Workaround for Clang bug http://llvm.org/bugs/show_bug.cgi?id=15136
+ # This flag is internal to Clang (or at least not documented on the man page)
+ # so it might change semantics at any time.
+ return ['-include-pch', os.path.join(pch_dir, self.get_pch_name(header))]
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return ['-MD', '-MT', outtarget, '-MF', outfile]
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return armclang_optimization_args[optimization_level]
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return clike_debug_args[is_debug]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-I' or i[:2] == '-L':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+
+ return parameter_list
diff --git a/mesonbuild/compilers/mixins/ccrx.py b/mesonbuild/compilers/mixins/ccrx.py
new file mode 100644
index 0000000..1c22214
--- /dev/null
+++ b/mesonbuild/compilers/mixins/ccrx.py
@@ -0,0 +1,134 @@
+# Copyright 2012-2019 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
+
+"""Representations specific to the Renesas CC-RX compiler family."""
+
+import os
+import typing as T
+
+from ...mesonlib import EnvironmentException
+
+if T.TYPE_CHECKING:
+ from ...envconfig import MachineInfo
+ from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
+ccrx_buildtype_args = {
+ 'plain': [],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+} # type: T.Dict[str, T.List[str]]
+
+ccrx_optimization_args = {
+ '0': ['-optimize=0'],
+ 'g': ['-optimize=0'],
+ '1': ['-optimize=1'],
+ '2': ['-optimize=2'],
+ '3': ['-optimize=max'],
+ 's': ['-optimize=2', '-size']
+} # type: T.Dict[str, T.List[str]]
+
+ccrx_debug_args = {
+ False: [],
+ True: ['-debug']
+} # type: T.Dict[bool, T.List[str]]
+
+
+class CcrxCompiler(Compiler):
+
+ if T.TYPE_CHECKING:
+ is_cross = True
+ can_compile_suffixes = set() # type: T.Set[str]
+
+ id = 'ccrx'
+
+ def __init__(self) -> None:
+ if not self.is_cross:
+ raise EnvironmentException('ccrx supports only cross-compilation.')
+ # Assembly
+ self.can_compile_suffixes.add('src')
+ default_warn_args = [] # type: T.List[str]
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + [],
+ '3': default_warn_args + [],
+ 'everything': default_warn_args + []} # type: T.Dict[str, T.List[str]]
+
+ def get_pic_args(self) -> T.List[str]:
+ # PIC support is not enabled by default for CCRX,
+ # if users want to use it, they need to add the required arguments explicitly
+ return []
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return ccrx_buildtype_args[buildtype]
+
+ def get_pch_suffix(self) -> str:
+ return 'pch'
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ return []
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ return []
+
+ def get_coverage_args(self) -> T.List[str]:
+ return []
+
+ def get_no_stdinc_args(self) -> T.List[str]:
+ return []
+
+ def get_no_stdlib_link_args(self) -> T.List[str]:
+ return []
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return ccrx_optimization_args[optimization_level]
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return ccrx_debug_args[is_debug]
+
+ @classmethod
+ def _unix_args_to_native(cls, args: T.List[str], info: MachineInfo) -> T.List[str]:
+ result = []
+ for i in args:
+ if i.startswith('-D'):
+ i = '-define=' + i[2:]
+ if i.startswith('-I'):
+ i = '-include=' + i[2:]
+ if i.startswith('-Wl,-rpath='):
+ continue
+ elif i == '--print-search-dirs':
+ continue
+ elif i.startswith('-L'):
+ continue
+ elif not i.startswith('-lib=') and i.endswith(('.a', '.lib')):
+ i = '-lib=' + i
+ result.append(i)
+ return result
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:9] == '-include=':
+ parameter_list[idx] = i[:9] + os.path.normpath(os.path.join(build_dir, i[9:]))
+
+ return parameter_list
diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py
new file mode 100644
index 0000000..cdb4c23
--- /dev/null
+++ b/mesonbuild/compilers/mixins/clang.py
@@ -0,0 +1,180 @@
+# Copyright 2019-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
+
+"""Abstractions for the LLVM/Clang compiler family."""
+
+import os
+import shutil
+import typing as T
+
+from ... import mesonlib
+from ...linkers import AppleDynamicLinker, ClangClDynamicLinker, LLVMDynamicLinker, GnuGoldDynamicLinker, \
+ MoldDynamicLinker
+from ...mesonlib import OptionKey
+from ..compilers import CompileCheckMode
+from .gnu import GnuLikeCompiler
+
+if T.TYPE_CHECKING:
+ from ...environment import Environment
+ from ...dependencies import Dependency # noqa: F401
+
+clang_color_args = {
+ 'auto': ['-fcolor-diagnostics'],
+ 'always': ['-fcolor-diagnostics'],
+ 'never': ['-fno-color-diagnostics'],
+} # type: T.Dict[str, T.List[str]]
+
+clang_optimization_args = {
+ 'plain': [],
+ '0': ['-O0'],
+ 'g': ['-Og'],
+ '1': ['-O1'],
+ '2': ['-O2'],
+ '3': ['-O3'],
+ 's': ['-Oz'],
+} # type: T.Dict[str, T.List[str]]
+
+class ClangCompiler(GnuLikeCompiler):
+
+ id = 'clang'
+
+ def __init__(self, defines: T.Optional[T.Dict[str, str]]):
+ super().__init__()
+ self.defines = defines or {}
+ self.base_options.update(
+ {OptionKey('b_colorout'), OptionKey('b_lto_threads'), OptionKey('b_lto_mode'), OptionKey('b_thinlto_cache'),
+ OptionKey('b_thinlto_cache_dir')})
+
+ # TODO: this really should be part of the linker base_options, but
+ # linkers don't have base_options.
+ if isinstance(self.linker, AppleDynamicLinker):
+ self.base_options.add(OptionKey('b_bitcode'))
+ # All Clang backends can also do LLVM IR
+ self.can_compile_suffixes.add('ll')
+
+ def get_colorout_args(self, colortype: str) -> T.List[str]:
+ return clang_color_args[colortype][:]
+
+ def has_builtin_define(self, define: str) -> bool:
+ return define in self.defines
+
+ def get_builtin_define(self, define: str) -> T.Optional[str]:
+ return self.defines.get(define)
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return clang_optimization_args[optimization_level]
+
+ def get_pch_suffix(self) -> str:
+ return 'pch'
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ # Workaround for Clang bug http://llvm.org/bugs/show_bug.cgi?id=15136
+ # This flag is internal to Clang (or at least not documented on the man page)
+ # so it might change semantics at any time.
+ return ['-include-pch', os.path.join(pch_dir, self.get_pch_name(header))]
+
+ def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
+ # Clang is different than GCC, it will return True when a symbol isn't
+ # defined in a header. Specifically this seems to have something to do
+ # with functions that may be in a header on some systems, but not all of
+ # them. `strlcat` specifically with can trigger this.
+ myargs: T.List[str] = ['-Werror=implicit-function-declaration']
+ if mode is CompileCheckMode.COMPILE:
+ myargs.extend(['-Werror=unknown-warning-option', '-Werror=unused-command-line-argument'])
+ if mesonlib.version_compare(self.version, '>=3.6.0'):
+ myargs.append('-Werror=ignored-optimization-argument')
+ return super().get_compiler_check_args(mode) + myargs
+
+ 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]:
+ if extra_args is None:
+ extra_args = []
+ # Starting with XCode 8, we need to pass this to force linker
+ # visibility to obey OS X/iOS/tvOS minimum version targets with
+ # -mmacosx-version-min, -miphoneos-version-min, -mtvos-version-min etc.
+ # https://github.com/Homebrew/homebrew-core/issues/3727
+ # TODO: this really should be communicated by the linker
+ if isinstance(self.linker, AppleDynamicLinker) and mesonlib.version_compare(self.version, '>=8.0'):
+ extra_args.append('-Wl,-no_weak_imports')
+ return super().has_function(funcname, prefix, env, extra_args=extra_args,
+ dependencies=dependencies)
+
+ def openmp_flags(self) -> T.List[str]:
+ if mesonlib.version_compare(self.version, '>=3.8.0'):
+ return ['-fopenmp']
+ elif mesonlib.version_compare(self.version, '>=3.7.0'):
+ return ['-fopenmp=libomp']
+ else:
+ # Shouldn't work, but it'll be checked explicitly in the OpenMP dependency.
+ return []
+
+ @classmethod
+ def use_linker_args(cls, linker: str, version: str) -> T.List[str]:
+ # Clang additionally can use a linker specified as a path, which GCC
+ # (and other gcc-like compilers) cannot. This is because clang (being
+ # llvm based) is retargetable, while GCC is not.
+ #
+
+ # qcld: Qualcomm Snapdragon linker, based on LLVM
+ if linker == 'qcld':
+ return ['-fuse-ld=qcld']
+ if linker == 'mold':
+ return ['-fuse-ld=mold']
+
+ if shutil.which(linker):
+ if not shutil.which(linker):
+ raise mesonlib.MesonException(
+ f'Cannot find linker {linker}.')
+ return [f'-fuse-ld={linker}']
+ return super().use_linker_args(linker, version)
+
+ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
+ # Clang only warns about unknown or ignored attributes, so force an
+ # error.
+ return ['-Werror=attributes']
+
+ def get_coverage_link_args(self) -> T.List[str]:
+ return ['--coverage']
+
+ def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T.List[str]:
+ args: T.List[str] = []
+ if mode == 'thin':
+ # ThinLTO requires the use of gold, lld, ld64, lld-link or mold 1.1+
+ if isinstance(self.linker, (MoldDynamicLinker)):
+ # https://github.com/rui314/mold/commit/46995bcfc3e3113133620bf16445c5f13cd76a18
+ if not mesonlib.version_compare(self.linker.version, '>=1.1'):
+ raise mesonlib.MesonException("LLVM's ThinLTO requires mold 1.1+")
+ elif not isinstance(self.linker, (AppleDynamicLinker, ClangClDynamicLinker, LLVMDynamicLinker, GnuGoldDynamicLinker)):
+ raise mesonlib.MesonException(f"LLVM's ThinLTO only works with gold, lld, lld-link, ld64 or mold, not {self.linker.id}")
+ args.append(f'-flto={mode}')
+ else:
+ assert mode == 'default', 'someone forgot to wire something up'
+ args.extend(super().get_lto_compile_args(threads=threads))
+ return args
+
+ def get_lto_link_args(self, *, threads: int = 0, mode: str = 'default',
+ thinlto_cache_dir: T.Optional[str] = None) -> T.List[str]:
+ args = self.get_lto_compile_args(threads=threads, mode=mode)
+ if mode == 'thin' and thinlto_cache_dir is not None:
+ # We check for ThinLTO linker support above in get_lto_compile_args, and all of them support
+ # get_thinlto_cache_args as well
+ args.extend(self.linker.get_thinlto_cache_args(thinlto_cache_dir))
+ # In clang -flto-jobs=0 means auto, and is the default if unspecified, just like in meson
+ if threads > 0:
+ if not mesonlib.version_compare(self.version, '>=4.0.0'):
+ raise mesonlib.MesonException('clang support for LTO threads requires clang >=4.0')
+ args.append(f'-flto-jobs={threads}')
+ return args
diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py
new file mode 100644
index 0000000..d44dd4d
--- /dev/null
+++ b/mesonbuild/compilers/mixins/clike.py
@@ -0,0 +1,1349 @@
+# 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
+
+
+"""Mixin classes to be shared between C and C++ compilers.
+
+Without this we'll end up with awful diamond inherintance problems. The goal
+of this is to have mixin's, which are classes that are designed *not* to be
+standalone, they only work through inheritance.
+"""
+
+import collections
+import functools
+import glob
+import itertools
+import os
+import re
+import subprocess
+import copy
+import typing as T
+from pathlib import Path
+
+from ... import arglist
+from ... import mesonlib
+from ... import mlog
+from ...linkers import GnuLikeDynamicLinkerMixin, SolarisDynamicLinker, CompCertDynamicLinker
+from ...mesonlib import LibType
+from ...coredata import OptionKey
+from .. import compilers
+from ..compilers import CompileCheckMode
+from .visualstudio import VisualStudioLikeCompiler
+
+if T.TYPE_CHECKING:
+ from ...dependencies import Dependency
+ from ..._typing import ImmutableListProtocol
+ from ...environment import Environment
+ from ...compilers.compilers import Compiler
+ from ...programs import ExternalProgram
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
+GROUP_FLAGS = re.compile(r'''\.so (?:\.[0-9]+)? (?:\.[0-9]+)? (?:\.[0-9]+)?$ |
+ ^(?:-Wl,)?-l |
+ \.a$''', re.X)
+
+class CLikeCompilerArgs(arglist.CompilerArgs):
+ prepend_prefixes = ('-I', '-L')
+ dedup2_prefixes = ('-I', '-isystem', '-L', '-D', '-U')
+
+ # NOTE: not thorough. A list of potential corner cases can be found in
+ # https://github.com/mesonbuild/meson/pull/4593#pullrequestreview-182016038
+ dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,--export-dynamic')
+ dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a')
+ dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread')
+
+ def to_native(self, copy: bool = False) -> T.List[str]:
+ # This seems to be allowed, but could never work?
+ assert isinstance(self.compiler, compilers.Compiler), 'How did you get here'
+
+ # Check if we need to add --start/end-group for circular dependencies
+ # between static libraries, and for recursively searching for symbols
+ # needed by static libraries that are provided by object files or
+ # shared libraries.
+ self.flush_pre_post()
+ if copy:
+ new = self.copy()
+ else:
+ new = self
+ # This covers all ld.bfd, ld.gold, ld.gold, and xild on Linux, which
+ # all act like (or are) gnu ld
+ # TODO: this could probably be added to the DynamicLinker instead
+ if isinstance(self.compiler.linker, (GnuLikeDynamicLinkerMixin, SolarisDynamicLinker, CompCertDynamicLinker)):
+ group_start = -1
+ group_end = -1
+ for i, each in enumerate(new):
+ if not GROUP_FLAGS.search(each):
+ continue
+ group_end = i
+ if group_start < 0:
+ # First occurrence of a library
+ group_start = i
+ if group_start >= 0:
+ # Last occurrence of a library
+ new.insert(group_end + 1, '-Wl,--end-group')
+ new.insert(group_start, '-Wl,--start-group')
+ # Remove system/default include paths added with -isystem
+ default_dirs = self.compiler.get_default_include_dirs()
+ if default_dirs:
+ real_default_dirs = [self._cached_realpath(i) for i in default_dirs]
+ bad_idx_list = [] # type: T.List[int]
+ for i, each in enumerate(new):
+ if not each.startswith('-isystem'):
+ continue
+
+ # Remove the -isystem and the path if the path is a default path
+ if (each == '-isystem' and
+ i < (len(new) - 1) and
+ self._cached_realpath(new[i + 1]) in real_default_dirs):
+ bad_idx_list += [i, i + 1]
+ elif each.startswith('-isystem=') and self._cached_realpath(each[9:]) in real_default_dirs:
+ bad_idx_list += [i]
+ elif self._cached_realpath(each[8:]) in real_default_dirs:
+ bad_idx_list += [i]
+ for i in reversed(bad_idx_list):
+ new.pop(i)
+ return self.compiler.unix_args_to_native(new._container)
+
+ @staticmethod
+ @functools.lru_cache(maxsize=None)
+ def _cached_realpath(arg: str) -> str:
+ return os.path.realpath(arg)
+
+ def __repr__(self) -> str:
+ self.flush_pre_post()
+ return f'CLikeCompilerArgs({self.compiler!r}, {self._container!r})'
+
+
+class CLikeCompiler(Compiler):
+
+ """Shared bits for the C and CPP Compilers."""
+
+ if T.TYPE_CHECKING:
+ warn_args = {} # type: T.Dict[str, T.List[str]]
+
+ # TODO: Replace this manual cache with functools.lru_cache
+ find_library_cache = {} # type: T.Dict[T.Tuple[T.Tuple[str, ...], str, T.Tuple[str, ...], str, LibType], T.Optional[T.List[str]]]
+ find_framework_cache = {} # type: T.Dict[T.Tuple[T.Tuple[str, ...], str, T.Tuple[str, ...], bool], T.Optional[T.List[str]]]
+ internal_libs = arglist.UNIXY_COMPILER_INTERNAL_LIBS
+
+ def __init__(self, exe_wrapper: T.Optional['ExternalProgram'] = None):
+ # If a child ObjC or CPP class has already set it, don't set it ourselves
+ self.can_compile_suffixes.add('h')
+ # If the exe wrapper was not found, pretend it wasn't set so that the
+ # sanity check is skipped and compiler checks use fallbacks.
+ if not exe_wrapper or not exe_wrapper.found() or not exe_wrapper.get_command():
+ self.exe_wrapper = None
+ else:
+ self.exe_wrapper = exe_wrapper
+ # Lazy initialized in get_preprocessor()
+ self.preprocessor: T.Optional[Compiler] = None
+
+ def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CLikeCompilerArgs:
+ # This is correct, mypy just doesn't understand co-operative inheritance
+ return CLikeCompilerArgs(self, args)
+
+ def needs_static_linker(self) -> bool:
+ return True # When compiling static libraries, so yes.
+
+ def get_always_args(self) -> T.List[str]:
+ '''
+ Args that are always-on for all C compilers other than MSVC
+ '''
+ return self.get_largefile_args()
+
+ def get_no_stdinc_args(self) -> T.List[str]:
+ return ['-nostdinc']
+
+ def get_no_stdlib_link_args(self) -> T.List[str]:
+ return ['-nostdlib']
+
+ def get_warn_args(self, level: str) -> T.List[str]:
+ # TODO: this should be an enum
+ return self.warn_args[level]
+
+ def get_no_warn_args(self) -> T.List[str]:
+ # Almost every compiler uses this for disabling warnings
+ return ['-w']
+
+ def get_depfile_suffix(self) -> str:
+ return 'd'
+
+ def get_preprocess_only_args(self) -> T.List[str]:
+ return ['-E', '-P']
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return ['-c']
+
+ def get_no_optimization_args(self) -> T.List[str]:
+ return ['-O0']
+
+ def get_output_args(self, outputname: str) -> T.List[str]:
+ return ['-o', outputname]
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['-Werror']
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if path == '':
+ path = '.'
+ if is_system:
+ return ['-isystem', path]
+ return ['-I' + path]
+
+ def get_compiler_dirs(self, env: 'Environment', name: str) -> T.List[str]:
+ '''
+ Get dirs from the compiler, either `libraries:` or `programs:`
+ '''
+ return []
+
+ @functools.lru_cache()
+ def _get_library_dirs(self, env: 'Environment',
+ elf_class: T.Optional[int] = None) -> 'ImmutableListProtocol[str]':
+ # TODO: replace elf_class with enum
+ dirs = self.get_compiler_dirs(env, 'libraries')
+ if elf_class is None or elf_class == 0:
+ return dirs
+
+ # if we do have an elf class for 32-bit or 64-bit, we want to check that
+ # the directory in question contains libraries of the appropriate class. Since
+ # system directories aren't mixed, we only need to check one file for each
+ # directory and go by that. If we can't check the file for some reason, assume
+ # the compiler knows what it's doing, and accept the directory anyway.
+ retval = []
+ for d in dirs:
+ files = [f for f in os.listdir(d) if f.endswith('.so') and os.path.isfile(os.path.join(d, f))]
+ # if no files, accept directory and move on
+ if not files:
+ retval.append(d)
+ continue
+
+ for f in files:
+ file_to_check = os.path.join(d, f)
+ try:
+ with open(file_to_check, 'rb') as fd:
+ header = fd.read(5)
+ # if file is not an ELF file, it's weird, but accept dir
+ # if it is elf, and the class matches, accept dir
+ if header[1:4] != b'ELF' or int(header[4]) == elf_class:
+ retval.append(d)
+ # at this point, it's an ELF file which doesn't match the
+ # appropriate elf_class, so skip this one
+ # stop scanning after the first successful read
+ break
+ except OSError:
+ # Skip the file if we can't read it
+ pass
+
+ return retval
+
+ def get_library_dirs(self, env: 'Environment',
+ elf_class: T.Optional[int] = None) -> T.List[str]:
+ """Wrap the lru_cache so that we return a new copy and don't allow
+ mutation of the cached value.
+ """
+ return self._get_library_dirs(env, elf_class).copy()
+
+ @functools.lru_cache()
+ def _get_program_dirs(self, env: 'Environment') -> 'ImmutableListProtocol[str]':
+ '''
+ Programs used by the compiler. Also where toolchain DLLs such as
+ libstdc++-6.dll are found with MinGW.
+ '''
+ return self.get_compiler_dirs(env, 'programs')
+
+ def get_program_dirs(self, env: 'Environment') -> T.List[str]:
+ return self._get_program_dirs(env).copy()
+
+ def get_pic_args(self) -> T.List[str]:
+ return ['-fPIC']
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ return ['-include', os.path.basename(header)]
+
+ def get_pch_name(self, name: str) -> str:
+ return os.path.basename(name) + '.' + self.get_pch_suffix()
+
+ def get_default_include_dirs(self) -> T.List[str]:
+ return []
+
+ def gen_export_dynamic_link_args(self, env: 'Environment') -> T.List[str]:
+ return self.linker.export_dynamic_args(env)
+
+ def gen_import_library_args(self, implibname: str) -> T.List[str]:
+ return self.linker.import_library_args(implibname)
+
+ def _sanity_check_impl(self, work_dir: str, environment: 'Environment',
+ sname: str, code: str) -> None:
+ mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', mesonlib.join_args(self.exelist))
+ mlog.debug(f'Is cross compiler: {self.is_cross!s}.')
+
+ source_name = os.path.join(work_dir, sname)
+ binname = sname.rsplit('.', 1)[0]
+ mode = CompileCheckMode.LINK
+ if self.is_cross:
+ binname += '_cross'
+ if self.exe_wrapper is None:
+ # Linking cross built C/C++ apps is painful. You can't really
+ # tell if you should use -nostdlib or not and for example
+ # on OSX the compiler binary is the same but you need
+ # a ton of compiler flags to differentiate between
+ # arm and x86_64. So just compile.
+ mode = CompileCheckMode.COMPILE
+ cargs, largs = self._get_basic_compiler_args(environment, mode)
+ extra_flags = cargs + self.linker_to_compiler_args(largs)
+
+ # Is a valid executable output for all toolchains and platforms
+ binname += '.exe'
+ # Write binary check source
+ binary_name = os.path.join(work_dir, binname)
+ with open(source_name, 'w', encoding='utf-8') as ofile:
+ ofile.write(code)
+ # Compile sanity check
+ # NOTE: extra_flags must be added at the end. On MSVC, it might contain a '/link' argument
+ # after which all further arguments will be passed directly to the linker
+ cmdlist = self.exelist + [sname] + self.get_output_args(binname) + extra_flags
+ pc, stdo, stde = mesonlib.Popen_safe(cmdlist, cwd=work_dir)
+ mlog.debug('Sanity check compiler command line:', mesonlib.join_args(cmdlist))
+ mlog.debug('Sanity check compile stdout:')
+ mlog.debug(stdo)
+ mlog.debug('-----\nSanity check compile stderr:')
+ mlog.debug(stde)
+ mlog.debug('-----')
+ if pc.returncode != 0:
+ raise mesonlib.EnvironmentException(f'Compiler {self.name_string()} can not compile programs.')
+ # Run sanity check
+ if self.is_cross:
+ if self.exe_wrapper is None:
+ # Can't check if the binaries run so we have to assume they do
+ return
+ cmdlist = self.exe_wrapper.get_command() + [binary_name]
+ else:
+ cmdlist = [binary_name]
+ mlog.debug('Running test binary command: ', mesonlib.join_args(cmdlist))
+ try:
+ # fortran code writes to stdout
+ pe = subprocess.run(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ except Exception as e:
+ raise mesonlib.EnvironmentException(f'Could not invoke sanity test executable: {e!s}.')
+ if pe.returncode != 0:
+ raise mesonlib.EnvironmentException(f'Executables created by {self.language} compiler {self.name_string()} are not runnable.')
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ code = 'int main(void) { int class=0; return class; }\n'
+ return self._sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
+
+ def check_header(self, hname: str, prefix: str, env: '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]:
+ code = f'''{prefix}
+ #include <{hname}>'''
+ return self.compiles(code, env, extra_args=extra_args,
+ dependencies=dependencies)
+
+ 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]:
+ code = f'''{prefix}
+ #ifdef __has_include
+ #if !__has_include("{hname}")
+ #error "Header '{hname}' could not be found"
+ #endif
+ #else
+ #include <{hname}>
+ #endif'''
+ return self.compiles(code, env, extra_args=extra_args,
+ dependencies=dependencies, mode='preprocess', disable_cache=disable_cache)
+
+ def has_header_symbol(self, hname: str, symbol: str, prefix: str,
+ env: '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]:
+ t = f'''{prefix}
+ #include <{hname}>
+ int main(void) {{
+ /* If it's not defined as a macro, try to use as a symbol */
+ #ifndef {symbol}
+ {symbol};
+ #endif
+ return 0;
+ }}'''
+ return self.compiles(t, env, extra_args=extra_args,
+ dependencies=dependencies)
+
+ def _get_basic_compiler_args(self, env: 'Environment', mode: CompileCheckMode) -> T.Tuple[T.List[str], T.List[str]]:
+ cargs = [] # type: T.List[str]
+ largs = [] # type: T.List[str]
+ if mode is CompileCheckMode.LINK:
+ # Sometimes we need to manually select the CRT to use with MSVC.
+ # One example is when trying to do a compiler check that involves
+ # linking with static libraries since MSVC won't select a CRT for
+ # us in that case and will error out asking us to pick one.
+ try:
+ crt_val = env.coredata.options[OptionKey('b_vscrt')].value
+ buildtype = env.coredata.options[OptionKey('buildtype')].value
+ cargs += self.get_crt_compile_args(crt_val, buildtype)
+ except (KeyError, AttributeError):
+ pass
+
+ # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS and CPPFLAGS from the env
+ sys_args = env.coredata.get_external_args(self.for_machine, self.language)
+ if isinstance(sys_args, str):
+ sys_args = [sys_args]
+ # Apparently it is a thing to inject linker flags both
+ # via CFLAGS _and_ LDFLAGS, even though the former are
+ # also used during linking. These flags can break
+ # argument checks. Thanks, Autotools.
+ cleaned_sys_args = self.remove_linkerlike_args(sys_args)
+ cargs += cleaned_sys_args
+
+ if mode is CompileCheckMode.LINK:
+ ld_value = env.lookup_binary_entry(self.for_machine, self.language + '_ld')
+ if ld_value is not None:
+ largs += self.use_linker_args(ld_value[0], self.version)
+
+ # Add LDFLAGS from the env
+ sys_ld_args = env.coredata.get_external_link_args(self.for_machine, self.language)
+ # CFLAGS and CXXFLAGS go to both linking and compiling, but we want them
+ # to only appear on the command line once. Remove dupes.
+ largs += [x for x in sys_ld_args if x not in sys_args]
+
+ cargs += self.get_compiler_args_for_mode(mode)
+ return cargs, largs
+
+ def build_wrapper_args(self, env: 'Environment',
+ extra_args: T.Union[None, arglist.CompilerArgs, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
+ dependencies: T.Optional[T.List['Dependency']],
+ mode: CompileCheckMode = CompileCheckMode.COMPILE) -> arglist.CompilerArgs:
+ # TODO: the caller should handle the listfing of these arguments
+ if extra_args is None:
+ extra_args = []
+ else:
+ # TODO: we want to do this in the caller
+ extra_args = mesonlib.listify(extra_args)
+ extra_args = mesonlib.listify([e(mode.value) if callable(e) else e for e in extra_args])
+
+ if dependencies is None:
+ dependencies = []
+ elif not isinstance(dependencies, collections.abc.Iterable):
+ # TODO: we want to ensure the front end does the listifing here
+ dependencies = [dependencies]
+ # Collect compiler arguments
+ cargs = self.compiler_args() # type: arglist.CompilerArgs
+ largs = [] # type: T.List[str]
+ for d in dependencies:
+ # Add compile flags needed by dependencies
+ cargs += d.get_compile_args()
+ if mode is CompileCheckMode.LINK:
+ # Add link flags needed to find dependencies
+ largs += d.get_link_args()
+
+ ca, la = self._get_basic_compiler_args(env, mode)
+ cargs += ca
+ largs += la
+
+ cargs += self.get_compiler_check_args(mode)
+
+ # on MSVC compiler and linker flags must be separated by the "/link" argument
+ # at this point, the '/link' argument may already be part of extra_args, otherwise, it is added here
+ if self.linker_to_compiler_args([]) == ['/link'] and largs != [] and '/link' not in extra_args:
+ extra_args += ['/link']
+
+ args = cargs + extra_args + largs
+ return args
+
+ 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) -> compilers.RunResult:
+ need_exe_wrapper = env.need_exe_wrapper(self.for_machine)
+ if need_exe_wrapper and self.exe_wrapper is None:
+ raise compilers.CrossNoRunException('Can not run test applications in this cross environment.')
+ with self._build_wrapper(code, env, extra_args, dependencies, mode='link', want_output=True) as p:
+ if p.returncode != 0:
+ mlog.debug(f'Could not compile test file {p.input_name}: {p.returncode}\n')
+ return compilers.RunResult(False)
+ if need_exe_wrapper:
+ cmdlist = self.exe_wrapper.get_command() + [p.output_name]
+ else:
+ cmdlist = [p.output_name]
+ try:
+ pe, so, se = mesonlib.Popen_safe(cmdlist)
+ except Exception as e:
+ mlog.debug(f'Could not run: {cmdlist} (error: {e})\n')
+ return compilers.RunResult(False)
+
+ mlog.debug('Program stdout:\n')
+ mlog.debug(so)
+ mlog.debug('Program stderr:\n')
+ mlog.debug(se)
+ return compilers.RunResult(True, pe.returncode, so, se)
+
+ def _compile_int(self, expression: str, prefix: str, env: 'Environment',
+ extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
+ dependencies: T.Optional[T.List['Dependency']]) -> bool:
+ t = f'''#include <stdio.h>
+ {prefix}
+ int main(void) {{ static int a[1-2*!({expression})]; a[0]=0; return 0; }}'''
+ return self.compiles(t, env, extra_args=extra_args,
+ dependencies=dependencies)[0]
+
+ def cross_compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
+ guess: T.Optional[int], prefix: str, env: 'Environment',
+ extra_args: T.Union[None, T.List[str], T.Callable[[CompileCheckMode], T.List[str]]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
+ # Try user's guess first
+ if isinstance(guess, int):
+ if self._compile_int(f'{expression} == {guess}', prefix, env, extra_args, dependencies):
+ return guess
+
+ # If no bounds are given, compute them in the limit of int32
+ maxint = 0x7fffffff
+ minint = -0x80000000
+ if not isinstance(low, int) or not isinstance(high, int):
+ if self._compile_int(f'{expression} >= 0', prefix, env, extra_args, dependencies):
+ low = cur = 0
+ while self._compile_int(f'{expression} > {cur}', prefix, env, extra_args, dependencies):
+ low = cur + 1
+ if low > maxint:
+ raise mesonlib.EnvironmentException('Cross-compile check overflowed')
+ cur = min(cur * 2 + 1, maxint)
+ high = cur
+ else:
+ high = cur = -1
+ while self._compile_int(f'{expression} < {cur}', prefix, env, extra_args, dependencies):
+ high = cur - 1
+ if high < minint:
+ raise mesonlib.EnvironmentException('Cross-compile check overflowed')
+ cur = max(cur * 2, minint)
+ low = cur
+ else:
+ # Sanity check limits given by user
+ if high < low:
+ raise mesonlib.EnvironmentException('high limit smaller than low limit')
+ condition = f'{expression} <= {high} && {expression} >= {low}'
+ if not self._compile_int(condition, prefix, env, extra_args, dependencies):
+ raise mesonlib.EnvironmentException('Value out of given range')
+
+ # Binary search
+ while low != high:
+ cur = low + int((high - low) / 2)
+ if self._compile_int(f'{expression} <= {cur}', prefix, env, extra_args, dependencies):
+ high = cur
+ else:
+ low = cur + 1
+
+ return low
+
+ 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']] = None) -> int:
+ if extra_args is None:
+ extra_args = []
+ if self.is_cross:
+ return self.cross_compute_int(expression, low, high, guess, prefix, env, extra_args, dependencies)
+ t = f'''#include<stdio.h>
+ {prefix}
+ int main(void) {{
+ printf("%ld\\n", (long)({expression}));
+ return 0;
+ }}'''
+ res = self.run(t, env, extra_args=extra_args,
+ dependencies=dependencies)
+ if not res.compiled:
+ return -1
+ if res.returncode != 0:
+ raise mesonlib.EnvironmentException('Could not run compute_int test binary.')
+ return int(res.stdout)
+
+ def cross_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:
+ if extra_args is None:
+ extra_args = []
+ t = f'''#include <stdio.h>
+ {prefix}
+ int main(void) {{
+ {typename} something;
+ return 0;
+ }}'''
+ if not self.compiles(t, env, extra_args=extra_args,
+ dependencies=dependencies)[0]:
+ return -1
+ return self.cross_compute_int(f'sizeof({typename})', None, None, None, prefix, env, extra_args, dependencies)
+
+ 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:
+ if extra_args is None:
+ extra_args = []
+ if self.is_cross:
+ return self.cross_sizeof(typename, prefix, env, extra_args=extra_args,
+ dependencies=dependencies)
+ t = f'''#include<stdio.h>
+ {prefix}
+ int main(void) {{
+ printf("%ld\\n", (long)(sizeof({typename})));
+ return 0;
+ }}'''
+ res = self.run(t, env, extra_args=extra_args,
+ dependencies=dependencies)
+ if not res.compiled:
+ return -1
+ if res.returncode != 0:
+ raise mesonlib.EnvironmentException('Could not run sizeof test binary.')
+ return int(res.stdout)
+
+ def cross_alignment(self, typename: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
+ if extra_args is None:
+ extra_args = []
+ t = f'''#include <stdio.h>
+ {prefix}
+ int main(void) {{
+ {typename} something;
+ return 0;
+ }}'''
+ if not self.compiles(t, env, extra_args=extra_args,
+ dependencies=dependencies)[0]:
+ return -1
+ t = f'''#include <stddef.h>
+ {prefix}
+ struct tmp {{
+ char c;
+ {typename} target;
+ }};'''
+ return self.cross_compute_int('offsetof(struct tmp, target)', None, None, None, t, env, extra_args, dependencies)
+
+ def alignment(self, typename: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
+ if extra_args is None:
+ extra_args = []
+ if self.is_cross:
+ return self.cross_alignment(typename, prefix, env, extra_args=extra_args,
+ dependencies=dependencies)
+ t = f'''#include <stdio.h>
+ #include <stddef.h>
+ {prefix}
+ struct tmp {{
+ char c;
+ {typename} target;
+ }};
+ int main(void) {{
+ printf("%d", (int)offsetof(struct tmp, target));
+ return 0;
+ }}'''
+ res = self.run(t, env, extra_args=extra_args,
+ dependencies=dependencies)
+ if not res.compiled:
+ raise mesonlib.EnvironmentException('Could not compile alignment test.')
+ if res.returncode != 0:
+ raise mesonlib.EnvironmentException('Could not run alignment test binary.')
+ align = int(res.stdout)
+ if align == 0:
+ raise mesonlib.EnvironmentException(f'Could not determine alignment of {typename}. Sorry. You might want to file a bug.')
+ return align
+
+ 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.Optional[T.List['Dependency']],
+ disable_cache: bool = False) -> T.Tuple[str, bool]:
+ delim = '"MESON_GET_DEFINE_DELIMITER"'
+ code = f'''
+ {prefix}
+ #ifndef {dname}
+ # define {dname}
+ #endif
+ {delim}\n{dname}'''
+ args = self.build_wrapper_args(env, extra_args, dependencies,
+ mode=CompileCheckMode.PREPROCESS).to_native()
+ func = functools.partial(self.cached_compile, code, env.coredata, extra_args=args, mode='preprocess')
+ if disable_cache:
+ func = functools.partial(self.compile, code, extra_args=args, mode='preprocess', temp_dir=env.scratch_dir)
+ with func() as p:
+ cached = p.cached
+ if p.returncode != 0:
+ raise mesonlib.EnvironmentException(f'Could not get define {dname!r}')
+ # Get the preprocessed value after the delimiter,
+ # minus the extra newline at the end and
+ # merge string literals.
+ return self._concatenate_string_literals(p.stdout.split(delim + '\n')[-1][:-1]), cached
+
+ def get_return_value(self, fname: str, rtype: str, prefix: str,
+ env: 'Environment', extra_args: T.Optional[T.List[str]],
+ dependencies: T.Optional[T.List['Dependency']]) -> T.Union[str, int]:
+ # TODO: rtype should be an enum.
+ # TODO: maybe we can use overload to tell mypy when this will return int vs str?
+ if rtype == 'string':
+ fmt = '%s'
+ cast = '(char*)'
+ elif rtype == 'int':
+ fmt = '%lli'
+ cast = '(long long int)'
+ else:
+ raise AssertionError(f'BUG: Unknown return type {rtype!r}')
+ code = f'''{prefix}
+ #include <stdio.h>
+ int main(void) {{
+ printf ("{fmt}", {cast} {fname}());
+ return 0;
+ }}'''
+ res = self.run(code, env, extra_args=extra_args, dependencies=dependencies)
+ if not res.compiled:
+ raise mesonlib.EnvironmentException(f'Could not get return value of {fname}()')
+ if rtype == 'string':
+ return res.stdout
+ elif rtype == 'int':
+ try:
+ return int(res.stdout.strip())
+ except ValueError:
+ raise mesonlib.EnvironmentException(f'Return value of {fname}() is not an int')
+ assert False, 'Unreachable'
+
+ @staticmethod
+ def _no_prototype_templ() -> T.Tuple[str, str]:
+ """
+ Try to find the function without a prototype from a header by defining
+ our own dummy prototype and trying to link with the C library (and
+ whatever else the compiler links in by default). This is very similar
+ to the check performed by Autoconf for AC_CHECK_FUNCS.
+ """
+ # Define the symbol to something else since it is defined by the
+ # includes or defines listed by the user or by the compiler. This may
+ # include, for instance _GNU_SOURCE which must be defined before
+ # limits.h, which includes features.h
+ # Then, undef the symbol to get rid of it completely.
+ head = '''
+ #define {func} meson_disable_define_of_{func}
+ {prefix}
+ #include <limits.h>
+ #undef {func}
+ '''
+ # Override any GCC internal prototype and declare our own definition for
+ # the symbol. Use char because that's unlikely to be an actual return
+ # value for a function which ensures that we override the definition.
+ head += '''
+ #ifdef __cplusplus
+ extern "C"
+ #endif
+ char {func} (void);
+ '''
+ # The actual function call
+ main = '''
+ int main(void) {{
+ return {func} ();
+ }}'''
+ return head, main
+
+ @staticmethod
+ def _have_prototype_templ() -> T.Tuple[str, str]:
+ """
+ Returns a head-er and main() call that uses the headers listed by the
+ user for the function prototype while checking if a function exists.
+ """
+ # Add the 'prefix', aka defines, includes, etc that the user provides
+ # This may include, for instance _GNU_SOURCE which must be defined
+ # before limits.h, which includes features.h
+ head = '{prefix}\n#include <limits.h>\n'
+ # We don't know what the function takes or returns, so return it as an int.
+ # Just taking the address or comparing it to void is not enough because
+ # compilers are smart enough to optimize it away. The resulting binary
+ # is not run so we don't care what the return value is.
+ main = '''\nint main(void) {{
+ void *a = (void*) &{func};
+ long long b = (long long) a;
+ return (int) b;
+ }}'''
+ return head, main
+
+ def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
+ """Determine if a function exists.
+
+ First, this function looks for the symbol in the default libraries
+ provided by the compiler (stdlib + a few others usually). If that
+ fails, it checks if any of the headers specified in the prefix provide
+ an implementation of the function, and if that fails, it checks if it's
+ implemented as a compiler-builtin.
+ """
+ if extra_args is None:
+ extra_args = []
+
+ # Short-circuit if the check is already provided by the cross-info file
+ varname = 'has function ' + funcname
+ varname = varname.replace(' ', '_')
+ if self.is_cross:
+ val = env.properties.host.get(varname, None)
+ if val is not None:
+ if isinstance(val, bool):
+ return val, False
+ raise mesonlib.EnvironmentException(f'Cross variable {varname} is not a boolean.')
+
+ # TODO: we really need a protocol for this,
+ #
+ # class StrProto(typing.Protocol):
+ # def __str__(self) -> str: ...
+ fargs = {'prefix': prefix, 'func': funcname} # type: T.Dict[str, T.Union[str, bool, int]]
+
+ # glibc defines functions that are not available on Linux as stubs that
+ # fail with ENOSYS (such as e.g. lchmod). In this case we want to fail
+ # instead of detecting the stub as a valid symbol.
+ # We already included limits.h earlier to ensure that these are defined
+ # for stub functions.
+ stubs_fail = '''
+ #if defined __stub_{func} || defined __stub___{func}
+ fail fail fail this function is not going to work
+ #endif
+ '''
+
+ # If we have any includes in the prefix supplied by the user, assume
+ # that the user wants us to use the symbol prototype defined in those
+ # includes. If not, then try to do the Autoconf-style check with
+ # a dummy prototype definition of our own.
+ # This is needed when the linker determines symbol availability from an
+ # SDK based on the prototype in the header provided by the SDK.
+ # Ignoring this prototype would result in the symbol always being
+ # marked as available.
+ if '#include' in prefix:
+ head, main = self._have_prototype_templ()
+ else:
+ head, main = self._no_prototype_templ()
+ templ = head + stubs_fail + main
+
+ res, cached = self.links(templ.format(**fargs), env, extra_args=extra_args,
+ dependencies=dependencies)
+ if res:
+ return True, cached
+
+ # MSVC does not have compiler __builtin_-s.
+ if self.get_id() in {'msvc', 'intel-cl'}:
+ return False, False
+
+ # Detect function as a built-in
+ #
+ # Some functions like alloca() are defined as compiler built-ins which
+ # are inlined by the compiler and you can't take their address, so we
+ # need to look for them differently. On nice compilers like clang, we
+ # can just directly use the __has_builtin() macro.
+ fargs['no_includes'] = '#include' not in prefix
+ is_builtin = funcname.startswith('__builtin_')
+ fargs['is_builtin'] = is_builtin
+ fargs['__builtin_'] = '' if is_builtin else '__builtin_'
+ t = '''{prefix}
+ int main(void) {{
+
+ /* With some toolchains (MSYS2/mingw for example) the compiler
+ * provides various builtins which are not really implemented and
+ * fall back to the stdlib where they aren't provided and fail at
+ * build/link time. In case the user provides a header, including
+ * the header didn't lead to the function being defined, and the
+ * function we are checking isn't a builtin itself we assume the
+ * builtin is not functional and we just error out. */
+ #if !{no_includes:d} && !defined({func}) && !{is_builtin:d}
+ #error "No definition for {__builtin_}{func} found in the prefix"
+ #endif
+
+ #ifdef __has_builtin
+ #if !__has_builtin({__builtin_}{func})
+ #error "{__builtin_}{func} not found"
+ #endif
+ #elif ! defined({func})
+ {__builtin_}{func};
+ #endif
+ return 0;
+ }}'''
+ return self.links(t.format(**fargs), env, extra_args=extra_args,
+ dependencies=dependencies)
+
+ 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]:
+ if extra_args is None:
+ extra_args = []
+ # Create code that accesses all members
+ members = ''.join(f'foo.{member};\n' for member in membernames)
+ t = f'''{prefix}
+ void bar(void) {{
+ {typename} foo;
+ {members}
+ }}'''
+ return self.compiles(t, env, extra_args=extra_args,
+ dependencies=dependencies)
+
+ 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]:
+ t = f'''{prefix}
+ void bar(void) {{
+ sizeof({typename});
+ }}'''
+ return self.compiles(t, env, extra_args=extra_args,
+ dependencies=dependencies)
+
+ def _symbols_have_underscore_prefix_searchbin(self, env: 'Environment') -> bool:
+ '''
+ Check if symbols have underscore prefix by compiling a small test binary
+ and then searching the binary for the string,
+ '''
+ symbol_name = b'meson_uscore_prefix'
+ code = '''#ifdef __cplusplus
+ extern "C" {
+ #endif
+ void ''' + symbol_name.decode() + ''' (void) {}
+ #ifdef __cplusplus
+ }
+ #endif
+ '''
+ args = self.get_compiler_check_args(CompileCheckMode.COMPILE)
+ n = '_symbols_have_underscore_prefix_searchbin'
+ with self._build_wrapper(code, env, extra_args=args, mode='compile', want_output=True, temp_dir=env.scratch_dir) as p:
+ if p.returncode != 0:
+ raise RuntimeError(f'BUG: Unable to compile {n!r} check: {p.stderr}')
+ if not os.path.isfile(p.output_name):
+ raise RuntimeError(f'BUG: Can\'t find compiled test code for {n!r} check')
+ with open(p.output_name, 'rb') as o:
+ for line in o:
+ # Check if the underscore form of the symbol is somewhere
+ # in the output file.
+ if b'_' + symbol_name in line:
+ mlog.debug("Underscore prefix check found prefixed function in binary")
+ return True
+ # Else, check if the non-underscored form is present
+ elif symbol_name in line:
+ mlog.debug("Underscore prefix check found non-prefixed function in binary")
+ return False
+ raise RuntimeError(f'BUG: {n!r} check did not find symbol string in binary')
+
+ def _symbols_have_underscore_prefix_define(self, env: 'Environment') -> T.Optional[bool]:
+ '''
+ Check if symbols have underscore prefix by querying the
+ __USER_LABEL_PREFIX__ define that most compilers provide
+ for this. Return if functions have underscore prefix or None
+ if it was not possible to determine, like when the compiler
+ does not set the define or the define has an unexpected value.
+ '''
+ delim = '"MESON_HAVE_UNDERSCORE_DELIMITER" '
+ code = f'''
+ #ifndef __USER_LABEL_PREFIX__
+ #define MESON_UNDERSCORE_PREFIX unsupported
+ #else
+ #define MESON_UNDERSCORE_PREFIX __USER_LABEL_PREFIX__
+ #endif
+ {delim}MESON_UNDERSCORE_PREFIX
+ '''
+ with self._build_wrapper(code, env, mode='preprocess', want_output=False, temp_dir=env.scratch_dir) as p:
+ if p.returncode != 0:
+ raise RuntimeError(f'BUG: Unable to preprocess _symbols_have_underscore_prefix_define check: {p.stdout}')
+ symbol_prefix = p.stdout.partition(delim)[-1].rstrip()
+
+ mlog.debug(f'Queried compiler for function prefix: __USER_LABEL_PREFIX__ is "{symbol_prefix!s}"')
+ if symbol_prefix == '_':
+ return True
+ elif symbol_prefix == '':
+ return False
+ else:
+ return None
+
+ def _symbols_have_underscore_prefix_list(self, env: 'Environment') -> T.Optional[bool]:
+ '''
+ Check if symbols have underscore prefix by consulting a hardcoded
+ list of cases where we know the results.
+ Return if functions have underscore prefix or None if unknown.
+ '''
+ m = env.machines[self.for_machine]
+ # Darwin always uses the underscore prefix, not matter what
+ if m.is_darwin():
+ return True
+ # Windows uses the underscore prefix on x86 (32bit) only
+ if m.is_windows() or m.is_cygwin():
+ return m.cpu_family == 'x86'
+ return None
+
+ def symbols_have_underscore_prefix(self, env: 'Environment') -> bool:
+ '''
+ Check if the compiler prefixes an underscore to global C symbols
+ '''
+ # First, try to query the compiler directly
+ result = self._symbols_have_underscore_prefix_define(env)
+ if result is not None:
+ return result
+
+ # Else, try to consult a hardcoded list of cases we know
+ # absolutely have an underscore prefix
+ result = self._symbols_have_underscore_prefix_list(env)
+ if result is not None:
+ return result
+
+ # As a last resort, try search in a compiled binary, which is the
+ # most unreliable way of checking this, see #5482
+ return self._symbols_have_underscore_prefix_searchbin(env)
+
+ def _get_patterns(self, env: 'Environment', prefixes: T.List[str], suffixes: T.List[str], shared: bool = False) -> T.List[str]:
+ patterns = [] # type: T.List[str]
+ for p in prefixes:
+ for s in suffixes:
+ patterns.append(p + '{}.' + s)
+ if shared and env.machines[self.for_machine].is_openbsd():
+ # Shared libraries on OpenBSD can be named libfoo.so.X.Y:
+ # https://www.openbsd.org/faq/ports/specialtopics.html#SharedLibs
+ #
+ # This globbing is probably the best matching we can do since regex
+ # is expensive. It's wrong in many edge cases, but it will match
+ # correctly-named libraries and hopefully no one on OpenBSD names
+ # their files libfoo.so.9a.7b.1.0
+ for p in prefixes:
+ patterns.append(p + '{}.so.[0-9]*.[0-9]*')
+ return patterns
+
+ def get_library_naming(self, env: 'Environment', libtype: LibType, strict: bool = False) -> T.Tuple[str, ...]:
+ '''
+ Get library prefixes and suffixes for the target platform ordered by
+ priority
+ '''
+ stlibext = ['a']
+ # We've always allowed libname to be both `foo` and `libfoo`, and now
+ # people depend on it. Also, some people use prebuilt `foo.so` instead
+ # of `libfoo.so` for unknown reasons, and may also want to create
+ # `foo.so` by setting name_prefix to ''
+ if strict and not isinstance(self, VisualStudioLikeCompiler): # lib prefix is not usually used with msvc
+ prefixes = ['lib']
+ else:
+ prefixes = ['lib', '']
+ # Library suffixes and prefixes
+ if env.machines[self.for_machine].is_darwin():
+ shlibext = ['dylib', 'so']
+ elif env.machines[self.for_machine].is_windows():
+ # FIXME: .lib files can be import or static so we should read the
+ # file, figure out which one it is, and reject the wrong kind.
+ if isinstance(self, VisualStudioLikeCompiler):
+ shlibext = ['lib']
+ else:
+ shlibext = ['dll.a', 'lib', 'dll']
+ # Yep, static libraries can also be foo.lib
+ stlibext += ['lib']
+ elif env.machines[self.for_machine].is_cygwin():
+ shlibext = ['dll', 'dll.a']
+ prefixes = ['cyg'] + prefixes
+ else:
+ # Linux/BSDs
+ shlibext = ['so']
+ # Search priority
+ if libtype is LibType.PREFER_SHARED:
+ patterns = self._get_patterns(env, prefixes, shlibext, True)
+ patterns.extend([x for x in self._get_patterns(env, prefixes, stlibext, False) if x not in patterns])
+ elif libtype is LibType.PREFER_STATIC:
+ patterns = self._get_patterns(env, prefixes, stlibext, False)
+ patterns.extend([x for x in self._get_patterns(env, prefixes, shlibext, True) if x not in patterns])
+ elif libtype is LibType.SHARED:
+ patterns = self._get_patterns(env, prefixes, shlibext, True)
+ else:
+ assert libtype is LibType.STATIC
+ patterns = self._get_patterns(env, prefixes, stlibext, False)
+ return tuple(patterns)
+
+ @staticmethod
+ def _sort_shlibs_openbsd(libs: T.List[str]) -> T.List[str]:
+ filtered = [] # type: T.List[str]
+ for lib in libs:
+ # Validate file as a shared library of type libfoo.so.X.Y
+ ret = lib.rsplit('.so.', maxsplit=1)
+ if len(ret) != 2:
+ continue
+ try:
+ float(ret[1])
+ except ValueError:
+ continue
+ filtered.append(lib)
+ float_cmp = lambda x: float(x.rsplit('.so.', maxsplit=1)[1])
+ return sorted(filtered, key=float_cmp, reverse=True)
+
+ @classmethod
+ def _get_trials_from_pattern(cls, pattern: str, directory: str, libname: str) -> T.List[Path]:
+ f = Path(directory) / pattern.format(libname)
+ # Globbing for OpenBSD
+ if '*' in pattern:
+ # NOTE: globbing matches directories and broken symlinks
+ # so we have to do an isfile test on it later
+ return [Path(x) for x in cls._sort_shlibs_openbsd(glob.glob(str(f)))]
+ return [f]
+
+ @staticmethod
+ def _get_file_from_list(env: 'Environment', paths: T.List[Path]) -> Path:
+ '''
+ We just check whether the library exists. We can't do a link check
+ because the library might have unresolved symbols that require other
+ libraries. On macOS we check if the library matches our target
+ architecture.
+ '''
+ # If not building on macOS for Darwin, do a simple file check
+ if not env.machines.host.is_darwin() or not env.machines.build.is_darwin():
+ for p in paths:
+ if p.is_file():
+ return p
+ # Run `lipo` and check if the library supports the arch we want
+ for p in paths:
+ if not p.is_file():
+ continue
+ archs = mesonlib.darwin_get_object_archs(str(p))
+ if archs and env.machines.host.cpu_family in archs:
+ return p
+ else:
+ mlog.debug(f'Rejected {p}, supports {archs} but need {env.machines.host.cpu_family}')
+ return None
+
+ @functools.lru_cache()
+ def output_is_64bit(self, env: 'Environment') -> bool:
+ '''
+ returns true if the output produced is 64-bit, false if 32-bit
+ '''
+ return self.sizeof('void *', '', env) == 8
+
+ def _find_library_real(self, libname: str, env: 'Environment', extra_dirs: T.List[str], code: str, libtype: LibType) -> T.Optional[T.List[str]]:
+ # First try if we can just add the library as -l.
+ # Gcc + co seem to prefer builtin lib dirs to -L dirs.
+ # Only try to find std libs if no extra dirs specified.
+ # The built-in search procedure will always favour .so and then always
+ # search for .a. This is only allowed if libtype is LibType.PREFER_SHARED
+ if ((not extra_dirs and libtype is LibType.PREFER_SHARED) or
+ libname in self.internal_libs):
+ cargs = ['-l' + libname]
+ largs = self.get_linker_always_args() + self.get_allow_undefined_link_args()
+ extra_args = cargs + self.linker_to_compiler_args(largs)
+
+ if self.links(code, env, extra_args=extra_args, disable_cache=True)[0]:
+ return cargs
+ # Don't do a manual search for internal libs
+ if libname in self.internal_libs:
+ return None
+ # Not found or we want to use a specific libtype? Try to find the
+ # library file itself.
+ patterns = self.get_library_naming(env, libtype)
+ # try to detect if we are 64-bit or 32-bit. If we can't
+ # detect, we will just skip path validity checks done in
+ # get_library_dirs() call
+ try:
+ if self.output_is_64bit(env):
+ elf_class = 2
+ else:
+ elf_class = 1
+ except (mesonlib.MesonException, KeyError): # TODO evaluate if catching KeyError is wanted here
+ elf_class = 0
+ # Search in the specified dirs, and then in the system libraries
+ for d in itertools.chain(extra_dirs, self.get_library_dirs(env, elf_class)):
+ for p in patterns:
+ trials = self._get_trials_from_pattern(p, d, libname)
+ if not trials:
+ continue
+ trial = self._get_file_from_list(env, trials)
+ if not trial:
+ continue
+ if libname.startswith('lib') and trial.name.startswith(libname):
+ mlog.warning(f'find_library({libname!r}) starting in "lib" only works by accident and is not portable')
+ return [trial.as_posix()]
+ return None
+
+ def _find_library_impl(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
+ code: str, libtype: LibType) -> T.Optional[T.List[str]]:
+ # These libraries are either built-in or invalid
+ if libname in self.ignore_libs:
+ return []
+ if isinstance(extra_dirs, str):
+ extra_dirs = [extra_dirs]
+ key = (tuple(self.exelist), libname, tuple(extra_dirs), code, libtype)
+ if key not in self.find_library_cache:
+ value = self._find_library_real(libname, env, extra_dirs, code, libtype)
+ self.find_library_cache[key] = value
+ else:
+ value = self.find_library_cache[key]
+ if value is None:
+ return None
+ return value.copy()
+
+ def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
+ libtype: LibType = LibType.PREFER_SHARED) -> T.Optional[T.List[str]]:
+ code = 'int main(void) { return 0; }\n'
+ return self._find_library_impl(libname, env, extra_dirs, code, libtype)
+
+ def find_framework_paths(self, env: 'Environment') -> T.List[str]:
+ '''
+ These are usually /Library/Frameworks and /System/Library/Frameworks,
+ unless you select a particular macOS SDK with the -isysroot flag.
+ You can also add to this by setting -F in CFLAGS.
+ '''
+ # TODO: this really needs to be *AppleClang*, not just any clang.
+ if self.id != 'clang':
+ raise mesonlib.MesonException('Cannot find framework path with non-clang compiler')
+ # Construct the compiler command-line
+ commands = self.get_exelist(ccache=False) + ['-v', '-E', '-']
+ commands += self.get_always_args()
+ # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env
+ commands += env.coredata.get_external_args(self.for_machine, self.language)
+ mlog.debug('Finding framework path by running: ', ' '.join(commands), '\n')
+ os_env = os.environ.copy()
+ os_env['LC_ALL'] = 'C'
+ _, _, stde = mesonlib.Popen_safe(commands, env=os_env, stdin=subprocess.PIPE)
+ paths = [] # T.List[str]
+ for line in stde.split('\n'):
+ if '(framework directory)' not in line:
+ continue
+ # line is of the form:
+ # ` /path/to/framework (framework directory)`
+ paths.append(line[:-21].strip())
+ return paths
+
+ def _find_framework_real(self, name: str, env: 'Environment', extra_dirs: T.List[str], allow_system: bool) -> T.Optional[T.List[str]]:
+ code = 'int main(void) { return 0; }'
+ link_args = []
+ for d in extra_dirs:
+ link_args += ['-F' + d]
+ # We can pass -Z to disable searching in the system frameworks, but
+ # then we must also pass -L/usr/lib to pick up libSystem.dylib
+ extra_args = [] if allow_system else ['-Z', '-L/usr/lib']
+ link_args += ['-framework', name]
+ if self.links(code, env, extra_args=(extra_args + link_args), disable_cache=True)[0]:
+ return link_args
+ return None
+
+ def _find_framework_impl(self, name: str, env: 'Environment', extra_dirs: T.List[str],
+ allow_system: bool) -> T.Optional[T.List[str]]:
+ if isinstance(extra_dirs, str):
+ extra_dirs = [extra_dirs]
+ key = (tuple(self.exelist), name, tuple(extra_dirs), allow_system)
+ if key in self.find_framework_cache:
+ value = self.find_framework_cache[key]
+ else:
+ value = self._find_framework_real(name, env, extra_dirs, allow_system)
+ self.find_framework_cache[key] = value
+ if value is None:
+ return None
+ return value.copy()
+
+ def find_framework(self, name: str, env: 'Environment', extra_dirs: T.List[str],
+ allow_system: bool = True) -> T.Optional[T.List[str]]:
+ '''
+ Finds the framework with the specified name, and returns link args for
+ the same or returns None when the framework is not found.
+ '''
+ # TODO: should probably check for macOS?
+ return self._find_framework_impl(name, env, extra_dirs, allow_system)
+
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ # TODO: does this belong here or in GnuLike or maybe PosixLike?
+ return []
+
+ def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ # TODO: does this belong here or in GnuLike or maybe PosixLike?
+ return []
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ # TODO: does this belong here or in GnuLike or maybe PosixLike?
+ host_m = env.machines[self.for_machine]
+ if host_m.is_haiku() or host_m.is_darwin():
+ return []
+ return ['-pthread']
+
+ def linker_to_compiler_args(self, args: T.List[str]) -> T.List[str]:
+ return args.copy()
+
+ def has_arguments(self, args: T.List[str], env: 'Environment', code: str,
+ mode: str) -> T.Tuple[bool, bool]:
+ return self.compiles(code, env, extra_args=args, mode=mode)
+
+ def _has_multi_arguments(self, args: T.List[str], env: 'Environment', code: str) -> T.Tuple[bool, bool]:
+ new_args = [] # type: T.List[str]
+ for arg in args:
+ # some compilers, e.g. GCC, don't warn for unsupported warning-disable
+ # flags, so when we are testing a flag like "-Wno-forgotten-towel", also
+ # check the equivalent enable flag too "-Wforgotten-towel"
+ if arg.startswith('-Wno-'):
+ new_args.append('-W' + arg[5:])
+ if arg.startswith('-Wl,'):
+ mlog.warning(f'{arg} looks like a linker argument, '
+ 'but has_argument and other similar methods only '
+ 'support checking compiler arguments. Using them '
+ 'to check linker arguments are never supported, '
+ 'and results are likely to be wrong regardless of '
+ 'the compiler you are using. has_link_argument or '
+ 'other similar method can be used instead.')
+ new_args.append(arg)
+ return self.has_arguments(new_args, env, code, mode='compile')
+
+ def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ return self._has_multi_arguments(args, env, 'extern int i;\nint i;\n')
+
+ def _has_multi_link_arguments(self, args: T.List[str], env: 'Environment', code: str) -> T.Tuple[bool, bool]:
+ # First time we check for link flags we need to first check if we have
+ # --fatal-warnings, otherwise some linker checks could give some
+ # false positive.
+ args = self.linker.fatal_warnings() + args
+ args = self.linker_to_compiler_args(args)
+ return self.has_arguments(args, env, code, mode='link')
+
+ def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ return self._has_multi_link_arguments(args, env, 'int main(void) { return 0; }\n')
+
+ @staticmethod
+ def _concatenate_string_literals(s: str) -> str:
+ pattern = re.compile(r'(?P<pre>.*([^\\]")|^")(?P<str1>([^\\"]|\\.)*)"\s+"(?P<str2>([^\\"]|\\.)*)(?P<post>".*)')
+ ret = s
+ m = pattern.match(ret)
+ while m:
+ ret = ''.join(m.group('pre', 'str1', 'str2', 'post'))
+ m = pattern.match(ret)
+ return ret
+
+ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
+ # Most compilers (such as GCC and Clang) only warn about unknown or
+ # ignored attributes, so force an error. Overridden in GCC and Clang
+ # mixins.
+ return ['-Werror']
+
+ def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]:
+ # Just assume that if we're not on windows that dllimport and dllexport
+ # don't work
+ m = env.machines[self.for_machine]
+ if not (m.is_windows() or m.is_cygwin()):
+ if name in {'dllimport', 'dllexport'}:
+ return False, False
+
+ return self.compiles(self.attribute_check_func(name), env,
+ extra_args=self.get_has_func_attribute_extra_args(name))
+
+ def get_disable_assert_args(self) -> T.List[str]:
+ return ['-DNDEBUG']
+
+ @functools.lru_cache(maxsize=None)
+ def can_compile(self, src: 'mesonlib.FileOrString') -> bool:
+ # Files we preprocess can be anything, e.g. .in
+ if self.mode == 'PREPROCESSOR':
+ return True
+ return super().can_compile(src)
+
+ def get_preprocessor(self) -> Compiler:
+ if not self.preprocessor:
+ self.preprocessor = copy.copy(self)
+ self.preprocessor.exelist = self.exelist + self.get_preprocess_to_file_args()
+ self.preprocessor.mode = 'PREPROCESSOR'
+ self.modes.append(self.preprocessor)
+ return self.preprocessor
diff --git a/mesonbuild/compilers/mixins/compcert.py b/mesonbuild/compilers/mixins/compcert.py
new file mode 100644
index 0000000..a0394a9
--- /dev/null
+++ b/mesonbuild/compilers/mixins/compcert.py
@@ -0,0 +1,137 @@
+# Copyright 2012-2019 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
+
+"""Representations specific to the CompCert C compiler family."""
+
+import os
+import re
+import typing as T
+
+if T.TYPE_CHECKING:
+ from envconfig import MachineInfo
+ from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
+ccomp_buildtype_args = {
+ 'plain': [''],
+ 'debug': ['-O0', '-g'],
+ 'debugoptimized': ['-O0', '-g'],
+ 'release': ['-O3'],
+ 'minsize': ['-Os'],
+ 'custom': ['-Obranchless'],
+} # type: T.Dict[str, T.List[str]]
+
+ccomp_optimization_args = {
+ 'plain': [],
+ '0': ['-O0'],
+ 'g': ['-O0'],
+ '1': ['-O1'],
+ '2': ['-O2'],
+ '3': ['-O3'],
+ 's': ['-Os']
+} # type: T.Dict[str, T.List[str]]
+
+ccomp_debug_args = {
+ False: [],
+ True: ['-g']
+} # type: T.Dict[bool, T.List[str]]
+
+# As of CompCert 20.04, these arguments should be passed to the underlying gcc linker (via -WUl,<arg>)
+# There are probably (many) more, but these are those used by picolibc
+ccomp_args_to_wul = [
+ r"^-ffreestanding$",
+ r"^-r$"
+] # type: T.List[str]
+
+class CompCertCompiler(Compiler):
+
+ id = 'ccomp'
+
+ def __init__(self) -> None:
+ # Assembly
+ self.can_compile_suffixes.add('s')
+ default_warn_args = [] # type: T.List[str]
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + [],
+ '3': default_warn_args + [],
+ 'everything': default_warn_args + []} # type: T.Dict[str, T.List[str]]
+
+ def get_always_args(self) -> T.List[str]:
+ return []
+
+ def get_pic_args(self) -> T.List[str]:
+ # As of now, CompCert does not support PIC
+ return []
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return ccomp_buildtype_args[buildtype]
+
+ def get_pch_suffix(self) -> str:
+ return 'pch'
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ return []
+
+ @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"
+ patched_args = [] # type: T.List[str]
+ for arg in args:
+ added = 0
+ for ptrn in ccomp_args_to_wul:
+ if re.match(ptrn, arg):
+ patched_args.append('-WUl,' + arg)
+ added = 1
+ if not added:
+ patched_args.append(arg)
+ return patched_args
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ return []
+
+ def get_preprocess_only_args(self) -> T.List[str]:
+ return ['-E']
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return ['-c']
+
+ def get_coverage_args(self) -> T.List[str]:
+ return []
+
+ def get_no_stdinc_args(self) -> T.List[str]:
+ return ['-nostdinc']
+
+ def get_no_stdlib_link_args(self) -> T.List[str]:
+ return ['-nostdlib']
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return ccomp_optimization_args[optimization_level]
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return ccomp_debug_args[is_debug]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:9] == '-I':
+ parameter_list[idx] = i[:9] + os.path.normpath(os.path.join(build_dir, i[9:]))
+
+ return parameter_list
diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py
new file mode 100644
index 0000000..872649b
--- /dev/null
+++ b/mesonbuild/compilers/mixins/elbrus.py
@@ -0,0 +1,101 @@
+# Copyright 2019 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
+
+"""Abstractions for the Elbrus family of compilers."""
+
+import os
+import typing as T
+import subprocess
+import re
+
+from .gnu import GnuLikeCompiler
+from .gnu import gnu_optimization_args
+from ...mesonlib import Popen_safe, OptionKey
+
+if T.TYPE_CHECKING:
+ from ...environment import Environment
+ from ...coredata import KeyedOptionDictType
+
+
+class ElbrusCompiler(GnuLikeCompiler):
+ # Elbrus compiler is nearly like GCC, but does not support
+ # PCH, LTO, sanitizers and color output as of version 1.21.x.
+
+ id = 'lcc'
+
+ def __init__(self) -> None:
+ super().__init__()
+ self.base_options = {OptionKey(o) for o in ['b_pgo', 'b_coverage', 'b_ndebug', 'b_staticpic', 'b_lundef', 'b_asneeded']}
+ default_warn_args = ['-Wall']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': default_warn_args + ['-Wextra', '-Wpedantic']}
+
+ # FIXME: use _build_wrapper to call this so that linker flags from the env
+ # get applied
+ def get_library_dirs(self, env: 'Environment', elf_class: T.Optional[int] = None) -> T.List[str]:
+ os_env = os.environ.copy()
+ os_env['LC_ALL'] = 'C'
+ stdo = Popen_safe(self.get_exelist(ccache=False) + ['--print-search-dirs'], env=os_env)[1]
+ for line in stdo.split('\n'):
+ if line.startswith('libraries:'):
+ # lcc does not include '=' in --print-search-dirs output. Also it could show nonexistent dirs.
+ libstr = line.split(' ', 1)[1]
+ return [os.path.realpath(p) for p in libstr.split(':') if os.path.exists(p)]
+ return []
+
+ def get_program_dirs(self, env: 'Environment') -> T.List[str]:
+ os_env = os.environ.copy()
+ os_env['LC_ALL'] = 'C'
+ stdo = Popen_safe(self.get_exelist(ccache=False) + ['--print-search-dirs'], env=os_env)[1]
+ for line in stdo.split('\n'):
+ if line.startswith('programs:'):
+ # lcc does not include '=' in --print-search-dirs output.
+ libstr = line.split(' ', 1)[1]
+ return [os.path.realpath(p) for p in libstr.split(':')]
+ return []
+
+ def get_default_include_dirs(self) -> T.List[str]:
+ os_env = os.environ.copy()
+ os_env['LC_ALL'] = 'C'
+ p = subprocess.Popen(self.get_exelist(ccache=False) + ['-xc', '-E', '-v', '-'], env=os_env, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stderr = p.stderr.read().decode('utf-8', errors='replace')
+ includes = []
+ for line in stderr.split('\n'):
+ if line.lstrip().startswith('--sys_include'):
+ includes.append(re.sub(r'\s*\\$', '', re.sub(r'^\s*--sys_include\s*', '', line)))
+ return includes
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return gnu_optimization_args[optimization_level]
+
+ def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.List[str]:
+ return ['-r', '-nodefaultlibs', '-nostartfiles', '-o', prelink_name] + obj_list
+
+ def get_pch_suffix(self) -> str:
+ # Actually it's not supported for now, but probably will be supported in future
+ return 'pch'
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ std = options[OptionKey('std', lang=self.language, machine=self.for_machine)]
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+ def openmp_flags(self) -> T.List[str]:
+ return ['-fopenmp']
diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py
new file mode 100644
index 0000000..77b1c33
--- /dev/null
+++ b/mesonbuild/compilers/mixins/emscripten.py
@@ -0,0 +1,101 @@
+# Copyright 2019 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
+
+"""Provides a mixin for shared code between C and C++ Emscripten compilers."""
+
+import os.path
+import typing as T
+
+from ... import coredata
+from ... import mesonlib
+from ...mesonlib import OptionKey
+from ...mesonlib import LibType
+
+if T.TYPE_CHECKING:
+ from ...environment import Environment
+ from ...compilers.compilers import Compiler
+ from ...dependencies import Dependency
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
+
+def wrap_js_includes(args: T.List[str]) -> T.List[str]:
+ final_args = []
+ for i in args:
+ if i.endswith('.js') and not i.startswith('-'):
+ final_args += ['--js-library', i]
+ else:
+ final_args += [i]
+ return final_args
+
+class EmscriptenMixin(Compiler):
+
+ def _get_compile_output(self, dirname: str, mode: str) -> str:
+ # In pre-processor mode, the output is sent to stdout and discarded
+ if mode == 'preprocess':
+ return None
+ # Unlike sane toolchains, emcc infers the kind of output from its name.
+ # This is the only reason why this method is overridden; compiler tests
+ # do not work well with the default exe/obj suffices.
+ if mode == 'link':
+ suffix = 'js'
+ else:
+ suffix = 'o'
+ return os.path.join(dirname, 'output.' + suffix)
+
+ def thread_link_flags(self, env: 'Environment') -> T.List[str]:
+ args = ['-pthread']
+ count: int = env.coredata.options[OptionKey('thread_count', lang=self.language, machine=self.for_machine)].value
+ if count:
+ args.append(f'-sPTHREAD_POOL_SIZE={count}')
+ return args
+
+ def get_options(self) -> 'coredata.MutableKeyedOptionDictType':
+ opts = super().get_options()
+ key = OptionKey('thread_count', machine=self.for_machine, lang=self.language)
+ opts.update({
+ key: coredata.UserIntegerOption(
+ 'Number of threads to use in web assembly, set to 0 to disable',
+ (0, None, 4), # Default was picked at random
+ ),
+ })
+
+ return opts
+
+ @classmethod
+ def native_args_to_unix(cls, args: T.List[str]) -> T.List[str]:
+ return wrap_js_includes(super().native_args_to_unix(args))
+
+ def get_dependency_link_args(self, dep: 'Dependency') -> T.List[str]:
+ return wrap_js_includes(super().get_dependency_link_args(dep))
+
+ def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
+ libtype: LibType = LibType.PREFER_SHARED) -> T.Optional[T.List[str]]:
+ if not libname.endswith('.js'):
+ return super().find_library(libname, env, extra_dirs, libtype)
+ if os.path.isabs(libname):
+ if os.path.exists(libname):
+ return [libname]
+ if len(extra_dirs) == 0:
+ raise mesonlib.EnvironmentException('Looking up Emscripten JS libraries requires either an absolute path or specifying extra_dirs.')
+ for d in extra_dirs:
+ abs_path = os.path.join(d, libname)
+ if os.path.exists(abs_path):
+ return [abs_path]
+ return None
diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py
new file mode 100644
index 0000000..022e7fd
--- /dev/null
+++ b/mesonbuild/compilers/mixins/gnu.py
@@ -0,0 +1,649 @@
+# Copyright 2019-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
+
+"""Provides mixins for GNU compilers and GNU-like compilers."""
+
+import abc
+import functools
+import os
+import multiprocessing
+import pathlib
+import re
+import subprocess
+import typing as T
+
+from ... import mesonlib
+from ... import mlog
+from ...mesonlib import OptionKey
+
+if T.TYPE_CHECKING:
+ from ..._typing import ImmutableListProtocol
+ from ...environment import Environment
+ from ..compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
+# XXX: prevent circular references.
+# FIXME: this really is a posix interface not a c-like interface
+clike_debug_args = {
+ False: [],
+ True: ['-g'],
+} # type: T.Dict[bool, T.List[str]]
+
+gnulike_buildtype_args = {
+ 'plain': [],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+} # type: T.Dict[str, T.List[str]]
+
+gnu_optimization_args = {
+ 'plain': [],
+ '0': ['-O0'],
+ 'g': ['-Og'],
+ '1': ['-O1'],
+ '2': ['-O2'],
+ '3': ['-O3'],
+ 's': ['-Os'],
+} # type: T.Dict[str, T.List[str]]
+
+gnulike_instruction_set_args = {
+ 'mmx': ['-mmmx'],
+ 'sse': ['-msse'],
+ 'sse2': ['-msse2'],
+ 'sse3': ['-msse3'],
+ 'ssse3': ['-mssse3'],
+ 'sse41': ['-msse4.1'],
+ 'sse42': ['-msse4.2'],
+ 'avx': ['-mavx'],
+ 'avx2': ['-mavx2'],
+ 'neon': ['-mfpu=neon'],
+} # type: T.Dict[str, T.List[str]]
+
+gnu_symbol_visibility_args = {
+ '': [],
+ 'default': ['-fvisibility=default'],
+ 'internal': ['-fvisibility=internal'],
+ 'hidden': ['-fvisibility=hidden'],
+ 'protected': ['-fvisibility=protected'],
+ 'inlineshidden': ['-fvisibility=hidden', '-fvisibility-inlines-hidden'],
+} # type: T.Dict[str, T.List[str]]
+
+gnu_color_args = {
+ 'auto': ['-fdiagnostics-color=auto'],
+ 'always': ['-fdiagnostics-color=always'],
+ 'never': ['-fdiagnostics-color=never'],
+} # type: T.Dict[str, T.List[str]]
+
+# Warnings collected from the GCC source and documentation. This is an
+# objective set of all the warnings flags that apply to general projects: the
+# only ones omitted are those that require a project-specific value, or are
+# related to non-standard or legacy language support. This behaves roughly
+# like -Weverything in clang. Warnings implied by -Wall, -Wextra, or
+# higher-level warnings already enabled here are not included in these lists to
+# keep them as short as possible. History goes back to GCC 3.0.0, everything
+# earlier is considered historical and listed under version 0.0.0.
+
+# GCC warnings for all C-family languages
+# Omitted non-general warnings:
+# -Wabi=
+# -Waggregate-return
+# -Walloc-size-larger-than=BYTES
+# -Walloca-larger-than=BYTES
+# -Wframe-larger-than=BYTES
+# -Wlarger-than=BYTES
+# -Wstack-usage=BYTES
+# -Wsystem-headers
+# -Wtrampolines
+# -Wvla-larger-than=BYTES
+#
+# Omitted warnings enabled elsewhere in meson:
+# -Winvalid-pch (GCC 3.4.0)
+gnu_common_warning_args = {
+ "0.0.0": [
+ "-Wcast-qual",
+ "-Wconversion",
+ "-Wfloat-equal",
+ "-Wformat=2",
+ "-Winline",
+ "-Wmissing-declarations",
+ "-Wredundant-decls",
+ "-Wshadow",
+ "-Wundef",
+ "-Wuninitialized",
+ "-Wwrite-strings",
+ ],
+ "3.0.0": [
+ "-Wdisabled-optimization",
+ "-Wpacked",
+ "-Wpadded",
+ ],
+ "3.3.0": [
+ "-Wmultichar",
+ "-Wswitch-default",
+ "-Wswitch-enum",
+ "-Wunused-macros",
+ ],
+ "4.0.0": [
+ "-Wmissing-include-dirs",
+ ],
+ "4.1.0": [
+ "-Wunsafe-loop-optimizations",
+ "-Wstack-protector",
+ ],
+ "4.2.0": [
+ "-Wstrict-overflow=5",
+ ],
+ "4.3.0": [
+ "-Warray-bounds=2",
+ "-Wlogical-op",
+ "-Wstrict-aliasing=3",
+ "-Wvla",
+ ],
+ "4.6.0": [
+ "-Wdouble-promotion",
+ "-Wsuggest-attribute=const",
+ "-Wsuggest-attribute=noreturn",
+ "-Wsuggest-attribute=pure",
+ "-Wtrampolines",
+ ],
+ "4.7.0": [
+ "-Wvector-operation-performance",
+ ],
+ "4.8.0": [
+ "-Wsuggest-attribute=format",
+ ],
+ "4.9.0": [
+ "-Wdate-time",
+ ],
+ "5.1.0": [
+ "-Wformat-signedness",
+ "-Wnormalized=nfc",
+ ],
+ "6.1.0": [
+ "-Wduplicated-cond",
+ "-Wnull-dereference",
+ "-Wshift-negative-value",
+ "-Wshift-overflow=2",
+ "-Wunused-const-variable=2",
+ ],
+ "7.1.0": [
+ "-Walloca",
+ "-Walloc-zero",
+ "-Wformat-overflow=2",
+ "-Wformat-truncation=2",
+ "-Wstringop-overflow=3",
+ ],
+ "7.2.0": [
+ "-Wduplicated-branches",
+ ],
+ "8.1.0": [
+ "-Wattribute-alias=2",
+ "-Wcast-align=strict",
+ "-Wsuggest-attribute=cold",
+ "-Wsuggest-attribute=malloc",
+ ],
+ "10.1.0": [
+ "-Wanalyzer-too-complex",
+ "-Warith-conversion",
+ ],
+ "12.1.0": [
+ "-Wbidi-chars=ucn",
+ "-Wopenacc-parallelism",
+ "-Wtrivial-auto-var-init",
+ ],
+} # type: T.Dict[str, T.List[str]]
+
+# GCC warnings for C
+# Omitted non-general or legacy warnings:
+# -Wc11-c2x-compat
+# -Wc90-c99-compat
+# -Wc99-c11-compat
+# -Wdeclaration-after-statement
+# -Wtraditional
+# -Wtraditional-conversion
+gnu_c_warning_args = {
+ "0.0.0": [
+ "-Wbad-function-cast",
+ "-Wmissing-prototypes",
+ "-Wnested-externs",
+ "-Wstrict-prototypes",
+ ],
+ "3.4.0": [
+ "-Wold-style-definition",
+ "-Winit-self",
+ ],
+ "4.1.0": [
+ "-Wc++-compat",
+ ],
+ "4.5.0": [
+ "-Wunsuffixed-float-constants",
+ ],
+} # type: T.Dict[str, T.List[str]]
+
+# GCC warnings for C++
+# Omitted non-general or legacy warnings:
+# -Wc++0x-compat
+# -Wc++1z-compat
+# -Wc++2a-compat
+# -Wctad-maybe-unsupported
+# -Wnamespaces
+# -Wtemplates
+gnu_cpp_warning_args = {
+ "0.0.0": [
+ "-Wctor-dtor-privacy",
+ "-Weffc++",
+ "-Wnon-virtual-dtor",
+ "-Wold-style-cast",
+ "-Woverloaded-virtual",
+ "-Wsign-promo",
+ ],
+ "4.0.1": [
+ "-Wstrict-null-sentinel",
+ ],
+ "4.6.0": [
+ "-Wnoexcept",
+ ],
+ "4.7.0": [
+ "-Wzero-as-null-pointer-constant",
+ ],
+ "4.8.0": [
+ "-Wabi-tag",
+ "-Wuseless-cast",
+ ],
+ "4.9.0": [
+ "-Wconditionally-supported",
+ ],
+ "5.1.0": [
+ "-Wsuggest-final-methods",
+ "-Wsuggest-final-types",
+ "-Wsuggest-override",
+ ],
+ "6.1.0": [
+ "-Wmultiple-inheritance",
+ "-Wplacement-new=2",
+ "-Wvirtual-inheritance",
+ ],
+ "7.1.0": [
+ "-Waligned-new=all",
+ "-Wnoexcept-type",
+ "-Wregister",
+ ],
+ "8.1.0": [
+ "-Wcatch-value=3",
+ "-Wextra-semi",
+ ],
+ "9.1.0": [
+ "-Wdeprecated-copy-dtor",
+ "-Wredundant-move",
+ ],
+ "10.1.0": [
+ "-Wcomma-subscript",
+ "-Wmismatched-tags",
+ "-Wredundant-tags",
+ "-Wvolatile",
+ ],
+ "11.1.0": [
+ "-Wdeprecated-enum-enum-conversion",
+ "-Wdeprecated-enum-float-conversion",
+ "-Winvalid-imported-macros",
+ ],
+} # type: T.Dict[str, T.List[str]]
+
+# GCC warnings for Objective C and Objective C++
+# Omitted non-general or legacy warnings:
+# -Wtraditional
+# -Wtraditional-conversion
+gnu_objc_warning_args = {
+ "0.0.0": [
+ "-Wselector",
+ ],
+ "3.3": [
+ "-Wundeclared-selector",
+ ],
+ "4.1.0": [
+ "-Wassign-intercept",
+ "-Wstrict-selector-match",
+ ],
+} # type: T.Dict[str, T.List[str]]
+
+
+@functools.lru_cache(maxsize=None)
+def gnulike_default_include_dirs(compiler: T.Tuple[str, ...], lang: str) -> 'ImmutableListProtocol[str]':
+ lang_map = {
+ 'c': 'c',
+ 'cpp': 'c++',
+ 'objc': 'objective-c',
+ 'objcpp': 'objective-c++'
+ }
+ if lang not in lang_map:
+ return []
+ lang = lang_map[lang]
+ env = os.environ.copy()
+ env["LC_ALL"] = 'C'
+ cmd = list(compiler) + [f'-x{lang}', '-E', '-v', '-']
+ _, stdout, _ = mesonlib.Popen_safe(cmd, stderr=subprocess.STDOUT, env=env)
+ parse_state = 0
+ paths = [] # type: T.List[str]
+ for line in stdout.split('\n'):
+ line = line.strip(' \n\r\t')
+ if parse_state == 0:
+ if line == '#include "..." search starts here:':
+ parse_state = 1
+ elif parse_state == 1:
+ if line == '#include <...> search starts here:':
+ parse_state = 2
+ else:
+ paths.append(line)
+ elif parse_state == 2:
+ if line == 'End of search list.':
+ break
+ else:
+ paths.append(line)
+ if not paths:
+ mlog.warning('No include directory found parsing "{cmd}" output'.format(cmd=" ".join(cmd)))
+ # Append a normalized copy of paths to make path lookup easier
+ paths += [os.path.normpath(x) for x in paths]
+ return paths
+
+
+class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
+ """
+ GnuLikeCompiler is a common interface to all compilers implementing
+ the GNU-style commandline interface. This includes GCC, Clang
+ and ICC. Certain functionality between them is different and requires
+ that the actual concrete subclass define their own implementation.
+ """
+
+ LINKER_PREFIX = '-Wl,'
+
+ def __init__(self) -> None:
+ self.base_options = {
+ OptionKey(o) for o in ['b_pch', 'b_lto', 'b_pgo', 'b_coverage',
+ 'b_ndebug', 'b_staticpic', 'b_pie']}
+ if not (self.info.is_windows() or self.info.is_cygwin() or self.info.is_openbsd()):
+ self.base_options.add(OptionKey('b_lundef'))
+ if not self.info.is_windows() or self.info.is_cygwin():
+ self.base_options.add(OptionKey('b_asneeded'))
+ if not self.info.is_hurd():
+ self.base_options.add(OptionKey('b_sanitize'))
+ # All GCC-like backends can do assembly
+ self.can_compile_suffixes.add('s')
+
+ def get_pic_args(self) -> T.List[str]:
+ if self.info.is_windows() or self.info.is_cygwin() or self.info.is_darwin():
+ return [] # On Window and OS X, pic is always on.
+ return ['-fPIC']
+
+ def get_pie_args(self) -> T.List[str]:
+ return ['-fPIE']
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return gnulike_buildtype_args[buildtype]
+
+ @abc.abstractmethod
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ pass
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return clike_debug_args[is_debug]
+
+ @abc.abstractmethod
+ def get_pch_suffix(self) -> str:
+ pass
+
+ def split_shlib_to_parts(self, fname: str) -> T.Tuple[str, str]:
+ return os.path.dirname(fname), fname
+
+ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
+ return gnulike_instruction_set_args.get(instruction_set, None)
+
+ def get_default_include_dirs(self) -> T.List[str]:
+ return gnulike_default_include_dirs(tuple(self.get_exelist(ccache=False)), self.language).copy()
+
+ @abc.abstractmethod
+ def openmp_flags(self) -> T.List[str]:
+ pass
+
+ def gnu_symbol_visibility_args(self, vistype: str) -> T.List[str]:
+ if vistype == 'inlineshidden' and self.language not in {'cpp', 'objcpp'}:
+ vistype = 'hidden'
+ return gnu_symbol_visibility_args[vistype]
+
+ def gen_vs_module_defs_args(self, defsfile: str) -> T.List[str]:
+ if not isinstance(defsfile, str):
+ raise RuntimeError('Module definitions file should be str')
+ # On Windows targets, .def files may be specified on the linker command
+ # line like an object file.
+ if self.info.is_windows() or self.info.is_cygwin():
+ return [defsfile]
+ # For other targets, discard the .def file.
+ return []
+
+ def get_argument_syntax(self) -> str:
+ return 'gcc'
+
+ def get_profile_generate_args(self) -> T.List[str]:
+ return ['-fprofile-generate']
+
+ def get_profile_use_args(self) -> T.List[str]:
+ return ['-fprofile-use']
+
+ def get_gui_app_args(self, value: bool) -> T.List[str]:
+ return ['-mwindows' if value else '-mconsole']
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-I' or i[:2] == '-L':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+
+ return parameter_list
+
+ @functools.lru_cache()
+ def _get_search_dirs(self, env: 'Environment') -> str:
+ extra_args = ['--print-search-dirs']
+ with self._build_wrapper('', env, extra_args=extra_args,
+ dependencies=None, mode='compile',
+ want_output=True) as p:
+ return p.stdout
+
+ def _split_fetch_real_dirs(self, pathstr: str) -> T.List[str]:
+ # We need to use the path separator used by the compiler for printing
+ # lists of paths ("gcc --print-search-dirs"). By default
+ # we assume it uses the platform native separator.
+ pathsep = os.pathsep
+
+ # clang uses ':' instead of ';' on Windows https://reviews.llvm.org/D61121
+ # so we need to repair things like 'C:\foo:C:\bar'
+ if pathsep == ';':
+ pathstr = re.sub(r':([^/\\])', r';\1', pathstr)
+
+ # pathlib treats empty paths as '.', so filter those out
+ paths = [p for p in pathstr.split(pathsep) if p]
+
+ result = []
+ for p in paths:
+ # GCC returns paths like this:
+ # /usr/lib/gcc/x86_64-linux-gnu/8/../../../../x86_64-linux-gnu/lib
+ # It would make sense to normalize them to get rid of the .. parts
+ # Sadly when you are on a merged /usr fs it also kills these:
+ # /lib/x86_64-linux-gnu
+ # since /lib is a symlink to /usr/lib. This would mean
+ # paths under /lib would be considered not a "system path",
+ # which is wrong and breaks things. Store everything, just to be sure.
+ pobj = pathlib.Path(p)
+ unresolved = pobj.as_posix()
+ if pobj.exists():
+ if unresolved not in result:
+ result.append(unresolved)
+ try:
+ resolved = pathlib.Path(p).resolve().as_posix()
+ if resolved not in result:
+ result.append(resolved)
+ except FileNotFoundError:
+ pass
+ return result
+
+ def get_compiler_dirs(self, env: 'Environment', name: str) -> T.List[str]:
+ '''
+ Get dirs from the compiler, either `libraries:` or `programs:`
+ '''
+ stdo = self._get_search_dirs(env)
+ for line in stdo.split('\n'):
+ if line.startswith(name + ':'):
+ return self._split_fetch_real_dirs(line.split('=', 1)[1])
+ return []
+
+ def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T.List[str]:
+ # This provides a base for many compilers, GCC and Clang override this
+ # for their specific arguments
+ return ['-flto']
+
+ def sanitizer_compile_args(self, value: str) -> T.List[str]:
+ if value == 'none':
+ return []
+ args = ['-fsanitize=' + value]
+ if 'address' in value: # for -fsanitize=address,undefined
+ args.append('-fno-omit-frame-pointer')
+ return args
+
+ def get_output_args(self, target: str) -> T.List[str]:
+ return ['-o', target]
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return ['-MD', '-MQ', outtarget, '-MF', outfile]
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return ['-c']
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if not path:
+ path = '.'
+ if is_system:
+ return ['-isystem' + path]
+ return ['-I' + path]
+
+ @classmethod
+ def use_linker_args(cls, linker: str, version: str) -> T.List[str]:
+ if linker not in {'gold', 'bfd', 'lld'}:
+ raise mesonlib.MesonException(
+ f'Unsupported linker, only bfd, gold, and lld are supported, not {linker}.')
+ return [f'-fuse-ld={linker}']
+
+ def get_coverage_args(self) -> T.List[str]:
+ return ['--coverage']
+
+ def get_preprocess_to_file_args(self) -> T.List[str]:
+ # We want to allow preprocessing files with any extension, such as
+ # foo.c.in. In that case we need to tell GCC/CLANG to treat them as
+ # assembly file.
+ return self.get_preprocess_only_args() + ['-x', 'assembler-with-cpp']
+
+
+class GnuCompiler(GnuLikeCompiler):
+ """
+ GnuCompiler represents an actual GCC in its many incarnations.
+ Compilers imitating GCC (Clang/Intel) should use the GnuLikeCompiler ABC.
+ """
+ id = 'gcc'
+
+ def __init__(self, defines: T.Optional[T.Dict[str, str]]):
+ super().__init__()
+ self.defines = defines or {}
+ self.base_options.update({OptionKey('b_colorout'), OptionKey('b_lto_threads')})
+
+ def get_colorout_args(self, colortype: str) -> T.List[str]:
+ if mesonlib.version_compare(self.version, '>=4.9.0'):
+ return gnu_color_args[colortype][:]
+ return []
+
+ def get_warn_args(self, level: str) -> T.List[str]:
+ # Mypy doesn't understand cooperative inheritance
+ args = super().get_warn_args(level)
+ if mesonlib.version_compare(self.version, '<4.8.0') and '-Wpedantic' in args:
+ # -Wpedantic was added in 4.8.0
+ # https://gcc.gnu.org/gcc-4.8/changes.html
+ args[args.index('-Wpedantic')] = '-pedantic'
+ return args
+
+ def supported_warn_args(self, warn_args_by_version: T.Dict[str, T.List[str]]) -> T.List[str]:
+ result = []
+ for version, warn_args in warn_args_by_version.items():
+ if mesonlib.version_compare(self.version, '>=' + version):
+ result += warn_args
+ return result
+
+ def has_builtin_define(self, define: str) -> bool:
+ return define in self.defines
+
+ def get_builtin_define(self, define: str) -> T.Optional[str]:
+ if define in self.defines:
+ return self.defines[define]
+ return None
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return gnu_optimization_args[optimization_level]
+
+ def get_pch_suffix(self) -> str:
+ return 'gch'
+
+ def openmp_flags(self) -> T.List[str]:
+ return ['-fopenmp']
+
+ def has_arguments(self, args: T.List[str], env: 'Environment', code: str,
+ mode: str) -> T.Tuple[bool, bool]:
+ # For some compiler command line arguments, the GNU compilers will
+ # emit a warning on stderr indicating that an option is valid for a
+ # another language, but still complete with exit_success
+ with self._build_wrapper(code, env, args, None, mode) as p:
+ result = p.returncode == 0
+ if self.language in {'cpp', 'objcpp'} and 'is valid for C/ObjC' in p.stderr:
+ result = False
+ if self.language in {'c', 'objc'} and 'is valid for C++/ObjC++' in p.stderr:
+ result = False
+ return result, p.cached
+
+ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
+ # GCC only warns about unknown or ignored attributes, so force an
+ # error.
+ return ['-Werror=attributes']
+
+ def get_prelink_args(self, prelink_name: str, obj_list: T.List[str]) -> T.List[str]:
+ return ['-r', '-o', prelink_name] + obj_list
+
+ def get_lto_compile_args(self, *, threads: int = 0, mode: str = 'default') -> T.List[str]:
+ if threads == 0:
+ if mesonlib.version_compare(self.version, '>= 10.0'):
+ return ['-flto=auto']
+ # This matches clang's behavior of using the number of cpus
+ return [f'-flto={multiprocessing.cpu_count()}']
+ elif threads > 0:
+ return [f'-flto={threads}']
+ return super().get_lto_compile_args(threads=threads)
+
+ @classmethod
+ def use_linker_args(cls, linker: str, version: str) -> T.List[str]:
+ if linker == 'mold' and mesonlib.version_compare(version, '>=12.0.1'):
+ return ['-fuse-ld=mold']
+ return super().use_linker_args(linker, version)
+
+ def get_profile_use_args(self) -> T.List[str]:
+ return super().get_profile_use_args() + ['-fprofile-correction']
diff --git a/mesonbuild/compilers/mixins/intel.py b/mesonbuild/compilers/mixins/intel.py
new file mode 100644
index 0000000..b793fa8
--- /dev/null
+++ b/mesonbuild/compilers/mixins/intel.py
@@ -0,0 +1,185 @@
+# Copyright 2019 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
+
+"""Abstractions for the Intel Compiler families.
+
+Intel provides both a posix/gcc-like compiler (ICC) for MacOS and Linux,
+with Meson mixin IntelGnuLikeCompiler.
+For Windows, the Intel msvc-like compiler (ICL) Meson mixin
+is IntelVisualStudioLikeCompiler.
+"""
+
+import os
+import typing as T
+
+from ... import mesonlib
+from ..compilers import CompileCheckMode
+from .gnu import GnuLikeCompiler
+from .visualstudio import VisualStudioLikeCompiler
+
+# XXX: avoid circular dependencies
+# TODO: this belongs in a posix compiler class
+# NOTE: the default Intel optimization is -O2, unlike GNU which defaults to -O0.
+# this can be surprising, particularly for debug builds, so we specify the
+# default as -O0.
+# https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-o
+# https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-g
+# https://software.intel.com/en-us/fortran-compiler-developer-guide-and-reference-o
+# https://software.intel.com/en-us/fortran-compiler-developer-guide-and-reference-g
+# https://software.intel.com/en-us/fortran-compiler-developer-guide-and-reference-traceback
+# https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
+
+
+class IntelGnuLikeCompiler(GnuLikeCompiler):
+ """
+ Tested on linux for ICC 14.0.3, 15.0.6, 16.0.4, 17.0.1, 19.0
+ debugoptimized: -g -O2
+ release: -O3
+ minsize: -O2
+ """
+
+ BUILD_ARGS = {
+ 'plain': [],
+ 'debug': ["-g", "-traceback"],
+ 'debugoptimized': ["-g", "-traceback"],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+ } # type: T.Dict[str, T.List[str]]
+
+ OPTIM_ARGS: T.Dict[str, T.List[str]] = {
+ 'plain': [],
+ '0': ['-O0'],
+ 'g': ['-O0'],
+ '1': ['-O1'],
+ '2': ['-O2'],
+ '3': ['-O3'],
+ 's': ['-Os'],
+ }
+ id = 'intel'
+
+ def __init__(self) -> None:
+ super().__init__()
+ # As of 19.0.0 ICC doesn't have sanitizer, color, or lto support.
+ #
+ # It does have IPO, which serves much the same purpose as LOT, but
+ # there is an unfortunate rule for using IPO (you can't control the
+ # name of the output file) which break assumptions meson makes
+ self.base_options = {mesonlib.OptionKey(o) for o in [
+ 'b_pch', 'b_lundef', 'b_asneeded', 'b_pgo', 'b_coverage',
+ 'b_ndebug', 'b_staticpic', 'b_pie']}
+ self.lang_header = 'none'
+
+ def get_pch_suffix(self) -> str:
+ return 'pchi'
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ return ['-pch', '-pch_dir', os.path.join(pch_dir), '-x',
+ self.lang_header, '-include', header, '-x', 'none']
+
+ def get_pch_name(self, header_name: str) -> str:
+ return os.path.basename(header_name) + '.' + self.get_pch_suffix()
+
+ def openmp_flags(self) -> T.List[str]:
+ if mesonlib.version_compare(self.version, '>=15.0.0'):
+ return ['-qopenmp']
+ else:
+ return ['-openmp']
+
+ def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
+ extra_args = [
+ '-diag-error', '10006', # ignoring unknown option
+ '-diag-error', '10148', # Option not supported
+ '-diag-error', '10155', # ignoring argument required
+ '-diag-error', '10156', # ignoring not argument allowed
+ '-diag-error', '10157', # Ignoring argument of the wrong type
+ '-diag-error', '10158', # Argument must be separate. Can be hit by trying an option like -foo-bar=foo when -foo=bar is a valid option but -foo-bar isn't
+ ]
+ return super().get_compiler_check_args(mode) + extra_args
+
+ def get_profile_generate_args(self) -> T.List[str]:
+ return ['-prof-gen=threadsafe']
+
+ def get_profile_use_args(self) -> T.List[str]:
+ return ['-prof-use']
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return self.BUILD_ARGS[buildtype]
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return self.OPTIM_ARGS[optimization_level]
+
+ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
+ return ['-diag-error', '1292']
+
+
+class IntelVisualStudioLikeCompiler(VisualStudioLikeCompiler):
+
+ """Abstractions for ICL, the Intel compiler on Windows."""
+
+ BUILD_ARGS = {
+ 'plain': [],
+ 'debug': ["/Zi", "/traceback"],
+ 'debugoptimized': ["/Zi", "/traceback"],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+ } # type: T.Dict[str, T.List[str]]
+
+ OPTIM_ARGS: T.Dict[str, T.List[str]] = {
+ 'plain': [],
+ '0': ['/Od'],
+ 'g': ['/Od'],
+ '1': ['/O1'],
+ '2': ['/O2'],
+ '3': ['/O3'],
+ 's': ['/Os'],
+ }
+
+ id = 'intel-cl'
+
+ def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
+ args = super().get_compiler_check_args(mode)
+ if mode is not CompileCheckMode.LINK:
+ args.extend([
+ '/Qdiag-error:10006', # ignoring unknown option
+ '/Qdiag-error:10148', # Option not supported
+ '/Qdiag-error:10155', # ignoring argument required
+ '/Qdiag-error:10156', # ignoring not argument allowed
+ '/Qdiag-error:10157', # Ignoring argument of the wrong type
+ '/Qdiag-error:10158', # Argument must be separate. Can be hit by trying an option like -foo-bar=foo when -foo=bar is a valid option but -foo-bar isn't
+ ])
+ return args
+
+ def get_toolset_version(self) -> T.Optional[str]:
+ # ICL provides a cl.exe that returns the version of MSVC it tries to
+ # emulate, so we'll get the version from that and pass it to the same
+ # function the real MSVC uses to calculate the toolset version.
+ _, _, err = mesonlib.Popen_safe(['cl.exe'])
+ v1, v2, *_ = mesonlib.search_version(err).split('.')
+ version = int(v1 + v2)
+ return self._calculate_toolset_version(version)
+
+ def openmp_flags(self) -> T.List[str]:
+ return ['/Qopenmp']
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return self.BUILD_ARGS[buildtype]
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return self.OPTIM_ARGS[optimization_level]
+
+ def get_pch_base_name(self, header: str) -> str:
+ return os.path.basename(header)
diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py
new file mode 100644
index 0000000..711e3e3
--- /dev/null
+++ b/mesonbuild/compilers/mixins/islinker.py
@@ -0,0 +1,130 @@
+# Copyright 2019 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
+
+"""Mixins for compilers that *are* linkers.
+
+While many compilers (such as gcc and clang) are used by meson to dispatch
+linker commands and other (like MSVC) are not, a few (such as DMD) actually
+are both the linker and compiler in one binary. This module provides mixin
+classes for those cases.
+"""
+
+import typing as T
+
+from ...mesonlib import EnvironmentException, MesonException, is_windows
+
+if T.TYPE_CHECKING:
+ from ...coredata import KeyedOptionDictType
+ from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
+
+class BasicLinkerIsCompilerMixin(Compiler):
+
+ """Provides a baseline of methods that a linker would implement.
+
+ In every case this provides a "no" or "empty" answer. If a compiler
+ implements any of these it needs a different mixin or to override that
+ functionality itself.
+ """
+
+ def sanitizer_link_args(self, value: str) -> 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 []
+
+ def can_linker_accept_rsp(self) -> bool:
+ return is_windows()
+
+ def get_linker_exelist(self) -> T.List[str]:
+ return self.exelist.copy()
+
+ def get_linker_output_args(self, outputname: str) -> T.List[str]:
+ return []
+
+ def get_linker_always_args(self) -> T.List[str]:
+ return []
+
+ def get_linker_lib_prefix(self) -> str:
+ return ''
+
+ def get_option_link_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return []
+
+ def has_multi_link_args(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ return False, False
+
+ def get_link_debugfile_args(self, targetfile: str) -> T.List[str]:
+ return []
+
+ def get_std_shared_lib_link_args(self) -> T.List[str]:
+ return []
+
+ def get_std_shared_module_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ return self.get_std_shared_lib_link_args()
+
+ def get_link_whole_for(self, args: T.List[str]) -> T.List[str]:
+ raise EnvironmentException(f'Linker {self.id} does not support link_whole')
+
+ def get_allow_undefined_link_args(self) -> T.List[str]:
+ raise EnvironmentException(f'Linker {self.id} does not support allow undefined')
+
+ def get_pie_link_args(self) -> T.List[str]:
+ raise EnvironmentException(f'Linker {self.id} does not support position-independent executable')
+
+ def get_undefined_link_args(self) -> T.List[str]:
+ return []
+
+ def get_coverage_link_args(self) -> T.List[str]:
+ return []
+
+ def no_undefined_link_args(self) -> T.List[str]:
+ return []
+
+ def bitcode_args(self) -> T.List[str]:
+ raise MesonException("This linker doesn't support bitcode bundles")
+
+ 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]:
+ raise MesonException("This linker doesn't support soname args")
+
+ 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 ([], set())
+
+ def get_asneeded_args(self) -> T.List[str]:
+ return []
+
+ def get_buildtype_linker_args(self, buildtype: str) -> T.List[str]:
+ return []
+
+ def get_link_debugfile_name(self, targetfile: str) -> str:
+ return ''
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ return []
+
+ def thread_link_flags(self, env: 'Environment') -> T.List[str]:
+ return []
diff --git a/mesonbuild/compilers/mixins/pgi.py b/mesonbuild/compilers/mixins/pgi.py
new file mode 100644
index 0000000..2fa736c
--- /dev/null
+++ b/mesonbuild/compilers/mixins/pgi.py
@@ -0,0 +1,113 @@
+# Copyright 2019 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
+
+"""Abstractions for the PGI family of compilers."""
+
+import typing as T
+import os
+from pathlib import Path
+
+from ..compilers import clike_debug_args, clike_optimization_args
+from ...mesonlib import OptionKey
+
+if T.TYPE_CHECKING:
+ from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
+pgi_buildtype_args = {
+ 'plain': [],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+} # type: T.Dict[str, T.List[str]]
+
+
+class PGICompiler(Compiler):
+
+ id = 'pgi'
+
+ def __init__(self) -> None:
+ self.base_options = {OptionKey('b_pch')}
+
+ default_warn_args = ['-Minform=inform']
+ self.warn_args: T.Dict[str, T.List[str]] = {
+ '0': [],
+ '1': default_warn_args,
+ '2': default_warn_args,
+ '3': default_warn_args,
+ 'everything': default_warn_args
+ }
+
+ def get_module_incdir_args(self) -> T.Tuple[str]:
+ return ('-module', )
+
+ def get_no_warn_args(self) -> T.List[str]:
+ return ['-silent']
+
+ def gen_import_library_args(self, implibname: str) -> T.List[str]:
+ return []
+
+ def get_pic_args(self) -> T.List[str]:
+ # PGI -fPIC is Linux only.
+ if self.info.is_linux():
+ return ['-fPIC']
+ return []
+
+ def openmp_flags(self) -> T.List[str]:
+ return ['-mp']
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return pgi_buildtype_args[buildtype]
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return clike_optimization_args[optimization_level]
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return clike_debug_args[is_debug]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-I' or i[:2] == '-L':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+ return parameter_list
+
+ def get_always_args(self) -> T.List[str]:
+ return []
+
+ def get_pch_suffix(self) -> str:
+ # PGI defaults to .pch suffix for PCH on Linux and Windows with --pch option
+ return 'pch'
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ # PGI supports PCH for C++ only.
+ hdr = Path(pch_dir).resolve().parent / header
+ if self.language == 'cpp':
+ return ['--pch',
+ '--pch_dir', str(hdr.parent),
+ f'-I{hdr.parent}']
+ else:
+ return []
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ # PGI cannot accept -pthread, it's already threaded
+ return []
diff --git a/mesonbuild/compilers/mixins/ti.py b/mesonbuild/compilers/mixins/ti.py
new file mode 100644
index 0000000..950c97f
--- /dev/null
+++ b/mesonbuild/compilers/mixins/ti.py
@@ -0,0 +1,151 @@
+# Copyright 2012-2019 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
+
+"""Representations specific to the Texas Instruments compiler family."""
+
+import os
+import typing as T
+
+from ...mesonlib import EnvironmentException
+
+if T.TYPE_CHECKING:
+ from ...envconfig import MachineInfo
+ from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
+ti_buildtype_args = {
+ 'plain': [],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+} # type: T.Dict[str, T.List[str]]
+
+ti_optimization_args = {
+ 'plain': [],
+ '0': ['-O0'],
+ 'g': ['-Ooff'],
+ '1': ['-O1'],
+ '2': ['-O2'],
+ '3': ['-O3'],
+ 's': ['-O4']
+} # type: T.Dict[str, T.List[str]]
+
+ti_debug_args = {
+ False: [],
+ True: ['-g']
+} # type: T.Dict[bool, T.List[str]]
+
+
+class TICompiler(Compiler):
+
+ id = 'ti'
+
+ def __init__(self) -> None:
+ if not self.is_cross:
+ raise EnvironmentException('TI compilers only support cross-compilation.')
+
+ self.can_compile_suffixes.add('asm') # Assembly
+ self.can_compile_suffixes.add('cla') # Control Law Accelerator (CLA) used in C2000
+
+ default_warn_args = [] # type: T.List[str]
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + [],
+ '3': default_warn_args + [],
+ 'everything': default_warn_args + []} # type: T.Dict[str, T.List[str]]
+
+ def get_pic_args(self) -> T.List[str]:
+ # PIC support is not enabled by default for TI compilers,
+ # if users want to use it, they need to add the required arguments explicitly
+ return []
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return ti_buildtype_args[buildtype]
+
+ def get_pch_suffix(self) -> str:
+ return 'pch'
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ return []
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ return []
+
+ def get_coverage_args(self) -> T.List[str]:
+ return []
+
+ def get_no_stdinc_args(self) -> T.List[str]:
+ return []
+
+ def get_no_stdlib_link_args(self) -> T.List[str]:
+ return []
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return ti_optimization_args[optimization_level]
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return ti_debug_args[is_debug]
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return []
+
+ def get_no_optimization_args(self) -> T.List[str]:
+ return ['-Ooff']
+
+ def get_output_args(self, target: str) -> T.List[str]:
+ return [f'--output_file={target}']
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['--emit_warnings_as_errors']
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if path == '':
+ path = '.'
+ return ['-I=' + path]
+
+ @classmethod
+ def _unix_args_to_native(cls, args: T.List[str], info: MachineInfo) -> T.List[str]:
+ result = []
+ for i in args:
+ if i.startswith('-D'):
+ i = '--define=' + i[2:]
+ if i.startswith('-Wl,-rpath='):
+ continue
+ elif i == '--print-search-dirs':
+ continue
+ elif i.startswith('-L'):
+ continue
+ result.append(i)
+ return result
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:15] == '--include_path=':
+ parameter_list[idx] = i[:15] + os.path.normpath(os.path.join(build_dir, i[15:]))
+ if i[:2] == '-I':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+
+ return parameter_list
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return ['--preproc_with_compile', f'--preproc_dependency={outfile}']
diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py
new file mode 100644
index 0000000..864d3b4
--- /dev/null
+++ b/mesonbuild/compilers/mixins/visualstudio.py
@@ -0,0 +1,500 @@
+# Copyright 2019 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
+
+"""Abstractions to simplify compilers that implement an MSVC compatible
+interface.
+"""
+
+import abc
+import os
+import typing as T
+
+from ... import arglist
+from ... import mesonlib
+from ... import mlog
+
+if T.TYPE_CHECKING:
+ from ...environment import Environment
+ from ...dependencies import Dependency
+ from .clike import CLikeCompiler as Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
+vs32_instruction_set_args = {
+ 'mmx': ['/arch:SSE'], # There does not seem to be a flag just for MMX
+ 'sse': ['/arch:SSE'],
+ 'sse2': ['/arch:SSE2'],
+ 'sse3': ['/arch:AVX'], # VS leaped from SSE2 directly to AVX.
+ 'sse41': ['/arch:AVX'],
+ 'sse42': ['/arch:AVX'],
+ 'avx': ['/arch:AVX'],
+ 'avx2': ['/arch:AVX2'],
+ 'neon': None,
+} # T.Dicst[str, T.Optional[T.List[str]]]
+
+# The 64 bit compiler defaults to /arch:avx.
+vs64_instruction_set_args = {
+ 'mmx': ['/arch:AVX'],
+ 'sse': ['/arch:AVX'],
+ 'sse2': ['/arch:AVX'],
+ 'sse3': ['/arch:AVX'],
+ 'ssse3': ['/arch:AVX'],
+ 'sse41': ['/arch:AVX'],
+ 'sse42': ['/arch:AVX'],
+ 'avx': ['/arch:AVX'],
+ 'avx2': ['/arch:AVX2'],
+ 'neon': None,
+} # T.Dicst[str, T.Optional[T.List[str]]]
+
+msvc_optimization_args = {
+ 'plain': [],
+ '0': ['/Od'],
+ 'g': [], # No specific flag to optimize debugging, /Zi or /ZI will create debug information
+ '1': ['/O1'],
+ '2': ['/O2'],
+ '3': ['/O2', '/Gw'],
+ 's': ['/O1', '/Gw'],
+} # type: T.Dict[str, T.List[str]]
+
+msvc_debug_args = {
+ False: [],
+ True: ['/Zi']
+} # type: T.Dict[bool, T.List[str]]
+
+
+class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
+
+ """A common interface for all compilers implementing an MSVC-style
+ interface.
+
+ A number of compilers attempt to mimic MSVC, with varying levels of
+ success, such as Clang-CL and ICL (the Intel C/C++ Compiler for Windows).
+ This class implements as much common logic as possible.
+ """
+
+ std_warn_args = ['/W3']
+ std_opt_args = ['/O2']
+ ignore_libs = arglist.UNIXY_COMPILER_INTERNAL_LIBS + ['execinfo']
+ internal_libs = [] # type: T.List[str]
+
+ crt_args = {
+ 'none': [],
+ 'md': ['/MD'],
+ 'mdd': ['/MDd'],
+ 'mt': ['/MT'],
+ 'mtd': ['/MTd'],
+ } # type: T.Dict[str, T.List[str]]
+
+ # /showIncludes is needed for build dependency tracking in Ninja
+ # See: https://ninja-build.org/manual.html#_deps
+ # Assume UTF-8 sources by default, but self.unix_args_to_native() removes it
+ # if `/source-charset` is set too.
+ # It is also dropped if Visual Studio 2013 or earlier is used, since it would
+ # not be supported in that case.
+ always_args = ['/nologo', '/showIncludes', '/utf-8']
+ warn_args = {
+ '0': [],
+ '1': ['/W2'],
+ '2': ['/W3'],
+ '3': ['/W4'],
+ 'everything': ['/Wall'],
+ } # type: T.Dict[str, T.List[str]]
+
+ INVOKES_LINKER = False
+
+ def __init__(self, target: str):
+ self.base_options = {mesonlib.OptionKey(o) for o in ['b_pch', 'b_ndebug', 'b_vscrt']} # FIXME add lto, pgo and the like
+ self.target = target
+ self.is_64 = ('x64' in target) or ('x86_64' in target)
+ # do some canonicalization of target machine
+ if 'x86_64' in target:
+ self.machine = 'x64'
+ elif '86' in target:
+ self.machine = 'x86'
+ elif 'aarch64' in target:
+ self.machine = 'arm64'
+ elif 'arm' in target:
+ self.machine = 'arm'
+ else:
+ self.machine = target
+ if mesonlib.version_compare(self.version, '>=19.28.29910'): # VS 16.9.0 includes cl 19.28.29910
+ self.base_options.add(mesonlib.OptionKey('b_sanitize'))
+ assert self.linker is not None
+ self.linker.machine = self.machine
+
+ # Override CCompiler.get_always_args
+ def get_always_args(self) -> T.List[str]:
+ # TODO: use ImmutableListProtocol[str] here instead
+ return self.always_args.copy()
+
+ def get_pch_suffix(self) -> str:
+ return 'pch'
+
+ def get_pch_name(self, header: str) -> str:
+ chopped = os.path.basename(header).split('.')[:-1]
+ chopped.append(self.get_pch_suffix())
+ pchname = '.'.join(chopped)
+ return pchname
+
+ def get_pch_base_name(self, header: str) -> str:
+ # This needs to be implemented by inheriting classes
+ raise NotImplementedError
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ base = self.get_pch_base_name(header)
+ pchname = self.get_pch_name(header)
+ return ['/FI' + base, '/Yu' + base, '/Fp' + os.path.join(pch_dir, pchname)]
+
+ def get_preprocess_only_args(self) -> T.List[str]:
+ return ['/EP']
+
+ def get_preprocess_to_file_args(self) -> T.List[str]:
+ return ['/EP', '/P']
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return ['/c']
+
+ def get_no_optimization_args(self) -> T.List[str]:
+ return ['/Od', '/Oi-']
+
+ def sanitizer_compile_args(self, value: str) -> T.List[str]:
+ if value == 'none':
+ return []
+ if value != 'address':
+ raise mesonlib.MesonException('VS only supports address sanitizer at the moment.')
+ return ['/fsanitize=address']
+
+ def get_output_args(self, target: str) -> T.List[str]:
+ if self.mode == 'PREPROCESSOR':
+ return ['/Fi' + target]
+ if target.endswith('.exe'):
+ return ['/Fe' + target]
+ return ['/Fo' + target]
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return []
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return msvc_debug_args[is_debug]
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ args = msvc_optimization_args[optimization_level]
+ if mesonlib.version_compare(self.version, '<18.0'):
+ args = [arg for arg in args if arg != '/Gw']
+ return args
+
+ def linker_to_compiler_args(self, args: T.List[str]) -> T.List[str]:
+ return ['/link'] + args
+
+ def get_pic_args(self) -> T.List[str]:
+ return [] # PIC is handled by the loader on Windows
+
+ def gen_vs_module_defs_args(self, defsfile: str) -> T.List[str]:
+ if not isinstance(defsfile, str):
+ raise RuntimeError('Module definitions file should be str')
+ # With MSVC, DLLs only export symbols that are explicitly exported,
+ # so if a module defs file is specified, we use that to export symbols
+ return ['/DEF:' + defsfile]
+
+ def gen_pch_args(self, header: str, source: str, pchname: str) -> T.Tuple[str, T.List[str]]:
+ objname = os.path.splitext(pchname)[0] + '.obj'
+ return objname, ['/Yc' + header, '/Fp' + pchname, '/Fo' + objname]
+
+ def openmp_flags(self) -> T.List[str]:
+ return ['/openmp']
+
+ def openmp_link_flags(self) -> T.List[str]:
+ return []
+
+ # FIXME, no idea what these should be.
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ return []
+
+ @classmethod
+ def unix_args_to_native(cls, args: T.List[str]) -> T.List[str]:
+ result: T.List[str] = []
+ for i in args:
+ # -mms-bitfields is specific to MinGW-GCC
+ # -pthread is only valid for GCC
+ if i in {'-mms-bitfields', '-pthread'}:
+ continue
+ if i.startswith('-LIBPATH:'):
+ i = '/LIBPATH:' + i[9:]
+ elif i.startswith('-L'):
+ i = '/LIBPATH:' + i[2:]
+ # Translate GNU-style -lfoo library name to the import library
+ elif i.startswith('-l'):
+ name = i[2:]
+ if name in cls.ignore_libs:
+ # With MSVC, these are provided by the C runtime which is
+ # linked in by default
+ continue
+ else:
+ i = name + '.lib'
+ elif i.startswith('-isystem'):
+ # just use /I for -isystem system include path s
+ if i.startswith('-isystem='):
+ i = '/I' + i[9:]
+ else:
+ i = '/I' + i[8:]
+ elif i.startswith('-idirafter'):
+ # same as -isystem, but appends the path instead
+ if i.startswith('-idirafter='):
+ i = '/I' + i[11:]
+ else:
+ i = '/I' + i[10:]
+ # -pthread in link flags is only used on Linux
+ elif i == '-pthread':
+ continue
+ # cl.exe does not allow specifying both, so remove /utf-8 that we
+ # added automatically in the case the user overrides it manually.
+ elif (i.startswith('/source-charset:')
+ or i.startswith('/execution-charset:')
+ or i == '/validate-charset-'):
+ try:
+ result.remove('/utf-8')
+ except ValueError:
+ pass
+ result.append(i)
+ return result
+
+ @classmethod
+ def native_args_to_unix(cls, args: T.List[str]) -> T.List[str]:
+ result = []
+ for arg in args:
+ if arg.startswith(('/LIBPATH:', '-LIBPATH:')):
+ result.append('-L' + arg[9:])
+ elif arg.endswith(('.a', '.lib')) and not os.path.isabs(arg):
+ result.append('-l' + arg)
+ else:
+ result.append(arg)
+ return result
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['/WX']
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if path == '':
+ path = '.'
+ # msvc does not have a concept of system header dirs.
+ return ['-I' + path]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-I' or i[:2] == '/I':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+ elif i[:9] == '/LIBPATH:':
+ parameter_list[idx] = i[:9] + os.path.normpath(os.path.join(build_dir, i[9:]))
+
+ return parameter_list
+
+ # Visual Studio is special. It ignores some arguments it does not
+ # understand and you can't tell it to error out on those.
+ # http://stackoverflow.com/questions/15259720/how-can-i-make-the-microsoft-c-compiler-treat-unknown-flags-as-errors-rather-t
+ def has_arguments(self, args: T.List[str], env: 'Environment', code: str, mode: str) -> T.Tuple[bool, bool]:
+ warning_text = '4044' if mode == 'link' else '9002'
+ with self._build_wrapper(code, env, extra_args=args, mode=mode) as p:
+ if p.returncode != 0:
+ return False, p.cached
+ return not (warning_text in p.stderr or warning_text in p.stdout), p.cached
+
+ def get_compile_debugfile_args(self, rel_obj: str, pch: bool = False) -> T.List[str]:
+ pdbarr = rel_obj.split('.')[:-1]
+ pdbarr += ['pdb']
+ args = ['/Fd' + '.'.join(pdbarr)]
+ return args
+
+ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
+ if self.is_64:
+ return vs64_instruction_set_args.get(instruction_set, None)
+ return vs32_instruction_set_args.get(instruction_set, None)
+
+ def _calculate_toolset_version(self, version: int) -> T.Optional[str]:
+ if version < 1310:
+ return '7.0'
+ elif version < 1400:
+ return '7.1' # (Visual Studio 2003)
+ elif version < 1500:
+ return '8.0' # (Visual Studio 2005)
+ elif version < 1600:
+ return '9.0' # (Visual Studio 2008)
+ elif version < 1700:
+ return '10.0' # (Visual Studio 2010)
+ elif version < 1800:
+ return '11.0' # (Visual Studio 2012)
+ elif version < 1900:
+ return '12.0' # (Visual Studio 2013)
+ elif version < 1910:
+ return '14.0' # (Visual Studio 2015)
+ elif version < 1920:
+ return '14.1' # (Visual Studio 2017)
+ elif version < 1930:
+ return '14.2' # (Visual Studio 2019)
+ elif version < 1940:
+ return '14.3' # (Visual Studio 2022)
+ mlog.warning(f'Could not find toolset for version {self.version!r}')
+ return None
+
+ def get_toolset_version(self) -> T.Optional[str]:
+ # See boost/config/compiler/visualc.cpp for up to date mapping
+ try:
+ version = int(''.join(self.version.split('.')[0:2]))
+ except ValueError:
+ return None
+ return self._calculate_toolset_version(version)
+
+ def get_default_include_dirs(self) -> T.List[str]:
+ if 'INCLUDE' not in os.environ:
+ return []
+ return os.environ['INCLUDE'].split(os.pathsep)
+
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ if crt_val in self.crt_args:
+ return self.crt_args[crt_val]
+ assert crt_val in {'from_buildtype', 'static_from_buildtype'}
+ dbg = 'mdd'
+ rel = 'md'
+ if crt_val == 'static_from_buildtype':
+ dbg = 'mtd'
+ rel = 'mt'
+ # Match what build type flags used to do.
+ if buildtype == 'plain':
+ return []
+ elif buildtype == 'debug':
+ return self.crt_args[dbg]
+ elif buildtype == 'debugoptimized':
+ return self.crt_args[rel]
+ elif buildtype == 'release':
+ return self.crt_args[rel]
+ elif buildtype == 'minsize':
+ return self.crt_args[rel]
+ else:
+ assert buildtype == 'custom'
+ raise mesonlib.EnvironmentException('Requested C runtime based on buildtype, but buildtype is "custom".')
+
+ def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]:
+ # MSVC doesn't have __attribute__ like Clang and GCC do, so just return
+ # false without compiling anything
+ return name in {'dllimport', 'dllexport'}, False
+
+ def get_argument_syntax(self) -> str:
+ return 'msvc'
+
+ def symbols_have_underscore_prefix(self, env: 'Environment') -> bool:
+ '''
+ Check if the compiler prefixes an underscore to global C symbols.
+
+ This overrides the Clike method, as for MSVC checking the
+ underscore prefix based on the compiler define never works,
+ so do not even try.
+ '''
+ # Try to consult a hardcoded list of cases we know
+ # absolutely have an underscore prefix
+ result = self._symbols_have_underscore_prefix_list(env)
+ if result is not None:
+ return result
+
+ # As a last resort, try search in a compiled binary
+ return self._symbols_have_underscore_prefix_searchbin(env)
+
+
+class MSVCCompiler(VisualStudioLikeCompiler):
+
+ """Specific to the Microsoft Compilers."""
+
+ id = 'msvc'
+
+ def __init__(self, target: str):
+ super().__init__(target)
+
+ # Visual Studio 2013 and erlier don't support the /utf-8 argument.
+ # We want to remove it. We also want to make an explicit copy so we
+ # don't mutate class constant state
+ if mesonlib.version_compare(self.version, '<19.00') and '/utf-8' in self.always_args:
+ self.always_args = [r for r in self.always_args if r != '/utf-8']
+
+ def get_compile_debugfile_args(self, rel_obj: str, pch: bool = False) -> T.List[str]:
+ args = super().get_compile_debugfile_args(rel_obj, pch)
+ # When generating a PDB file with PCH, all compile commands write
+ # to the same PDB file. Hence, we need to serialize the PDB
+ # writes using /FS since we do parallel builds. This slows down the
+ # build obviously, which is why we only do this when PCH is on.
+ # This was added in Visual Studio 2013 (MSVC 18.0). Before that it was
+ # always on: https://msdn.microsoft.com/en-us/library/dn502518.aspx
+ if pch and mesonlib.version_compare(self.version, '>=18.0'):
+ args = ['/FS'] + args
+ return args
+
+ # Override CCompiler.get_always_args
+ # We want to drop '/utf-8' for Visual Studio 2013 and earlier
+ def get_always_args(self) -> T.List[str]:
+ return self.always_args
+
+ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
+ if self.version.split('.')[0] == '16' and instruction_set == 'avx':
+ # VS documentation says that this exists and should work, but
+ # it does not. The headers do not contain AVX intrinsics
+ # and they can not be called.
+ return None
+ return super().get_instruction_set_args(instruction_set)
+
+ def get_pch_base_name(self, header: str) -> str:
+ return os.path.basename(header)
+
+
+class ClangClCompiler(VisualStudioLikeCompiler):
+
+ """Specific to Clang-CL."""
+
+ id = 'clang-cl'
+
+ def __init__(self, target: str):
+ super().__init__(target)
+
+ # Assembly
+ self.can_compile_suffixes.add('s')
+
+ def has_arguments(self, args: T.List[str], env: 'Environment', code: str, mode: str) -> T.Tuple[bool, bool]:
+ if mode != 'link':
+ args = args + ['-Werror=unknown-argument', '-Werror=unknown-warning-option']
+ return super().has_arguments(args, env, code, mode)
+
+ def get_toolset_version(self) -> T.Optional[str]:
+ # XXX: what is the right thing to do here?
+ return '14.1'
+
+ def get_pch_base_name(self, header: str) -> str:
+ return header
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ if path == '':
+ path = '.'
+ return ['/clang:-isystem' + path] if is_system else ['-I' + path]
+
+ def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]:
+ if dep.get_include_type() == 'system':
+ converted = []
+ for i in dep.get_compile_args():
+ if i.startswith('-isystem'):
+ converted += ['/clang:' + i]
+ else:
+ converted += [i]
+ return converted
+ else:
+ return dep.get_compile_args()
diff --git a/mesonbuild/compilers/mixins/xc16.py b/mesonbuild/compilers/mixins/xc16.py
new file mode 100644
index 0000000..09949a2
--- /dev/null
+++ b/mesonbuild/compilers/mixins/xc16.py
@@ -0,0 +1,132 @@
+# Copyright 2012-2019 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
+
+"""Representations specific to the Microchip XC16 C compiler family."""
+
+import os
+import typing as T
+
+from ...mesonlib import EnvironmentException
+
+if T.TYPE_CHECKING:
+ from ...envconfig import MachineInfo
+ from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
+xc16_buildtype_args = {
+ 'plain': [],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
+ 'custom': [],
+} # type: T.Dict[str, T.List[str]]
+
+xc16_optimization_args = {
+ 'plain': [],
+ '0': ['-O0'],
+ 'g': ['-O0'],
+ '1': ['-O1'],
+ '2': ['-O2'],
+ '3': ['-O3'],
+ 's': ['-Os']
+} # type: T.Dict[str, T.List[str]]
+
+xc16_debug_args = {
+ False: [],
+ True: []
+} # type: T.Dict[bool, T.List[str]]
+
+
+class Xc16Compiler(Compiler):
+
+ id = 'xc16'
+
+ def __init__(self) -> None:
+ if not self.is_cross:
+ raise EnvironmentException('xc16 supports only cross-compilation.')
+ # Assembly
+ self.can_compile_suffixes.add('s')
+ default_warn_args = [] # type: T.List[str]
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + [],
+ '3': default_warn_args + [],
+ 'everything': default_warn_args + []} # type: T.Dict[str, T.List[str]]
+
+ def get_always_args(self) -> T.List[str]:
+ return []
+
+ def get_pic_args(self) -> T.List[str]:
+ # PIC support is not enabled by default for xc16,
+ # if users want to use it, they need to add the required arguments explicitly
+ return []
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return xc16_buildtype_args[buildtype]
+
+ def get_pch_suffix(self) -> str:
+ return 'pch'
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ return []
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ return []
+
+ def get_coverage_args(self) -> T.List[str]:
+ return []
+
+ def get_no_stdinc_args(self) -> T.List[str]:
+ return ['-nostdinc']
+
+ def get_no_stdlib_link_args(self) -> T.List[str]:
+ return ['--nostdlib']
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return xc16_optimization_args[optimization_level]
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return xc16_debug_args[is_debug]
+
+ @classmethod
+ def _unix_args_to_native(cls, args: T.List[str], info: MachineInfo) -> T.List[str]:
+ result = []
+ for i in args:
+ if i.startswith('-D'):
+ i = '-D' + i[2:]
+ if i.startswith('-I'):
+ i = '-I' + i[2:]
+ if i.startswith('-Wl,-rpath='):
+ continue
+ elif i == '--print-search-dirs':
+ continue
+ elif i.startswith('-L'):
+ continue
+ result.append(i)
+ return result
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:9] == '-I':
+ parameter_list[idx] = i[:9] + os.path.normpath(os.path.join(build_dir, i[9:]))
+
+ return parameter_list
diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py
new file mode 100644
index 0000000..83dcaad
--- /dev/null
+++ b/mesonbuild/compilers/objc.py
@@ -0,0 +1,114 @@
+# Copyright 2012-2017 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 typing as T
+
+from .. import coredata
+from ..mesonlib import OptionKey
+
+from .compilers import Compiler
+from .mixins.clike import CLikeCompiler
+from .mixins.gnu import GnuCompiler, gnu_common_warning_args, gnu_objc_warning_args
+from .mixins.clang import ClangCompiler
+
+if T.TYPE_CHECKING:
+ from ..programs import ExternalProgram
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..linkers import DynamicLinker
+ from ..mesonlib import MachineChoice
+
+
+class ObjCCompiler(CLikeCompiler, Compiler):
+
+ language = 'objc'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrap: T.Optional['ExternalProgram'],
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ Compiler.__init__(self, ccache, exelist, version, for_machine, info,
+ is_cross=is_cross, full_version=full_version,
+ linker=linker)
+ CLikeCompiler.__init__(self, exe_wrap)
+
+ @staticmethod
+ def get_display_language() -> str:
+ return 'Objective-C'
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ code = '#import<stddef.h>\nint main(void) { return 0; }\n'
+ return self._sanity_check_impl(work_dir, environment, 'sanitycheckobjc.m', code)
+
+
+class GnuObjCCompiler(GnuCompiler, ObjCCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ ObjCCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ GnuCompiler.__init__(self, defines)
+ default_warn_args = ['-Wall', '-Winvalid-pch']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': (default_warn_args + ['-Wextra', '-Wpedantic'] +
+ self.supported_warn_args(gnu_common_warning_args) +
+ self.supported_warn_args(gnu_objc_warning_args))}
+
+
+class ClangObjCCompiler(ClangCompiler, ObjCCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ ObjCCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ClangCompiler.__init__(self, defines)
+ default_warn_args = ['-Wall', '-Winvalid-pch']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': ['-Weverything']}
+
+ def get_options(self) -> 'coredata.MutableKeyedOptionDictType':
+ opts = super().get_options()
+ opts.update({
+ OptionKey('std', machine=self.for_machine, lang='c'): coredata.UserComboOption(
+ 'C language standard to use',
+ ['none', 'c89', 'c99', 'c11', 'c17', 'gnu89', 'gnu99', 'gnu11', 'gnu17'],
+ 'none',
+ )
+ })
+ return opts
+
+ def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T.List[str]:
+ args = []
+ std = options[OptionKey('std', machine=self.for_machine, lang='c')]
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+class AppleClangObjCCompiler(ClangObjCCompiler):
+
+ """Handle the differences between Apple's clang and vanilla clang."""
diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py
new file mode 100644
index 0000000..1f9f756
--- /dev/null
+++ b/mesonbuild/compilers/objcpp.py
@@ -0,0 +1,115 @@
+# Copyright 2012-2017 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 typing as T
+
+from .. import coredata
+from ..mesonlib import OptionKey
+
+from .mixins.clike import CLikeCompiler
+from .compilers import Compiler
+from .mixins.gnu import GnuCompiler, gnu_common_warning_args, gnu_objc_warning_args
+from .mixins.clang import ClangCompiler
+
+if T.TYPE_CHECKING:
+ from ..programs import ExternalProgram
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..linkers import DynamicLinker
+ from ..mesonlib import MachineChoice
+
+class ObjCPPCompiler(CLikeCompiler, Compiler):
+
+ language = 'objcpp'
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrap: T.Optional['ExternalProgram'],
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ Compiler.__init__(self, ccache, exelist, version, for_machine, info,
+ is_cross=is_cross, full_version=full_version,
+ linker=linker)
+ CLikeCompiler.__init__(self, exe_wrap)
+
+ @staticmethod
+ def get_display_language() -> str:
+ return 'Objective-C++'
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ code = '#import<stdio.h>\nclass MyClass;int main(void) { return 0; }\n'
+ return self._sanity_check_impl(work_dir, environment, 'sanitycheckobjcpp.mm', code)
+
+
+class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler):
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ ObjCPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ GnuCompiler.__init__(self, defines)
+ default_warn_args = ['-Wall', '-Winvalid-pch']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': (default_warn_args + ['-Wextra', '-Wpedantic'] +
+ self.supported_warn_args(gnu_common_warning_args) +
+ self.supported_warn_args(gnu_objc_warning_args))}
+
+
+class ClangObjCPPCompiler(ClangCompiler, ObjCPPCompiler):
+
+ def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ ObjCPPCompiler.__init__(self, ccache, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ClangCompiler.__init__(self, defines)
+ default_warn_args = ['-Wall', '-Winvalid-pch']
+ self.warn_args = {'0': [],
+ '1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic'],
+ 'everything': ['-Weverything']}
+
+ def get_options(self) -> 'coredata.MutableKeyedOptionDictType':
+ opts = super().get_options()
+ opts.update({
+ OptionKey('std', machine=self.for_machine, lang='cpp'): coredata.UserComboOption(
+ 'C++ language standard to use',
+ ['none', 'c++98', 'c++11', 'c++14', 'c++17', 'gnu++98', 'gnu++11', 'gnu++14', 'gnu++17'],
+ 'none',
+ )
+ })
+ return opts
+
+ def get_option_compile_args(self, options: 'coredata.KeyedOptionDictType') -> T.List[str]:
+ args = []
+ std = options[OptionKey('std', machine=self.for_machine, lang='cpp')]
+ if std.value != 'none':
+ args.append('-std=' + std.value)
+ return args
+
+
+class AppleClangObjCPPCompiler(ClangObjCPPCompiler):
+
+ """Handle the differences between Apple's clang and vanilla clang."""
diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py
new file mode 100644
index 0000000..9e5ebc8
--- /dev/null
+++ b/mesonbuild/compilers/rust.py
@@ -0,0 +1,208 @@
+# 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 subprocess, os.path
+import textwrap
+import typing as T
+
+from .. import coredata
+from ..mesonlib import EnvironmentException, MesonException, Popen_safe, OptionKey
+from .compilers import Compiler, rust_buildtype_args, clike_debug_args
+
+if T.TYPE_CHECKING:
+ from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType
+ from ..envconfig import MachineInfo
+ from ..environment import Environment # noqa: F401
+ from ..linkers import DynamicLinker
+ from ..mesonlib import MachineChoice
+ from ..programs import ExternalProgram
+
+
+rust_optimization_args = {
+ 'plain': [],
+ '0': [],
+ 'g': ['-C', 'opt-level=0'],
+ '1': ['-C', 'opt-level=1'],
+ '2': ['-C', 'opt-level=2'],
+ '3': ['-C', 'opt-level=3'],
+ 's': ['-C', 'opt-level=s'],
+} # type: T.Dict[str, T.List[str]]
+
+class RustCompiler(Compiler):
+
+ # rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX
+ language = 'rust'
+ id = 'rustc'
+
+ _WARNING_LEVELS: T.Dict[str, T.List[str]] = {
+ '0': ['-A', 'warnings'],
+ '1': [],
+ '2': [],
+ '3': ['-W', 'warnings'],
+ }
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ full_version: T.Optional[str] = None,
+ linker: T.Optional['DynamicLinker'] = None):
+ super().__init__([], exelist, version, for_machine, info,
+ is_cross=is_cross, full_version=full_version,
+ linker=linker)
+ self.exe_wrapper = exe_wrapper
+ self.base_options.add(OptionKey('b_colorout'))
+ if 'link' in self.linker.id:
+ self.base_options.add(OptionKey('b_vscrt'))
+
+ def needs_static_linker(self) -> bool:
+ return False
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ source_name = os.path.join(work_dir, 'sanity.rs')
+ output_name = os.path.join(work_dir, 'rusttest')
+ with open(source_name, 'w', encoding='utf-8') as ofile:
+ ofile.write(textwrap.dedent(
+ '''fn main() {
+ }
+ '''))
+ pc = subprocess.Popen(self.exelist + ['-o', output_name, source_name],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ cwd=work_dir)
+ _stdo, _stde = pc.communicate()
+ assert isinstance(_stdo, bytes)
+ assert isinstance(_stde, bytes)
+ stdo = _stdo.decode('utf-8', errors='replace')
+ stde = _stde.decode('utf-8', errors='replace')
+ if pc.returncode != 0:
+ raise EnvironmentException('Rust compiler {} can not compile programs.\n{}\n{}'.format(
+ self.name_string(),
+ stdo,
+ stde))
+ if self.is_cross:
+ if self.exe_wrapper is None:
+ # Can't check if the binaries run so we have to assume they do
+ return
+ cmdlist = self.exe_wrapper.get_command() + [output_name]
+ else:
+ cmdlist = [output_name]
+ pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by Rust compiler %s are not runnable.' % self.name_string())
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return ['--dep-info', outfile]
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return rust_buildtype_args[buildtype]
+
+ def get_sysroot(self) -> str:
+ cmd = self.get_exelist(ccache=False) + ['--print', 'sysroot']
+ p, stdo, stde = Popen_safe(cmd)
+ return stdo.split('\n', maxsplit=1)[0]
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return clike_debug_args[is_debug]
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return rust_optimization_args[optimization_level]
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-L':
+ for j in ['dependency', 'crate', 'native', 'framework', 'all']:
+ combined_len = len(j) + 3
+ if i[:combined_len] == f'-L{j}=':
+ parameter_list[idx] = i[:combined_len] + os.path.normpath(os.path.join(build_dir, i[combined_len:]))
+ break
+
+ return parameter_list
+
+ def get_output_args(self, outputname: str) -> T.List[str]:
+ return ['-o', outputname]
+
+ @classmethod
+ def use_linker_args(cls, linker: str, version: str) -> T.List[str]:
+ return ['-C', f'linker={linker}']
+
+ # Rust does not have a use_linker_args because it dispatches to a gcc-like
+ # C compiler for dynamic linking, as such we invoke the C compiler's
+ # use_linker_args method instead.
+
+ def get_options(self) -> 'MutableKeyedOptionDictType':
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ return {
+ key: coredata.UserComboOption(
+ 'Rust edition to use',
+ ['none', '2015', '2018', '2021'],
+ 'none',
+ ),
+ }
+
+ def get_option_compile_args(self, options: 'KeyedOptionDictType') -> T.List[str]:
+ args = []
+ key = OptionKey('std', machine=self.for_machine, lang=self.language)
+ std = options[key]
+ if std.value != 'none':
+ args.append('--edition=' + std.value)
+ return args
+
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ # Rust handles this for us, we don't need to do anything
+ return []
+
+ def get_colorout_args(self, colortype: str) -> T.List[str]:
+ if colortype in {'always', 'never', 'auto'}:
+ return [f'--color={colortype}']
+ raise MesonException(f'Invalid color type for rust {colortype}')
+
+ def get_linker_always_args(self) -> T.List[str]:
+ args: T.List[str] = []
+ for a in super().get_linker_always_args():
+ args.extend(['-C', f'link-arg={a}'])
+ return args
+
+ def get_werror_args(self) -> T.List[str]:
+ # Use -D warnings, which makes every warning not explicitly allowed an
+ # error
+ return ['-D', 'warnings']
+
+ def get_warn_args(self, level: str) -> T.List[str]:
+ # TODO: I'm not really sure what to put here, Rustc doesn't have warning
+ return self._WARNING_LEVELS[level]
+
+ def get_no_warn_args(self) -> T.List[str]:
+ return self._WARNING_LEVELS["0"]
+
+ def get_pic_args(self) -> T.List[str]:
+ # This defaults to
+ return ['-C', 'relocation-model=pic']
+
+ def get_pie_args(self) -> T.List[str]:
+ # Rustc currently has no way to toggle this, it's controlled by whether
+ # pic is on by rustc
+ return []
+
+
+class ClippyRustCompiler(RustCompiler):
+
+ """Clippy is a linter that wraps Rustc.
+
+ This just provides us a different id
+ """
+
+ id = 'clippy-driver rustc'
diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py
new file mode 100644
index 0000000..ec4c7a3
--- /dev/null
+++ b/mesonbuild/compilers/swift.py
@@ -0,0 +1,130 @@
+# Copyright 2012-2017 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 subprocess, os.path
+import typing as T
+
+from ..mesonlib import EnvironmentException
+
+from .compilers import Compiler, swift_buildtype_args, clike_debug_args
+
+if T.TYPE_CHECKING:
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..linkers import DynamicLinker
+ from ..mesonlib import MachineChoice
+
+swift_optimization_args = {
+ 'plain': [],
+ '0': [],
+ 'g': [],
+ '1': ['-O'],
+ '2': ['-O'],
+ '3': ['-O'],
+ 's': ['-O'],
+} # type: T.Dict[str, T.List[str]]
+
+class SwiftCompiler(Compiler):
+
+ LINKER_PREFIX = ['-Xlinker']
+ language = 'swift'
+ id = 'llvm'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo', full_version: T.Optional[str] = None,
+ linker: T.Optional['DynamicLinker'] = None):
+ super().__init__([], exelist, version, for_machine, info,
+ is_cross=is_cross, full_version=full_version,
+ linker=linker)
+ self.version = version
+
+ def needs_static_linker(self) -> bool:
+ return True
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['--fatal-warnings']
+
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
+ return ['-emit-dependencies']
+
+ def depfile_for_object(self, objfile: str) -> T.Optional[str]:
+ return os.path.splitext(objfile)[0] + '.' + self.get_depfile_suffix()
+
+ def get_depfile_suffix(self) -> str:
+ return 'd'
+
+ def get_output_args(self, target: str) -> T.List[str]:
+ return ['-o', target]
+
+ def get_header_import_args(self, headername: str) -> T.List[str]:
+ return ['-import-objc-header', headername]
+
+ def get_warn_args(self, level: str) -> T.List[str]:
+ return []
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ return swift_buildtype_args[buildtype]
+
+ def get_std_exe_link_args(self) -> T.List[str]:
+ return ['-emit-executable']
+
+ def get_module_args(self, modname: str) -> T.List[str]:
+ return ['-module-name', modname]
+
+ def get_mod_gen_args(self) -> T.List[str]:
+ return ['-emit-module']
+
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
+ return ['-I' + path]
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return ['-c']
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:2] == '-I' or i[:2] == '-L':
+ parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
+
+ return parameter_list
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ src = 'swifttest.swift'
+ source_name = os.path.join(work_dir, src)
+ output_name = os.path.join(work_dir, 'swifttest')
+ extra_flags: T.List[str] = []
+ extra_flags += environment.coredata.get_external_args(self.for_machine, self.language)
+ if self.is_cross:
+ extra_flags += self.get_compile_only_args()
+ else:
+ extra_flags += environment.coredata.get_external_link_args(self.for_machine, self.language)
+ with open(source_name, 'w', encoding='utf-8') as ofile:
+ ofile.write('''print("Swift compilation is working.")
+''')
+ pc = subprocess.Popen(self.exelist + extra_flags + ['-emit-executable', '-o', output_name, src], cwd=work_dir)
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('Swift compiler %s can not compile programs.' % self.name_string())
+ if self.is_cross:
+ # Can't check if the binaries run so we have to assume they do
+ return
+ if subprocess.call(output_name) != 0:
+ raise EnvironmentException('Executables created by Swift compiler %s are not runnable.' % self.name_string())
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return clike_debug_args[is_debug]
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return swift_optimization_args[optimization_level]
diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py
new file mode 100644
index 0000000..e56a9fc
--- /dev/null
+++ b/mesonbuild/compilers/vala.py
@@ -0,0 +1,140 @@
+# Copyright 2012-2017 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 os.path
+import typing as T
+
+from .. import mlog
+from ..mesonlib import EnvironmentException, version_compare, OptionKey
+
+from .compilers import Compiler, LibType
+
+if T.TYPE_CHECKING:
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..mesonlib import MachineChoice
+
+class ValaCompiler(Compiler):
+
+ language = 'vala'
+ id = 'valac'
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo'):
+ super().__init__([], exelist, version, for_machine, info, is_cross=is_cross)
+ self.version = version
+ self.base_options = {OptionKey('b_colorout')}
+
+ def needs_static_linker(self) -> bool:
+ return False # Because compiles into C.
+
+ def get_optimization_args(self, optimization_level: str) -> T.List[str]:
+ return []
+
+ def get_debug_args(self, is_debug: bool) -> T.List[str]:
+ return ['--debug'] if is_debug else []
+
+ def get_output_args(self, target: str) -> T.List[str]:
+ return [] # Because compiles into C.
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return [] # Because compiles into C.
+
+ def get_pic_args(self) -> T.List[str]:
+ return []
+
+ def get_pie_args(self) -> T.List[str]:
+ return []
+
+ def get_pie_link_args(self) -> T.List[str]:
+ return []
+
+ def get_always_args(self) -> T.List[str]:
+ return ['-C']
+
+ def get_warn_args(self, warning_level: str) -> T.List[str]:
+ return []
+
+ def get_no_warn_args(self) -> T.List[str]:
+ return ['--disable-warnings']
+
+ def get_werror_args(self) -> T.List[str]:
+ return ['--fatal-warnings']
+
+ def get_colorout_args(self, colortype: str) -> T.List[str]:
+ if version_compare(self.version, '>=0.37.1'):
+ return ['--color=' + colortype]
+ return []
+
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
+ for idx, i in enumerate(parameter_list):
+ if i[:9] == '--girdir=':
+ parameter_list[idx] = i[:9] + os.path.normpath(os.path.join(build_dir, i[9:]))
+ if i[:10] == '--vapidir=':
+ parameter_list[idx] = i[:10] + os.path.normpath(os.path.join(build_dir, i[10:]))
+ if i[:13] == '--includedir=':
+ parameter_list[idx] = i[:13] + os.path.normpath(os.path.join(build_dir, i[13:]))
+ if i[:14] == '--metadatadir=':
+ parameter_list[idx] = i[:14] + os.path.normpath(os.path.join(build_dir, i[14:]))
+
+ return parameter_list
+
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
+ code = 'class MesonSanityCheck : Object { }'
+ extra_flags: T.List[str] = []
+ extra_flags += environment.coredata.get_external_args(self.for_machine, self.language)
+ if self.is_cross:
+ extra_flags += self.get_compile_only_args()
+ else:
+ extra_flags += environment.coredata.get_external_link_args(self.for_machine, self.language)
+ with self.cached_compile(code, environment.coredata, extra_args=extra_flags, mode='compile') as p:
+ if p.returncode != 0:
+ msg = f'Vala compiler {self.name_string()!r} can not compile programs'
+ raise EnvironmentException(msg)
+
+ def get_buildtype_args(self, buildtype: str) -> T.List[str]:
+ if buildtype in {'debug', 'debugoptimized', 'minsize'}:
+ return ['--debug']
+ return []
+
+ def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
+ libtype: LibType = LibType.PREFER_SHARED) -> T.Optional[T.List[str]]:
+ if extra_dirs and isinstance(extra_dirs, str):
+ extra_dirs = [extra_dirs]
+ # Valac always looks in the default vapi dir, so only search there if
+ # no extra dirs are specified.
+ if not extra_dirs:
+ code = 'class MesonFindLibrary : Object { }'
+ args: T.List[str] = []
+ args += env.coredata.get_external_args(self.for_machine, self.language)
+ vapi_args = ['--pkg', libname]
+ args += vapi_args
+ with self.cached_compile(code, env.coredata, extra_args=args, mode='compile') as p:
+ if p.returncode == 0:
+ return vapi_args
+ # Not found? Try to find the vapi file itself.
+ for d in extra_dirs:
+ vapi = os.path.join(d, libname + '.vapi')
+ if os.path.isfile(vapi):
+ return [vapi]
+ mlog.debug(f'Searched {extra_dirs!r} and {libname!r} wasn\'t found')
+ return None
+
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ return []
+
+ def thread_link_flags(self, env: 'Environment') -> T.List[str]:
+ return []