aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2017-01-26 22:56:05 +0200
committerGitHub <noreply@github.com>2017-01-26 22:56:05 +0200
commitb81d00b4b6144b8b62d6bd7cfc247c95b9b015c3 (patch)
tree0a16c85d79779495bc9bc13b51e0ff15b63fbd4d
parent1d6147a592521a88a85710db1eb13dfbad102a46 (diff)
parente084a47a0a28f0bae70057ddb7812cb283456d07 (diff)
downloadmeson-b81d00b4b6144b8b62d6bd7cfc247c95b9b015c3.zip
meson-b81d00b4b6144b8b62d6bd7cfc247c95b9b015c3.tar.gz
meson-b81d00b4b6144b8b62d6bd7cfc247c95b9b015c3.tar.bz2
Merge pull request #1328 from centricular/has-function-prefix
Add prefix before including limits.h in has_function checks
-rw-r--r--mesonbuild/compilers.py308
-rw-r--r--test cases/common/43 has function/meson.build38
2 files changed, 195 insertions, 151 deletions
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index 68a0cd7..0a88d6b 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 <stdio.h>
-{0}
-int main(int argc, char **argv) {{
- {1} something;
-}}
-'''
- templ = '''#include <stdio.h>
-%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 <stdio.h>
+ {prefix}
+ int main(int argc, char **argv) {{
+ {name} something;
+ }}'''
+ if not self.compiles(t.format(**fargs), env, extra_args, dependencies):
+ return -1
+ t = '''#include <stdio.h>
+ {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<stdio.h>
-%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<stdio.h>
+ {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 <stdio.h>
-int main(int argc, char **argv) {{
- {0} something;
-}}
-'''
- templ = '''#include<stddef.h>
-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 <stdio.h>
+ 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 <stddef.h>
+ 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<stdio.h>
-#include<stddef.h>
-
-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 <stdio.h>
+ #include <stddef.h>
+ 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 <limits.h>
- {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 <limits.h>\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 <limits.h>\n'
# We don't know what the function takes or returns, so return it as an int.
# Just taking the address or comparing it to void is not enough because
# compilers are smart enough to optimize it away. The resulting binary
# is not run so we don't care what the return value is.
- main = '\nint main() {{ void *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 323ed00..eb30acd 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 <stdio.h>',
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 <string.h>'),
+ 'strcmp intrinsic should have been found with #include on MSVC')
endif
if cc.has_function('hfkerhisadf', prefix : '#include<stdio.h>',
@@ -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 <poll.h>',
args : unit_test_args),
'couldn\'t detect "poll" when defined by a header')
lchmod_prefix = '#include <sys/stat.h>\n#include <unistd.h>'
- 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 <alloca.h>',
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 <alloca.h>\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