diff options
Diffstat (limited to 'mesonbuild/compilers/mixins')
-rw-r--r-- | mesonbuild/compilers/mixins/arm.py | 28 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/clang.py | 17 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/clike.py | 185 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/gnu.py | 2 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/islinker.py | 7 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/visualstudio.py | 5 |
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 [] |