aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/compilers/mixins
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/compilers/mixins')
-rw-r--r--mesonbuild/compilers/mixins/arm.py28
-rw-r--r--mesonbuild/compilers/mixins/clang.py17
-rw-r--r--mesonbuild/compilers/mixins/clike.py185
-rw-r--r--mesonbuild/compilers/mixins/gnu.py2
-rw-r--r--mesonbuild/compilers/mixins/islinker.py7
-rw-r--r--mesonbuild/compilers/mixins/visualstudio.py5
6 files changed, 176 insertions, 68 deletions
diff --git a/mesonbuild/compilers/mixins/arm.py b/mesonbuild/compilers/mixins/arm.py
index aa5d15d..b331d8f 100644
--- a/mesonbuild/compilers/mixins/arm.py
+++ b/mesonbuild/compilers/mixins/arm.py
@@ -27,10 +27,10 @@ if T.TYPE_CHECKING:
arm_buildtype_args = {
'plain': [],
- 'debug': ['-O0', '--debug'],
- 'debugoptimized': ['-O1', '--debug'],
- 'release': ['-O3', '-Otime'],
- 'minsize': ['-O3', '-Ospace'],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
'custom': [],
} # type: T.Dict[str, T.List[str]]
@@ -38,27 +38,27 @@ arm_optimization_args = {
'0': ['-O0'],
'g': ['-g'],
'1': ['-O1'],
- '2': ['-O2'],
- '3': ['-O3'],
- 's': [],
+ '2': [], # Compiler defaults to -O2
+ '3': ['-O3', '-Otime'],
+ 's': ['-O3'], # Compiler defaults to -Ospace
} # type: T.Dict[str, T.List[str]]
armclang_buildtype_args = {
'plain': [],
- 'debug': ['-O0', '-g'],
- 'debugoptimized': ['-O1', '-g'],
- 'release': ['-Os'],
- 'minsize': ['-Oz'],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
'custom': [],
} # type: T.Dict[str, T.List[str]]
armclang_optimization_args = {
- '0': ['-O0'],
+ '0': [], # Compiler defaults to -O0
'g': ['-g'],
'1': ['-O1'],
'2': ['-O2'],
'3': ['-O3'],
- 's': ['-Os']
+ 's': ['-Oz']
} # type: T.Dict[str, T.List[str]]
@@ -181,7 +181,7 @@ class ArmclangCompiler:
# Override CCompiler.get_dependency_gen_args
def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
- return []
+ return ['-MD', '-MT', outtarget, '-MF', outfile]
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
return armclang_optimization_args[optimization_level]
diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py
index 1c0ee45..7525c12 100644
--- a/mesonbuild/compilers/mixins/clang.py
+++ b/mesonbuild/compilers/mixins/clang.py
@@ -42,9 +42,10 @@ clang_optimization_args = {
} # type: T.Dict[str, T.List[str]]
class ClangCompiler(GnuLikeCompiler):
- def __init__(self):
+ def __init__(self, defines: T.Optional[T.Dict[str, str]]):
super().__init__()
self.id = 'clang'
+ self.defines = defines or {}
self.base_options.append('b_colorout')
# TODO: this really should be part of the linker base_options, but
# linkers don't have base_options.
@@ -56,6 +57,12 @@ class ClangCompiler(GnuLikeCompiler):
def get_colorout_args(self, colortype: str) -> T.List[str]:
return clang_color_args[colortype][:]
+ def has_builtin_define(self, define: str) -> bool:
+ return define in self.defines
+
+ def get_builtin_define(self, define: str) -> T.Optional[str]:
+ return self.defines.get(define)
+
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
return clang_optimization_args[optimization_level]
@@ -106,6 +113,11 @@ class ClangCompiler(GnuLikeCompiler):
# (and other gcc-like compilers) cannot. This is becuse clang (being
# llvm based) is retargetable, while GCC is not.
#
+
+ # qcld: Qualcomm Snapdragon linker, based on LLVM
+ if linker == 'qcld':
+ return ['-fuse-ld=qcld']
+
if shutil.which(linker):
if not shutil.which(linker):
raise mesonlib.MesonException(
@@ -117,3 +129,6 @@ class ClangCompiler(GnuLikeCompiler):
# Clang only warns about unknown or ignored attributes, so force an
# error.
return ['-Werror=attributes']
+
+ def get_coverage_link_args(self) -> T.List[str]:
+ return ['--coverage']
diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py
index 260342e..a42b050 100644
--- a/mesonbuild/compilers/mixins/clike.py
+++ b/mesonbuild/compilers/mixins/clike.py
@@ -29,15 +29,79 @@ import subprocess
import typing as T
from pathlib import Path
+from ... import arglist
from ... import mesonlib
-from ...mesonlib import LibType
from ... import mlog
+from ...linkers import GnuLikeDynamicLinkerMixin, SolarisDynamicLinker
+from ...mesonlib import LibType
from .. import compilers
from .visualstudio import VisualStudioLikeCompiler
if T.TYPE_CHECKING:
from ...environment import Environment
+SOREGEX = re.compile(r'.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$')
+
+class CLikeCompilerArgs(arglist.CompilerArgs):
+ prepend_prefixes = ('-I', '-L')
+ dedup2_prefixes = ('-I', '-isystem', '-L', '-D', '-U')
+
+ # NOTE: not thorough. A list of potential corner cases can be found in
+ # https://github.com/mesonbuild/meson/pull/4593#pullrequestreview-182016038
+ dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,--export-dynamic')
+ dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a')
+ dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread')
+
+ def to_native(self, copy: bool = False) -> T.List[str]:
+ # Check if we need to add --start/end-group for circular dependencies
+ # between static libraries, and for recursively searching for symbols
+ # needed by static libraries that are provided by object files or
+ # shared libraries.
+ self.flush_pre_post()
+ if copy:
+ new = self.copy()
+ else:
+ new = self
+ # This covers all ld.bfd, ld.gold, ld.gold, and xild on Linux, which
+ # all act like (or are) gnu ld
+ # TODO: this could probably be added to the DynamicLinker instead
+ if isinstance(self.compiler.linker, (GnuLikeDynamicLinkerMixin, SolarisDynamicLinker)):
+ group_start = -1
+ group_end = -1
+ for i, each in enumerate(new):
+ if not each.startswith(('-Wl,-l', '-l')) and not each.endswith('.a') and \
+ not SOREGEX.match(each):
+ continue
+ group_end = i
+ if group_start < 0:
+ # First occurrence of a library
+ group_start = i
+ if group_start >= 0:
+ # Last occurrence of a library
+ new.insert(group_end + 1, '-Wl,--end-group')
+ new.insert(group_start, '-Wl,--start-group')
+ # Remove system/default include paths added with -isystem
+ if hasattr(self.compiler, 'get_default_include_dirs'):
+ default_dirs = self.compiler.get_default_include_dirs()
+ bad_idx_list = [] # type: T.List[int]
+ for i, each in enumerate(new):
+ # Remove the -isystem and the path if the path is a default path
+ if (each == '-isystem' and
+ i < (len(new) - 1) and
+ new[i + 1] in default_dirs):
+ bad_idx_list += [i, i + 1]
+ elif each.startswith('-isystem=') and each[9:] in default_dirs:
+ bad_idx_list += [i]
+ elif each.startswith('-isystem') and each[8:] in default_dirs:
+ bad_idx_list += [i]
+ for i in reversed(bad_idx_list):
+ new.pop(i)
+ return self.compiler.unix_args_to_native(new._container)
+
+ def __repr__(self) -> str:
+ self.flush_pre_post()
+ return 'CLikeCompilerArgs({!r}, {!r})'.format(self.compiler, self._container)
+
class CLikeCompiler:
@@ -48,7 +112,7 @@ class CLikeCompiler:
program_dirs_cache = {}
find_library_cache = {}
find_framework_cache = {}
- internal_libs = compilers.unixy_compiler_internal_libs
+ internal_libs = arglist.UNIXY_COMPILER_INTERNAL_LIBS
def __init__(self, is_cross: bool, exe_wrapper: T.Optional[str] = None):
# If a child ObjC or CPP class has already set it, don't set it ourselves
@@ -61,6 +125,9 @@ class CLikeCompiler:
else:
self.exe_wrapper = exe_wrapper.get_command()
+ def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CLikeCompilerArgs:
+ return CLikeCompilerArgs(self, args)
+
def needs_static_linker(self):
return True # When compiling static libraries, so yes.
@@ -152,15 +219,24 @@ class CLikeCompiler:
if not files:
retval.append(d)
continue
- file_to_check = os.path.join(d, files[0])
- with open(file_to_check, 'rb') as fd:
- header = fd.read(5)
- # if file is not an ELF file, it's weird, but accept dir
- # if it is elf, and the class matches, accept dir
- if header[1:4] != b'ELF' or int(header[4]) == elf_class:
- retval.append(d)
- # at this point, it's an ELF file which doesn't match the
- # appropriate elf_class, so skip this one
+
+ for f in files:
+ file_to_check = os.path.join(d, f)
+ try:
+ with open(file_to_check, 'rb') as fd:
+ header = fd.read(5)
+ # if file is not an ELF file, it's weird, but accept dir
+ # if it is elf, and the class matches, accept dir
+ if header[1:4] != b'ELF' or int(header[4]) == elf_class:
+ retval.append(d)
+ # at this point, it's an ELF file which doesn't match the
+ # appropriate elf_class, so skip this one
+ # stop scanning after the first sucessful read
+ break
+ except OSError:
+ # Skip the file if we can't read it
+ pass
+
return tuple(retval)
@functools.lru_cache()
@@ -254,14 +330,14 @@ class CLikeCompiler:
code = 'int main(void) { int class=0; return class; }\n'
return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
- def check_header(self, hname, prefix, env, *, extra_args=None, dependencies=None):
+ def check_header(self, hname: str, prefix: str, env, *, extra_args=None, dependencies=None):
fargs = {'prefix': prefix, 'header': hname}
code = '''{prefix}
#include <{header}>'''
return self.compiles(code.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
- def has_header(self, hname, prefix, env, *, extra_args=None, dependencies=None, disable_cache=False):
+ def has_header(self, hname: str, prefix: str, env, *, extra_args=None, dependencies=None, disable_cache: bool = False):
fargs = {'prefix': prefix, 'header': hname}
code = '''{prefix}
#ifdef __has_include
@@ -274,7 +350,7 @@ class CLikeCompiler:
return self.compiles(code.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies, mode='preprocess', disable_cache=disable_cache)
- def has_header_symbol(self, hname, symbol, prefix, env, *, extra_args=None, dependencies=None):
+ def has_header_symbol(self, hname: str, symbol: str, prefix: str, env, *, extra_args=None, dependencies=None):
fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
t = '''{prefix}
#include <{header}>
@@ -288,11 +364,19 @@ class CLikeCompiler:
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
- def _get_basic_compiler_args(self, env, mode):
+ def _get_basic_compiler_args(self, env, mode: str):
cargs, largs = [], []
- # Select a CRT if needed since we're linking
if mode == 'link':
- cargs += self.get_linker_debug_crt_args()
+ # Sometimes we need to manually select the CRT to use with MSVC.
+ # One example is when trying to do a compiler check that involves
+ # linking with static libraries since MSVC won't select a CRT for
+ # us in that case and will error out asking us to pick one.
+ try:
+ crt_val = env.coredata.base_options['b_vscrt'].value
+ buildtype = env.coredata.base_options['buildtype'].value
+ cargs += self.get_crt_compile_args(crt_val, buildtype)
+ except (KeyError, AttributeError):
+ pass
# Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS and CPPFLAGS from the env
sys_args = env.coredata.get_external_args(self.for_machine, self.language)
@@ -329,7 +413,7 @@ class CLikeCompiler:
elif not isinstance(dependencies, list):
dependencies = [dependencies]
# Collect compiler arguments
- cargs = compilers.CompilerArgs(self)
+ cargs = self.compiler_args()
largs = []
for d in dependencies:
# Add compile flags needed by dependencies
@@ -354,11 +438,11 @@ class CLikeCompiler:
def compiles(self, code: str, env, *,
extra_args: T.Sequence[T.Union[T.Sequence[str], str]] = None,
- dependencies=None, mode: str = 'compile', disable_cache=False) -> T.Tuple[bool, bool]:
+ dependencies=None, mode: str = 'compile', disable_cache: bool = False) -> T.Tuple[bool, bool]:
with self._build_wrapper(code, env, extra_args, dependencies, mode, disable_cache=disable_cache) as p:
return p.returncode == 0, p.cached
- def _build_wrapper(self, code: str, env, extra_args, dependencies=None, mode: str = 'compile', want_output: bool = False, disable_cache: bool = False, temp_dir=None) -> T.Tuple[bool, bool]:
+ def _build_wrapper(self, code: str, env, extra_args, dependencies=None, mode: str = 'compile', want_output: bool = False, disable_cache: bool = False, temp_dir: str = None) -> T.Tuple[bool, bool]:
args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
if disable_cache or want_output:
return self.compile(code, extra_args=args, mode=mode, want_output=want_output, temp_dir=env.scratch_dir)
@@ -369,7 +453,8 @@ class CLikeCompiler:
dependencies=dependencies, mode='link', disable_cache=disable_cache)
def run(self, code: str, env, *, extra_args=None, dependencies=None):
- if self.is_cross and self.exe_wrapper is None:
+ need_exe_wrapper = env.need_exe_wrapper(self.for_machine)
+ if need_exe_wrapper and self.exe_wrapper is None:
raise compilers.CrossNoRunException('Can not run test applications in this cross environment.')
with self._build_wrapper(code, env, extra_args, dependencies, mode='link', want_output=True) as p:
if p.returncode != 0:
@@ -377,7 +462,7 @@ class CLikeCompiler:
p.input_name,
p.returncode))
return compilers.RunResult(False)
- if self.is_cross:
+ if need_exe_wrapper:
cmdlist = self.exe_wrapper + [p.output_name]
else:
cmdlist = p.output_name
@@ -658,7 +743,7 @@ class CLikeCompiler:
# is not run so we don't care what the return value is.
main = '''\nint main(void) {{
void *a = (void*) &{func};
- long b = (long) a;
+ long long b = (long long) a;
return (int) b;
}}'''
return head, main
@@ -727,24 +812,29 @@ class CLikeCompiler:
# need to look for them differently. On nice compilers like clang, we
# can just directly use the __has_builtin() macro.
fargs['no_includes'] = '#include' not in prefix
- fargs['__builtin_'] = '' if funcname.startswith('__builtin_') else '__builtin_'
+ is_builtin = funcname.startswith('__builtin_')
+ fargs['is_builtin'] = is_builtin
+ fargs['__builtin_'] = '' if is_builtin else '__builtin_'
t = '''{prefix}
int main(void) {{
+
+ /* With some toolchains (MSYS2/mingw for example) the compiler
+ * provides various builtins which are not really implemented and
+ * fall back to the stdlib where they aren't provided and fail at
+ * build/link time. In case the user provides a header, including
+ * the header didn't lead to the function being defined, and the
+ * function we are checking isn't a builtin itself we assume the
+ * builtin is not functional and we just error out. */
+ #if !{no_includes:d} && !defined({func}) && !{is_builtin:d}
+ #error "No definition for {__builtin_}{func} found in the prefix"
+ #endif
+
#ifdef __has_builtin
#if !__has_builtin({__builtin_}{func})
#error "{__builtin_}{func} not found"
#endif
#elif ! defined({func})
- /* Check for {__builtin_}{func} only if no includes were added to the
- * prefix above, which means no definition of {func} can be found.
- * We would always check for this, but we get false positives on
- * MSYS2 if we do. Their toolchain is broken, but we can at least
- * give them a workaround. */
- #if {no_includes:d}
- {__builtin_}{func};
- #else
- #error "No definition for {__builtin_}{func} found in the prefix"
- #endif
+ {__builtin_}{func};
#endif
return 0;
}}'''
@@ -910,21 +1000,21 @@ class CLikeCompiler:
architecture.
'''
# If not building on macOS for Darwin, do a simple file check
- files = [Path(f) for f in files]
+ paths = [Path(f) for f in files]
if not env.machines.host.is_darwin() or not env.machines.build.is_darwin():
- for f in files:
- if f.is_file():
- return f
+ for p in paths:
+ if p.is_file():
+ return p
# Run `lipo` and check if the library supports the arch we want
- for f in files:
- if not f.is_file():
+ for p in paths:
+ if not p.is_file():
continue
- archs = mesonlib.darwin_get_object_archs(f)
+ archs = mesonlib.darwin_get_object_archs(str(p))
if archs and env.machines.host.cpu_family in archs:
- return f
+ return p
else:
mlog.debug('Rejected {}, supports {} but need {}'
- .format(f, archs, env.machines.host.cpu_family))
+ .format(p, archs, env.machines.host.cpu_family))
return None
@functools.lru_cache()
@@ -993,7 +1083,7 @@ class CLikeCompiler:
return value[:]
def find_library(self, libname, env, extra_dirs, libtype: LibType = LibType.PREFER_SHARED):
- code = 'int main(void) { return 0; }'
+ code = 'int main(void) { return 0; }\n'
return self.find_library_impl(libname, env, extra_dirs, code, libtype)
def find_framework_paths(self, env):
@@ -1093,7 +1183,7 @@ class CLikeCompiler:
'the compiler you are using. has_link_argument or '
'other similar method can be used instead.'
.format(arg))
- code = 'int i;\n'
+ code = 'extern int i;\nint i;\n'
return self.has_arguments(args, env, code, mode='compile')
def has_multi_link_arguments(self, args, env):
@@ -1102,7 +1192,7 @@ class CLikeCompiler:
# false positive.
args = self.linker.fatal_warnings() + args
args = self.linker_to_compiler_args(args)
- code = 'int main(void) { return 0; }'
+ code = 'int main(void) { return 0; }\n'
return self.has_arguments(args, env, code, mode='link')
@staticmethod
@@ -1131,3 +1221,6 @@ class CLikeCompiler:
return self.compiles(self.attribute_check_func(name), env,
extra_args=self.get_has_func_attribute_extra_args(name))
+
+ def get_disable_assert_args(self) -> T.List[str]:
+ return ['-DNDEBUG']
diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py
index 7cfb2c2..f5fd7ef 100644
--- a/mesonbuild/compilers/mixins/gnu.py
+++ b/mesonbuild/compilers/mixins/gnu.py
@@ -363,7 +363,7 @@ class GnuCompiler(GnuLikeCompiler):
# For some compiler command line arguments, the GNU compilers will
# emit a warning on stderr indicating that an option is valid for a
# another language, but still complete with exit_success
- with self._build_wrapper(code, env, args, None, mode, disable_cache=False, want_output=True) as p:
+ with self._build_wrapper(code, env, args, None, mode) as p:
result = p.returncode == 0
if self.language in {Language.CPP, Language.OBJCPP} and 'is valid for C/ObjC' in p.stde:
result = False
diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py
index 681c816..a9967d6 100644
--- a/mesonbuild/compilers/mixins/islinker.py
+++ b/mesonbuild/compilers/mixins/islinker.py
@@ -107,11 +107,8 @@ class BasicLinkerIsCompilerMixin:
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str,
- install_rpath: str) -> T.List[str]:
- return []
-
- def get_linker_debug_crt_args(self) -> T.List[str]:
- return []
+ install_rpath: str) -> T.Tuple[T.List[str], T.Set[bytes]]:
+ return ([], set())
def get_asneeded_args(self) -> T.List[str]:
return []
diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py
index d0004ce..93101b5 100644
--- a/mesonbuild/compilers/mixins/visualstudio.py
+++ b/mesonbuild/compilers/mixins/visualstudio.py
@@ -114,7 +114,7 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta):
# See: https://ninja-build.org/manual.html#_deps
always_args = ['/nologo', '/showIncludes']
warn_args = {
- '0': ['/W1'],
+ '0': [],
'1': ['/W2'],
'2': ['/W3'],
'3': ['/W4'],
@@ -208,6 +208,9 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta):
def openmp_flags(self) -> T.List[str]:
return ['/openmp']
+ def openmp_link_flags(self) -> T.List[str]:
+ return []
+
# FIXME, no idea what these should be.
def thread_flags(self, env: 'Environment') -> T.List[str]:
return []