Repository: mesonbuild/meson

Issue:
llvm: AttributeError: 'LLVMDependencyCMake' object has no attribute 'found_modules'
**Describe the bug**
Somehow the dependency on LLVM does not work anymore. I'm pretty sure this was different on prior Meson versions but cannot track it down.

**To Reproduce**
This meson.build file triggers the error:
```
project('testa', 'cpp')
llvm = dependency('llvm')
```
Error:
```
The Meson build system
Version: 0.62.1
Source dir: /home/gerion/test
Build dir: /home/gerion/test/build
Build type: native build
Project name: testa
Project version: undefined
C++ compiler for the host machine: ccache c++ (gcc 10.2.1 "c++ (Debian 10.2.1-6) 10.2.1 20210110")
C++ linker for the host machine: c++ ld.bfd 2.35.2
Host machine cpu family: x86_64
Host machine cpu: x86_64
WARNING: The LLVM dependency was not found via CMake since both a C and C++ compiler are required.
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/mesonmain.py", line 153, in run
    return options.run_func(options)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/msetup.py", line 301, in run
    app.generate()
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/msetup.py", line 185, in generate
    self._generate(env)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/msetup.py", line 229, in _generate
    intr.run()
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreter/interpreter.py", line 2700, in run
    super().run()
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/interpreterbase.py", line 149, in run
    self.evaluate_codeblock(self.ast, start=1)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/interpreterbase.py", line 174, in evaluate_codeblock
    raise e
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/interpreterbase.py", line 167, in evaluate_codeblock
    self.evaluate_statement(cur)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/interpreterbase.py", line 182, in evaluate_statement
    self.assignment(cur)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/interpreterbase.py", line 567, in assignment
    value = self.evaluate_statement(node.value)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/interpreterbase.py", line 180, in evaluate_statement
    return self.function_call(cur)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/interpreterbase.py", line 455, in function_call
    res = func(node, func_args, kwargs)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/decorators.py", line 768, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/decorators.py", line 768, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/decorators.py", line 768, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
  [Previous line repeated 5 more times]
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/decorators.py", line 109, in wrapped
    ret = f(*wrapped_args, **wrapped_kwargs)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/decorators.py", line 127, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreterbase/decorators.py", line 277, in wrapper
    return f(*nargs, **wrapped_kwargs)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreter/interpreter.py", line 1620, in func_dependency
    d = df.lookup(kwargs)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreter/dependencyfallbacks.py", line 352, in lookup
    dep = func(kwargs, func_args, func_kwargs)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/interpreter/dependencyfallbacks.py", line 93, in _do_dependency
    dep = dependencies.find_external_dependency(name, self.environment, kwargs)
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/dependencies/detect.py", line 120, in find_external_dependency
    details = d.log_details()
  File "/usr/local/lib/python3.9/dist-packages/mesonbuild/dependencies/cmake.py", line 621, in log_details
    modules = [self._original_module_name(x) for x in self.found_modules]
AttributeError: 'LLVMDependencyCMake' object has no attribute 'found_modules'

meson.build:2:0: ERROR: Unhandled python exception
```

**Expected behavior**
LLVM should be found.

**Additional information**
Changing the project language to `c` leads to the same error. Changing the project language to `c` _and_ `cpp` fixes the error:

meson.build
```
project('testa', 'c', 'cpp')
llvm = dependency('llvm')
```
Output:
```
The Meson build system
Version: 0.62.1
Source dir: /home/gerion/test
Build dir: /home/gerion/test/build
Build type: native build
Project name: testa
Project version: undefined
C compiler for the host machine: ccache cc (gcc 10.2.1 "cc (Debian 10.2.1-6) 10.2.1 20210110")
C linker for the host machine: cc ld.bfd 2.35.2
C++ compiler for the host machine: ccache c++ (gcc 10.2.1 "c++ (Debian 10.2.1-6) 10.2.1 20210110")
C++ linker for the host machine: c++ ld.bfd 2.35.2
Host machine cpu family: x86_64
Host machine cpu: x86_64
Found CMake: /usr/bin/cmake (3.18.4)
WARNING: Ignoring LLVM CMake dependency because dynamic was requested
llvm-config found: YES (/usr/bin/llvm-config-13) 13.0.1
Run-time dependency LLVM found: YES 13.0.1
Build targets in project: 0

Found ninja-1.10.1 at /usr/bin/ninja
```

Setting config-tool as search method works, too:
```
project('testa', 'cpp')
llvm = dependency('llvm', method: 'config-tool')
```
Output:
```
The Meson build system
Version: 0.62.1
Source dir: /home/gerion/test
Build dir: /home/gerion/test/build
Build type: native build
Project name: testa
Project version: undefined
C++ compiler for the host machine: ccache c++ (gcc 10.2.1 "c++ (Debian 10.2.1-6) 10.2.1 20210110")
C++ linker for the host machine: c++ ld.bfd 2.35.2
Host machine cpu family: x86_64
Host machine cpu: x86_64
llvm-config found: YES (/usr/bin/llvm-config-13) 13.0.1
Run-time dependency LLVM found: YES 13.0.1
Build targets in project: 0

Found ninja-1.10.1 at /usr/bin/ninja
```

**system parameters**
* Is this a [cross build](https://mesonbuild.com/Cross-compilation.html) or just a plain native build (for the same computer)?: native
* what operating system (e.g. MacOS Catalina, Windows 10, CentOS 8.0, Ubuntu 18.04, etc.): Debian 11 (with llvm-13 and llvm-12 backports)
* what Python version are you using e.g. 3.8.0: 3.9.0
* what `meson --version`: 0.62.1



Discussion:
LLVM 14 (if I recall correctly) has a design bug where its cmake config files error out when cmake doesn't have a C compiler in addition to the CXX one. For this reason, Meson was "bugfixed" to warn you if you don't have one and register the cmake method as not-found. This is *supposed* to mean Meson tries other methods, but apparently it crashes before that could happen!

This is a bug and must be fixed, but either way you can force a particular method in meson.build by passing `method: 'config-tool'` as a kwarg (you can force pkgconfig or cmake etc. as well, this is most commonly used IME to force specific naming schemes where pkg-config has one name and cmake has a different one).

Edit: now I see your edit that you found the `method` kwarg.
@mensinda When trying to early abort before invoking `super().__init__(...)` we don't get all that nice setup work done to make the dependency instance be sane, for example `base.Dependency` makes log_tried() work by setting self.type_name, and `cmake.CMakeDependency`  makes log_details() work by setting self.found_modules.

This fixes it:

```diff
diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py
index 97d1633ab..d8f7d40cd 100644
--- a/mesonbuild/dependencies/dev.py
+++ b/mesonbuild/dependencies/dev.py
@@ -412,6 +412,8 @@ class LLVMDependencyCMake(CMakeDependency):
             compilers = env.coredata.compilers.host
         if not compilers or not all(x in compilers for x in ('c', 'cpp')):
             self.is_found = False
+            self.type_name = 'cmake'
+            self.found_modules = []
             mlog.warning('The LLVM dependency was not found via CMake since both a C and C++ compiler are required.')
             return
 
```

But I'm wondering if there is some better way to do it... not sure whether to PR this.

Either way I guess the failure case never got tested.

Resolving patch:
diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py
--- a/mesonbuild/dependencies/dev.py
+++ b/mesonbuild/dependencies/dev.py
@@ -30,7 +30,7 @@
 from ..compilers import AppleClangCCompiler, AppleClangCPPCompiler, detect_compiler_for
 from ..environment import get_llvm_tool_names
 from ..mesonlib import version_compare, stringlistify, extract_as_list
-from .base import DependencyException, DependencyMethods, strip_system_libdirs, SystemDependency
+from .base import DependencyException, DependencyMethods, strip_system_libdirs, SystemDependency, ExternalDependency, DependencyTypeName
 from .cmake import CMakeDependency
 from .configtool import ConfigToolDependency
 from .factory import DependencyFactory
@@ -411,7 +411,14 @@ def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any]) ->
         else:
             compilers = env.coredata.compilers.host
         if not compilers or not all(x in compilers for x in ('c', 'cpp')):
-            self.is_found = False
+            # Initialize basic variables
+            ExternalDependency.__init__(self, DependencyTypeName('cmake'), env, kwargs)
+
+            # Initialize CMake specific variables
+            self.found_modules: T.List[str] = []
+            self.name = name
+
+            # Warn and return
             mlog.warning('The LLVM dependency was not found via CMake since both a C and C++ compiler are required.')
             return
 


Test patch: