Repository: mesonbuild/meson

Issue:
Unhandled python exception
**Describe the bug**

```
Did not find pkg-config by name 'pkg-config'
Found Pkg-config: NO
Did not find CMake 'cmake'
Found CMake: NO
Traceback (most recent call last):
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/dependencies/detect.py", line 124, in find_external_dependency
    d = c()
        ^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/dependencies/misc.py", line 602, in <lambda>
    cmake_class=lambda name, env, kwargs: CMakeDependency('OpenSSL', env, dict(kwargs, modules=['OpenSSL::Crypto', 'OpenSSL::SSL'])),
                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/dependencies/cmake.py", line 127, in __init__
    raise DependencyException(msg)
mesonbuild.dependencies.base.DependencyException: CMake binary for machine 1 not found. Giving up.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/mesonmain.py", line 194, in run
    return options.run_func(options)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/msetup.py", line 358, in run
    app.generate()
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/msetup.py", line 183, in generate
    return self._generate(env, capture, vslite_ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/msetup.py", line 228, in _generate
    intr.run()
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreter/interpreter.py", line 2997, in run
    super().run()
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/interpreterbase.py", line 164, in run
    self.evaluate_codeblock(self.ast, start=1)
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/interpreterbase.py", line 190, in evaluate_codeblock
    raise e
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/interpreterbase.py", line 182, in evaluate_codeblock
    self.evaluate_statement(cur)
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/interpreterbase.py", line 198, in evaluate_statement
    self.assignment(cur)
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/interpreterbase.py", line 635, in assignment
    value = self.evaluate_statement(node.value)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/interpreterbase.py", line 196, in evaluate_statement
    return self.function_call(cur)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/interpreterbase.py", line 521, in function_call
    res = func(node, func_args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/decorators.py", line 826, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/decorators.py", line 826, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/decorators.py", line 826, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  [Previous line repeated 4 more times]
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/decorators.py", line 109, in wrapped
    ret = f(*wrapped_args, **wrapped_kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/decorators.py", line 127, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/decorators.py", line 277, in wrapper
    return f(*nargs, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreterbase/decorators.py", line 596, in wrapper
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreter/interpreter.py", line 1774, in func_dependency
    d = df.lookup(kwargs)
        ^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreter/dependencyfallbacks.py", line 355, in lookup
    dep = func(kwargs, func_args, func_kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/interpreter/dependencyfallbacks.py", line 87, in _do_dependency
    dep = dependencies.find_external_dependency(name, self.environment, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:/MSYS2/mingw64/lib/python3.11/site-packages/mesonbuild/dependencies/detect.py", line 129, in find_external_dependency
    bettermsg = f'Dependency lookup for {name} with method {c.func.log_tried()!r} failed: {e}'
                                                            ^^^^^^^^^^^^^^^^
AttributeError: 'function' object has no attribute 'log_tried'

meson.build:15:21: ERROR: Unhandled python exception

    This is a Meson bug and should be reported!
```

**To Reproduce**

The problem pops up while checking for the openssl dependency. Neither pkg-config, nor cmake are available.

A meson.build as simple as this is enough, if pkg-config or cmake are not available.

```
project('bug', 'cpp')
openssl_dep = dependency('openssl')
```

**Expected behavior**

A proper error message would be nice.

**system parameters**
* Native build
* Windows, inside an MSYS2
* python 3.11.4
* meson 1.2.1
* ninja 1.11.1



Discussion:

Resolving patch:
diff --git a/mesonbuild/dependencies/cmake.py b/mesonbuild/dependencies/cmake.py
--- a/mesonbuild/dependencies/cmake.py
+++ b/mesonbuild/dependencies/cmake.py
@@ -652,3 +652,19 @@ def get_variable(self, *, cmake: T.Optional[str] = None, pkgconfig: T.Optional[s
         if default_value is not None:
             return default_value
         raise DependencyException(f'Could not get cmake variable and no default provided for {self!r}')
+
+
+class CMakeDependencyFactory:
+
+    def __init__(self, name: T.Optional[str] = None, modules: T.Optional[T.List[str]] = None):
+        self.name = name
+        self.modules = modules
+
+    def __call__(self, name: str, env: Environment, kwargs: T.Dict[str, T.Any], language: T.Optional[str] = None, force_use_global_compilers: bool = False) -> CMakeDependency:
+        if self.modules:
+            kwargs['modules'] = self.modules
+        return CMakeDependency(self.name or name, env, kwargs, language, force_use_global_compilers)
+
+    @staticmethod
+    def log_tried() -> str:
+        return CMakeDependency.log_tried()
diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py
--- a/mesonbuild/dependencies/misc.py
+++ b/mesonbuild/dependencies/misc.py
@@ -23,7 +23,7 @@
 from .. import mlog
 from .base import DependencyException, DependencyMethods
 from .base import BuiltinDependency, SystemDependency
-from .cmake import CMakeDependency
+from .cmake import CMakeDependency, CMakeDependencyFactory
 from .configtool import ConfigToolDependency
 from .detect import packages
 from .factory import DependencyFactory, factory_methods
@@ -599,19 +599,19 @@ def shaderc_factory(env: 'Environment',
     'openssl',
     [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM, DependencyMethods.CMAKE],
     system_class=OpensslSystemDependency,
-    cmake_class=lambda name, env, kwargs: CMakeDependency('OpenSSL', env, dict(kwargs, modules=['OpenSSL::Crypto', 'OpenSSL::SSL'])),
+    cmake_class=CMakeDependencyFactory('OpenSSL', modules=['OpenSSL::Crypto', 'OpenSSL::SSL']),
 )
 
 packages['libcrypto'] = libcrypto_factory = DependencyFactory(
     'libcrypto',
     [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM, DependencyMethods.CMAKE],
     system_class=OpensslSystemDependency,
-    cmake_class=lambda name, env, kwargs: CMakeDependency('OpenSSL', env, dict(kwargs, modules=['OpenSSL::Crypto'])),
+    cmake_class=CMakeDependencyFactory('OpenSSL', modules=['OpenSSL::Crypto']),
 )
 
 packages['libssl'] = libssl_factory = DependencyFactory(
     'libssl',
     [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM, DependencyMethods.CMAKE],
     system_class=OpensslSystemDependency,
-    cmake_class=lambda name, env, kwargs: CMakeDependency('OpenSSL', env, dict(kwargs, modules=['OpenSSL::SSL'])),
+    cmake_class=CMakeDependencyFactory('OpenSSL', modules=['OpenSSL::SSL']),
 )
diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py
--- a/unittests/platformagnostictests.py
+++ b/unittests/platformagnostictests.py
@@ -271,3 +271,10 @@ def test_meson_package_cache_dir(self):
         builddir = os.path.join(srcdir, '_build')
         self.change_builddir(builddir)
         self.init(srcdir, override_envvars={'MESON_PACKAGE_CACHE_DIR': os.path.join(srcdir, 'cache_dir')})
+
+    def test_cmake_openssl_not_found_bug(self):
+        """Issue #12098"""
+        testdir = os.path.join(self.unit_test_dir, '117 openssl cmake bug')
+        self.meson_native_files.append(os.path.join(testdir, 'nativefile.ini'))
+        out = self.init(testdir, allow_fail=True)
+        self.assertNotIn('Unhandled python exception', out)


Test patch:
diff --git a/test cases/unit/117 openssl cmake bug/meson.build b/test cases/unit/117 openssl cmake bug/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/unit/117 openssl cmake bug/meson.build	
@@ -0,0 +1,5 @@
+project('bug', 'cpp')
+
+# When cmake is not available,
+# this triggers the bug described in #12098
+openssl_dep = dependency('openssl')
diff --git a/test cases/unit/117 openssl cmake bug/nativefile.ini b/test cases/unit/117 openssl cmake bug/nativefile.ini
new file mode 100644
--- /dev/null
+++ b/test cases/unit/117 openssl cmake bug/nativefile.ini	
@@ -0,0 +1,7 @@
+[binaries]
+
+cmake = '/path/to/nothing'
+
+[built-in options]
+
+pkg_config_path = ''
\ No newline at end of file