aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2016-04-13 19:09:22 +0300
committerJussi Pakkanen <jpakkane@gmail.com>2016-04-13 19:09:22 +0300
commit5d65c4b6789f159a6d83aa93696dac5030cae81e (patch)
treec842c5c70d9b5737553c56d62d8e97e1670b7ca3
parent48e678db76948cc4b02369b64bc072810535f12f (diff)
parente72523ae410d780c3a703f0e3296105408fcc122 (diff)
downloadmeson-5d65c4b6789f159a6d83aa93696dac5030cae81e.zip
meson-5d65c4b6789f159a6d83aa93696dac5030cae81e.tar.gz
meson-5d65c4b6789f159a6d83aa93696dac5030cae81e.tar.bz2
Merge pull request #490 from centricular/has_function_link
Use the linker to check if a specific function is provided by the toolchain or runtime
-rw-r--r--mesonbuild/compilers.py71
-rw-r--r--mesonbuild/interpreter.py19
-rw-r--r--test cases/common/111 has header symbol/meson.build18
-rw-r--r--test cases/common/43 has function/meson.build24
4 files changed, 122 insertions, 10 deletions
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index f80a28d..0535653 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -260,6 +260,9 @@ class Compiler():
def has_header(self, *args, **kwargs):
raise EnvironmentException('Language %s does not support header checks.' % self.language)
+ def has_header_symbol(self, *args, **kwargs):
+ raise EnvironmentException('Language %s does not support header symbol checks.' % self.language)
+
def compiles(self, *args, **kwargs):
raise EnvironmentException('Language %s does not support compile checks.' % self.language)
@@ -349,6 +352,9 @@ class CCompiler(Compiler):
def get_compile_only_args(self):
return ['-c']
+ def get_no_optimization_args(self):
+ return ['-O0']
+
def get_output_args(self, target):
return ['-o', target]
@@ -462,6 +468,14 @@ int someSymbolHereJustForFun;
'''
return self.compiles(templ % hname, extra_args)
+ def has_header_symbol(self, hname, symbol, prefix, extra_args=[]):
+ templ = '''{2}
+#include <{0}>
+int main () {{ {1}; }}'''
+ # Pass -O0 to ensure that the symbol isn't optimized away
+ extra_args += self.get_no_optimization_args()
+ return self.compiles(templ.format(hname, symbol, prefix), extra_args)
+
def compile(self, code, srcname, extra_args=[]):
commands = self.get_exelist()
commands.append(srcname)
@@ -639,15 +653,41 @@ int main(int argc, char **argv) {
return align
def has_function(self, funcname, prefix, env, extra_args=[]):
- # This fails (returns true) if funcname is a ptr or a variable.
- # The correct check is a lot more difficult.
- # Fix this to do that eventually.
- templ = '''%s
-int main(int argc, char **argv) {
- void *ptr = (void*)(%s);
- return 0;
-};
-'''
+ # Define the symbol to something else in case it is defined by the
+ # includes or defines listed by the user `{0}` or by the compiler.
+ # Then, undef the symbol to get rid of it completely.
+ templ = '''
+ #define {1} meson_disable_define_of_{1}
+ {0}
+ #undef {1}
+ '''
+
+ # Override any GCC internal prototype and declare our own definition for
+ # the symbol. Use char because that's unlikely to be an actual return
+ # value for a function which ensures that we override the definition.
+ templ += '''
+ #ifdef __cplusplus
+ extern "C"
+ #endif
+ char {1} ();
+ '''
+
+ # 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.
+ templ += '''
+ #if defined __stub_{1} || defined __stub___{1}
+ fail fail fail this function is not going to work
+ #endif
+ '''
+
+ # And finally the actual function call
+ templ += '''
+ int
+ main ()
+ {{
+ return {1} ();
+ }}'''
varname = 'has function ' + funcname
varname = varname.replace(' ', '_')
if self.is_cross:
@@ -656,7 +696,15 @@ int main(int argc, char **argv) {
if isinstance(val, bool):
return val
raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname))
- return self.compiles(templ % (prefix, funcname), extra_args)
+ if self.links(templ.format(prefix, funcname), extra_args):
+ return True
+ # Some functions like alloca() are defined as compiler built-ins which
+ # are inlined by the compiler, so test for that instead. Built-ins are
+ # special functions that ignore all includes and defines, so we just
+ # directly try to link via main().
+ # Add -O0 to ensure that the symbol isn't optimized away by the compiler
+ extra_args += self.get_no_optimization_args()
+ return self.links('int main() {{ {0}; }}'.format('__builtin_' + funcname), extra_args)
def has_member(self, typename, membername, prefix, extra_args=[]):
templ = '''%s
@@ -1257,6 +1305,9 @@ class VisualStudioCCompiler(CCompiler):
def get_compile_only_args(self):
return ['/c']
+ def get_no_optimization_args(self):
+ return ['/Od']
+
def get_output_args(self, target):
if target.endswith('.exe'):
return ['/Fe' + target]
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 0a88ce4..d6a3a3e 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -575,6 +575,7 @@ class CompilerHolder(InterpreterObject):
'get_id': self.get_id_method,
'sizeof': self.sizeof_method,
'has_header': self.has_header_method,
+ 'has_header_symbol': self.has_header_symbol_method,
'run' : self.run_method,
'has_function' : self.has_function_method,
'has_member' : self.has_member_method,
@@ -752,6 +753,24 @@ class CompilerHolder(InterpreterObject):
mlog.log('Has header "%s":' % string, h)
return haz
+ def has_header_symbol_method(self, args, kwargs):
+ if len(args) != 2:
+ raise InterpreterException('has_header_symbol method takes exactly two arguments.')
+ check_stringlist(args)
+ hname = args[0]
+ symbol = args[1]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of has_function must be a string.')
+ extra_args = self.determine_args(kwargs)
+ haz = self.compiler.has_header_symbol(hname, symbol, prefix, extra_args)
+ if haz:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO')
+ mlog.log('Header <{0}> has symbol "{1}":'.format(hname, symbol), h)
+ return haz
+
def find_library_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('find_library method takes one argument.')
diff --git a/test cases/common/111 has header symbol/meson.build b/test cases/common/111 has header symbol/meson.build
new file mode 100644
index 0000000..e0afb42
--- /dev/null
+++ b/test cases/common/111 has header symbol/meson.build
@@ -0,0 +1,18 @@
+project('has header symbol', 'c')
+
+cc = meson.get_compiler('c')
+
+assert (cc.has_header_symbol('stdio.h', 'int'), 'base types should always be available')
+assert (cc.has_header_symbol('stdio.h', 'printf'), 'printf function not found')
+assert (cc.has_header_symbol('stdio.h', 'FILE'), 'FILE structure not found')
+assert (cc.has_header_symbol('limits.h', 'INT_MAX'), 'INT_MAX define not found')
+assert (not cc.has_header_symbol('limits.h', 'guint64'), 'guint64 is not defined in limits.h')
+assert (not cc.has_header_symbol('stdlib.h', 'FILE'), 'FILE structure is defined in stdio.h, not stdlib.h')
+assert (not cc.has_header_symbol('stdlol.h', 'printf'), 'stdlol.h shouldn\'t exist')
+assert (not cc.has_header_symbol('stdlol.h', 'int'), 'shouldn\'t be able to find "int" with invalid header')
+
+# This is likely only available on Glibc, so just test for it
+if cc.has_function('ppoll')
+ assert (not cc.has_header_symbol('poll.h', 'ppoll'), 'ppoll should not be accessible without _GNU_SOURCE')
+ assert (cc.has_header_symbol('poll.h', 'ppoll', prefix : '#define _GNU_SOURCE'), 'ppoll should be accessible with _GNU_SOURCE')
+endif
diff --git a/test cases/common/43 has function/meson.build b/test cases/common/43 has function/meson.build
index 8fccaef..3736a3d 100644
--- a/test cases/common/43 has function/meson.build
+++ b/test cases/common/43 has function/meson.build
@@ -6,6 +6,30 @@ if not cc.has_function('printf', prefix : '#include<stdio.h>')
error('Existing function not found.')
endif
+# Should also be able to detect it without specifying the header
+# We check for a different function here to make sure the result is
+# not taken from a cache (ie. the check above)
+assert(cc.has_function('fprintf'), 'Existing function not found without include')
+
if cc.has_function('hfkerhisadf', prefix : '#include<stdio.h>')
error('Found non-existant function.')
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_machine.system() == 'linux' and cc.get_id() == 'gcc'
+ assert (cc.has_function('poll', prefix : '#include <poll.h>'), 'couldn\'t detect poll when defined by a header')
+ assert (not cc.has_function('lchmod', prefix : '''#include <sys/stat.h>
+ #include <unistd.h>'''), 'lchmod check should have failed')
+endif
+
+# For some functions one needs to define _GNU_SOURCE before including the
+# right headers to get them picked up. Make sure we can detect these functions
+# as well without any prefix
+if cc.has_header_symbol('sys/socket.h', 'recvmmsg', prefix : '#define _GNU_SOURCE')
+ # We assume that if recvmmsg exists sendmmsg does too
+ assert (cc.has_function('sendmmsg'), 'Failed to detect existing function')
+endif