Repository: mesonbuild/meson

Issue:
add includedir or dependencies to has_header 
I need to check if `boost/numeric/odeint/external/eigen/eigen_algebra.hpp` is there.

Tried to debug:

``` cpp
#include<boost/numeric/odeint/external/eigen/eigen_algebra.hpp>
int someSymbolHereJustForFun;
```

/usr/include/boost/numeric/odeint/external/eigen/eigen_algebra.hpp:21:23: fatal error: Eigen/Dense: No such file or directory

I also need pass `pkg-config --cflags eigen3`



Discussion:

Resolving patch:
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -572,15 +572,15 @@ def sanity_check(self, work_dir, environment):
         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}
@@ -588,7 +588,7 @@ def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None):
 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:
@@ -608,18 +608,23 @@ def compile(self, code, srcname, extra_args=None):
         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
@@ -636,19 +641,25 @@ def compiles(self, code, env, extra_args=None):
             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)
@@ -662,17 +673,23 @@ def links(self, code, env, extra_args=None):
             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
@@ -721,7 +738,7 @@ def run(self, code, env, extra_args=None):
             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>
@@ -735,11 +752,11 @@ def cross_sizeof(self, element, prefix, env, extra_args=None):
 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
@@ -747,11 +764,11 @@ def cross_sizeof(self, element, prefix, env, extra_args=None):
                 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
 
@@ -760,14 +777,14 @@ def sizeof(self, element, prefix, env, extra_args=None):
     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>
@@ -784,11 +801,11 @@ def cross_alignment(self, typename, env, extra_args=None):
 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
@@ -796,11 +813,11 @@ def cross_alignment(self, typename, env, extra_args=None):
                 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>
 
@@ -814,7 +831,7 @@ def alignment(self, typename, env, extra_args=None):
   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:
@@ -824,7 +841,7 @@ def alignment(self, typename, env, extra_args=None):
             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
@@ -881,7 +898,7 @@ def has_function(self, funcname, prefix, env, extra_args=None):
                 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()
@@ -890,15 +907,15 @@ def has_function(self, funcname, prefix, env, extra_args=None):
         # 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}
@@ -912,15 +929,15 @@ def has_members(self, typename, membernames, prefix, env, extra_args=None):
         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.
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -623,6 +623,25 @@ def determine_args(self, kwargs):
         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.')
@@ -642,7 +661,8 @@ def run_method(self, args, kwargs):
         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')
@@ -672,8 +692,9 @@ def has_member_method(self, args, kwargs):
         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:
@@ -690,8 +711,9 @@ def has_members_method(self, args, kwargs):
         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:
@@ -710,7 +732,8 @@ def has_function_method(self, args, kwargs):
         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:
@@ -727,7 +750,8 @@ def has_type_method(self, args, kwargs):
         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:
@@ -744,7 +768,8 @@ def sizeof_method(self, args, kwargs):
         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
 
@@ -757,7 +782,8 @@ def compiles_method(self, args, kwargs):
         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')
@@ -775,7 +801,8 @@ def links_method(self, args, kwargs):
         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')
@@ -790,7 +817,8 @@ def has_header_method(self, args, kwargs):
         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:
@@ -808,7 +836,8 @@ def has_header_symbol_method(self, args, kwargs):
         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:
@@ -817,6 +846,7 @@ def has_header_symbol_method(self, args, kwargs):
         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]


Test patch:
diff --git a/test cases/linuxlike/9 compiler checks with dependencies/meson.build b/test cases/linuxlike/9 compiler checks with dependencies/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/linuxlike/9 compiler checks with dependencies/meson.build	
@@ -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