aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/compilers.py
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2016-11-02 11:41:32 -0700
committerGitHub <noreply@github.com>2016-11-02 11:41:32 -0700
commit97c2321740602ed43a5fbf0ca2cad53a2a622cee (patch)
tree809b2b82276627c499808f35b5aaebcf1216d735 /mesonbuild/compilers.py
parentafe00697faab16928fc3a801229175a5746d57b7 (diff)
parenteea951c9f358a3a67fcfd479e20c151e91cbca04 (diff)
downloadmeson-97c2321740602ed43a5fbf0ca2cad53a2a622cee.zip
meson-97c2321740602ed43a5fbf0ca2cad53a2a622cee.tar.gz
meson-97c2321740602ed43a5fbf0ca2cad53a2a622cee.tar.bz2
Merge pull request #949 from centricular/has-function-xcode8-fixes
Fix has_function with XCode 8 and related changes
Diffstat (limited to 'mesonbuild/compilers.py')
-rw-r--r--mesonbuild/compilers.py181
1 files changed, 103 insertions, 78 deletions
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index d7cd1f7..94e8a54 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -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,11 +2138,8 @@ 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):
+class ClangCPPCompiler(ClangCompiler, CPPCompiler):
def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None):
CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper)
ClangCompiler.__init__(self, cltype)
@@ -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):