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
pull/513/head
Jussi Pakkanen 9 years ago
commit 5d65c4b678
  1. 69
      mesonbuild/compilers.py
  2. 19
      mesonbuild/interpreter.py
  3. 18
      test cases/common/111 has header symbol/meson.build
  4. 24
      test cases/common/43 has function/meson.build

@ -260,6 +260,9 @@ class Compiler():
def has_header(self, *args, **kwargs): def has_header(self, *args, **kwargs):
raise EnvironmentException('Language %s does not support header checks.' % self.language) 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): def compiles(self, *args, **kwargs):
raise EnvironmentException('Language %s does not support compile checks.' % self.language) raise EnvironmentException('Language %s does not support compile checks.' % self.language)
@ -349,6 +352,9 @@ class CCompiler(Compiler):
def get_compile_only_args(self): def get_compile_only_args(self):
return ['-c'] return ['-c']
def get_no_optimization_args(self):
return ['-O0']
def get_output_args(self, target): def get_output_args(self, target):
return ['-o', target] return ['-o', target]
@ -462,6 +468,14 @@ int someSymbolHereJustForFun;
''' '''
return self.compiles(templ % hname, extra_args) 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=[]): def compile(self, code, srcname, extra_args=[]):
commands = self.get_exelist() commands = self.get_exelist()
commands.append(srcname) commands.append(srcname)
@ -639,15 +653,41 @@ int main(int argc, char **argv) {
return align return align
def has_function(self, funcname, prefix, env, extra_args=[]): def has_function(self, funcname, prefix, env, extra_args=[]):
# This fails (returns true) if funcname is a ptr or a variable. # Define the symbol to something else in case it is defined by the
# The correct check is a lot more difficult. # includes or defines listed by the user `{0}` or by the compiler.
# Fix this to do that eventually. # Then, undef the symbol to get rid of it completely.
templ = '''%s templ = '''
int main(int argc, char **argv) { #define {1} meson_disable_define_of_{1}
void *ptr = (void*)(%s); {0}
return 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 = 'has function ' + funcname
varname = varname.replace(' ', '_') varname = varname.replace(' ', '_')
if self.is_cross: if self.is_cross:
@ -656,7 +696,15 @@ int main(int argc, char **argv) {
if isinstance(val, bool): if isinstance(val, bool):
return val return val
raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname)) 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=[]): def has_member(self, typename, membername, prefix, extra_args=[]):
templ = '''%s templ = '''%s
@ -1257,6 +1305,9 @@ class VisualStudioCCompiler(CCompiler):
def get_compile_only_args(self): def get_compile_only_args(self):
return ['/c'] return ['/c']
def get_no_optimization_args(self):
return ['/Od']
def get_output_args(self, target): def get_output_args(self, target):
if target.endswith('.exe'): if target.endswith('.exe'):
return ['/Fe' + target] return ['/Fe' + target]

@ -575,6 +575,7 @@ class CompilerHolder(InterpreterObject):
'get_id': self.get_id_method, 'get_id': self.get_id_method,
'sizeof': self.sizeof_method, 'sizeof': self.sizeof_method,
'has_header': self.has_header_method, 'has_header': self.has_header_method,
'has_header_symbol': self.has_header_symbol_method,
'run' : self.run_method, 'run' : self.run_method,
'has_function' : self.has_function_method, 'has_function' : self.has_function_method,
'has_member' : self.has_member_method, 'has_member' : self.has_member_method,
@ -752,6 +753,24 @@ class CompilerHolder(InterpreterObject):
mlog.log('Has header "%s":' % string, h) mlog.log('Has header "%s":' % string, h)
return haz 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): def find_library_method(self, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise InterpreterException('find_library method takes one argument.') raise InterpreterException('find_library method takes one argument.')

@ -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

@ -6,6 +6,30 @@ if not cc.has_function('printf', prefix : '#include<stdio.h>')
error('Existing function not found.') error('Existing function not found.')
endif 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>') if cc.has_function('hfkerhisadf', prefix : '#include<stdio.h>')
error('Found non-existant function.') error('Found non-existant function.')
endif 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

Loading…
Cancel
Save