Add dependency support to the checks using the compiler

It is extremely common to need to know within a given dependency if
a given header, symbol, member, function, etc exists that cannot be
determined from the version number alone.

Without passing dependency objects to the various compiler/linker
checks and with many libraries headers/libraries being located in
their own subdirs of the standard prefix, the check for the library
would not find the header/function/symbol/etc.

This commit allows passing dependency objects to the compiler checks so
that the test program can be compiled/linked/run with the necessary
compilation and/or linking flags for that library.
pull/828/head
Matthew Waters 8 years ago committed by Jussi Pakkanen
parent 5ebc77f722
commit 13e91ab499
  1. 77
      mesonbuild/compilers.py
  2. 50
      mesonbuild/interpreter.py
  3. 31
      test cases/linuxlike/9 compiler checks with dependencies/meson.build

@ -588,15 +588,15 @@ class CCompiler(Compiler):
code = 'int main(int argc, char **argv) { int class=0; return class; }\n'
return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
def has_header(self, hname, env, extra_args=None):
def has_header(self, hname, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
templ = '''#include<%s>
int someSymbolHereJustForFun;
'''
return self.compiles(templ % hname, env, extra_args)
return self.compiles(templ % hname, env, extra_args, dependencies)
def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None):
def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
templ = '''{2}
@ -604,7 +604,7 @@ int someSymbolHereJustForFun;
int main () {{ {1}; }}'''
# Pass -O0 to ensure that the symbol isn't optimized away
args = extra_args + self.get_no_optimization_args()
return self.compiles(templ.format(hname, symbol, prefix), env, args)
return self.compiles(templ.format(hname, symbol, prefix), env, args, dependencies)
def compile(self, code, srcname, extra_args=None):
if extra_args is None:
@ -624,18 +624,23 @@ int main () {{ {1}; }}'''
os.remove(srcname)
return p
def compiles(self, code, env, extra_args=None):
def compiles(self, code, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
if isinstance(extra_args, str):
extra_args = [extra_args]
if dependencies is None:
dependencies = []
elif not isinstance(dependencies, list):
dependencies = [dependencies]
suflen = len(self.default_suffix)
(fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix)
os.close(fd)
with open(srcname, 'w') as ofile:
ofile.write(code)
cargs = [a for d in dependencies for a in d.get_compile_args()]
# Convert flags to the native type of the selected compiler
args = self.unix_link_flags_to_native(extra_args)
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)
# We only want to compile; not link
@ -652,19 +657,25 @@ int main () {{ {1}; }}'''
pass
return p.returncode == 0
def links(self, code, env, extra_args=None):
def links(self, code, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
elif isinstance(extra_args, str):
extra_args = [extra_args]
if dependencies is None:
dependencies = []
elif not isinstance(dependencies, list):
dependencies = [dependencies]
(fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix)
os.close(fd)
(fd, dstname) = tempfile.mkstemp()
os.close(fd)
with open(srcname, 'w') as ofile:
ofile.write(code)
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(extra_args)
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_args/c_link_args/cpp_args/cpp_link_args/etc from the cross-info file (if needed)
@ -678,17 +689,23 @@ int main () {{ {1}; }}'''
pass
return p.returncode == 0
def run(self, code, env, extra_args=None):
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.')
(fd, srcname) = tempfile.mkstemp(suffix='.'+self.default_suffix)
os.close(fd)
with open(srcname, 'w') as ofile:
ofile.write(code)
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(extra_args)
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
@ -737,7 +754,7 @@ int main () {{ {1}; }}'''
pass
return RunResult(True, pe.returncode, so, se)
def cross_sizeof(self, element, prefix, env, extra_args=None):
def cross_sizeof(self, element, prefix, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
element_exists_templ = '''#include <stdio.h>
@ -751,11 +768,11 @@ int main(int argc, char **argv) {{
int temparray[%d-sizeof(%s)];
'''
args = extra_args + self.get_no_optimization_args()
if not self.compiles(element_exists_templ.format(prefix, element), env, args):
if not self.compiles(element_exists_templ.format(prefix, element), env, args, dependencies):
return -1
for i in range(1, 1024):
code = templ % (prefix, i, element)
if self.compiles(code, env, args):
if self.compiles(code, env, args, dependencies):
if self.id == 'msvc':
# MSVC refuses to construct an array of zero size, so
# the test only succeeds when i is sizeof(element) + 1
@ -763,11 +780,11 @@ int temparray[%d-sizeof(%s)];
return i
raise EnvironmentException('Cross checking sizeof overflowed.')
def sizeof(self, element, prefix, env, extra_args=None):
def sizeof(self, element, prefix, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
if self.is_cross:
return self.cross_sizeof(element, prefix, env, extra_args)
return self.cross_sizeof(element, prefix, env, extra_args, dependencies)
templ = '''#include<stdio.h>
%s
@ -776,14 +793,14 @@ int main(int argc, char **argv) {
return 0;
};
'''
res = self.run(templ % (prefix, element), env, extra_args)
res = self.run(templ % (prefix, element), env, extra_args, dependencies)
if not res.compiled:
return -1
if res.returncode != 0:
raise EnvironmentException('Could not run sizeof test binary.')
return int(res.stdout)
def cross_alignment(self, typename, env, extra_args=None):
def cross_alignment(self, typename, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
type_exists_templ = '''#include <stdio.h>
@ -800,11 +817,11 @@ struct tmp {
int testarray[%d-offsetof(struct tmp, target)];
'''
args = extra_args + self.get_no_optimization_args()
if not self.compiles(type_exists_templ.format(typename), env, args):
if not self.compiles(type_exists_templ.format(typename), env, args, dependencies):
return -1
for i in range(1, 1024):
code = templ % (typename, i)
if self.compiles(code, env, args):
if self.compiles(code, env, args, dependencies):
if self.id == 'msvc':
# MSVC refuses to construct an array of zero size, so
# the test only succeeds when i is sizeof(element) + 1
@ -812,11 +829,11 @@ int testarray[%d-offsetof(struct tmp, target)];
return i
raise EnvironmentException('Cross checking offsetof overflowed.')
def alignment(self, typename, env, extra_args=None):
def alignment(self, typename, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
if self.is_cross:
return self.cross_alignment(typename, env, extra_args)
return self.cross_alignment(typename, env, extra_args, dependencies)
templ = '''#include<stdio.h>
#include<stddef.h>
@ -830,7 +847,7 @@ int main(int argc, char **argv) {
return 0;
}
'''
res = self.run(templ % typename, env, extra_args)
res = self.run(templ % typename, env, extra_args, dependencies)
if not res.compiled:
raise EnvironmentException('Could not compile alignment test.')
if res.returncode != 0:
@ -840,7 +857,7 @@ 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):
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
@ -897,7 +914,7 @@ 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):
if self.links(templ.format(prefix, funcname), env, extra_args, dependencies):
return True
# Add -O0 to ensure that the symbol isn't optimized away by the compiler
args = extra_args + self.get_no_optimization_args()
@ -906,15 +923,15 @@ int main(int argc, char **argv) {
# 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):
if self.links(header_templ.format(prefix, funcname), env, 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
# special functions that ignore all includes and defines, so we just
# directly try to link via main().
return self.links('int main() {{ {0}; }}'.format('__builtin_' + funcname), env, args)
return self.links('int main() {{ {0}; }}'.format('__builtin_' + funcname), env, args, dependencies)
def has_members(self, typename, membernames, prefix, env, extra_args=None):
def has_members(self, typename, membernames, prefix, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
templ = '''{0}
@ -928,15 +945,15 @@ void bar() {{
for m in membernames:
members += 'foo.{};\n'.format(m)
code = templ.format(prefix, typename, 'foo', members)
return self.compiles(code, env, extra_args)
return self.compiles(code, env, extra_args, dependencies)
def has_type(self, typename, prefix, env, extra_args):
def has_type(self, typename, prefix, env, extra_args, dependencies=None):
templ = '''%s
void bar() {
sizeof(%s);
};
'''
return self.compiles(templ % (prefix, typename), env, extra_args)
return self.compiles(templ % (prefix, typename), env, extra_args, dependencies)
def find_library(self, libname, env, extra_dirs):
# First try if we can just add the library as -l.

@ -666,6 +666,25 @@ class CompilerHolder(InterpreterObject):
args += mesonlib.stringlistify(kwargs.get('args', []))
return args
def determine_dependencies(self, kwargs, allowed_dep_types=None):
deps = kwargs.get('dependencies', None)
if allowed_dep_types is None:
allowed_dep_types = (dependencies.Dependency, dependencies.ExternalLibrary)
if deps is not None:
if not isinstance(deps, list):
deps = [deps]
final_deps = []
for d in deps:
try:
d = d.held_object
except Exception:
pass
if not isinstance(d, allowed_dep_types):
raise InterpreterException('Dependencies must be external deps')
final_deps.append(d)
deps = final_deps
return deps
def alignment_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('Alignment method takes exactly one positional argument.')
@ -685,7 +704,8 @@ class CompilerHolder(InterpreterObject):
if not isinstance(testname, str):
raise InterpreterException('Testname argument must be a string.')
extra_args = self.determine_args(kwargs)
result = self.compiler.run(code, self.environment, extra_args)
deps = self.determine_dependencies(kwargs)
result = self.compiler.run(code, self.environment, extra_args, deps)
if len(testname) > 0:
if not result.compiled:
h = mlog.red('DID NOT COMPILE')
@ -715,8 +735,9 @@ class CompilerHolder(InterpreterObject):
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_member must be a string.')
extra_args = self.determine_args(kwargs)
deps = self.determine_dependencies(kwargs, allowed_dep_types=(dependencies.Dependency,))
had = self.compiler.has_members(typename, [membername], prefix,
self.environment, extra_args)
self.environment, extra_args, deps)
if had:
hadtxt = mlog.green('YES')
else:
@ -733,8 +754,9 @@ class CompilerHolder(InterpreterObject):
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_members must be a string.')
extra_args = self.determine_args(kwargs)
deps = self.determine_dependencies(kwargs, allowed_dep_types=(dependencies.Dependency,))
had = self.compiler.has_members(typename, membernames, prefix,
self.environment, extra_args)
self.environment, extra_args, deps)
if had:
hadtxt = mlog.green('YES')
else:
@ -753,7 +775,8 @@ class CompilerHolder(InterpreterObject):
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_function must be a string.')
extra_args = self.determine_args(kwargs)
had = self.compiler.has_function(funcname, prefix, self.environment, extra_args)
deps = self.determine_dependencies(kwargs)
had = self.compiler.has_function(funcname, prefix, self.environment, extra_args, deps)
if had:
hadtxt = mlog.green('YES')
else:
@ -770,7 +793,8 @@ class CompilerHolder(InterpreterObject):
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_type must be a string.')
extra_args = self.determine_args(kwargs)
had = self.compiler.has_type(typename, prefix, self.environment, extra_args)
deps = self.determine_dependencies(kwargs)
had = self.compiler.has_type(typename, prefix, self.environment, extra_args, deps)
if had:
hadtxt = mlog.green('YES')
else:
@ -787,7 +811,8 @@ class CompilerHolder(InterpreterObject):
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of sizeof must be a string.')
extra_args = self.determine_args(kwargs)
esize = self.compiler.sizeof(element, prefix, self.environment, extra_args)
deps = self.determine_dependencies(kwargs, allowed_dep_types=(dependencies.Dependency,))
esize = self.compiler.sizeof(element, prefix, self.environment, extra_args, deps)
mlog.log('Checking for size of "%s": %d' % (element, esize))
return esize
@ -800,7 +825,8 @@ class CompilerHolder(InterpreterObject):
if not isinstance(testname, str):
raise InterpreterException('Testname argument must be a string.')
extra_args = self.determine_args(kwargs)
result = self.compiler.compiles(code, self.environment, extra_args)
deps = self.determine_dependencies(kwargs, allowed_dep_types=(dependencies.Dependency,))
result = self.compiler.compiles(code, self.environment, extra_args, deps)
if len(testname) > 0:
if result:
h = mlog.green('YES')
@ -818,7 +844,8 @@ class CompilerHolder(InterpreterObject):
if not isinstance(testname, str):
raise InterpreterException('Testname argument must be a string.')
extra_args = self.determine_args(kwargs)
result = self.compiler.links(code, self.environment, extra_args)
deps = self.determine_dependencies(kwargs)
result = self.compiler.links(code, self.environment, extra_args, deps)
if len(testname) > 0:
if result:
h = mlog.green('YES')
@ -833,7 +860,8 @@ class CompilerHolder(InterpreterObject):
check_stringlist(args)
string = args[0]
extra_args = self.determine_args(kwargs)
haz = self.compiler.has_header(string, self.environment, extra_args)
deps = self.determine_dependencies(kwargs, allowed_dep_types=(dependencies.Dependency,))
haz = self.compiler.has_header(string, self.environment, extra_args, deps)
if haz:
h = mlog.green('YES')
else:
@ -851,7 +879,8 @@ class CompilerHolder(InterpreterObject):
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, self.environment, extra_args)
deps = self.determine_dependencies(kwargs, allowed_dep_types=(dependencies.Dependency,))
haz = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment, extra_args, deps)
if haz:
h = mlog.green('YES')
else:
@ -860,6 +889,7 @@ class CompilerHolder(InterpreterObject):
return haz
def find_library_method(self, args, kwargs):
# TODO add dependencies support?
if len(args) != 1:
raise InterpreterException('find_library method takes one argument.')
libname = args[0]

@ -0,0 +1,31 @@
project('compiler checks with dependencies', 'c')
cc = meson.get_compiler('c')
glib = dependency ('glib-2.0')
if glib.found()
assert (cc.has_header('glib.h', dependencies : glib), 'glib.h not found')
assert (cc.has_type('gint32', prefix : '#include <glib.h>', dependencies : glib), 'gint32 not found')
assert (cc.has_function('g_print', dependencies : glib), 'g_print not found')
assert (cc.has_member('GError', 'message', prefix : '#include <glib.h>', dependencies : glib), 'GError::message not found')
assert (cc.has_header_symbol('glib.h', 'gint32', dependencies : glib), 'gint32 symbol not found')
linkcode = '''#include <glib.h>
int main (int argc, char *argv[]) {
GError *error = g_error_new_literal (0, 0, NULL);
return error == NULL;
}
'''
assert (cc.links(linkcode, dependencies : glib, name : 'Test link against glib'), 'Linking test against glib failed')
endif
zlib = cc.find_library ('z')
if zlib.found()
linkcode = '''#include<zlib.h>
int main(int argc, char *argv[]) {
void *ptr = (void*)(deflate);
return ptr == 0;
}
'''
assert (cc.has_function('deflate', prefix : '#include<zlib.h>', dependencies : zlib, name : 'Test for function in zlib'), 'has_function test failed.')
assert (cc.links(linkcode, dependencies : zlib, name : 'Test link against zlib'), 'Linking test failed against zlib.')
endif
Loading…
Cancel
Save