diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index 68a0cd77b..0a88d6b52 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -716,23 +716,31 @@ class CCompiler(Compiler): def has_header(self, hname, prefix, env, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] - code = '{}\n#include<{}>\nint someUselessSymbol;'.format(prefix, hname) - return self.compiles(code, env, extra_args, dependencies, 'preprocess') + fargs = {'prefix': prefix, 'header': hname} + code = '''{prefix} + #ifdef __has_include + #if !__has_include(<{header}>) + #error "Header '{header}' could not be found" + #endif + #else + #include<{header}> + #endif''' + return self.compiles(code.format(**fargs), env, extra_args, + dependencies, 'preprocess') def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] - templ = '''{2} -#include <{0}> -int main () {{ - /* If it's not defined as a macro, try to use as a symbol */ - #ifndef {1} - {1}; - #endif - return 0; -}}''' - return self.compiles(templ.format(hname, symbol, prefix), env, - extra_args, dependencies) + fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol} + t = '''{prefix} + #include <{header}> + int main () {{ + /* If it's not defined as a macro, try to use as a symbol */ + #ifndef {symbol} + {symbol}; + #endif + }}''' + return self.compiles(t.format(**fargs), env, extra_args, dependencies) @staticmethod def _override_args(args, override): @@ -840,45 +848,47 @@ int main () {{ mlog.debug(se) return RunResult(True, pe.returncode, so, se) - def cross_sizeof(self, element, prefix, env, extra_args=None, dependencies=None): - if extra_args is None: - extra_args = [] - element_exists_templ = '''#include -{0} -int main(int argc, char **argv) {{ - {1} something; -}} -''' - templ = '''#include -%s -int temparray[%d-sizeof(%s)]; -''' - if not self.compiles(element_exists_templ.format(prefix, element), env, extra_args, dependencies): - return -1 + def _bisect_compiles(self, t, fargs, env, extra_args, dependencies): + # FIXME: Does not actually do bisection right now for i in range(1, 1024): - code = templ % (prefix, i, element) - if self.compiles(code, env, extra_args, dependencies): + fargs['size'] = i + if self.compiles(t.format(**fargs), env, extra_args, dependencies): if self.id == 'msvc': # MSVC refuses to construct an array of zero size, so # the test only succeeds when i is sizeof(element) + 1 return i - 1 return i - raise EnvironmentException('Cross checking sizeof overflowed.') + raise EnvironmentException('Cross-compile check overflowed') + + def cross_sizeof(self, element, prefix, env, extra_args=None, dependencies=None): + if extra_args is None: + extra_args = [] + fargs = {'prefix': prefix, 'name': element} + t = '''#include + {prefix} + int main(int argc, char **argv) {{ + {name} something; + }}''' + if not self.compiles(t.format(**fargs), env, extra_args, dependencies): + return -1 + t = '''#include + {prefix} + int temparray[{size}-sizeof({name})];''' + return self._bisect_compiles(t, fargs, env, extra_args, dependencies) def sizeof(self, element, prefix, env, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] + fargs = {'prefix': prefix, 'name': element} if self.is_cross: return self.cross_sizeof(element, prefix, env, extra_args, dependencies) - templ = '''#include -%s - -int main(int argc, char **argv) { - printf("%%ld\\n", (long)(sizeof(%s))); - return 0; -}; -''' - res = self.run(templ % (prefix, element), env, extra_args, dependencies) + t = '''#include + {prefix} + int main(int argc, char **argv) {{ + printf("%ld\\n", (long)(sizeof({name}))); + return 0; + }};''' + res = self.run(t.format(**fargs), env, extra_args, dependencies) if not res.compiled: return -1 if res.returncode != 0: @@ -888,50 +898,38 @@ int main(int argc, char **argv) { def cross_alignment(self, typename, env, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] - type_exists_templ = '''#include -int main(int argc, char **argv) {{ - {0} something; -}} -''' - templ = '''#include -struct tmp { - char c; - %s target; -}; - -int testarray[%d-offsetof(struct tmp, target)]; -''' - if not self.compiles(type_exists_templ.format(typename), env, extra_args, dependencies): + fargs = {'type': typename} + t = '''#include + int main(int argc, char **argv) {{ + {type} something; + }}''' + if not self.compiles(t.format(**fargs), env, extra_args, dependencies): return -1 - for i in range(1, 1024): - code = templ % (typename, i) - if self.compiles(code, env, extra_args, dependencies): - if self.id == 'msvc': - # MSVC refuses to construct an array of zero size, so - # the test only succeeds when i is sizeof(element) + 1 - return i - 1 - return i - raise EnvironmentException('Cross checking offsetof overflowed.') + t = '''#include + struct tmp {{ + char c; + {type} target; + }}; + int testarray[{size}-offsetof(struct tmp, target)];''' + return self._bisect_compiles(t, fargs, env, extra_args, dependencies) def alignment(self, typename, env, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] if self.is_cross: return self.cross_alignment(typename, env, extra_args, dependencies) - templ = '''#include -#include - -struct tmp { - char c; - %s target; -}; - -int main(int argc, char **argv) { - printf("%%d", (int)offsetof(struct tmp, target)); - return 0; -} -''' - res = self.run(templ % typename, env, extra_args, dependencies) + fargs = {'type': typename} + t = '''#include + #include + struct tmp {{ + char c; + {type} target; + }}; + int main(int argc, char **argv) {{ + printf("%d", (int)offsetof(struct tmp, target)); + return 0; + }}''' + res = self.run(t.format(**fargs), env, extra_args, dependencies) if not res.compiled: raise EnvironmentException('Could not compile alignment test.') if res.returncode != 0: @@ -950,13 +948,15 @@ int main(int argc, char **argv) { to the check performed by Autoconf for AC_CHECK_FUNCS. """ # Define the symbol to something else since it is defined by the - # includes or defines listed by the user (prefix -> {0}) or by the - # compiler. Then, undef the symbol to get rid of it completely. + # includes or defines listed by the user or by the compiler. This may + # include, for instance _GNU_SOURCE which must be defined before + # limits.h, which includes features.h + # Then, undef the symbol to get rid of it completely. head = ''' - #define {1} meson_disable_define_of_{1} + #define {func} meson_disable_define_of_{func} + {prefix} #include - {0} - #undef {1} + #undef {func} ''' # Override any GCC internal prototype and declare our own definition for # the symbol. Use char because that's unlikely to be an actual return @@ -965,13 +965,12 @@ int main(int argc, char **argv) { #ifdef __cplusplus extern "C" #endif - char {1} (); + char {func} (); ''' # The actual function call main = ''' - int main () - {{ - return {1} (); + int main () {{ + return {func} (); }}''' return head, main @@ -982,12 +981,18 @@ int main(int argc, char **argv) { user for the function prototype while checking if a function exists. """ # Add the 'prefix', aka defines, includes, etc that the user provides - head = '#include \n{0}\n' + # This may include, for instance _GNU_SOURCE which must be defined + # before limits.h, which includes features.h + head = '{prefix}\n#include \n' # We don't know what the function takes or returns, so return it as an int. # Just taking the address or comparing it to void is not enough because # compilers are smart enough to optimize it away. The resulting binary # is not run so we don't care what the return value is. - main = '\nint main() {{ void *a = (void*) &{1}; long b = (long) a; return (int) b; }}' + main = '''\nint main() {{ + void *a = (void*) &{func}; + long b = (long) a; + return (int) b; + }}''' return head, main def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): @@ -1011,13 +1016,15 @@ int main(int argc, char **argv) { return val raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname)) + fargs = {'prefix': prefix, 'func': funcname} + # glibc defines functions that are not available on Linux as stubs that # fail with ENOSYS (such as e.g. lchmod). In this case we want to fail # instead of detecting the stub as a valid symbol. # We already included limits.h earlier to ensure that these are defined # for stub functions. stubs_fail = ''' - #if defined __stub_{1} || defined __stub___{1} + #if defined __stub_{func} || defined __stub___{func} fail fail fail this function is not going to work #endif ''' @@ -1036,50 +1043,64 @@ int main(int argc, char **argv) { head, main = self._no_prototype_templ() templ = head + stubs_fail + main - if self.links(templ.format(prefix, funcname), env, extra_args, dependencies): + if self.links(templ.format(**fargs), env, extra_args, dependencies): return True + + # MSVC does not have compiler __builtin_-s. + if self.get_id() == 'msvc': + return False + + # Detect function as a built-in + # # Some functions like alloca() are defined as compiler built-ins which - # are inlined by the compiler, so look for __builtin_symbol in the libc - # if there's no #include-s in prefix which would've #define-d the - # symbol correctly. If there is a #include, just check for the symbol - # directly. This is needed because the above #undef fancy footwork - # doesn't work for builtins. - # This fixes instances such as #1083 where MSYS2 defines - # __builtin_posix_memalign in the C library but doesn't define - # posix_memalign in the headers to point to that builtin which results - # in an invalid detection. - if '#include' not in prefix: - code = 'int main() {{ {0}; }}' - return self.links(code.format('__builtin_' + funcname), env, - extra_args, dependencies) - else: - code = '{0}\n' + stubs_fail + '\nint main() {{ {1}; }}' - return self.links(code.format(prefix, funcname), env, extra_args, - dependencies) + # are inlined by the compiler and you can't take their address, so we + # need to look for them differently. On nice compilers like clang, we + # can just directly use the __has_builtin() macro. + fargs['no_includes'] = '#include' not in prefix + t = '''{prefix} + int main() {{ + #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 + #endif + }}''' + return self.links(t.format(**fargs), env, extra_args, dependencies) def has_members(self, typename, membernames, prefix, env, extra_args=None, dependencies=None): if extra_args is None: extra_args = [] - templ = '''{0} -void bar() {{ - {1} {2}; - {3} -}}; -''' + fargs = {'prefix': prefix, 'type': typename, 'name': 'foo'} # Create code that accesses all members members = '' - for m in membernames: - members += 'foo.{};\n'.format(m) - code = templ.format(prefix, typename, 'foo', members) - return self.compiles(code, env, extra_args, dependencies) + for member in membernames: + members += '{}.{};\n'.format(fargs['name'], member) + fargs['members'] = members + t = '''{prefix} + void bar() {{ + {type} {name}; + {members} + }};''' + return self.compiles(t.format(**fargs), env, extra_args, dependencies) def has_type(self, typename, prefix, env, extra_args, dependencies=None): - templ = '''%s -void bar() { - sizeof(%s); -}; -''' - return self.compiles(templ % (prefix, typename), env, extra_args, dependencies) + fargs = {'prefix': prefix, 'type': typename} + t = '''{prefix} + void bar() {{ + sizeof({type}); + }};''' + return self.compiles(t.format(**fargs), env, extra_args, dependencies) def symbols_have_underscore_prefix(self, env): ''' @@ -1162,6 +1183,12 @@ class CPPCompiler(CCompiler): code = 'class breakCCompiler;int main(int argc, char **argv) { return 0; }\n' return self.sanity_check_impl(work_dir, environment, 'sanitycheckcpp.cc', code) + def get_compiler_check_args(self): + # -fpermissive allows non-conforming code to compile which is necessary + # for many C++ checks. Particularly, the has_header_symbol check is + # too strict without this and always fails. + return super().get_compiler_check_args() + ['-fpermissive'] + def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): # Check if it's a C-like symbol if super().has_header_symbol(hname, symbol, prefix, env, extra_args, dependencies): @@ -1169,12 +1196,12 @@ class CPPCompiler(CCompiler): # Check if it's a class or a template if extra_args is None: extra_args = [] - templ = '''{2} -#include <{0}> -using {1}; -int main () {{ return 0; }}''' - return self.compiles(templ.format(hname, symbol, prefix), env, - extra_args, dependencies) + fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol} + t = '''{prefix} + #include <{header}> + using {symbol}; + int main () {{ return 0; }}''' + return self.compiles(t.format(**fargs), env, extra_args, dependencies) class ObjCCompiler(CCompiler): def __init__(self, exelist, version, is_cross, exe_wrap): @@ -2058,6 +2085,11 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler): def get_option_link_args(self, options): return options['cpp_winlibs'].value[:] + def get_compiler_check_args(self): + # Visual Studio C++ compiler doesn't support -fpermissive, + # so just use the plain C args. + return super(VisualStudioCCompiler, self).get_compiler_check_args() + GCC_STANDARD = 0 GCC_OSX = 1 GCC_MINGW = 2 @@ -2224,12 +2256,6 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): return options['cpp_winlibs'].value[:] return [] - def get_compiler_check_args(self): - # -fpermissive allows non-conforming code to compile which is necessary - # for many C++ checks. Particularly, the has_header_symbol check is - # too strict without this and always fails. - return self.get_no_optimization_args() + ['-fpermissive'] - class GnuObjCCompiler(GnuCompiler, ObjCCompiler): @@ -2255,11 +2281,6 @@ class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): '2': default_warn_args + ['-Wextra'], '3': default_warn_args + ['-Wextra', '-Wpedantic']} - def get_compiler_check_args(self): - # -fpermissive allows non-conforming code to compile which is necessary - # for many ObjC++ checks. Particularly, the has_header_symbol check is - # too strict without this and always fails. - return self.get_no_optimization_args() + ['-fpermissive'] class ClangCompiler: def __init__(self, clang_type): @@ -2473,7 +2494,7 @@ class IntelCCompiler(IntelCompiler, CCompiler): return ['-shared'] def has_multi_arguments(self, args, env): - return super(IntelCCompiler, self).has_multi_arguments(args + ['-diag-error', '10006'], env) + return super().has_multi_arguments(args + ['-diag-error', '10006'], env) class IntelCPPCompiler(IntelCompiler, CPPCompiler): @@ -2517,11 +2538,8 @@ class IntelCPPCompiler(IntelCompiler, CPPCompiler): def get_option_link_args(self, options): return [] - def get_compiler_check_args(self): - return self.get_no_optimization_args() - def has_multi_arguments(self, args, env): - return super(IntelCPPCompiler, self).has_multi_arguments(args + ['-diag-error', '10006'], env) + return super().has_multi_arguments(args + ['-diag-error', '10006'], env) class FortranCompiler(Compiler): diff --git a/test cases/common/43 has function/meson.build b/test cases/common/43 has function/meson.build index 323ed0093..eb30acd07 100644 --- a/test cases/common/43 has function/meson.build +++ b/test cases/common/43 has function/meson.build @@ -4,6 +4,10 @@ host_system = host_machine.system() # This is used in the `test_compiler_check_flags_order` unit test unit_test_args = '-I/tmp' +defines_has_builtin = '''#ifndef __has_builtin +#error "no __has_builtin" +#endif +''' compilers = [meson.get_compiler('c'), meson.get_compiler('cpp')] foreach cc : compilers @@ -24,6 +28,11 @@ foreach cc : compilers assert(cc.has_function('fprintf', prefix : '#include ', args : unit_test_args), '"fprintf" function not found with include (on msvc).') + # Compiler intrinsics + assert(cc.has_function('strcmp'), + 'strcmp intrinsic should have been found on MSVC') + assert(cc.has_function('strcmp', prefix : '#include '), + 'strcmp intrinsic should have been found with #include on MSVC') endif if cc.has_function('hfkerhisadf', prefix : '#include', @@ -31,25 +40,42 @@ foreach cc : compilers error('Found non-existent function "hfkerhisadf".') endif + if cc.has_function('hfkerhisadf', args : unit_test_args) + error('Found non-existent function "hfkerhisadf".') + endif + # With glibc on Linux lchmod is a stub that will always return an error, # we want to detect that and declare that the function is not available. # We can't check for the C library used here of course, but if it's not # implemented in glibc it's probably not implemented in any other 'slimmer' # C library variants either, so the check should be safe either way hopefully. - if host_system == 'linux' and cc.get_id() == 'gcc' + if host_system == 'linux' or host_system == 'darwin' assert (cc.has_function('poll', prefix : '#include ', args : unit_test_args), 'couldn\'t detect "poll" when defined by a header') lchmod_prefix = '#include \n#include ' - assert (not cc.has_function('lchmod', prefix : lchmod_prefix, - args : unit_test_args), - '"lchmod" check should have failed') + if host_system == 'linux' + assert (not cc.has_function('lchmod', prefix : lchmod_prefix, + args : unit_test_args), + '"lchmod" check should have failed') + else + # macOS and *BSD have lchmod + assert (cc.has_function('lchmod', prefix : lchmod_prefix, + args : unit_test_args), + '"lchmod" check should have succeeded') + endif # Check that built-ins are found properly both with and without headers assert(cc.has_function('alloca', args : unit_test_args), - 'built-in alloca must be found on Linux') + 'built-in alloca must be found on ' + host_system) assert(cc.has_function('alloca', prefix : '#include ', args : unit_test_args), - 'built-in alloca must be found on Linux with #include') + 'built-in alloca must be found with #include') + if not cc.compiles(defines_has_builtin, args : unit_test_args) + assert(not cc.has_function('alloca', + prefix : '#include \n#undef alloca', + args : unit_test_args), + 'built-in alloca must not be found with #include and #undef') + endif endif # For some functions one needs to define _GNU_SOURCE before including the