Repository: mesonbuild/meson

Issue:
OSError trying to find Qt dependency
**Describe the bug**
Trying to find the Qt5 dependency for VLC, meson produces a stacktrace instead of a proper error.

```
qmake5 found: YES (/usr/bin/qmake) 5.15.8
Traceback (most recent call last):
  File "/home/epirat/Projects/meson/mesonbuild/mesonmain.py", line 194, in run
    return options.run_func(options)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/msetup.py", line 356, in run
    app.generate()
  File "/home/epirat/Projects/meson/mesonbuild/msetup.py", line 179, in generate
    return self._generate(env, capture, vslite_ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/msetup.py", line 224, in _generate
    intr.run()
  File "/home/epirat/Projects/meson/mesonbuild/interpreter/interpreter.py", line 3014, in run
    super().run()
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 162, in run
    self.evaluate_codeblock(self.ast, start=1)
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 188, in evaluate_codeblock
    raise e
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 180, in evaluate_codeblock
    self.evaluate_statement(cur)
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 194, in evaluate_statement
    return self.function_call(cur)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 520, in function_call
    res = func(node, func_args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 276, in wrapper
    return f(*nargs, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 595, in wrapper
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreter/interpreter.py", line 2419, in func_subdir
    self.evaluate_codeblock(codeblock)
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 188, in evaluate_codeblock
    raise e
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 180, in evaluate_codeblock
    self.evaluate_statement(cur)
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 194, in evaluate_statement
    return self.function_call(cur)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 520, in function_call
    res = func(node, func_args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 276, in wrapper
    return f(*nargs, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 595, in wrapper
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreter/interpreter.py", line 2419, in func_subdir
    self.evaluate_codeblock(codeblock)
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 188, in evaluate_codeblock
    raise e
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 180, in evaluate_codeblock
    self.evaluate_statement(cur)
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 194, in evaluate_statement
    return self.function_call(cur)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 520, in function_call
    res = func(node, func_args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 276, in wrapper
    return f(*nargs, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 595, in wrapper
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreter/interpreter.py", line 2419, in func_subdir
    self.evaluate_codeblock(codeblock)
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 188, in evaluate_codeblock
    raise e
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 180, in evaluate_codeblock
    self.evaluate_statement(cur)
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 196, in evaluate_statement
    self.assignment(cur)
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 634, in assignment
    value = self.evaluate_statement(node.value)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 194, in evaluate_statement
    return self.function_call(cur)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/interpreterbase.py", line 520, in function_call
    res = func(node, func_args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 825, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 825, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 825, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  [Previous line repeated 4 more times]
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 108, in wrapped
    ret = f(*wrapped_args, **wrapped_kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 126, in wrapped
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 276, in wrapper
    return f(*nargs, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreterbase/decorators.py", line 595, in wrapper
    return f(*wrapped_args, **wrapped_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreter/interpreter.py", line 1791, in func_dependency
    d = df.lookup(kwargs)
        ^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreter/dependencyfallbacks.py", line 355, in lookup
    dep = func(kwargs, func_args, func_kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/interpreter/dependencyfallbacks.py", line 87, in _do_dependency
    dep = dependencies.find_external_dependency(name, self.environment, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/dependencies/detect.py", line 124, in find_external_dependency
    d = c()
        ^^^
  File "/home/epirat/Projects/meson/mesonbuild/dependencies/qt.py", line 327, in __init__
    priv_inc = self.get_private_includes(mincdir, module)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/dependencies/qt.py", line 402, in get_private_includes
    return _qt_get_private_includes(mod_inc_dir, module, self.version)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/epirat/Projects/meson/mesonbuild/dependencies/qt.py", line 57, in _qt_get_private_includes
    dirs = [filename for filename in os.listdir(mod_inc_dir)
                                     ^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '/usr/include/x86_64-linux-gnu/qt5/QtQml'

ERROR: Unhandled python OSError. This is probably not a Meson bug, but an issue with your build environment.
```

**To Reproduce**
Clone VLC git master and try to run `meson setup build` on Lubuntu 23.04

**Expected behavior**
It should either find or not find Qt5, or if there is something quite wrong with Qt on the machine, print a more helpful error that can be understood...

**system parameters**
* Native build
* Lubuntu 23.04
* Python 3.11.4
* Meson 1.2.99 (6b63c4161d06f7ca79e71246900df1c276a4c1d8)
* Ninja 1.11.1



Discussion:
Workaround:

Ensure the Qt modules (as needed) packages are installed:

```
libqt5svg5-dev
qtdeclarative5-dev
qtquickcontrols2-5-dev
```

Additionally I get some confusing messages about lrelease:

```
Program lrelease5 found: NO
Program lrelease-qt5 found: NO
Program lrelease found: NO found  but need: '== 5.15.8' (/usr/bin/lrelease)
```
> Additionally I get some confusing messages about lrelease:

That was fixed in #10623
> It should either find or not find Qt5, or if there is something quite wrong with Qt on the machine, print a more helpful error that can be understood...

This is a side effect of 2fc0a11062757c7f621b53d6ccfc158889c18544 I guess. Seems relatively niche -- the entire codepath only gets hit when one uses `private_headers: true`.
Ah indeed, we are using `private_headers` for `qquickwindow_p.h`.

Resolving patch:
diff --git a/mesonbuild/dependencies/qt.py b/mesonbuild/dependencies/qt.py
--- a/mesonbuild/dependencies/qt.py
+++ b/mesonbuild/dependencies/qt.py
@@ -53,7 +53,7 @@ def _qt_get_private_includes(mod_inc_dir: str, module: str, mod_version: str) ->
 
     private_dir = os.path.join(mod_inc_dir, mod_version)
     # fallback, let's try to find a directory with the latest version
-    if not os.path.exists(private_dir):
+    if os.path.isdir(mod_inc_dir) and not os.path.exists(private_dir):
         dirs = [filename for filename in os.listdir(mod_inc_dir)
                 if os.path.isdir(os.path.join(mod_inc_dir, filename))]
 


Test patch: