Merge pull request #949 from centricular/has-function-xcode8-fixes

Fix has_function with XCode 8 and related changes
pull/987/head
Jussi Pakkanen 8 years ago committed by GitHub
commit 97c2321740
  1. 179
      mesonbuild/compilers.py
  2. 12
      mesonbuild/environment.py
  3. 5
      mesonbuild/interpreter.py
  4. 26
      test cases/osx/3 has function xcode8/meson.build

@ -17,7 +17,7 @@ import subprocess, os.path
import tempfile
from .import mesonlib
from . import mlog
from .mesonlib import MesonException
from .mesonlib import MesonException, version_compare
from . import coredata
"""This file contains the data files of all compilers Meson knows
@ -707,12 +707,16 @@ int main () {{ {1}; }}'''
args = self.unix_link_flags_to_native(cargs + extra_args)
# Read c_args/cpp_args/etc from the cross-info file (if needed)
args += self.get_cross_extra_flags(env, compile=True, link=False)
# Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env
# We assume that the user has ensured these are compiler-specific
args += env.coredata.external_args[self.language]
# We only want to compile; not link
args += self.get_compile_only_args()
with self.compile(code, args) as p:
return p.returncode == 0
def links(self, code, env, extra_args=None, dependencies=None):
def _links_wrapper(self, code, env, extra_args, dependencies):
"Shares common code between self.links and self.run"
if extra_args is None:
extra_args = []
elif isinstance(extra_args, str):
@ -729,27 +733,19 @@ int main () {{ {1}; }}'''
args += self.get_linker_debug_crt_args()
# Read c_args/c_link_args/cpp_args/cpp_link_args/etc from the cross-info file (if needed)
args += self.get_cross_extra_flags(env, compile=True, link=True)
with self.compile(code, args) as p:
# Add LDFLAGS from the env. We assume that the user has ensured these
# are compiler-specific
args += env.coredata.external_link_args[self.language]
return self.compile(code, args)
def links(self, code, env, extra_args=None, dependencies=None):
with self._links_wrapper(code, env, extra_args, dependencies) as p:
return p.returncode == 0
def run(self, code, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
if dependencies is None:
dependencies = []
elif not isinstance(dependencies, list):
dependencies = [dependencies]
if self.is_cross and self.exe_wrapper is None:
raise CrossNoRunException('Can not run test applications in this cross environment.')
cargs = [a for d in dependencies for a in d.get_compile_args()]
link_args = [a for d in dependencies for a in d.get_link_args()]
# Convert flags to the native type of the selected compiler
args = self.unix_link_flags_to_native(cargs + link_args + extra_args)
# Select a CRT if needed since we're linking
args += self.get_linker_debug_crt_args()
# Read c_link_args/cpp_link_args/etc from the cross-info file
args += self.get_cross_extra_flags(env, compile=True, link=True)
with self.compile(code, args) as p:
with self._links_wrapper(code, env, extra_args, dependencies) as p:
if p.returncode != 0:
mlog.debug('Could not compile test file %s: %d\n' % (
p.input_name,
@ -878,55 +874,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 try to use it as
# a function pointer
main = '\nint main() {{ int a = (int) &{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 +941,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
@ -2073,6 +2098,20 @@ class ClangCompiler():
raise MesonException('Unreachable code when converting clang type to gcc type.')
return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion)
def has_argument(self, arg, env):
return super().has_argument(['-Werror=unknown-warning-option', arg], env)
def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
# Starting with XCode 8, we need to pass this to force linker
# visibility to obey OS X and iOS minimum version targets with
# -mmacosx-version-min, -miphoneos-version-min, etc.
# https://github.com/Homebrew/homebrew-core/issues/3727
if self.clang_type == CLANG_OSX and version_compare(self.version, '>=8.0'):
extra_args.append('-Wl,-no_weak_imports')
return super().has_function(funcname, prefix, env, extra_args, dependencies)
class ClangCCompiler(ClangCompiler, CCompiler):
def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None):
CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
@ -2099,9 +2138,6 @@ class ClangCCompiler(ClangCompiler, CCompiler):
def get_option_link_args(self, options):
return []
def has_argument(self, arg, env):
return super().has_argument(['-Werror=unknown-warning-option', arg], env)
class ClangCPPCompiler(ClangCompiler, CPPCompiler):
def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None):
@ -2127,28 +2163,17 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler):
def get_option_link_args(self, options):
return []
def has_argument(self, arg, env):
return super().has_argument(['-Werror=unknown-warning-option', arg], env)
class ClangObjCCompiler(GnuObjCCompiler):
class ClangObjCCompiler(ClangCompiler, GnuObjCCompiler):
def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None):
super().__init__(exelist, version, is_cross, exe_wrapper)
self.id = 'clang'
GnuObjCCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
ClangCompiler.__init__(self, cltype)
self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage']
self.clang_type = cltype
if self.clang_type != CLANG_OSX:
self.base_options.append('b_lundef')
self.base_options.append('b_asneeded')
class ClangObjCPPCompiler(GnuObjCPPCompiler):
class ClangObjCPPCompiler(ClangCompiler, GnuObjCPPCompiler):
def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None):
super().__init__(exelist, version, is_cross, exe_wrapper)
self.id = 'clang'
self.clang_type = cltype
GnuObjCPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
ClangCompiler.__init__(self, cltype)
self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage']
if self.clang_type != CLANG_OSX:
self.base_options.append('b_lundef')
self.base_options.append('b_asneeded')
class FortranCompiler(Compiler):
def __init__(self, exelist, version, is_cross, exe_wrapper=None):

@ -833,9 +833,9 @@ class Environment():
return self.coredata.get_builtin_option('datadir')
def get_args_from_envvars(lang, compiler_is_linker):
def get_args_from_envvars(compiler):
"""
@lang: Language to fetch environment flags for
@compiler: Compiler to fetch environment flags for
Returns a tuple of (compile_flags, link_flags) for the specified language
from the inherited environment
@ -844,14 +844,18 @@ def get_args_from_envvars(lang, compiler_is_linker):
if val:
mlog.log('Appending {} from environment: {!r}'.format(var, val))
lang = compiler.get_language()
compiler_is_linker = False
if hasattr(compiler, 'get_linker_exelist'):
compiler_is_linker = (compiler.get_exelist() == compiler.get_linker_exelist())
if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'):
return ([], [])
# Compile flags
cflags_mapping = {'c': 'CFLAGS', 'cpp': 'CXXFLAGS',
'objc': 'OBJCFLAGS', 'objcpp': 'OBJCXXFLAGS',
'fortran': 'FFLAGS',
'd': 'DFLAGS'}
'fortran': 'FFLAGS', 'd': 'DFLAGS'}
compile_flags = os.environ.get(cflags_mapping[lang], '')
log_var(cflags_mapping[lang], compile_flags)
compile_flags = compile_flags.split()

@ -1792,11 +1792,8 @@ class Interpreter():
else:
raise
mlog.log('Native %s compiler: ' % lang, mlog.bold(' '.join(comp.get_exelist())), ' (%s %s)' % (comp.id, comp.version), sep='')
compiler_is_linker = False
if hasattr(comp, 'get_linker_exelist'):
compiler_is_linker = (comp.get_exelist() == comp.get_linker_exelist())
if not comp.get_language() in self.coredata.external_args:
(ext_compile_args, ext_link_args) = environment.get_args_from_envvars(comp.get_language(), compiler_is_linker)
(ext_compile_args, ext_link_args) = environment.get_args_from_envvars(comp)
self.coredata.external_args[comp.get_language()] = ext_compile_args
self.coredata.external_link_args[comp.get_language()] = ext_link_args
self.build.add_compiler(comp)

@ -0,0 +1,26 @@
project('has function xcode8', 'c')
cc = meson.get_compiler('c')
# XCode 8 location for the macOS 10.12 SDK
sdk_args = ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk']
args_10_11 = ['-mmacosx-version-min=10.11'] + sdk_args
args_10_12 = ['-mmacosx-version-min=10.12'] + sdk_args
# Test requires XCode 8 which has the MacOSX 10.12 SDK
if cc.version().version_compare('>=8.0')
if cc.has_function('clock_gettime', args : args_10_11, prefix : '#include <time.h>')
error('Should not have found clock_gettime via <time.h> when targetting Mac OS X 10.11')
endif
if not cc.has_function('clock_gettime', args : args_10_12, prefix : '#include <time.h>')
error('Did NOT find clock_gettime via <time.h> when targetting Mac OS X 10.12')
endif
if not cc.has_function('clock_gettime', args : args_10_11)
error('Did NOT find clock_gettime w/o a prototype when targetting Mac OS X 10.11')
endif
if not cc.has_function('clock_gettime', args : args_10_12)
error('Did NOT find clock_gettime w/o a prototype when targetting Mac OS X 10.12')
endif
else
message('Test needs XCode 8, skipping...')
endif
Loading…
Cancel
Save