diff options
-rw-r--r-- | mesonbuild/compilers.py | 107 |
1 files changed, 68 insertions, 39 deletions
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index f03e34d..03fb4f2 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -878,55 +878,65 @@ int main(int argc, char **argv) { raise EnvironmentException('Could not determine alignment of %s. Sorry. You might want to file a bug.' % typename) return align - def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + @staticmethod + def _no_prototype_templ(): """ - First, this function looks for the symbol in the default libraries - provided by the compiler (stdlib + a few others usually). If that - fails, it checks if any of the headers specified in the prefix provide - an implementation of the function, and if that fails, it checks if it's - implemented as a compiler-builtin. + Try to find the function without a prototype from a header by defining + our own dummy prototype and trying to link with the C library (and + whatever else the compiler links in by default). This is very similar + to the check performed by Autoconf for AC_CHECK_FUNCS. """ - if extra_args is None: - extra_args = [] - # 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 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. + head = ''' #define {1} meson_disable_define_of_{1} #include <limits.h> {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 += ''' + head += ''' #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. - # We always include limits.h above to ensure that these are defined for - # stub functions. - stubs_fail = ''' - #if defined __stub_{1} || defined __stub___{1} - fail fail fail this function is not going to work - #endif - ''' - templ += stubs_fail - - # And finally the actual function call - templ += ''' - int - main () + # The actual function call + main = ''' + int main () {{ return {1} (); }}''' + return head, main + + @staticmethod + def _have_prototype_templ(): + """ + Returns a head-er and main() call that uses the headers listed by the + 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' + # We don't know what the function takes or returns, so just add + # a useless reference to it + main = '\nint main() {{ {1}; }}' + return head, main + + def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + """ + First, this function looks for the symbol in the default libraries + provided by the compiler (stdlib + a few others usually). If that + fails, it checks if any of the headers specified in the prefix provide + an implementation of the function, and if that fails, it checks if it's + implemented as a compiler-builtin. + """ + if extra_args is None: + extra_args = [] + + # Short-circuit if the check is already provided by the cross-info file varname = 'has function ' + funcname varname = varname.replace(' ', '_') if self.is_cross: @@ -935,16 +945,35 @@ int main(int argc, char **argv) { if isinstance(val, bool): return val raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname)) - if self.links(templ.format(prefix, funcname), env, extra_args, dependencies): - return True + + # 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} + fail fail fail this function is not going to work + #endif + ''' + + # If we have any includes in the prefix supplied by the user, assume + # that the user wants us to use the symbol prototype defined in those + # includes. If not, then try to do the Autoconf-style check with + # a dummy prototype definition of our own. + # This is needed when the linker determines symbol availability from an + # SDK based on the prototype in the header provided by the SDK. + # Ignoring this prototype would result in the symbol always being + # marked as available. + if '#include' in prefix: + head, main = self._have_prototype_templ() + else: + head, main = self._no_prototype_templ() + templ = head + stubs_fail + main + # Add -O0 to ensure that the symbol isn't optimized away by the compiler args = extra_args + self.get_no_optimization_args() - # Sometimes the implementation is provided by the header, or the header - # redefines the symbol to be something else. In that case, we want to - # still detect the function. We still want to fail if __stub_foo or - # _stub_foo are defined, of course. - header_templ = '#include <limits.h>\n{0}\n' + stubs_fail + '\nint main() {{ {1}; }}' - if self.links(header_templ.format(prefix, funcname), env, args, dependencies): + if self.links(templ.format(prefix, funcname), env, extra_args, dependencies): 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 |