aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/ast/introspection.py3
-rw-r--r--mesonbuild/backend/backends.py4
-rw-r--r--mesonbuild/build.py4
-rw-r--r--mesonbuild/compilers/__init__.py33
-rw-r--r--mesonbuild/compilers/detect.py1209
-rw-r--r--mesonbuild/compilers/mixins/intel.py5
-rw-r--r--mesonbuild/dependencies/dev.py4
-rw-r--r--mesonbuild/environment.py1368
-rw-r--r--mesonbuild/interpreter/interpreter.py2
-rw-r--r--mesonbuild/linkers/__init__.py11
-rw-r--r--mesonbuild/linkers/detect.py216
-rw-r--r--mesonbuild/mesonlib/universal.py44
-rwxr-xr-xrun_project_tests.py9
-rwxr-xr-xrun_unittests.py156
14 files changed, 1611 insertions, 1457 deletions
diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py
index 97ebb5a..42813db 100644
--- a/mesonbuild/ast/introspection.py
+++ b/mesonbuild/ast/introspection.py
@@ -23,6 +23,7 @@ from ..mesonlib import MachineChoice, OptionKey
from ..interpreterbase import InvalidArguments, TYPE_nvar
from ..build import BuildTarget, Executable, Jar, SharedLibrary, SharedModule, StaticLibrary
from ..mparser import BaseNode, ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode
+from ..compilers import detect_compiler_for
import typing as T
import os
import argparse
@@ -162,7 +163,7 @@ class IntrospectionInterpreter(AstInterpreter):
for lang in sorted(langs, key=compilers.sort_clink):
lang = lang.lower()
if lang not in self.coredata.compilers[for_machine]:
- self.environment.detect_compiler_for(lang, for_machine)
+ detect_compiler_for(self.environment, lang, for_machine)
def func_dependency(self, node: BaseNode, args: T.List[TYPE_nvar], kwargs: T.Dict[str, TYPE_nvar]) -> None:
args = self.flatten_args(args)
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 4e470cf..a342acc 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -29,7 +29,7 @@ from .. import dependencies
from .. import programs
from .. import mesonlib
from .. import mlog
-from ..compilers import LANGUAGES_USING_LDFLAGS
+from ..compilers import LANGUAGES_USING_LDFLAGS, detect
from ..mesonlib import (
File, MachineChoice, MesonException, OptionType, OrderedSet, OptionOverrideProxy,
classify_unity_sources, OptionKey, join_args
@@ -1315,7 +1315,7 @@ class Backend:
mlog.warning('Cross file does not specify strip binary, result will not be stripped.')
else:
# TODO go through all candidates, like others
- strip_bin = [self.environment.default_strip[0]]
+ strip_bin = [detect.defaults['strip'][0]]
d = InstallData(self.environment.get_source_dir(),
self.environment.get_build_dir(),
self.environment.get_prefix(),
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index f88bf74..25c8ca1 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -37,7 +37,7 @@ from .mesonlib import (
)
from .compilers import (
Compiler, is_object, clink_langs, sort_clink, lang_suffixes,
- is_known_suffix
+ is_known_suffix, detect_static_linker
)
from .linkers import StaticLinker
from .interpreterbase import FeatureNew
@@ -275,7 +275,7 @@ class Build:
def ensure_static_linker(self, compiler):
if self.static_linker[compiler.for_machine] is None and compiler.needs_static_linker():
- self.static_linker[compiler.for_machine] = self.environment.detect_static_linker(compiler)
+ self.static_linker[compiler.for_machine] = detect_static_linker(self.environment, compiler)
def get_project(self):
return self.projects['']
diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py
index be4809f..3d39c9b 100644
--- a/mesonbuild/compilers/__init__.py
+++ b/mesonbuild/compilers/__init__.py
@@ -35,6 +35,22 @@ __all__ = [
'lang_suffixes',
'sort_clink',
+ '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',
+
'AppleClangCCompiler',
'AppleClangCPPCompiler',
'AppleClangObjCCompiler',
@@ -133,6 +149,23 @@ from .compilers import (
LANGUAGES_USING_LDFLAGS,
sort_clink,
)
+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,
+)
from .c import (
CCompiler,
AppleClangCCompiler,
diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py
new file mode 100644
index 0000000..f79a61b
--- /dev/null
+++ b/mesonbuild/compilers/detect.py
@@ -0,0 +1,1209 @@
+# Copyright 2012-2021 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 ..mesonlib import (
+ MachineChoice, MesonException, EnvironmentException,
+ 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,
+ AIXArLinker,
+ ArLinker,
+ ArmarLinker,
+ ArmClangDynamicLinker,
+ ArmDynamicLinker,
+ CcrxLinker,
+ CcrxDynamicLinker,
+ CompCertLinker,
+ CompCertDynamicLinker,
+ C2000Linker,
+ C2000DynamicLinker,
+ DLinker,
+ NvidiaHPC_DynamicLinker,
+ PGIDynamicLinker,
+ PGIStaticLinker,
+ StaticLinker,
+ Xc16Linker,
+ Xc16DynamicLinker,
+ XilinkDynamicLinker,
+ CudaLinker,
+ IntelVisualStudioLinker,
+ VisualStudioLinker,
+ VisualStudioLikeLinkerMixin,
+ WASMDynamicLinker,
+)
+from .compilers import Compiler
+from .c import (
+ AppleClangCCompiler,
+ ArmCCompiler,
+ ArmclangCCompiler,
+ ClangCCompiler,
+ ClangClCCompiler,
+ GnuCCompiler,
+ ElbrusCCompiler,
+ EmscriptenCCompiler,
+ IntelCCompiler,
+ IntelClCCompiler,
+ NvidiaHPC_CCompiler,
+ PGICCompiler,
+ CcrxCCompiler,
+ Xc16CCompiler,
+ CompCertCCompiler,
+ C2000CCompiler,
+ VisualStudioCCompiler,
+)
+from .cpp import (
+ AppleClangCPPCompiler,
+ ArmCPPCompiler,
+ ArmclangCPPCompiler,
+ ClangCPPCompiler,
+ ClangClCPPCompiler,
+ GnuCPPCompiler,
+ ElbrusCPPCompiler,
+ EmscriptenCPPCompiler,
+ IntelCPPCompiler,
+ IntelClCPPCompiler,
+ NvidiaHPC_CPPCompiler,
+ PGICPPCompiler,
+ CcrxCPPCompiler,
+ C2000CPPCompiler,
+ VisualStudioCPPCompiler,
+)
+from .cs import MonoCompiler, VisualStudioCsCompiler
+from .d import (
+ DCompiler,
+ DmdDCompiler,
+ GnuDCompiler,
+ LLVMDCompiler,
+)
+from .cuda import CudaCompiler
+from .fortran import (
+ G95FortranCompiler,
+ GnuFortranCompiler,
+ ElbrusFortranCompiler,
+ FlangFortranCompiler,
+ IntelFortranCompiler,
+ IntelClFortranCompiler,
+ NAGFortranCompiler,
+ Open64FortranCompiler,
+ PathScaleFortranCompiler,
+ NvidiaHPC_FortranCompiler,
+ PGIFortranCompiler,
+ SunFortranCompiler,
+)
+from .java import JavaCompiler
+from .objc import (
+ AppleClangObjCCompiler,
+ ClangObjCCompiler,
+ GnuObjCCompiler,
+)
+from .objcpp import (
+ AppleClangObjCPPCompiler,
+ ClangObjCPPCompiler,
+ GnuObjCPPCompiler,
+)
+from .cython import CythonCompiler
+from .rust import RustCompiler
+from .swift import SwiftCompiler
+from .vala import ValaCompiler
+from .mixins.visualstudio import VisualStudioLikeCompiler
+from .mixins.gnu import GnuCompiler
+from .mixins.clang import ClangCompiler
+
+import subprocess
+import platform
+import re
+import shutil
+import tempfile
+import os
+import typing as T
+
+if T.TYPE_CHECKING:
+ 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':
+ # There are no objc or objc++ compilers for Elbrus,
+ # and there's no clang which can build binaries for host.
+ defaults['c'] = ['cc', 'gcc', 'lcc']
+ defaults['cpp'] = ['c++', 'g++', 'l++']
+ defaults['objc'] = []
+ defaults['objcpp'] = []
+ else:
+ defaults['c'] = ['cc', 'gcc', 'clang', 'nvc', 'pgcc', 'icc']
+ defaults['cpp'] = ['c++', 'g++', 'clang++', 'nvc++', 'pgc++', 'icpc']
+ defaults['objc'] = ['cc', 'gcc', 'clang']
+ defaults['objcpp'] = ['c++', 'g++', 'clang++']
+ defaults['fortran'] = ['gfortran', 'flang', 'nvfortran', 'pgfortran', 'ifort', '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']
+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']
+
+
+def compiler_from_language(env: 'Environment', lang: str, for_machine: MachineChoice):
+ lang_map = {
+ '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,
+ }
+ 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):
+ 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[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:
+ compilers, ccache = BinaryTable.parse_entry(value)
+ # Return value has to be a list of compiler 'choices'
+ compilers = [compilers]
+ 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 = defaults[lang]
+ ccache = BinaryTable.detect_ccache()
+
+ if env.machines.matches_build_machine(for_machine):
+ exe_wrap = None
+ else:
+ exe_wrap = env.get_exe_wrapper()
+
+ return compilers, ccache, exe_wrap
+
+def _handle_exceptions(exceptions, binaries, bintype='compiler'):
+ 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):
+ linker = env.lookup_binary_entry(compiler.for_machine, 'ar')
+ if linker is not None:
+ linkers = [linker]
+ else:
+ default_linkers = [[l] for l in defaults['static_linker']]
+ if isinstance(compiler, CudaCompiler):
+ linkers = [defaults['cuda_static_linker']] + default_linkers
+ elif isinstance(compiler, VisualStudioLikeCompiler):
+ linkers = [defaults['vs_static_linker'], defaults['clang_cl_static_linker']]
+ elif isinstance(compiler, GnuCompiler):
+ # Use gcc-ar if available; needed for LTO
+ linkers = [defaults['gcc_static_linker']] + default_linkers
+ elif isinstance(compiler, ClangCompiler):
+ # Use llvm-ar if available; needed for LTO
+ linkers = [defaults['clang_static_linker']] + default_linkers
+ elif isinstance(compiler, DCompiler):
+ # Prefer static linkers over linkers used by D compilers
+ if is_windows():
+ linkers = [defaults['vs_static_linker'], defaults['clang_cl_static_linker'], compiler.get_linker_exelist()]
+ else:
+ linkers = default_linkers
+ elif isinstance(compiler, IntelClCCompiler):
+ # Intel has it's own linker that acts like microsoft's lib
+ linkers = ['xilib']
+ elif isinstance(compiler, (PGICCompiler, PGIFortranCompiler)) and is_windows():
+ linkers = [['ar']] # For PGI on Windows, "ar" is just a wrapper calling link/lib.
+ else:
+ linkers = default_linkers
+ popen_exceptions = {}
+ for linker in linkers:
+ if not {'lib', 'lib.exe', 'llvm-lib', 'llvm-lib.exe', 'xilib', 'xilib.exe'}.isdisjoint(linker):
+ arg = '/?'
+ elif not {'ar2000', 'ar2000.exe'}.isdisjoint(linker):
+ arg = '?'
+ else:
+ arg = '--version'
+ try:
+ p, out, err = Popen_safe(linker + [arg])
+ except OSError as e:
+ popen_exceptions[' '.join(linker + [arg])] = e
+ continue
+ if "xilib: executing 'lib'" in err:
+ return IntelVisualStudioLinker(linker, getattr(compiler, 'machine', None))
+ if '/OUT:' in out.upper() or '/OUT:' in err.upper():
+ return VisualStudioLinker(linker, getattr(compiler, 'machine', None))
+ if 'ar-Error-Unknown switch: --version' in err:
+ return PGIStaticLinker(linker)
+ if p.returncode == 0 and ('armar' in linker or 'armar.exe' in linker):
+ return ArmarLinker(linker)
+ if 'DMD32 D Compiler' in out or 'DMD64 D Compiler' in out:
+ return DLinker(linker, compiler.arch)
+ if 'LDC - the LLVM D compiler' in out:
+ return DLinker(linker, compiler.arch, rsp_syntax=compiler.rsp_file_syntax())
+ if 'GDC' in out and ' based on D ' in out:
+ return DLinker(linker, compiler.arch)
+ if err.startswith('Renesas') and ('rlink' in linker or 'rlink.exe' in linker):
+ return CcrxLinker(linker)
+ if out.startswith('GNU ar') and ('xc16-ar' in linker or 'xc16-ar.exe' in linker):
+ return Xc16Linker(linker)
+ if out.startswith('TMS320C2000') and ('ar2000' in linker or 'ar2000.exe' in linker):
+ return C2000Linker(linker)
+ if out.startswith('The CompCert'):
+ return CompCertLinker(linker)
+ if p.returncode == 0:
+ return ArLinker(linker)
+ if p.returncode == 1 and err.startswith('usage'): # OSX
+ return ArLinker(linker)
+ if p.returncode == 1 and err.startswith('Usage'): # AIX
+ return AIXArLinker(linker)
+ if p.returncode == 1 and err.startswith('ar: bad option: --'): # Solaris
+ return ArLinker(linker)
+ _handle_exceptions(popen_exceptions, linkers, 'linker')
+ raise EnvironmentException('Unknown static linker "{}"'.format(' '.join(linkers)))
+
+
+
+# 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.
+ """
+ popen_exceptions = {}
+ 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]
+
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ compiler_name = os.path.basename(compiler[0])
+
+ if not {'cl', 'cl.exe', 'clang-cl', 'clang-cl.exe'}.isdisjoint(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):
+ 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'))]
+ found_cl = sanitize(shutil.which('cl'))
+ if found_cl in watcom_cls:
+ 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 'cl2000' in compiler_name:
+ 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'
+
+ try:
+ p, out, err = Popen_safe(compiler + [arg])
+ except OSError as e:
+ popen_exceptions[' '.join(compiler + [arg])] = e
+ continue
+
+ if 'ccrx' in compiler_name:
+ out = err
+
+ full_version = out.split('\n', 1)[0]
+ version = search_version(out)
+
+ guess_gcc_or_lcc = False
+ 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 = False
+
+ if guess_gcc_or_lcc:
+ defines = _get_gnu_compiler_defines(compiler)
+ if not defines:
+ popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
+ continue
+
+ if guess_gcc_or_lcc == 'lcc':
+ version = _get_lcc_version_from_defines(defines)
+ cls = ElbrusCCompiler if lang == 'c' else ElbrusCPPCompiler
+ else:
+ version = _get_gnu_version_from_defines(defines)
+ cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler
+
+ linker = guess_nix_linker(env, compiler, cls, 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 = EmscriptenCCompiler if lang == 'c' else 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. Old emcc versions ignore -Wl,--version completely,
+ # however. We'll report "unknown version" in that case.
+ with tempfile.NamedTemporaryFile(suffix='.c') as f:
+ cmd = compiler + [cls.LINKER_PREFIX + "--version", f.name]
+ _, o, _ = Popen_safe(cmd)
+
+ linker = 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 '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_str = re.search('.*Component.*', out)
+ if arm_ver_str is None:
+ popen_exceptions[' '.join(compiler)] = 'version string not found'
+ continue
+ arm_ver_str = arm_ver_str.group(0)
+ # Override previous values
+ version = search_version(arm_ver_str)
+ full_version = arm_ver_str
+ cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler
+ linker = 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(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 = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler
+ linker = guess_win_linker(env, ['lld-link'], cls, 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 = AppleClangCCompiler if lang == 'c' else AppleClangCPPCompiler
+ else:
+ cls = ClangCCompiler if lang == 'c' else 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, for_machine, invoked_directly=False)
+ except MesonException:
+ pass
+ if linker is None:
+ linker = guess_nix_linker(env, compiler, cls, 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 = IntelClCCompiler if lang == 'c' else IntelClCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = 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:
+ m = 'Failed to detect MSVC compiler version: stderr was\n{!r}'
+ raise EnvironmentException(m.format(err))
+ cl_signature = lookat.split('\n')[0]
+ match = re.search(r'.*(x86|x64|ARM|ARM64)([^_A-Za-z0-9]|$)', cl_signature)
+ if match:
+ target = match.group(1)
+ else:
+ m = 'Failed to detect MSVC compiler target architecture: \'cl /?\' output is\n{}'
+ raise EnvironmentException(m.format(cl_signature))
+ cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler
+ linker = guess_win_linker(env, ['link'], cls, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info, target,
+ exe_wrap, full_version=cl_signature, linker=linker)
+ if 'PGI Compilers' in out:
+ cls = PGICCompiler if lang == 'c' else PGICPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = 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 = NvidiaHPC_CCompiler if lang == 'c' else NvidiaHPC_CPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = 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 = IntelCCompiler if lang == 'c' else IntelCPPCompiler
+ l = guess_nix_linker(env, compiler, cls, for_machine)
+ return cls(
+ ccache + compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=l)
+ if 'ARM' in out:
+ cls = ArmCCompiler if lang == 'c' else ArmCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = 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 = CcrxCCompiler if lang == 'c' else CcrxCPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = 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 = Xc16CCompiler if lang == 'c' else Xc16CCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = 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 = CompCertCCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = CompCertDynamicLinker(for_machine, version=version)
+ return cls(
+ ccache + compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'TMS320C2000 C/C++' in out:
+ cls = C2000CCompiler if lang == 'c' else C2000CPPCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = C2000DynamicLinker(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)
+
+def detect_c_compiler(env: 'Environment', for_machine: MachineChoice):
+ return _detect_c_or_cpp_compiler(env, 'c', for_machine)
+
+def detect_cpp_compiler(env: 'Environment', for_machine: MachineChoice):
+ return _detect_c_or_cpp_compiler(env, 'cpp', for_machine)
+
+def detect_cuda_compiler(env: 'Environment', for_machine: MachineChoice):
+ 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:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ arg = '--version'
+ try:
+ p, out, err = Popen_safe(compiler + [arg])
+ except OSError as e:
+ popen_exceptions[' '.join(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().split('V')[-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('Could not find suitable CUDA compiler: "' + ' '.join(compilers) + '"')
+
+def detect_fortran_compiler(env: 'Environment', for_machine: MachineChoice):
+ popen_exceptions = {}
+ compilers, ccache, exe_wrap = _get_compilers(env, 'fortran', for_machine)
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ for arg in ['--version', '-V']:
+ try:
+ p, out, err = Popen_safe(compiler + [arg])
+ except OSError as e:
+ popen_exceptions[' '.join(compiler + [arg])] = e
+ continue
+
+ version = search_version(out)
+ full_version = out.split('\n', 1)[0]
+
+ guess_gcc_or_lcc = False
+ 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(compiler)] = 'no pre-processor defines'
+ continue
+ if guess_gcc_or_lcc == 'lcc':
+ version = _get_lcc_version_from_defines(defines)
+ cls = ElbrusFortranCompiler
+ else:
+ version = _get_gnu_version_from_defines(defines)
+ cls = GnuFortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, for_machine)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, defines, full_version=full_version,
+ linker=linker)
+
+ if 'G95' in out:
+ cls = G95FortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, for_machine)
+ return G95FortranCompiler(
+ 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 = SunFortranCompiler
+ linker = guess_nix_linker(env, compiler, cls, for_machine)
+ return SunFortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'Intel(R) Visual Fortran' in err:
+ version = search_version(err)
+ target = 'x86' if 'IA-32' in err else 'x86_64'
+ cls = IntelClFortranCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = XilinkDynamicLinker(for_machine, [], version=version)
+ return cls(
+ compiler, version, for_machine, is_cross, info,
+ target, exe_wrap, linker=linker)
+
+ if 'ifort (IFORT)' in out:
+ linker = guess_nix_linker(env, compiler, IntelFortranCompiler, for_machine)
+ return IntelFortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'PathScale EKOPath(tm)' in err:
+ return PathScaleFortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version)
+
+ if 'PGI Compilers' in out:
+ cls = PGIFortranCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = 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 = NvidiaHPC_FortranCompiler
+ env.coredata.add_lang_args(cls.language, cls, for_machine, env)
+ linker = 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:
+ linker = guess_nix_linker(env,
+ compiler, FlangFortranCompiler, for_machine)
+ return FlangFortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'Open64 Compiler Suite' in err:
+ linker = guess_nix_linker(env,
+ compiler, Open64FortranCompiler, for_machine)
+ return Open64FortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ if 'NAG Fortran' in err:
+ linker = guess_nix_linker(env,
+ compiler, NAGFortranCompiler, for_machine)
+ return NAGFortranCompiler(
+ compiler, version, for_machine, is_cross, info,
+ exe_wrap, full_version=full_version, linker=linker)
+
+ _handle_exceptions(popen_exceptions, compilers)
+
+def detect_objc_compiler(env: 'Environment', for_machine: MachineChoice) -> 'Compiler':
+ return _detect_objc_or_objcpp_compiler(env, for_machine, True)
+
+def detect_objcpp_compiler(env: 'Environment', for_machine: MachineChoice) -> 'Compiler':
+ return _detect_objc_or_objcpp_compiler(env, for_machine, False)
+
+def _detect_objc_or_objcpp_compiler(env: 'Environment', for_machine: MachineChoice, objc: bool) -> 'Compiler':
+ popen_exceptions = {}
+ compilers, ccache, exe_wrap = _get_compilers(env, 'objc' if objc else 'objcpp', for_machine)
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ arg = ['--version']
+ try:
+ p, out, err = Popen_safe(compiler + arg)
+ except OSError as e:
+ popen_exceptions[' '.join(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(compiler)] = 'no pre-processor defines'
+ continue
+ version = _get_gnu_version_from_defines(defines)
+ comp = GnuObjCCompiler if objc else GnuObjCPPCompiler
+ linker = guess_nix_linker(env, compiler, comp, 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(compiler)] = 'no pre-processor defines'
+ continue
+ if 'Apple' in out:
+ comp = AppleClangObjCCompiler if objc else AppleClangObjCPPCompiler
+ else:
+ comp = ClangObjCCompiler if objc else 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, for_machine)
+ except MesonException:
+ pass
+
+ if not linker:
+ linker = guess_nix_linker(env, compiler, comp, for_machine)
+ return comp(
+ ccache + compiler, version, for_machine,
+ is_cross, info, exe_wrap, linker=linker, defines=defines)
+ _handle_exceptions(popen_exceptions, compilers)
+
+def detect_java_compiler(env: 'Environment', for_machine):
+ 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(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(exelist) + '"')
+
+def detect_cs_compiler(env: 'Environment', for_machine):
+ compilers, ccache, exe_wrap = _get_compilers(env, 'cs', for_machine)
+ popen_exceptions = {}
+ info = env.machines[for_machine]
+ for comp in compilers:
+ if not isinstance(comp, list):
+ comp = [comp]
+ try:
+ p, out, err = Popen_safe(comp + ['--version'])
+ except OSError as e:
+ popen_exceptions[' '.join(comp + ['--version'])] = e
+ continue
+
+ version = search_version(out)
+ if 'Mono' in out:
+ cls = MonoCompiler
+ elif "Visual C#" in out:
+ cls = 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)
+
+def detect_cython_compiler(env: 'Environment', for_machine: MachineChoice) -> CythonCompiler:
+ """Search for a cython compiler."""
+ compilers, _, _ = _get_compilers(env, 'cython', for_machine)
+ is_cross = env.is_cross_build(for_machine)
+ info = env.machines[for_machine]
+
+ popen_exceptions: T.Dict[str, Exception] = {}
+ for comp in compilers:
+ if isinstance(comp, str):
+ comp = [comp]
+ try:
+ err = Popen_safe(comp + ['-V'])[2]
+ except OSError as e:
+ popen_exceptions[' '.join(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)
+
+def detect_vala_compiler(env: 'Environment', for_machine):
+ exelist = env.lookup_binary_entry(for_machine, '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(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, info, is_cross)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+def detect_rust_compiler(env: 'Environment', for_machine: MachineChoice) -> RustCompiler:
+ 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, VisualStudioLikeLinkerMixin)
+ override = env.lookup_binary_entry(for_machine, 'rust_ld')
+
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ arg = ['--version']
+ try:
+ out = Popen_safe(compiler + arg)[1]
+ except OSError as e:
+ popen_exceptions[' '.join(compiler + arg)] = e
+ continue
+
+ version = search_version(out)
+
+ 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.')
+
+ if override is None:
+ extra_args = {}
+ always_args = []
+ if is_link_exe:
+ compiler.extend(RustCompiler.use_linker_args(cc.linker.exelist[0]))
+ extra_args['direct'] = True
+ extra_args['machine'] = cc.linker.machine
+ else:
+ exelist = cc.linker.exelist.copy()
+ if 'ccache' in exelist[0]:
+ del exelist[0]
+ c = exelist.pop(0)
+ compiler.extend(RustCompiler.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
+ if is_link_exe:
+ linker = type(cc.linker)(for_machine, always_args, exelist=cc.linker.exelist,
+ version=cc.linker.version, **extra_args)
+ 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, RustCompiler, for_machine, use_linker_prefix=False)
+ # rustc takes linker arguments without a prefix, and
+ # inserts the correct prefix itself.
+ linker.direct = True
+ compiler.extend(RustCompiler.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(RustCompiler.use_linker_args(c))
+
+ env.coredata.add_lang_args(RustCompiler.language, RustCompiler, for_machine, env)
+ return RustCompiler(
+ compiler, version, for_machine, is_cross, info, exe_wrap,
+ linker=linker)
+
+ _handle_exceptions(popen_exceptions, compilers)
+
+def detect_d_compiler(env: 'Environment', for_machine: MachineChoice):
+ 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'], 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)
+ 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 not isinstance(exelist, list):
+ exelist = [exelist]
+ if os.path.basename(exelist[-1]).startswith(('ldmd', 'gdmd')):
+ raise EnvironmentException(
+ 'Meson does not support {} 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.'.format(exelist[-1]))
+ try:
+ p, out = Popen_safe(exelist + ['--version'])[0:2]
+ except OSError as e:
+ popen_exceptions[' '.join(exelist + ['--version'])] = e
+ continue
+ version = search_version(out)
+ full_version = out.split('\n', 1)[0]
+
+ if 'LLVM D compiler' in out:
+ # 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,
+ LLVMDCompiler, 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, LLVMDCompiler, for_machine,
+ extra_args=[f])
+ finally:
+ windows_proof_rm(f)
+ windows_proof_rm(objfile)
+
+ return LLVMDCompiler(
+ exelist, version, for_machine, info, arch,
+ full_version=full_version, linker=linker, version_output=out)
+ elif 'gdc' in out:
+ linker = guess_nix_linker(env, exelist, GnuDCompiler, for_machine)
+ return GnuDCompiler(
+ 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:
+ # 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, DmdDCompiler, for_machine,
+ invoked_directly=False, extra_args=[f, arch_arg])
+ else:
+ objfile = os.path.basename(f)[:-1] + 'o'
+ linker = guess_nix_linker(env,
+ exelist, DmdDCompiler, for_machine,
+ extra_args=[f, arch_arg])
+ finally:
+ windows_proof_rm(f)
+ windows_proof_rm(objfile)
+
+ return DmdDCompiler(
+ exelist, version, for_machine, info, arch,
+ full_version=full_version, linker=linker)
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+ _handle_exceptions(popen_exceptions, compilers)
+
+def detect_swift_compiler(env: 'Environment', for_machine):
+ 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(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:
+ linker = guess_nix_linker(env,
+ exelist, SwiftCompiler, for_machine,
+ extra_args=[f.name])
+ return SwiftCompiler(
+ exelist, version, for_machine, info, is_cross, linker=linker)
+
+ raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
+
+
+# GNU/Clang defines and version
+# =============================
+
+def _get_gnu_compiler_defines(compiler):
+ """
+ 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', '-']
+ p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
+ if p.returncode != 0:
+ raise EnvironmentException('Unable to detect GNU compiler type:\n' + output + error)
+ # Parse several lines of the type:
+ # `#define ___SOME_DEF some_value`
+ # and extract `___SOME_DEF`
+ defines = {}
+ 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] = True
+ if len(rest) == 2:
+ defines[rest[0]] = rest[1]
+ return defines
+
+def _get_clang_compiler_defines(compiler):
+ """
+ Get the list of Clang pre-processor defines
+ """
+ args = compiler + ['-E', '-dM', '-']
+ p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
+ if p.returncode != 0:
+ raise EnvironmentException('Unable to get clang pre-processor defines:\n' + output + error)
+ defines = {}
+ 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] = True
+ if len(rest) == 2:
+ defines[rest[0]] = rest[1]
+ return defines
+
+def _get_gnu_version_from_defines(defines):
+ 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):
+ 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/mixins/intel.py b/mesonbuild/compilers/mixins/intel.py
index 5bca254..89f3518 100644
--- a/mesonbuild/compilers/mixins/intel.py
+++ b/mesonbuild/compilers/mixins/intel.py
@@ -168,14 +168,11 @@ class IntelVisualStudioLikeCompiler(VisualStudioLikeCompiler):
return args
def get_toolset_version(self) -> T.Optional[str]:
- # Avoid circular dependencies....
- from ...environment import search_version
-
# 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, *_ = search_version(err).split('.')
+ v1, v2, *_ = mesonlib.search_version(err).split('.')
version = int(v1 + v2)
return self._calculate_toolset_version(version)
diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py
index 60c4b74..0ab8332 100644
--- a/mesonbuild/dependencies/dev.py
+++ b/mesonbuild/dependencies/dev.py
@@ -23,7 +23,7 @@ import shutil
import typing as T
from .. import mesonlib, mlog
-from ..compilers import AppleClangCCompiler, AppleClangCPPCompiler
+from ..compilers import AppleClangCCompiler, AppleClangCPPCompiler, detect_compiler_for
from ..environment import get_llvm_tool_names
from ..mesonlib import version_compare, stringlistify, extract_as_list, MachineChoice
from .base import DependencyException, DependencyMethods, strip_system_libdirs, SystemDependency
@@ -520,7 +520,7 @@ class JDKSystemDependency(SystemDependency):
m = self.env.machines[self.for_machine]
if 'java' not in environment.coredata.compilers[self.for_machine]:
- environment.detect_compiler_for('java', self.for_machine)
+ detect_compiler_for(environment, 'java', self.for_machine)
self.javac = environment.coredata.compilers[self.for_machine]['java']
self.version = self.javac.version
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 6248c58..df9c7eb 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -13,18 +13,16 @@
# limitations under the License.
import itertools
-import os, platform, re, sys, shutil, subprocess
-import tempfile
-import shlex
+import os, platform, re, sys, shutil
import typing as T
import collections
from . import coredata
-from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker, CcrxLinker, Xc16Linker, CompCertLinker, C2000Linker, IntelVisualStudioLinker, AIXArLinker
from . import mesonlib
from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, Popen_safe, PerMachine,
- PerMachineDefaultable, PerThreeMachineDefaultable, split_args, quote_arg, OptionKey
+ PerMachineDefaultable, PerThreeMachineDefaultable, split_args, quote_arg, OptionKey,
+ search_version
)
from . import mlog
from .programs import (
@@ -44,93 +42,8 @@ from .compilers import (
is_object,
is_source,
)
-from .linkers import (
- AppleDynamicLinker,
- ArmClangDynamicLinker,
- ArmDynamicLinker,
- CcrxDynamicLinker,
- Xc16DynamicLinker,
- CompCertDynamicLinker,
- C2000DynamicLinker,
- ClangClDynamicLinker,
- DynamicLinker,
- GnuBFDDynamicLinker,
- GnuGoldDynamicLinker,
- LLVMDynamicLinker,
- QualcommLLVMDynamicLinker,
- MSVCDynamicLinker,
- OptlinkDynamicLinker,
- NvidiaHPC_DynamicLinker,
- NvidiaHPC_StaticLinker,
- PGIDynamicLinker,
- PGIStaticLinker,
- SolarisDynamicLinker,
- AIXDynamicLinker,
- XilinkDynamicLinker,
- CudaLinker,
- VisualStudioLikeLinkerMixin,
- WASMDynamicLinker,
-)
+
from functools import lru_cache
-from .compilers import (
- ArmCCompiler,
- ArmCPPCompiler,
- ArmclangCCompiler,
- ArmclangCPPCompiler,
- AppleClangCCompiler,
- AppleClangCPPCompiler,
- AppleClangObjCCompiler,
- AppleClangObjCPPCompiler,
- ClangCCompiler,
- ClangCPPCompiler,
- ClangObjCCompiler,
- ClangObjCPPCompiler,
- ClangClCCompiler,
- ClangClCPPCompiler,
- CythonCompiler,
- FlangFortranCompiler,
- G95FortranCompiler,
- GnuCCompiler,
- GnuCPPCompiler,
- GnuFortranCompiler,
- GnuObjCCompiler,
- GnuObjCPPCompiler,
- ElbrusCCompiler,
- ElbrusCPPCompiler,
- ElbrusFortranCompiler,
- EmscriptenCCompiler,
- EmscriptenCPPCompiler,
- IntelCCompiler,
- IntelClCCompiler,
- IntelCPPCompiler,
- IntelClCPPCompiler,
- IntelFortranCompiler,
- IntelClFortranCompiler,
- JavaCompiler,
- MonoCompiler,
- CudaCompiler,
- VisualStudioCsCompiler,
- NAGFortranCompiler,
- Open64FortranCompiler,
- PathScaleFortranCompiler,
- NvidiaHPC_CCompiler,
- NvidiaHPC_CPPCompiler,
- NvidiaHPC_FortranCompiler,
- PGICCompiler,
- PGICPPCompiler,
- PGIFortranCompiler,
- RustCompiler,
- CcrxCCompiler,
- CcrxCPPCompiler,
- Xc16CCompiler,
- CompCertCCompiler,
- C2000CCompiler,
- C2000CPPCompiler,
- SunFortranCompiler,
- ValaCompiler,
- VisualStudioCCompiler,
- VisualStudioCPPCompiler,
-)
from mesonbuild import envconfig
if T.TYPE_CHECKING:
@@ -211,7 +124,7 @@ def detect_ninja(version: str = '1.8.2', log: bool = False) -> T.List[str]:
r = detect_ninja_command_and_version(version, log)
return r[0] if r else None
-def detect_ninja_command_and_version(version: str = '1.8.2', log: bool = False) -> (T.List[str], str):
+def detect_ninja_command_and_version(version: str = '1.8.2', log: bool = False) -> T.Tuple[T.List[str], str]:
env_ninja = os.environ.get('NINJA', None)
for n in [env_ninja] if env_ninja else ['ninja', 'ninja-build', 'samu']:
prog = ExternalProgram(n, silent=True)
@@ -520,49 +433,6 @@ def machine_info_can_run(machine_info: MachineInfo):
((true_build_cpu_family == 'x86_64') and (machine_info.cpu_family == 'x86')) or \
((true_build_cpu_family == 'aarch64') and (machine_info.cpu_family == 'arm'))
-def search_version(text: str) -> str:
- # Usually of the type 4.1.4 but compiler output may contain
- # stuff like this:
- # (Sourcery CodeBench Lite 2014.05-29) 4.8.3 20140320 (prerelease)
- # Limiting major version number to two digits seems to work
- # thus far. When we get to GCC 100, this will break, but
- # if we are still relevant when that happens, it can be
- # considered an achievement in itself.
- #
- # This regex is reaching magic levels. If it ever needs
- # to be updated, do not complexify but convert to something
- # saner instead.
- # We'll demystify it a bit with a verbose definition.
- version_regex = re.compile(r"""
- (?<! # Zero-width negative lookbehind assertion
- (
- \d # One digit
- | \. # Or one period
- ) # One occurrence
- )
- # Following pattern must not follow a digit or period
- (
- \d{1,2} # One or two digits
- (
- \.\d+ # Period and one or more digits
- )+ # One or more occurrences
- (
- -[a-zA-Z0-9]+ # Hyphen and one or more alphanumeric
- )? # Zero or one occurrence
- ) # One occurrence
- """, re.VERBOSE)
- match = version_regex.search(text)
- if match:
- return match.group(0)
-
- # try a simpler regex that has like "blah 2020.01.100 foo" or "blah 2020.01 foo"
- version_regex = re.compile(r"(\d{1,4}\.\d{1,4}\.?\d{0,4})")
- match = version_regex.search(text)
- if match:
- return match.group(0)
-
- return 'unknown version'
-
class Environment:
private_dir = 'meson-private'
log_dir = 'meson-logs'
@@ -699,50 +569,6 @@ class Environment:
else:
self.exe_wrapper = None
- # List of potential compilers.
- if mesonlib.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.
- self.default_c = ['icl', 'cl', 'cc', 'gcc', 'clang', 'clang-cl', 'pgcc']
- # There is currently no pgc++ for Windows, only for Mac and Linux.
- self.default_cpp = ['icl', 'cl', 'c++', 'g++', 'clang++', 'clang-cl']
- self.default_fortran = ['ifort', 'gfortran', 'flang', 'pgfortran', 'g95']
- # Clang and clang++ are valid, but currently unsupported.
- self.default_objc = ['cc', 'gcc']
- self.default_objcpp = ['c++', 'g++']
- self.default_cs = ['csc', 'mcs']
- else:
- if platform.machine().lower() == 'e2k':
- # There are no objc or objc++ compilers for Elbrus,
- # and there's no clang which can build binaries for host.
- self.default_c = ['cc', 'gcc', 'lcc']
- self.default_cpp = ['c++', 'g++', 'l++']
- self.default_objc = []
- self.default_objcpp = []
- else:
- self.default_c = ['cc', 'gcc', 'clang', 'nvc', 'pgcc', 'icc']
- self.default_cpp = ['c++', 'g++', 'clang++', 'nvc++', 'pgc++', 'icpc']
- self.default_objc = ['cc', 'gcc', 'clang']
- self.default_objcpp = ['c++', 'g++', 'clang++']
- self.default_fortran = ['gfortran', 'flang', 'nvfortran', 'pgfortran', 'ifort', 'g95']
- self.default_cs = ['mcs', 'csc']
- self.default_d = ['ldc2', 'ldc', 'gdc', 'dmd']
- self.default_java = ['javac']
- self.default_cuda = ['nvcc']
- self.default_rust = ['rustc']
- self.default_swift = ['swiftc']
- self.default_vala = ['valac']
- self.default_cython = [['cython']]
- self.default_static_linker = ['ar', 'gar']
- self.default_strip = ['strip']
- self.vs_static_linker = ['lib']
- self.clang_cl_static_linker = ['llvm-lib']
- self.cuda_static_linker = ['nvlink']
- self.gcc_static_linker = ['gcc-ar']
- self.clang_static_linker = ['llvm-ar']
self.default_cmake = ['cmake']
self.default_pkgconfig = ['pkg-config']
self.wrap_resolver = None
@@ -952,1193 +778,9 @@ class Environment:
def lookup_binary_entry(self, for_machine: MachineChoice, name: str) -> T.Optional[T.List[str]]:
return self.binaries[for_machine].lookup_entry(name)
- @staticmethod
- def get_gnu_compiler_defines(compiler):
- """
- 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', '-']
- p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
- if p.returncode != 0:
- raise EnvironmentException('Unable to detect GNU compiler type:\n' + output + error)
- # Parse several lines of the type:
- # `#define ___SOME_DEF some_value`
- # and extract `___SOME_DEF`
- defines = {}
- 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] = True
- if len(rest) == 2:
- defines[rest[0]] = rest[1]
- return defines
-
- @staticmethod
- def get_gnu_version_from_defines(defines):
- dot = '.'
- major = defines.get('__GNUC__', '0')
- minor = defines.get('__GNUC_MINOR__', '0')
- patch = defines.get('__GNUC_PATCHLEVEL__', '0')
- return dot.join((major, minor, patch))
-
- @staticmethod
- def get_lcc_version_from_defines(defines):
- 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))
-
- @staticmethod
- def get_clang_compiler_defines(compiler):
- """
- Get the list of Clang pre-processor defines
- """
- args = compiler + ['-E', '-dM', '-']
- p, output, error = Popen_safe(args, write='', stdin=subprocess.PIPE)
- if p.returncode != 0:
- raise EnvironmentException('Unable to get clang pre-processor defines:\n' + output + error)
- defines = {}
- 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] = True
- if len(rest) == 2:
- defines[rest[0]] = rest[1]
- return defines
-
- def _get_compilers(self, 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 = self.lookup_binary_entry(for_machine, lang)
- if value is not None:
- compilers, ccache = BinaryTable.parse_entry(value)
- # Return value has to be a list of compiler 'choices'
- compilers = [compilers]
- else:
- if not self.machines.matches_build_machine(for_machine):
- raise EnvironmentException(f'{lang!r} compiler binary not defined in cross or native file')
- compilers = getattr(self, 'default_' + lang)
- ccache = BinaryTable.detect_ccache()
-
- if self.machines.matches_build_machine(for_machine):
- exe_wrap = None
- else:
- exe_wrap = self.get_exe_wrapper()
-
- return compilers, ccache, exe_wrap
-
- def _handle_exceptions(self, exceptions, binaries, bintype='compiler'):
- 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)
-
- @staticmethod
- def __failed_to_detect_linker(compiler: T.List[str], args: T.List[str], stdout: str, stderr: str) -> 'T.NoReturn':
- msg = 'Unable to detect linker for compiler "{} {}"\nstdout: {}\nstderr: {}'.format(
- ' '.join(compiler), ' '.join(args), stdout, stderr)
- raise EnvironmentException(msg)
-
- def _guess_win_linker(self, compiler: T.List[str], comp_class: Compiler,
- for_machine: MachineChoice, *,
- use_linker_prefix: bool = True, invoked_directly: bool = True,
- extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
- self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self)
-
- # Explicitly pass logo here so that we can get the version of link.exe
- if not use_linker_prefix or comp_class.LINKER_PREFIX is None:
- check_args = ['/logo', '--version']
- elif isinstance(comp_class.LINKER_PREFIX, str):
- check_args = [comp_class.LINKER_PREFIX + '/logo', comp_class.LINKER_PREFIX + '--version']
- elif isinstance(comp_class.LINKER_PREFIX, list):
- check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version']
-
- check_args += self.coredata.options[OptionKey('args', lang=comp_class.language, machine=for_machine)].value
-
- override = [] # type: T.List[str]
- value = self.lookup_binary_entry(for_machine, comp_class.language + '_ld')
- if value is not None:
- override = comp_class.use_linker_args(value[0])
- check_args += override
-
- if extra_args is not None:
- check_args.extend(extra_args)
-
- p, o, _ = Popen_safe(compiler + check_args)
- if o.startswith('LLD'):
- if '(compatible with GNU linkers)' in o:
- return LLVMDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX,
- override, version=search_version(o))
- elif not invoked_directly:
- return ClangClDynamicLinker(
- for_machine, override, exelist=compiler, prefix=comp_class.LINKER_PREFIX,
- version=search_version(o), direct=False, machine=None)
-
- if value is not None and invoked_directly:
- compiler = value
- # We've already hanedled the non-direct case above
-
- p, o, e = Popen_safe(compiler + check_args)
- if o.startswith('LLD'):
- return ClangClDynamicLinker(
- for_machine, [],
- prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
- exelist=compiler, version=search_version(o), direct=invoked_directly)
- elif 'OPTLINK' in o:
- # Opltink's stdout *may* beging with a \r character.
- return OptlinkDynamicLinker(compiler, for_machine, version=search_version(o))
- elif o.startswith('Microsoft') or e.startswith('Microsoft'):
- out = o or e
- match = re.search(r'.*(X86|X64|ARM|ARM64).*', out)
- if match:
- target = str(match.group(1))
- else:
- target = 'x86'
-
- return MSVCDynamicLinker(
- for_machine, [], machine=target, exelist=compiler,
- prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
- version=search_version(out), direct=invoked_directly)
- elif 'GNU coreutils' in o:
- raise EnvironmentException(
- "Found GNU link.exe instead of MSVC link.exe. This link.exe "
- "is not a linker. You may need to reorder entries to your "
- "%PATH% variable to resolve this.")
- self.__failed_to_detect_linker(compiler, check_args, o, e)
-
- def _guess_nix_linker(self, compiler: T.List[str], comp_class: T.Type[Compiler],
- for_machine: MachineChoice, *,
- extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
- """Helper for guessing what linker to use on Unix-Like OSes.
-
- :compiler: Invocation to use to get linker
- :comp_class: The Compiler Type (uninstantiated)
- :for_machine: which machine this linker targets
- :extra_args: Any additional arguments required (such as a source file)
- """
- self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self)
- extra_args = extra_args or []
- extra_args += self.coredata.options[OptionKey('args', lang=comp_class.language, machine=for_machine)].value
-
- if isinstance(comp_class.LINKER_PREFIX, str):
- check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args
- else:
- check_args = comp_class.LINKER_PREFIX + ['--version'] + extra_args
-
- override = [] # type: T.List[str]
- value = self.lookup_binary_entry(for_machine, comp_class.language + '_ld')
- if value is not None:
- override = comp_class.use_linker_args(value[0])
- check_args += override
-
- _, o, e = Popen_safe(compiler + check_args)
- v = search_version(o + e)
- if o.startswith('LLD'):
- linker = LLVMDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v) # type: DynamicLinker
- elif 'Snapdragon' in e and 'LLVM' in e:
- linker = QualcommLLVMDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v) # type: DynamicLinker
- elif e.startswith('lld-link: '):
- # The LLD MinGW frontend didn't respond to --version before version 9.0.0,
- # and produced an error message about failing to link (when no object
- # files were specified), instead of printing the version number.
- # Let's try to extract the linker invocation command to grab the version.
-
- _, o, e = Popen_safe(compiler + check_args + ['-v'])
-
- try:
- linker_cmd = re.match(r'.*\n(.*?)\nlld-link: ', e, re.DOTALL).group(1)
- linker_cmd = shlex.split(linker_cmd)[0]
- except (AttributeError, IndexError, ValueError):
- pass
- else:
- _, o, e = Popen_safe([linker_cmd, '--version'])
- v = search_version(o)
-
- linker = LLVMDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
- # first is for apple clang, second is for real gcc, the third is icc
- elif e.endswith('(use -v to see invocation)\n') or 'macosx_version' in e or 'ld: unknown option:' in e:
- if isinstance(comp_class.LINKER_PREFIX, str):
- _, _, e = Popen_safe(compiler + [comp_class.LINKER_PREFIX + '-v'] + extra_args)
- else:
- _, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX + ['-v'] + extra_args)
- for line in e.split('\n'):
- if 'PROJECT:ld' in line:
- v = line.split('-')[1]
- break
- else:
- v = 'unknown version'
- linker = AppleDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
- elif 'GNU' in o or 'GNU' in e:
- if 'gold' in o or 'gold' in e:
- cls = GnuGoldDynamicLinker
- else:
- cls = GnuBFDDynamicLinker
- linker = cls(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
- elif 'Solaris' in e or 'Solaris' in o:
- for line in (o+e).split('\n'):
- if 'ld: Software Generation Utilities' in line:
- v = line.split(':')[2].lstrip()
- break
- else:
- v = 'unknown version'
- linker = SolarisDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX, override,
- version=v)
- elif 'ld: 0706-012 The -- flag is not recognized' in e:
- if isinstance(comp_class.LINKER_PREFIX, str):
- _, _, e = Popen_safe(compiler + [comp_class.LINKER_PREFIX + '-V'] + extra_args)
- else:
- _, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX + ['-V'] + extra_args)
- linker = AIXDynamicLinker(
- compiler, for_machine, comp_class.LINKER_PREFIX, override,
- version=search_version(e))
- else:
- self.__failed_to_detect_linker(compiler, check_args, o, e)
- return linker
-
- def _detect_c_or_cpp_compiler(self, 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.
- """
- popen_exceptions = {}
- compilers, ccache, exe_wrap = self._get_compilers(lang, for_machine)
- if override_compiler is not None:
- compilers = [override_compiler]
- is_cross = self.is_cross_build(for_machine)
- info = self.machines[for_machine]
-
- for compiler in compilers:
- if isinstance(compiler, str):
- compiler = [compiler]
- compiler_name = os.path.basename(compiler[0])
-
- if not {'cl', 'cl.exe', 'clang-cl', 'clang-cl.exe'}.isdisjoint(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):
- 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'))]
- found_cl = sanitize(shutil.which('cl'))
- if found_cl in watcom_cls:
- 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 'cl2000' in compiler_name:
- 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'
-
- try:
- p, out, err = Popen_safe(compiler + [arg])
- except OSError as e:
- popen_exceptions[' '.join(compiler + [arg])] = e
- continue
-
- if 'ccrx' in compiler_name:
- out = err
-
- full_version = out.split('\n', 1)[0]
- version = search_version(out)
-
- guess_gcc_or_lcc = False
- 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 = False
-
- if guess_gcc_or_lcc:
- defines = self.get_gnu_compiler_defines(compiler)
- if not defines:
- popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
- continue
-
- if guess_gcc_or_lcc == 'lcc':
- version = self.get_lcc_version_from_defines(defines)
- cls = ElbrusCCompiler if lang == 'c' else ElbrusCPPCompiler
- else:
- version = self.get_gnu_version_from_defines(defines)
- cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler
-
- linker = self._guess_nix_linker(compiler, cls, 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 = EmscriptenCCompiler if lang == 'c' else EmscriptenCPPCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
-
- # 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. Old emcc versions ignore -Wl,--version completely,
- # however. We'll report "unknown version" in that case.
- with tempfile.NamedTemporaryFile(suffix='.c') as f:
- cmd = compiler + [cls.LINKER_PREFIX + "--version", f.name]
- _, o, _ = Popen_safe(cmd)
-
- linker = 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 '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_str = re.search('.*Component.*', out)
- if arm_ver_str is None:
- popen_exceptions[' '.join(compiler)] = 'version string not found'
- continue
- arm_ver_str = arm_ver_str.group(0)
- # Override previous values
- version = search_version(arm_ver_str)
- full_version = arm_ver_str
- cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler
- linker = ArmClangDynamicLinker(for_machine, version=version)
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- 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(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 = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler
- linker = self._guess_win_linker(['lld-link'], cls, 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 = self.get_clang_compiler_defines(compiler)
-
- # Even if the for_machine is darwin, we could be using vanilla
- # clang.
- if 'Apple' in out:
- cls = AppleClangCCompiler if lang == 'c' else AppleClangCPPCompiler
- else:
- cls = ClangCCompiler if lang == 'c' else ClangCPPCompiler
-
- if 'windows' in out or self.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 = self._guess_win_linker(compiler, cls, for_machine, invoked_directly=False)
- except MesonException:
- pass
- if linker is None:
- linker = self._guess_nix_linker(compiler, cls, 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 = IntelClCCompiler if lang == 'c' else IntelClCPPCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- linker = 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:
- m = 'Failed to detect MSVC compiler version: stderr was\n{!r}'
- raise EnvironmentException(m.format(err))
- cl_signature = lookat.split('\n')[0]
- match = re.search(r'.*(x86|x64|ARM|ARM64)([^_A-Za-z0-9]|$)', cl_signature)
- if match:
- target = match.group(1)
- else:
- m = 'Failed to detect MSVC compiler target architecture: \'cl /?\' output is\n{}'
- raise EnvironmentException(m.format(cl_signature))
- cls = VisualStudioCCompiler if lang == 'c' else VisualStudioCPPCompiler
- linker = self._guess_win_linker(['link'], cls, for_machine)
- return cls(
- compiler, version, for_machine, is_cross, info, target,
- exe_wrap, full_version=cl_signature, linker=linker)
- if 'PGI Compilers' in out:
- cls = PGICCompiler if lang == 'c' else PGICPPCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- linker = 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 = NvidiaHPC_CCompiler if lang == 'c' else NvidiaHPC_CPPCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- linker = 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 = IntelCCompiler if lang == 'c' else IntelCPPCompiler
- l = self._guess_nix_linker(compiler, cls, for_machine)
- return cls(
- ccache + compiler, version, for_machine, is_cross, info,
- exe_wrap, full_version=full_version, linker=l)
- if 'ARM' in out:
- cls = ArmCCompiler if lang == 'c' else ArmCPPCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- linker = 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 = CcrxCCompiler if lang == 'c' else CcrxCPPCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- linker = 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 = Xc16CCompiler if lang == 'c' else Xc16CCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- linker = 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 = CompCertCCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- linker = CompCertDynamicLinker(for_machine, version=version)
- return cls(
- ccache + compiler, version, for_machine, is_cross, info,
- exe_wrap, full_version=full_version, linker=linker)
-
- if 'TMS320C2000 C/C++' in out:
- cls = C2000CCompiler if lang == 'c' else C2000CPPCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- linker = C2000DynamicLinker(for_machine, version=version)
- return cls(
- ccache + compiler, version, for_machine, is_cross, info,
- exe_wrap, full_version=full_version, linker=linker)
-
-
- self._handle_exceptions(popen_exceptions, compilers)
-
- def detect_c_compiler(self, for_machine):
- return self._detect_c_or_cpp_compiler('c', for_machine)
-
- def detect_cpp_compiler(self, for_machine):
- return self._detect_c_or_cpp_compiler('cpp', for_machine)
-
- def detect_cuda_compiler(self, for_machine):
- popen_exceptions = {}
- is_cross = self.is_cross_build(for_machine)
- compilers, ccache, exe_wrap = self._get_compilers('cuda', for_machine)
- info = self.machines[for_machine]
- for compiler in compilers:
- if isinstance(compiler, str):
- compiler = [compiler]
- arg = '--version'
- try:
- p, out, err = Popen_safe(compiler + [arg])
- except OSError as e:
- popen_exceptions[' '.join(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().split('V')[-1]
- cpp_compiler = self.detect_cpp_compiler(for_machine)
- cls = CudaCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- 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('Could not find suitable CUDA compiler: "' + ' '.join(compilers) + '"')
-
- def detect_fortran_compiler(self, for_machine: MachineChoice):
- popen_exceptions = {}
- compilers, ccache, exe_wrap = self._get_compilers('fortran', for_machine)
- is_cross = self.is_cross_build(for_machine)
- info = self.machines[for_machine]
- for compiler in compilers:
- if isinstance(compiler, str):
- compiler = [compiler]
- for arg in ['--version', '-V']:
- try:
- p, out, err = Popen_safe(compiler + [arg])
- except OSError as e:
- popen_exceptions[' '.join(compiler + [arg])] = e
- continue
-
- version = search_version(out)
- full_version = out.split('\n', 1)[0]
-
- guess_gcc_or_lcc = False
- 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 = self.get_gnu_compiler_defines(compiler)
- if not defines:
- popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
- continue
- if guess_gcc_or_lcc == 'lcc':
- version = self.get_lcc_version_from_defines(defines)
- cls = ElbrusFortranCompiler
- else:
- version = self.get_gnu_version_from_defines(defines)
- cls = GnuFortranCompiler
- linker = self._guess_nix_linker(
- compiler, cls, for_machine)
- return cls(
- compiler, version, for_machine, is_cross, info,
- exe_wrap, defines, full_version=full_version,
- linker=linker)
-
- if 'G95' in out:
- linker = self._guess_nix_linker(
- compiler, cls, for_machine)
- return G95FortranCompiler(
- compiler, version, for_machine, is_cross, info,
- exe_wrap, full_version=full_version, linker=linker)
-
- if 'Sun Fortran' in err:
- version = search_version(err)
- linker = self._guess_nix_linker(
- compiler, cls, for_machine)
- return SunFortranCompiler(
- compiler, version, for_machine, is_cross, info,
- exe_wrap, full_version=full_version, linker=linker)
-
- if 'Intel(R) Visual Fortran' in err:
- version = search_version(err)
- target = 'x86' if 'IA-32' in err else 'x86_64'
- cls = IntelClFortranCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- linker = XilinkDynamicLinker(for_machine, [], version=version)
- return cls(
- compiler, version, for_machine, is_cross, info,
- target, exe_wrap, linker=linker)
-
- if 'ifort (IFORT)' in out:
- linker = self._guess_nix_linker(compiler, IntelFortranCompiler, for_machine)
- return IntelFortranCompiler(
- compiler, version, for_machine, is_cross, info,
- exe_wrap, full_version=full_version, linker=linker)
-
- if 'PathScale EKOPath(tm)' in err:
- return PathScaleFortranCompiler(
- compiler, version, for_machine, is_cross, info,
- exe_wrap, full_version=full_version)
-
- if 'PGI Compilers' in out:
- cls = PGIFortranCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- linker = 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 = NvidiaHPC_FortranCompiler
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- linker = 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:
- linker = self._guess_nix_linker(
- compiler, FlangFortranCompiler, for_machine)
- return FlangFortranCompiler(
- compiler, version, for_machine, is_cross, info,
- exe_wrap, full_version=full_version, linker=linker)
-
- if 'Open64 Compiler Suite' in err:
- linker = self._guess_nix_linker(
- compiler, Open64FortranCompiler, for_machine)
- return Open64FortranCompiler(
- compiler, version, for_machine, is_cross, info,
- exe_wrap, full_version=full_version, linker=linker)
-
- if 'NAG Fortran' in err:
- linker = self._guess_nix_linker(
- compiler, NAGFortranCompiler, for_machine)
- return NAGFortranCompiler(
- compiler, version, for_machine, is_cross, info,
- exe_wrap, full_version=full_version, linker=linker)
-
- self._handle_exceptions(popen_exceptions, compilers)
-
def get_scratch_dir(self) -> str:
return self.scratch_dir
- def detect_objc_compiler(self, for_machine: MachineChoice) -> 'Compiler':
- return self._detect_objc_or_objcpp_compiler(for_machine, True)
-
- def detect_objcpp_compiler(self, for_machine: MachineChoice) -> 'Compiler':
- return self._detect_objc_or_objcpp_compiler(for_machine, False)
-
- def _detect_objc_or_objcpp_compiler(self, for_machine: MachineChoice, objc: bool) -> 'Compiler':
- popen_exceptions = {}
- compilers, ccache, exe_wrap = self._get_compilers('objc' if objc else 'objcpp', for_machine)
- is_cross = self.is_cross_build(for_machine)
- info = self.machines[for_machine]
-
- for compiler in compilers:
- if isinstance(compiler, str):
- compiler = [compiler]
- arg = ['--version']
- try:
- p, out, err = Popen_safe(compiler + arg)
- except OSError as e:
- popen_exceptions[' '.join(compiler + arg)] = e
- continue
- version = search_version(out)
- if 'Free Software Foundation' in out:
- defines = self.get_gnu_compiler_defines(compiler)
- if not defines:
- popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
- continue
- version = self.get_gnu_version_from_defines(defines)
- comp = GnuObjCCompiler if objc else GnuObjCPPCompiler
- linker = self._guess_nix_linker(compiler, comp, for_machine)
- return comp(
- ccache + compiler, version, for_machine, is_cross, info,
- exe_wrap, defines, linker=linker)
- if 'clang' in out:
- linker = None
- defines = self.get_clang_compiler_defines(compiler)
- if not defines:
- popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
- continue
- if 'Apple' in out:
- comp = AppleClangObjCCompiler if objc else AppleClangObjCPPCompiler
- else:
- comp = ClangObjCCompiler if objc else ClangObjCPPCompiler
- if 'windows' in out or self.machines[for_machine].is_windows():
- # If we're in a MINGW context this actually will use a gnu style ld
- try:
- linker = self._guess_win_linker(compiler, comp, for_machine)
- except MesonException:
- pass
-
- if not linker:
- linker = self._guess_nix_linker(
- compiler, comp, for_machine)
- return comp(
- ccache + compiler, version, for_machine,
- is_cross, info, exe_wrap, linker=linker, defines=defines)
- self._handle_exceptions(popen_exceptions, compilers)
-
- def detect_java_compiler(self, for_machine):
- exelist = self.lookup_binary_entry(for_machine, 'java')
- info = self.machines[for_machine]
- if exelist is None:
- # TODO support fallback
- exelist = [self.default_java[0]]
-
- try:
- p, out, err = Popen_safe(exelist + ['-version'])
- except OSError:
- raise EnvironmentException('Could not execute Java compiler "{}"'.format(' '.join(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
- self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self)
- return comp_class(exelist, version, for_machine, info)
- raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
-
- def detect_cs_compiler(self, for_machine):
- compilers, ccache, exe_wrap = self._get_compilers('cs', for_machine)
- popen_exceptions = {}
- info = self.machines[for_machine]
- for comp in compilers:
- if not isinstance(comp, list):
- comp = [comp]
- try:
- p, out, err = Popen_safe(comp + ['--version'])
- except OSError as e:
- popen_exceptions[' '.join(comp + ['--version'])] = e
- continue
-
- version = search_version(out)
- if 'Mono' in out:
- cls = MonoCompiler
- elif "Visual C#" in out:
- cls = VisualStudioCsCompiler
- else:
- continue
- self.coredata.add_lang_args(cls.language, cls, for_machine, self)
- return cls(comp, version, for_machine, info)
-
- self._handle_exceptions(popen_exceptions, compilers)
-
- def detect_cython_compiler(self, for_machine: MachineChoice) -> CythonCompiler:
- """Search for a cython compiler."""
- compilers = self.lookup_binary_entry(for_machine, 'cython')
- is_cross = self.is_cross_build(for_machine)
- info = self.machines[for_machine]
- if compilers is None:
- # TODO support fallback
- compilers = [self.default_cython[0]]
-
- popen_exceptions: T.Dict[str, Exception] = {}
- for comp in compilers:
- try:
- err = Popen_safe(comp + ['-V'])[2]
- except OSError as e:
- popen_exceptions[' '.join(comp + ['-V'])] = e
- continue
-
- version = search_version(err)
- if 'Cython' in err:
- comp_class = CythonCompiler
- self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self)
- return comp_class(comp, version, for_machine, info, is_cross=is_cross)
- self._handle_exceptions(popen_exceptions, compilers)
-
- def detect_vala_compiler(self, for_machine):
- exelist = self.lookup_binary_entry(for_machine, 'vala')
- is_cross = self.is_cross_build(for_machine)
- info = self.machines[for_machine]
- if exelist is None:
- # TODO support fallback
- exelist = [self.default_vala[0]]
-
- try:
- p, out = Popen_safe(exelist + ['--version'])[0:2]
- except OSError:
- raise EnvironmentException('Could not execute Vala compiler "{}"'.format(' '.join(exelist)))
- version = search_version(out)
- if 'Vala' in out:
- comp_class = ValaCompiler
- self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self)
- return comp_class(exelist, version, for_machine, info, is_cross)
- raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
-
- def detect_rust_compiler(self, for_machine: MachineChoice) -> RustCompiler:
- popen_exceptions = {} # type: T.Dict[str, Exception]
- compilers, _, exe_wrap = self._get_compilers('rust', for_machine)
- is_cross = self.is_cross_build(for_machine)
- info = self.machines[for_machine]
-
- cc = self.detect_c_compiler(for_machine)
- is_link_exe = isinstance(cc.linker, VisualStudioLikeLinkerMixin)
- override = self.lookup_binary_entry(for_machine, 'rust_ld')
-
- for compiler in compilers:
- if isinstance(compiler, str):
- compiler = [compiler]
- arg = ['--version']
- try:
- out = Popen_safe(compiler + arg)[1]
- except OSError as e:
- popen_exceptions[' '.join(compiler + arg)] = e
- continue
-
- version = search_version(out)
-
- 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.')
-
- if override is None:
- extra_args = {}
- always_args = []
- if is_link_exe:
- compiler.extend(RustCompiler.use_linker_args(cc.linker.exelist[0]))
- extra_args['direct'] = True
- extra_args['machine'] = cc.linker.machine
- else:
- exelist = cc.linker.exelist.copy()
- if 'ccache' in exelist[0]:
- del exelist[0]
- c = exelist.pop(0)
- compiler.extend(RustCompiler.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
- if is_link_exe:
- linker = type(cc.linker)(for_machine, always_args, exelist=cc.linker.exelist,
- version=cc.linker.version, **extra_args)
- 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 = self._guess_win_linker(
- override, RustCompiler, for_machine, use_linker_prefix=False)
- # rustc takes linker arguments without a prefix, and
- # inserts the correct prefix itself.
- linker.direct = True
- compiler.extend(RustCompiler.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 = self._detect_c_or_cpp_compiler('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(RustCompiler.use_linker_args(c))
-
- self.coredata.add_lang_args(RustCompiler.language, RustCompiler, for_machine, self)
- return RustCompiler(
- compiler, version, for_machine, is_cross, info, exe_wrap,
- linker=linker)
-
- self._handle_exceptions(popen_exceptions, compilers)
-
- def detect_d_compiler(self, for_machine: MachineChoice):
- info = self.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': self.detect_c_compiler(for_machine)}
- is_msvc = isinstance(c_compiler['c'], VisualStudioCCompiler)
- if not is_msvc:
- c_compiler = {}
-
- arch = detect_cpu_family(c_compiler)
- if is_msvc and arch == 'x86':
- arch = 'x86_mscoff'
-
- popen_exceptions = {}
- is_cross = self.is_cross_build(for_machine)
- results, ccache, exe_wrap = self._get_compilers('d', for_machine)
- for exelist in results:
- # 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 not isinstance(exelist, list):
- exelist = [exelist]
- if os.path.basename(exelist[-1]).startswith(('ldmd', 'gdmd')):
- raise EnvironmentException(
- 'Meson does not support {} 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.'.format(exelist[-1]))
- try:
- p, out = Popen_safe(exelist + ['--version'])[0:2]
- except OSError as e:
- popen_exceptions[' '.join(exelist + ['--version'])] = e
- continue
- version = search_version(out)
- full_version = out.split('\n', 1)[0]
-
- if 'LLVM D compiler' in out:
- # 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 = self._guess_win_linker(
- exelist,
- compilers.LLVMDCompiler, 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 = self._guess_nix_linker(
- exelist, compilers.LLVMDCompiler, for_machine,
- extra_args=[f])
- finally:
- mesonlib.windows_proof_rm(f)
- mesonlib.windows_proof_rm(objfile)
-
- return compilers.LLVMDCompiler(
- exelist, version, for_machine, info, arch,
- full_version=full_version, linker=linker, version_output=out)
- elif 'gdc' in out:
- linker = self._guess_nix_linker(exelist, compilers.GnuDCompiler, for_machine)
- return compilers.GnuDCompiler(
- 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:
- # 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 = self._guess_win_linker(
- exelist, compilers.DmdDCompiler, for_machine,
- invoked_directly=False, extra_args=[f, arch_arg])
- else:
- objfile = os.path.basename(f)[:-1] + 'o'
- linker = self._guess_nix_linker(
- exelist, compilers.DmdDCompiler, for_machine,
- extra_args=[f, arch_arg])
- finally:
- mesonlib.windows_proof_rm(f)
- mesonlib.windows_proof_rm(objfile)
-
- return compilers.DmdDCompiler(
- exelist, version, for_machine, info, arch,
- full_version=full_version, linker=linker)
- raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
-
- self._handle_exceptions(popen_exceptions, compilers)
-
- def detect_swift_compiler(self, for_machine):
- exelist = self.lookup_binary_entry(for_machine, 'swift')
- is_cross = self.is_cross_build(for_machine)
- info = self.machines[for_machine]
- if exelist is None:
- # TODO support fallback
- exelist = [self.default_swift[0]]
-
- try:
- p, _, err = Popen_safe(exelist + ['-v'])
- except OSError:
- raise EnvironmentException('Could not execute Swift compiler "{}"'.format(' '.join(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:
- linker = self._guess_nix_linker(
- exelist, compilers.SwiftCompiler, for_machine,
- extra_args=[f.name])
- return compilers.SwiftCompiler(
- exelist, version, for_machine, info, is_cross, linker=linker)
-
- raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"')
-
- def compiler_from_language(self, lang: str, for_machine: MachineChoice):
- if lang == 'c':
- comp = self.detect_c_compiler(for_machine)
- elif lang == 'cpp':
- comp = self.detect_cpp_compiler(for_machine)
- elif lang == 'objc':
- comp = self.detect_objc_compiler(for_machine)
- elif lang == 'cuda':
- comp = self.detect_cuda_compiler(for_machine)
- elif lang == 'objcpp':
- comp = self.detect_objcpp_compiler(for_machine)
- elif lang == 'java':
- comp = self.detect_java_compiler(for_machine)
- elif lang == 'cs':
- comp = self.detect_cs_compiler(for_machine)
- elif lang == 'vala':
- comp = self.detect_vala_compiler(for_machine)
- elif lang == 'd':
- comp = self.detect_d_compiler(for_machine)
- elif lang == 'rust':
- comp = self.detect_rust_compiler(for_machine)
- elif lang == 'fortran':
- comp = self.detect_fortran_compiler(for_machine)
- elif lang == 'swift':
- comp = self.detect_swift_compiler(for_machine)
- elif lang == 'cython':
- comp = self.detect_cython_compiler(for_machine)
- else:
- comp = None
- return comp
-
- def detect_compiler_for(self, lang: str, for_machine: MachineChoice):
- comp = self.compiler_from_language(lang, for_machine)
- if comp is not None:
- assert comp.for_machine == for_machine
- self.coredata.process_new_compiler(lang, comp, self)
- return comp
-
- def detect_static_linker(self, compiler):
- linker = self.lookup_binary_entry(compiler.for_machine, 'ar')
- if linker is not None:
- linkers = [linker]
- else:
- defaults = [[l] for l in self.default_static_linker]
- if isinstance(compiler, compilers.CudaCompiler):
- linkers = [self.cuda_static_linker] + defaults
- elif isinstance(compiler, compilers.VisualStudioLikeCompiler):
- linkers = [self.vs_static_linker, self.clang_cl_static_linker]
- elif isinstance(compiler, compilers.GnuCompiler):
- # Use gcc-ar if available; needed for LTO
- linkers = [self.gcc_static_linker] + defaults
- elif isinstance(compiler, compilers.ClangCompiler):
- # Use llvm-ar if available; needed for LTO
- linkers = [self.clang_static_linker] + defaults
- elif isinstance(compiler, compilers.DCompiler):
- # Prefer static linkers over linkers used by D compilers
- if mesonlib.is_windows():
- linkers = [self.vs_static_linker, self.clang_cl_static_linker, compiler.get_linker_exelist()]
- else:
- linkers = defaults
- elif isinstance(compiler, IntelClCCompiler):
- # Intel has it's own linker that acts like microsoft's lib
- linkers = ['xilib']
- elif isinstance(compiler, (PGICCompiler, PGIFortranCompiler)) and mesonlib.is_windows():
- linkers = [['ar']] # For PGI on Windows, "ar" is just a wrapper calling link/lib.
- else:
- linkers = defaults
- popen_exceptions = {}
- for linker in linkers:
- if not {'lib', 'lib.exe', 'llvm-lib', 'llvm-lib.exe', 'xilib', 'xilib.exe'}.isdisjoint(linker):
- arg = '/?'
- elif not {'ar2000', 'ar2000.exe'}.isdisjoint(linker):
- arg = '?'
- else:
- arg = '--version'
- try:
- p, out, err = Popen_safe(linker + [arg])
- except OSError as e:
- popen_exceptions[' '.join(linker + [arg])] = e
- continue
- if "xilib: executing 'lib'" in err:
- return IntelVisualStudioLinker(linker, getattr(compiler, 'machine', None))
- if '/OUT:' in out.upper() or '/OUT:' in err.upper():
- return VisualStudioLinker(linker, getattr(compiler, 'machine', None))
- if 'ar-Error-Unknown switch: --version' in err:
- return PGIStaticLinker(linker)
- if p.returncode == 0 and ('armar' in linker or 'armar.exe' in linker):
- return ArmarLinker(linker)
- if 'DMD32 D Compiler' in out or 'DMD64 D Compiler' in out:
- return DLinker(linker, compiler.arch)
- if 'LDC - the LLVM D compiler' in out:
- return DLinker(linker, compiler.arch, rsp_syntax=compiler.rsp_file_syntax())
- if 'GDC' in out and ' based on D ' in out:
- return DLinker(linker, compiler.arch)
- if err.startswith('Renesas') and ('rlink' in linker or 'rlink.exe' in linker):
- return CcrxLinker(linker)
- if out.startswith('GNU ar') and ('xc16-ar' in linker or 'xc16-ar.exe' in linker):
- return Xc16Linker(linker)
- if out.startswith('TMS320C2000') and ('ar2000' in linker or 'ar2000.exe' in linker):
- return C2000Linker(linker)
- if out.startswith('The CompCert'):
- return CompCertLinker(linker)
- if p.returncode == 0:
- return ArLinker(linker)
- if p.returncode == 1 and err.startswith('usage'): # OSX
- return ArLinker(linker)
- if p.returncode == 1 and err.startswith('Usage'): # AIX
- return AIXArLinker(linker)
- if p.returncode == 1 and err.startswith('ar: bad option: --'): # Solaris
- return ArLinker(linker)
- self._handle_exceptions(popen_exceptions, linkers, 'linker')
- raise EnvironmentException('Unknown static linker "{}"'.format(' '.join(linkers)))
-
def get_source_dir(self) -> str:
return self.source_dir
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index f1c7f33..d9ecd25 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -1328,7 +1328,7 @@ external dependencies (including libraries) must go to "dependencies".''')
comp = clist[lang]
else:
try:
- comp = self.environment.detect_compiler_for(lang, for_machine)
+ comp = compilers.detect_compiler_for(self.environment, lang, for_machine)
if comp is None:
raise InvalidArguments('Tried to use unknown language "%s".' % lang)
if self.should_skip_sanity_check(for_machine):
diff --git a/mesonbuild/linkers/__init__.py b/mesonbuild/linkers/__init__.py
index 0d6fbc1..9182fa1 100644
--- a/mesonbuild/linkers/__init__.py
+++ b/mesonbuild/linkers/__init__.py
@@ -12,6 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from .detect import (
+ defaults,
+ guess_win_linker,
+ guess_nix_linker,
+)
from .linkers import (
RSPFileSyntax,
@@ -64,6 +69,12 @@ from .linkers import (
)
__all__ = [
+ # detect.py
+ 'defaults',
+ 'guess_win_linker',
+ 'guess_nix_linker',
+
+ # linkers.py
'RSPFileSyntax',
'StaticLinker',
diff --git a/mesonbuild/linkers/detect.py b/mesonbuild/linkers/detect.py
new file mode 100644
index 0000000..702bf3d
--- /dev/null
+++ b/mesonbuild/linkers/detect.py
@@ -0,0 +1,216 @@
+# Copyright 2012-2021 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 ..mesonlib import (
+ EnvironmentException, MachineChoice, OptionKey,
+ Popen_safe, search_version
+)
+from .linkers import (
+ DynamicLinker,
+ AppleDynamicLinker,
+ GnuDynamicLinker,
+ GnuGoldDynamicLinker,
+ GnuBFDDynamicLinker,
+ LLVMDynamicLinker,
+ QualcommLLVMDynamicLinker,
+ MSVCDynamicLinker,
+ ClangClDynamicLinker,
+ SolarisDynamicLinker,
+ AIXDynamicLinker,
+ OptlinkDynamicLinker,
+)
+
+import re
+import shlex
+import typing as T
+
+if T.TYPE_CHECKING:
+ from ..environment import Environment
+ from ..compilers import Compiler
+
+defaults: T.Dict[str, T.List[str]] = {}
+defaults['static_linker'] = ['ar', 'gar']
+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']
+
+def __failed_to_detect_linker(compiler: T.List[str], args: T.List[str], stdout: str, stderr: str) -> 'T.NoReturn':
+ msg = 'Unable to detect linker for compiler "{} {}"\nstdout: {}\nstderr: {}'.format(
+ ' '.join(compiler), ' '.join(args), stdout, stderr)
+ raise EnvironmentException(msg)
+
+
+def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Type['Compiler'],
+ for_machine: MachineChoice, *,
+ use_linker_prefix: bool = True, invoked_directly: bool = True,
+ extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
+ env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+
+ # Explicitly pass logo here so that we can get the version of link.exe
+ if not use_linker_prefix or comp_class.LINKER_PREFIX is None:
+ check_args = ['/logo', '--version']
+ elif isinstance(comp_class.LINKER_PREFIX, str):
+ check_args = [comp_class.LINKER_PREFIX + '/logo', comp_class.LINKER_PREFIX + '--version']
+ elif isinstance(comp_class.LINKER_PREFIX, list):
+ check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version']
+
+ check_args += env.coredata.options[OptionKey('args', lang=comp_class.language, machine=for_machine)].value
+
+ override = [] # type: T.List[str]
+ value = env.lookup_binary_entry(for_machine, comp_class.language + '_ld')
+ if value is not None:
+ override = comp_class.use_linker_args(value[0])
+ check_args += override
+
+ if extra_args is not None:
+ check_args.extend(extra_args)
+
+ p, o, _ = Popen_safe(compiler + check_args)
+ if o.startswith('LLD'):
+ if '(compatible with GNU linkers)' in o:
+ return LLVMDynamicLinker(
+ compiler, for_machine, comp_class.LINKER_PREFIX,
+ override, version=search_version(o))
+ elif not invoked_directly:
+ return ClangClDynamicLinker(
+ for_machine, override, exelist=compiler, prefix=comp_class.LINKER_PREFIX,
+ version=search_version(o), direct=False, machine=None)
+
+ if value is not None and invoked_directly:
+ compiler = value
+ # We've already hanedled the non-direct case above
+
+ p, o, e = Popen_safe(compiler + check_args)
+ if o.startswith('LLD'):
+ return ClangClDynamicLinker(
+ for_machine, [],
+ prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
+ exelist=compiler, version=search_version(o), direct=invoked_directly)
+ elif 'OPTLINK' in o:
+ # Opltink's stdout *may* beging with a \r character.
+ return OptlinkDynamicLinker(compiler, for_machine, version=search_version(o))
+ elif o.startswith('Microsoft') or e.startswith('Microsoft'):
+ out = o or e
+ match = re.search(r'.*(X86|X64|ARM|ARM64).*', out)
+ if match:
+ target = str(match.group(1))
+ else:
+ target = 'x86'
+
+ return MSVCDynamicLinker(
+ for_machine, [], machine=target, exelist=compiler,
+ prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
+ version=search_version(out), direct=invoked_directly)
+ elif 'GNU coreutils' in o:
+ raise EnvironmentException(
+ "Found GNU link.exe instead of MSVC link.exe. This link.exe "
+ "is not a linker. You may need to reorder entries to your "
+ "%PATH% variable to resolve this.")
+ __failed_to_detect_linker(compiler, check_args, o, e)
+
+def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Type['Compiler'],
+ for_machine: MachineChoice, *,
+ extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
+ """Helper for guessing what linker to use on Unix-Like OSes.
+
+ :compiler: Invocation to use to get linker
+ :comp_class: The Compiler Type (uninstantiated)
+ :for_machine: which machine this linker targets
+ :extra_args: Any additional arguments required (such as a source file)
+ """
+ env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
+ extra_args = extra_args or []
+ extra_args += env.coredata.options[OptionKey('args', lang=comp_class.language, machine=for_machine)].value
+
+ if isinstance(comp_class.LINKER_PREFIX, str):
+ check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args
+ else:
+ check_args = comp_class.LINKER_PREFIX + ['--version'] + extra_args
+
+ override = [] # type: T.List[str]
+ value = env.lookup_binary_entry(for_machine, comp_class.language + '_ld')
+ if value is not None:
+ override = comp_class.use_linker_args(value[0])
+ check_args += override
+
+ _, o, e = Popen_safe(compiler + check_args)
+ v = search_version(o + e)
+ linker: DynamicLinker
+ if o.startswith('LLD'):
+ linker = LLVMDynamicLinker(
+ compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
+ elif 'Snapdragon' in e and 'LLVM' in e:
+ linker = QualcommLLVMDynamicLinker(
+ compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
+ elif e.startswith('lld-link: '):
+ # The LLD MinGW frontend didn't respond to --version before version 9.0.0,
+ # and produced an error message about failing to link (when no object
+ # files were specified), instead of printing the version number.
+ # Let's try to extract the linker invocation command to grab the version.
+
+ _, o, e = Popen_safe(compiler + check_args + ['-v'])
+
+ try:
+ linker_cmd = re.match(r'.*\n(.*?)\nlld-link: ', e, re.DOTALL).group(1)
+ linker_cmd = shlex.split(linker_cmd)[0]
+ except (AttributeError, IndexError, ValueError):
+ pass
+ else:
+ _, o, e = Popen_safe([linker_cmd, '--version'])
+ v = search_version(o)
+
+ linker = LLVMDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
+ # first is for apple clang, second is for real gcc, the third is icc
+ elif e.endswith('(use -v to see invocation)\n') or 'macosx_version' in e or 'ld: unknown option:' in e:
+ if isinstance(comp_class.LINKER_PREFIX, str):
+ _, _, e = Popen_safe(compiler + [comp_class.LINKER_PREFIX + '-v'] + extra_args)
+ else:
+ _, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX + ['-v'] + extra_args)
+ for line in e.split('\n'):
+ if 'PROJECT:ld' in line:
+ v = line.split('-')[1]
+ break
+ else:
+ v = 'unknown version'
+ linker = AppleDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
+ elif 'GNU' in o or 'GNU' in e:
+ cls: T.Type[GnuDynamicLinker]
+ if 'gold' in o or 'gold' in e:
+ cls = GnuGoldDynamicLinker
+ else:
+ cls = GnuBFDDynamicLinker
+ linker = cls(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
+ elif 'Solaris' in e or 'Solaris' in o:
+ for line in (o+e).split('\n'):
+ if 'ld: Software Generation Utilities' in line:
+ v = line.split(':')[2].lstrip()
+ break
+ else:
+ v = 'unknown version'
+ linker = SolarisDynamicLinker(
+ compiler, for_machine, comp_class.LINKER_PREFIX, override,
+ version=v)
+ elif 'ld: 0706-012 The -- flag is not recognized' in e:
+ if isinstance(comp_class.LINKER_PREFIX, str):
+ _, _, e = Popen_safe(compiler + [comp_class.LINKER_PREFIX + '-V'] + extra_args)
+ else:
+ _, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX + ['-V'] + extra_args)
+ linker = AIXDynamicLinker(
+ compiler, for_machine, comp_class.LINKER_PREFIX, override,
+ version=search_version(e))
+ else:
+ __failed_to_detect_linker(compiler, check_args, o, e)
+ return linker
diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py
index 160cc37..4b4eb6e 100644
--- a/mesonbuild/mesonlib/universal.py
+++ b/mesonbuild/mesonlib/universal.py
@@ -135,6 +135,7 @@ __all__ = [
'version_compare',
'version_compare_condition_with_min',
'version_compare_many',
+ 'search_version',
'windows_proof_rm',
'windows_proof_rmtree',
]
@@ -883,6 +884,49 @@ def version_compare_condition_with_min(condition: str, minimum: str) -> bool:
return T.cast(bool, cmpop(Version(minimum), Version(condition)))
+def search_version(text: str) -> str:
+ # Usually of the type 4.1.4 but compiler output may contain
+ # stuff like this:
+ # (Sourcery CodeBench Lite 2014.05-29) 4.8.3 20140320 (prerelease)
+ # Limiting major version number to two digits seems to work
+ # thus far. When we get to GCC 100, this will break, but
+ # if we are still relevant when that happens, it can be
+ # considered an achievement in itself.
+ #
+ # This regex is reaching magic levels. If it ever needs
+ # to be updated, do not complexify but convert to something
+ # saner instead.
+ # We'll demystify it a bit with a verbose definition.
+ version_regex = re.compile(r"""
+ (?<! # Zero-width negative lookbehind assertion
+ (
+ \d # One digit
+ | \. # Or one period
+ ) # One occurrence
+ )
+ # Following pattern must not follow a digit or period
+ (
+ \d{1,2} # One or two digits
+ (
+ \.\d+ # Period and one or more digits
+ )+ # One or more occurrences
+ (
+ -[a-zA-Z0-9]+ # Hyphen and one or more alphanumeric
+ )? # Zero or one occurrence
+ ) # One occurrence
+ """, re.VERBOSE)
+ match = version_regex.search(text)
+ if match:
+ return match.group(0)
+
+ # try a simpler regex that has like "blah 2020.01.100 foo" or "blah 2020.01 foo"
+ version_regex = re.compile(r"(\d{1,4}\.\d{1,4}\.?\d{0,4})")
+ match = version_regex.search(text)
+ if match:
+ return match.group(0)
+
+ return 'unknown version'
+
def default_libdir() -> str:
if is_debianlike():
diff --git a/run_project_tests.py b/run_project_tests.py
index 3522009..c9b7a8f 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -42,6 +42,7 @@ from mesonbuild import compilers
from mesonbuild import mesonlib
from mesonbuild import mlog
from mesonbuild import mtest
+from mesonbuild.compilers import compiler_from_language, detect_objc_compiler, detect_objcpp_compiler
from mesonbuild.build import ConfigurationData
from mesonbuild.mesonlib import MachineChoice, Popen_safe, TemporaryDirectoryWinProof
from mesonbuild.mlog import blue, bold, cyan, green, red, yellow, normal_green
@@ -884,7 +885,7 @@ def have_objc_compiler(use_tmp: bool) -> bool:
with TemporaryDirectoryWinProof(prefix='b ', dir=None if use_tmp else '.') as build_dir:
env = environment.Environment(None, build_dir, get_fake_options('/'))
try:
- objc_comp = env.detect_objc_compiler(MachineChoice.HOST)
+ objc_comp = detect_objc_compiler(env, MachineChoice.HOST)
except mesonlib.MesonException:
return False
if not objc_comp:
@@ -900,7 +901,7 @@ def have_objcpp_compiler(use_tmp: bool) -> bool:
with TemporaryDirectoryWinProof(prefix='b ', dir=None if use_tmp else '.') as build_dir:
env = environment.Environment(None, build_dir, get_fake_options('/'))
try:
- objcpp_comp = env.detect_objcpp_compiler(MachineChoice.HOST)
+ objcpp_comp = detect_objcpp_compiler(env, MachineChoice.HOST)
except mesonlib.MesonException:
return False
if not objcpp_comp:
@@ -1392,7 +1393,7 @@ def detect_system_compiler(options: 'CompilerArgumentType') -> None:
for lang in sorted(compilers.all_languages):
try:
- comp = env.compiler_from_language(lang, MachineChoice.HOST)
+ comp = compiler_from_language(env, lang, MachineChoice.HOST)
# note compiler id for later use with test.json matrix
compiler_id_map[lang] = comp.get_id()
except mesonlib.MesonException:
@@ -1412,7 +1413,7 @@ def print_compilers(env: 'Environment', machine: MachineChoice) -> None:
print()
for lang in sorted(compilers.all_languages):
try:
- comp = env.compiler_from_language(lang, machine)
+ comp = compiler_from_language(env, lang, machine)
details = '{:<10} {} {}'.format('[' + comp.get_id() + ']', ' '.join(comp.get_exelist()), comp.get_version_string())
except mesonlib.MesonException:
details = '[not found]'
diff --git a/run_unittests.py b/run_unittests.py
index 7f74e91..f1ff2b4 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -59,7 +59,13 @@ from mesonbuild.mesonlib import (
BuildDirLock, LibType, MachineChoice, PerMachine, Version, is_windows,
is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku, is_sunos,
windows_proof_rmtree, windows_proof_rm, python_command,
- version_compare, split_args, quote_arg, relpath, is_linux, git
+ version_compare, split_args, quote_arg, relpath, is_linux, git,
+ search_version
+)
+from mesonbuild.compilers import (
+ detect_static_linker, detect_c_compiler, detect_cpp_compiler,
+ detect_objc_compiler, detect_objcpp_compiler, detect_d_compiler,
+ detect_swift_compiler, compiler_from_language
)
from mesonbuild.environment import detect_ninja
from mesonbuild.mesonlib import MesonException, EnvironmentException, OptionKey
@@ -140,7 +146,7 @@ def _git_init(project_dir):
# If a user has git configuration init.defaultBranch set we want to override that
with tempfile.TemporaryDirectory() as d:
out = git(['--version'], str(d))[1]
- if version_compare(mesonbuild.environment.search_version(out), '>= 2.28'):
+ if version_compare(search_version(out), '>= 2.28'):
extra_cmd = ['--initial-branch', 'master']
else:
extra_cmd = []
@@ -226,14 +232,12 @@ def skip_if_no_cmake(f):
return f(*args, **kwargs)
return wrapped
-def skip_if_not_language(lang):
+def skip_if_not_language(lang: str):
def wrapper(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
try:
- env = get_fake_env()
- f = getattr(env, f'detect_{lang}_compiler')
- f(MachineChoice.HOST)
+ compiler_from_language(get_fake_env(), lang, MachineChoice.HOST)
except EnvironmentException:
raise unittest.SkipTest(f'No {lang} compiler found.')
return func(*args, **kwargs)
@@ -269,7 +273,7 @@ def skip_if_not_base_option(feature):
@functools.wraps(f)
def wrapped(*args, **kwargs):
env = get_fake_env()
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
key = OptionKey(feature)
if key not in cc.base_options:
raise unittest.SkipTest(
@@ -327,18 +331,17 @@ def no_pkgconfig():
class InternalTests(unittest.TestCase):
def test_version_number(self):
- searchfunc = mesonbuild.environment.search_version
- self.assertEqual(searchfunc('foobar 1.2.3'), '1.2.3')
- self.assertEqual(searchfunc('1.2.3'), '1.2.3')
- self.assertEqual(searchfunc('foobar 2016.10.28 1.2.3'), '1.2.3')
- self.assertEqual(searchfunc('2016.10.28 1.2.3'), '1.2.3')
- self.assertEqual(searchfunc('foobar 2016.10.128'), '2016.10.128')
- self.assertEqual(searchfunc('2016.10.128'), '2016.10.128')
- self.assertEqual(searchfunc('2016.10'), '2016.10')
- self.assertEqual(searchfunc('2016.10 1.2.3'), '1.2.3')
- self.assertEqual(searchfunc('oops v1.2.3'), '1.2.3')
- self.assertEqual(searchfunc('2016.oops 1.2.3'), '1.2.3')
- self.assertEqual(searchfunc('2016.x'), 'unknown version')
+ self.assertEqual(search_version('foobar 1.2.3'), '1.2.3')
+ self.assertEqual(search_version('1.2.3'), '1.2.3')
+ self.assertEqual(search_version('foobar 2016.10.28 1.2.3'), '1.2.3')
+ self.assertEqual(search_version('2016.10.28 1.2.3'), '1.2.3')
+ self.assertEqual(search_version('foobar 2016.10.128'), '2016.10.128')
+ self.assertEqual(search_version('2016.10.128'), '2016.10.128')
+ self.assertEqual(search_version('2016.10'), '2016.10')
+ self.assertEqual(search_version('2016.10 1.2.3'), '1.2.3')
+ self.assertEqual(search_version('oops v1.2.3'), '1.2.3')
+ self.assertEqual(search_version('2016.oops 1.2.3'), '1.2.3')
+ self.assertEqual(search_version('2016.x'), 'unknown version')
def test_mode_symbolic_to_bits(self):
modefunc = mesonbuild.mesonlib.FileMode.perms_s_to_bits
@@ -837,7 +840,7 @@ class InternalTests(unittest.TestCase):
'{}.dll.a', '{}.lib', '{}.dll'),
'static': msvc_static}}
env = get_fake_env()
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if is_osx():
self._test_all_naming(cc, env, patterns, 'darwin')
elif is_cygwin():
@@ -881,7 +884,7 @@ class InternalTests(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmpdir:
pkgbin = ExternalProgram('pkg-config', command=['pkg-config'], silent=True)
env = get_fake_env()
- compiler = env.detect_c_compiler(MachineChoice.HOST)
+ compiler = detect_c_compiler(env, MachineChoice.HOST)
env.coredata.compilers.host = {'c': compiler}
env.coredata.options[OptionKey('link_args', lang='c')] = FakeCompilerOptions()
p1 = Path(tmpdir) / '1'
@@ -1063,7 +1066,7 @@ class InternalTests(unittest.TestCase):
Ensure that the toolset version returns the correct value for this MSVC
'''
env = get_fake_env()
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_argument_syntax() != 'msvc':
raise unittest.SkipTest('Test only applies to MSVC-like compilers')
toolset_ver = cc.get_toolset_version()
@@ -1752,8 +1755,8 @@ class DataTests(unittest.TestCase):
self.assertIsNotNone(md)
env = get_fake_env()
# FIXME: Support other compilers
- cc = env.detect_c_compiler(MachineChoice.HOST)
- cpp = env.detect_cpp_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
+ cpp = detect_cpp_compiler(env, MachineChoice.HOST)
for comp in (cc, cpp):
for opt in comp.get_options():
self.assertIn(str(opt), md)
@@ -2556,7 +2559,7 @@ class AllPlatformTests(BasePlatformTests):
def test_clike_get_library_dirs(self):
env = get_fake_env()
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
for d in cc.get_library_dirs(env):
self.assertTrue(os.path.exists(d))
self.assertTrue(os.path.isdir(d))
@@ -2572,8 +2575,8 @@ class AllPlatformTests(BasePlatformTests):
'''
testdir = os.path.join(self.common_test_dir, '3 static')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
- static_linker = env.detect_static_linker(cc)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
+ static_linker = detect_static_linker(env, cc)
if is_windows():
raise unittest.SkipTest('https://github.com/mesonbuild/meson/issues/1526')
if not isinstance(static_linker, mesonbuild.linkers.ArLinker):
@@ -3041,9 +3044,9 @@ class AllPlatformTests(BasePlatformTests):
for lang, evar in langs:
# Detect with evar and do sanity checks on that
if evar in os.environ:
- ecc = getattr(env, f'detect_{lang}_compiler')(MachineChoice.HOST)
+ ecc = compiler_from_language(env, lang, MachineChoice.HOST)
self.assertTrue(ecc.version)
- elinker = env.detect_static_linker(ecc)
+ elinker = detect_static_linker(env, ecc)
# Pop it so we don't use it for the next detection
evalue = os.environ.pop(evar)
# Very rough/strict heuristics. Would never work for actual
@@ -3069,9 +3072,9 @@ class AllPlatformTests(BasePlatformTests):
# Check that we actually used the evalue correctly as the compiler
self.assertEqual(ecc.get_exelist(), split_args(evalue))
# Do auto-detection of compiler based on platform, PATH, etc.
- cc = getattr(env, f'detect_{lang}_compiler')(MachineChoice.HOST)
+ cc = compiler_from_language(env, lang, MachineChoice.HOST)
self.assertTrue(cc.version)
- linker = env.detect_static_linker(cc)
+ linker = detect_static_linker(env, cc)
# Check compiler type
if isinstance(cc, gnu):
self.assertIsInstance(linker, ar)
@@ -3129,8 +3132,8 @@ class AllPlatformTests(BasePlatformTests):
# Need a new env to re-run environment loading
env = get_fake_env(testdir, self.builddir, self.prefix)
- wcc = getattr(env, f'detect_{lang}_compiler')(MachineChoice.HOST)
- wlinker = env.detect_static_linker(wcc)
+ wcc = compiler_from_language(env, lang, MachineChoice.HOST)
+ wlinker = detect_static_linker(env, wcc)
# Pop it so we don't use it for the next detection
evalue = os.environ.pop('AR')
# Must be the same type since it's a wrapper around the same exelist
@@ -3148,7 +3151,7 @@ class AllPlatformTests(BasePlatformTests):
testdir = os.path.join(self.common_test_dir, '133 c cpp and asm')
# Skip if building with MSVC
env = get_fake_env(testdir, self.builddir, self.prefix)
- if env.detect_c_compiler(MachineChoice.HOST).get_id() == 'msvc':
+ if detect_c_compiler(env, MachineChoice.HOST).get_id() == 'msvc':
raise unittest.SkipTest('MSVC can\'t compile assembly')
self.init(testdir)
commands = {'c-asm': {}, 'cpp-asm': {}, 'cpp-c-asm': {}, 'c-cpp-asm': {}}
@@ -3309,7 +3312,7 @@ class AllPlatformTests(BasePlatformTests):
testdir = os.path.join(self.common_test_dir, '5 linkstatic')
env = get_fake_env(testdir, self.builddir, self.prefix)
- if env.detect_c_compiler(MachineChoice.HOST).get_id() == 'clang' and is_windows():
+ if detect_c_compiler(env, MachineChoice.HOST).get_id() == 'clang' and is_windows():
raise unittest.SkipTest('LTO not (yet) supported by windows clang')
self.init(testdir, extra_args='-Db_lto=true')
@@ -3321,7 +3324,7 @@ class AllPlatformTests(BasePlatformTests):
testdir = os.path.join(self.common_test_dir, '6 linkshared')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
extra_args: T.List[str] = []
if cc.get_id() == 'clang':
if is_windows():
@@ -3347,7 +3350,7 @@ class AllPlatformTests(BasePlatformTests):
testdir = os.path.join(self.common_test_dir, '6 linkshared')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() != 'clang':
raise unittest.SkipTest('Only clang currently supports thinLTO')
if cc.linker.id not in {'ld.lld', 'ld.gold', 'ld64', 'lld-link'}:
@@ -3615,8 +3618,8 @@ class AllPlatformTests(BasePlatformTests):
def detect_prebuild_env(self):
env = get_fake_env()
- cc = env.detect_c_compiler(MachineChoice.HOST)
- stlinker = env.detect_static_linker(cc)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
+ stlinker = detect_static_linker(env, cc)
if mesonbuild.mesonlib.is_windows():
object_suffix = 'obj'
shared_suffix = 'dll'
@@ -4060,7 +4063,7 @@ class AllPlatformTests(BasePlatformTests):
env = get_fake_env()
for l in ['cpp', 'cs', 'd', 'java', 'cuda', 'fortran', 'objc', 'objcpp', 'rust']:
try:
- comp = env.detect_compiler_for(l, MachineChoice.HOST)
+ comp = mesonbuild.compilers.detect_compiler_for(env, l, MachineChoice.HOST)
with tempfile.TemporaryDirectory() as d:
comp.sanity_check(d, env)
langs.append(l)
@@ -4170,7 +4173,7 @@ class AllPlatformTests(BasePlatformTests):
extra_args = None
libdir_flags = ['-L']
env = get_fake_env(testdirlib, self.builddir, self.prefix)
- if env.detect_c_compiler(MachineChoice.HOST).get_id() in {'msvc', 'clang-cl', 'intel-cl'}:
+ if detect_c_compiler(env, MachineChoice.HOST).get_id() in {'msvc', 'clang-cl', 'intel-cl'}:
# msvc-like compiler, also test it with msvc-specific flags
libdir_flags += ['/LIBPATH:', '-LIBPATH:']
else:
@@ -5562,7 +5565,7 @@ class AllPlatformTests(BasePlatformTests):
raise unittest.SkipTest('gcovr not found, or too old')
testdir = os.path.join(self.common_test_dir, '1 trivial')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() == 'clang':
if not mesonbuild.environment.detect_llvm_cov():
raise unittest.SkipTest('llvm-cov not found')
@@ -5582,7 +5585,7 @@ class AllPlatformTests(BasePlatformTests):
raise unittest.SkipTest('gcovr not found, or too old')
testdir = os.path.join(self.common_test_dir, '105 generatorcustom')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() == 'clang':
if not mesonbuild.environment.detect_llvm_cov():
raise unittest.SkipTest('llvm-cov not found')
@@ -5602,7 +5605,7 @@ class AllPlatformTests(BasePlatformTests):
raise unittest.SkipTest('gcovr not found, or too old')
testdir = os.path.join(self.common_test_dir, '1 trivial')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() == 'clang':
if not mesonbuild.environment.detect_llvm_cov():
raise unittest.SkipTest('llvm-cov not found')
@@ -5622,7 +5625,7 @@ class AllPlatformTests(BasePlatformTests):
raise unittest.SkipTest('gcovr not found, or too old')
testdir = os.path.join(self.common_test_dir, '1 trivial')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() == 'clang':
if not mesonbuild.environment.detect_llvm_cov():
raise unittest.SkipTest('llvm-cov not found')
@@ -5642,7 +5645,7 @@ class AllPlatformTests(BasePlatformTests):
raise unittest.SkipTest('gcovr not found, or too old')
testdir = os.path.join(self.common_test_dir, '1 trivial')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() == 'clang':
if not mesonbuild.environment.detect_llvm_cov():
raise unittest.SkipTest('llvm-cov not found')
@@ -5842,7 +5845,7 @@ class AllPlatformTests(BasePlatformTests):
'bar/barfile'
]
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_argument_syntax() == 'msvc':
main_expected.append('bin/foo.pdb')
bar_expected.append('bin/bar.pdb')
@@ -5936,18 +5939,18 @@ class AllPlatformTests(BasePlatformTests):
env = get_fake_env()
# Get the compiler so we know which compiler class to mock.
- cc = env.detect_compiler_for('c', MachineChoice.HOST)
+ cc = mesonbuild.compilers.detect_compiler_for(env, 'c', MachineChoice.HOST)
cc_type = type(cc)
# Test a compiler that acts as a linker
with mock.patch.object(cc_type, 'INVOKES_LINKER', True):
- cc = env.detect_compiler_for('c', MachineChoice.HOST)
+ cc = mesonbuild.compilers.detect_compiler_for(env, 'c', MachineChoice.HOST)
link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language)
self.assertEqual(sorted(link_args), sorted(['-DCFLAG', '-flto']))
# And one that doesn't
with mock.patch.object(cc_type, 'INVOKES_LINKER', False):
- cc = env.detect_compiler_for('c', MachineChoice.HOST)
+ cc = mesonbuild.compilers.detect_compiler_for(env, 'c', MachineChoice.HOST)
link_args = env.coredata.get_external_link_args(cc.for_machine, cc.language)
self.assertEqual(sorted(link_args), sorted(['-flto']))
@@ -6141,8 +6144,8 @@ class FailureTests(BasePlatformTests):
'''
env = get_fake_env()
try:
- env.detect_objc_compiler(MachineChoice.HOST)
- env.detect_objcpp_compiler(MachineChoice.HOST)
+ detect_objc_compiler(env, MachineChoice.HOST)
+ detect_objcpp_compiler(env, MachineChoice.HOST)
except EnvironmentException:
code = "add_languages('objc')\nadd_languages('objcpp')"
self.assertMesonRaises(code, "Unknown compiler")
@@ -6362,7 +6365,7 @@ class WindowsTests(BasePlatformTests):
'''
testdir = os.path.join(self.platform_test_dir, '1 basic')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_argument_syntax() != 'msvc':
raise unittest.SkipTest('Not using MSVC')
# To force people to update this test, and also test
@@ -6375,7 +6378,7 @@ class WindowsTests(BasePlatformTests):
# resource compiler depfile generation is not yet implemented for msvc
env = get_fake_env(testdir, self.builddir, self.prefix)
- depfile_works = env.detect_c_compiler(MachineChoice.HOST).get_id() not in {'msvc', 'clang-cl', 'intel-cl'}
+ depfile_works = detect_c_compiler(env, MachineChoice.HOST).get_id() not in {'msvc', 'clang-cl', 'intel-cl'}
self.init(testdir)
self.build()
@@ -6406,7 +6409,7 @@ class WindowsTests(BasePlatformTests):
testdir = os.path.join(self.unit_test_dir, '45 vscpp17')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_argument_syntax() != 'msvc':
raise unittest.SkipTest('Test only applies to MSVC-like compilers')
@@ -6424,7 +6427,7 @@ class WindowsTests(BasePlatformTests):
testdir = os.path.join(self.platform_test_dir, '1 basic')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_argument_syntax() != 'msvc':
raise unittest.SkipTest('Test only applies to MSVC-like compilers')
@@ -6448,28 +6451,28 @@ class WindowsTests(BasePlatformTests):
with mock.patch.dict(os.environ, {envvar: name}):
env = get_fake_env()
try:
- comp = getattr(env, f'detect_{lang}_compiler')(MachineChoice.HOST)
+ comp = compiler_from_language(env, lang, MachineChoice.HOST)
except EnvironmentException:
raise unittest.SkipTest(f'Could not find a compiler for {lang}')
self.assertEqual(comp.linker.id, expected)
def test_link_environment_variable_lld_link(self):
env = get_fake_env()
- comp = getattr(env, 'detect_c_compiler')(MachineChoice.HOST)
+ comp = detect_c_compiler(env, MachineChoice.HOST)
if isinstance(comp, mesonbuild.compilers.GnuLikeCompiler):
raise unittest.SkipTest('GCC cannot be used with link compatible linkers.')
self._check_ld('lld-link', 'c', 'lld-link')
def test_link_environment_variable_link(self):
env = get_fake_env()
- comp = getattr(env, 'detect_c_compiler')(MachineChoice.HOST)
+ comp = detect_c_compiler(env, MachineChoice.HOST)
if isinstance(comp, mesonbuild.compilers.GnuLikeCompiler):
raise unittest.SkipTest('GCC cannot be used with link compatible linkers.')
self._check_ld('link', 'c', 'link')
def test_link_environment_variable_optlink(self):
env = get_fake_env()
- comp = getattr(env, 'detect_c_compiler')(MachineChoice.HOST)
+ comp = detect_c_compiler(env, MachineChoice.HOST)
if isinstance(comp, mesonbuild.compilers.GnuLikeCompiler):
raise unittest.SkipTest('GCC cannot be used with link compatible linkers.')
self._check_ld('optlink', 'c', 'optlink')
@@ -6481,7 +6484,7 @@ class WindowsTests(BasePlatformTests):
@skip_if_not_language('d')
def test_link_environment_variable_d(self):
env = get_fake_env()
- comp = getattr(env, 'detect_d_compiler')(MachineChoice.HOST)
+ comp = detect_d_compiler(env, MachineChoice.HOST)
if comp.id == 'dmd':
raise unittest.SkipTest('meson cannot reliably make DMD use a different linker.')
self._check_ld('lld-link', 'd', 'lld-link')
@@ -6498,7 +6501,7 @@ class WindowsTests(BasePlatformTests):
self.build()
# Test that binaries have a non-zero checksum
env = get_fake_env()
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
cc_id = cc.get_id()
ld_id = cc.get_linker_id()
dll = glob(os.path.join(self.builddir, '*mycpplib.dll'))[0]
@@ -6520,7 +6523,7 @@ class WindowsTests(BasePlatformTests):
'''
# Verify that the `b_vscrt` option is available
env = get_fake_env()
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if OptionKey('b_vscrt') not in cc.base_options:
raise unittest.SkipTest('Compiler does not support setting the VS CRT')
# Verify that qmake is for Qt5
@@ -6546,7 +6549,7 @@ class WindowsTests(BasePlatformTests):
'''
# Verify that the `b_vscrt` option is available
env = get_fake_env()
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if OptionKey('b_vscrt') not in cc.base_options:
raise unittest.SkipTest('Compiler does not support setting the VS CRT')
@@ -6614,7 +6617,7 @@ class DarwinTests(BasePlatformTests):
'''
testdir = os.path.join(self.platform_test_dir, '7 bitcode')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.id != 'clang':
raise unittest.SkipTest('Not using Clang on OSX')
# Try with bitcode enabled
@@ -6794,7 +6797,7 @@ class LinuxlikeTests(BasePlatformTests):
self.assertEqual(libhello_nolib.get_pkgconfig_variable('escaped_var', {}), r'hello\ world')
self.assertEqual(libhello_nolib.get_pkgconfig_variable('unescaped_var', {}), 'hello world')
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
if cc.get_id() in {'gcc', 'clang'}:
for name in {'ct', 'ct0'}:
ct_dep = PkgConfigDependency(name, env, kwargs)
@@ -7069,7 +7072,7 @@ class LinuxlikeTests(BasePlatformTests):
'''
testdir = os.path.join(self.common_test_dir, '36 has function')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cpp = env.detect_cpp_compiler(MachineChoice.HOST)
+ cpp = detect_cpp_compiler(env, MachineChoice.HOST)
Oflag = '-O3'
OflagCPP = Oflag
if cpp.get_id() in ('clang', 'gcc'):
@@ -7161,7 +7164,7 @@ class LinuxlikeTests(BasePlatformTests):
'''
testdir = os.path.join(self.common_test_dir, '1 trivial')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
self._test_stds_impl(testdir, cc)
def test_compiler_cpp_stds(self):
@@ -7171,7 +7174,7 @@ class LinuxlikeTests(BasePlatformTests):
'''
testdir = os.path.join(self.common_test_dir, '2 cpp')
env = get_fake_env(testdir, self.builddir, self.prefix)
- cpp = env.detect_cpp_compiler(MachineChoice.HOST)
+ cpp = detect_cpp_compiler(env, MachineChoice.HOST)
self._test_stds_impl(testdir, cpp)
def test_unity_subproj(self):
@@ -8011,7 +8014,7 @@ class LinuxlikeTests(BasePlatformTests):
if is_cygwin() or is_osx():
raise unittest.SkipTest('Not applicable on Cygwin or OSX.')
env = get_fake_env()
- cc = env.detect_c_compiler(MachineChoice.HOST)
+ cc = detect_c_compiler(env, MachineChoice.HOST)
linker = cc.linker
if not linker.export_dynamic_args(env):
raise unittest.SkipTest('Not applicable for linkers without --export-dynamic')
@@ -8124,7 +8127,7 @@ class LinuxlikeTests(BasePlatformTests):
for envvar in envvars:
with mock.patch.dict(os.environ, {envvar: name}):
env = get_fake_env()
- comp = getattr(env, f'detect_{lang}_compiler')(MachineChoice.HOST)
+ comp = compiler_from_language(env, lang, MachineChoice.HOST)
if isinstance(comp, (mesonbuild.compilers.AppleClangCCompiler,
mesonbuild.compilers.AppleClangCPPCompiler,
mesonbuild.compilers.AppleClangObjCCompiler,
@@ -8910,8 +8913,7 @@ class NativeFileTests(BasePlatformTests):
with more than one implementation, such as C, C++, ObjC, ObjC++, and D.
"""
env = get_fake_env()
- getter = getattr(env, f'detect_{lang}_compiler')
- getter = functools.partial(getter, for_machine)
+ getter = lambda: compiler_from_language(env, lang, for_machine)
cc = getter()
binary, newid = cb(cc)
env.binaries[for_machine].binaries[lang] = binary
@@ -9115,10 +9117,8 @@ class NativeFileTests(BasePlatformTests):
"""
wrapper = self.helper_create_binary_wrapper(binary, version=version_str)
env = get_fake_env()
- getter = getattr(env, f'detect_{lang}_compiler')
- getter = functools.partial(getter, MachineChoice.HOST)
env.binaries.host.binaries[lang] = [wrapper]
- compiler = getter()
+ compiler = compiler_from_language(env, lang, MachineChoice.HOST)
self.assertEqual(compiler.version, version)
@skip_if_not_language('vala')
@@ -9145,7 +9145,7 @@ class NativeFileTests(BasePlatformTests):
extra_args={'Xlinker': 'macosx_version. PROJECT:ld - 1.2.3'})
env = get_fake_env()
env.binaries.host.binaries['swift'] = [wrapper]
- compiler = env.detect_swift_compiler(MachineChoice.HOST)
+ compiler = detect_swift_compiler(env, MachineChoice.HOST)
self.assertEqual(compiler.version, '1.2345')
def test_native_file_dirs(self):
@@ -10023,7 +10023,7 @@ class SubprojectsCommandTests(BasePlatformTests):
# If a user has git configuration init.defaultBranch set we want to override that
with tempfile.TemporaryDirectory() as d:
out = git(['--version'], str(d))[1]
- if version_compare(mesonbuild.environment.search_version(out), '>= 2.28'):
+ if version_compare(search_version(out), '>= 2.28'):
extra_cmd = ['--initial-branch', 'master']
else:
extra_cmd = []