Repository: mesonbuild/meson

Issue:
CMake for dependencies can't use modules
CMake for finding dependencies could easily be further extended with off-the-shelf `Find<foo>.cmake` scripts. The only thing that needs to happen for that is that the invocation of `cmake` needs to point `-DCMAKE_MODULE_PATH=<dir>` at the directory containing these scripts.

A good place for this might be here:
https://github.com/mesonbuild/meson/blob/90c9b868b20b11bb089fc5e0c634d5ed76fea0cb/mesonbuild/dependencies/base.py#L1422

As a simple suggestion, just redefine all `CMAKE_*` environment variables as CMake variables:

```python
for key in env.keys():
  if key.startswith('CMAKE_'):
    args.append('-D%s=%s' % (key, env[key]))
```

That should do the trick (some testing required).

As an added benefit, you can influence CMake further with other `CMAKE_*` variables in the environment.


Discussion:
I don't know if depending on environment variables is a good idea for this use case. I would rather add a `cmake_options` or `module_path` key to the dependency function than reading environment variables. This way everything stays in the meson file and the CMake dependency backend doesn't depend on the user's environment.

As far as I can tell, this should be sufficient for off-the-shelf CMake scripts. Am I missing a use case?
Not that I can think of. And I'm fine with solving things differently. This is how I hacked it for me locally, is all.

Since I don't know the meson code base very well (yet), and I don't particularly want to get side tracked right now, I figured I'd post my hack and let you figure out how to do it well - otherwise this would've been a PR :)
Err, that closing was an accident - sorry.

Resolving patch:
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -27,13 +27,14 @@
 import platform
 import itertools
 import ctypes
+from typing import List
 from enum import Enum
 from pathlib import PurePath
 
 from .. import mlog
 from .. import mesonlib
 from ..compilers import clib_langs
-from ..environment import BinaryTable
+from ..environment import BinaryTable, Environment
 from ..mesonlib import MachineChoice, MesonException, OrderedSet, PerMachine
 from ..mesonlib import Popen_safe, version_compare_many, version_compare, listify
 
@@ -908,7 +909,7 @@ class CMakeDependency(ExternalDependency):
     def _gen_exception(self, msg):
         return DependencyException('Dependency {} not found: {}'.format(self.name, msg))
 
-    def __init__(self, name, environment, kwargs, language=None):
+    def __init__(self, name: str, environment: Environment, kwargs, language=None):
         super().__init__('cmake', environment, language, kwargs)
         self.name = name
         self.is_libtool = False
@@ -956,16 +957,25 @@ def __init__(self, name, environment, kwargs, language=None):
             return
 
         modules = kwargs.get('modules', [])
+        cm_path = kwargs.get('cmake_module_path', [])
+        cm_args = kwargs.get('cmake_args', [])
         if not isinstance(modules, list):
             modules = [modules]
-        self._detect_dep(name, modules)
+        if not isinstance(cm_path, list):
+            cm_path = [cm_path]
+        if not isinstance(cm_args, list):
+            cm_args = [cm_args]
+        cm_path = [x if os.path.isabs(x) else os.path.join(environment.get_source_dir(), x) for x in cm_path]
+        if cm_path:
+            cm_args += ['-DCMAKE_MODULE_PATH={}'.format(';'.join(cm_path))]
+        self._detect_dep(name, modules, cm_args)
 
     def __repr__(self):
         s = '<{0} {1}: {2} {3}>'
         return s.format(self.__class__.__name__, self.name, self.is_found,
                         self.version_reqs)
 
-    def _detect_dep(self, name, modules):
+    def _detect_dep(self, name: str, modules: List[str], args: List[str]):
         # Detect a dependency with CMake using the '--find-package' mode
         # and the trace output (stderr)
         #
@@ -981,7 +991,7 @@ def _detect_dep(self, name, modules):
             mlog.debug('Try CMake generator: {}'.format(i if len(i) > 0 else 'auto'))
 
             # Prepare options
-            cmake_opts = ['--trace-expand', '-DNAME={}'.format(name), '.']
+            cmake_opts = ['--trace-expand', '-DNAME={}'.format(name)] + args + ['.']
             if len(i) > 0:
                 cmake_opts = ['-G', i] + cmake_opts
 
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1926,6 +1926,7 @@ def get_cross_property_method(self, args, kwargs):
                                    'main',
                                    'method',
                                    'modules',
+                                   'cmake_module_path',
                                    'optional_modules',
                                    'native',
                                    'not_found_message',
@@ -1933,6 +1934,7 @@ def get_cross_property_method(self, args, kwargs):
                                    'static',
                                    'version',
                                    'private_headers',
+                                   'cmake_args',
                                    },
                     'declare_dependency': {'include_directories',
                                            'link_with',
@@ -2944,10 +2946,10 @@ def _handle_featurenew_dependencies(self, name):
         elif name == 'openmp':
             FeatureNew('OpenMP Dependency', '0.46.0').use(self.subproject)
 
+    @FeatureNewKwargs('dependency', '0.50.0', ['not_found_message', 'cmake_module_path', 'cmake_args'])
     @FeatureNewKwargs('dependency', '0.49.0', ['disabler'])
     @FeatureNewKwargs('dependency', '0.40.0', ['method'])
     @FeatureNewKwargs('dependency', '0.38.0', ['default_options'])
-    @FeatureNewKwargs('dependency', '0.50.0', ['not_found_message'])
     @disablerIfNotFound
     @permittedKwargs(permitted_kwargs['dependency'])
     def func_dependency(self, node, args, kwargs):


Test patch:
diff --git a/test cases/linuxlike/13 cmake dependency/cmake/FindSomethingLikeZLIB.cmake b/test cases/linuxlike/13 cmake dependency/cmake/FindSomethingLikeZLIB.cmake
new file mode 100644
--- /dev/null
+++ b/test cases/linuxlike/13 cmake dependency/cmake/FindSomethingLikeZLIB.cmake	
@@ -0,0 +1,9 @@
+find_package(ZLIB)
+
+if(ZLIB_FOUND OR ZLIB_Found)
+  set(SomethingLikeZLIB_FOUND        ON)
+  set(SomethingLikeZLIB_LIBRARIES    ${ZLIB_LIBRARY})
+  set(SomethingLikeZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR})
+else()
+  set(SomethingLikeZLIB_FOUND       OFF)
+endif()
diff --git a/test cases/linuxlike/13 cmake dependency/meson.build b/test cases/linuxlike/13 cmake dependency/meson.build
--- a/test cases/linuxlike/13 cmake dependency/meson.build	
+++ b/test cases/linuxlike/13 cmake dependency/meson.build	
@@ -36,6 +36,12 @@ depf2 = dependency('ZLIB', required : false, method : 'cmake', modules : 'dfggh:
 
 assert(depf2.found() == false, 'Invalid CMake targets should fail')
 
+# Try to find a dependency with a custom CMake module
+
+depm1 = dependency('SomethingLikeZLIB', required : true, method : 'cmake', cmake_module_path : 'cmake')
+depm2 = dependency('SomethingLikeZLIB', required : true, method : 'cmake', cmake_module_path : ['cmake'])
+depm3 = dependency('SomethingLikeZLIB', required : true, cmake_module_path : 'cmake')
+
 # Try to compile a test that takes a dep and an include_directories
 
 cc = meson.get_compiler('c')