Repository: mesonbuild/meson

Issue:
hdf5.py fails with exception if the project is not set up for C
**Describe the bug**
what it says in the title. If you have, e.g. a c++ project, trying to call
```dependency('hdf5', language: 'cpp', method: 'config-tool')```
will cause an exception
```
<...>
  File "/usr/lib/python3.11/site-packages/mesonbuild/dependencies/hdf5.py", line 119, in __init__
    compiler = environment.coredata.compilers[for_machine][language]
               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
KeyError: 'c'
```
hdf5.py always tries to configure ```c``` language, which is a problem whan it's not set in the project

**To Reproduce**
```
project('test', 'cpp')
dependency('hdf5', language: 'cpp', method: 'config-tool')
```

**Expected behavior**
no exception. If C is always needed for HDF5 - make a special error for this case.

**system parameters**
meson 1.0.1
ninja 1.11.1
fedora 38



Discussion:
That shouldn't be happenning, if you're setting language it should be using c++ to try to find hdf5, that means somewhere along the line the `langauge` keyword is getting dropped between the interpreter and the actual dependency

I had initially thought this is a good first issue because it was trying to use a language it should have but that language wasn't present. That bug is there, and if someone wanted a good first issue we should raise a nice error if someone tries to add this dependency without the requested language.
Okay, yeah. It seems that C is required, it's added as a sub-dependency.

We could go a couple of ways here. We can just nicely error out in this case which would be easy. Or, we could add a C compiler for the target machine for you and go from there. The second would be more user friendly, but a lot more work to get right (especially when cross compiling).

If someone wanted to fix this, you'd basically want to add a check to make sure that both 'c' and language if are in the compilers for the current machine, and otherwise set the dependency to not-found and return.

It would probably be nice in the case where language is not 'c' and you don't have a C compiler to give a nice message about why
Why do we even care about the compiler there? We only need the list of libraries. Moreover, the expected env for the custom C linker is ```HDF5_CLINKER``` not ```HDF5_CCLINKER``` (see https://github.com/HDFGroup/hdf5/blob/develop/bin/h5cc.in#L97), so it never even worked.
the `config-tool` method wraps around the `h5cc`, `h5c++`, and `h5fc` programs, which themselves wrap a compiler invocation. For them to work we need an appropriate compiler. if you use the `pkg-config` method then you don't have this problem, it's just a text file with a list of libraries, include directories, and compiler flags to add
That's not how I understood the code. The tools are called with ```-show``` (see https://github.com/mesonbuild/meson/blob/master/mesonbuild/dependencies/hdf5.py#L131) which means that we are interested only in the list of flags and libs. We extract the list, store it in our internal structures, and forget about these tools. The actual compilation command has nothing to do with these tools anymore.

To use the pkg-config method you need the ```.pc``` file that HDF5 upstream doesn't provide.
I just removed the sub-dependency on C libs, and the project built and run without issues. All the libs were linked. The configuration tool from HDF5 links to the C libs directly (see https://github.com/HDFGroup/hdf5/blob/develop/c%2B%2B/src/h5c%2B%2B.in#L315) without the need for sub-dependencies. So where did the information that C is needed as sub-dependency even come from? I couldn't find anything about it on the HDF5 official sources.

Resolving patch:
diff --git a/mesonbuild/dependencies/hdf5.py b/mesonbuild/dependencies/hdf5.py
--- a/mesonbuild/dependencies/hdf5.py
+++ b/mesonbuild/dependencies/hdf5.py
@@ -98,12 +98,15 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.
 
         if language == 'c':
             cenv = 'CC'
+            lenv = 'C'
             tools = ['h5cc', 'h5pcc']
         elif language == 'cpp':
             cenv = 'CXX'
+            lenv = 'CXX'
             tools = ['h5c++', 'h5pc++']
         elif language == 'fortran':
             cenv = 'FC'
+            lenv = 'F'
             tools = ['h5fc', 'h5pfc']
         else:
             raise DependencyException('How did you get here?')
@@ -120,11 +123,11 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.
         compiler = environment.coredata.compilers[for_machine][language]
         try:
             os.environ[f'HDF5_{cenv}'] = join_args(compiler.get_exelist())
-            os.environ[f'HDF5_{cenv}LINKER'] = join_args(compiler.get_linker_exelist())
+            os.environ[f'HDF5_{lenv}LINKER'] = join_args(compiler.get_linker_exelist())
             super().__init__(name, environment, nkwargs, language)
         finally:
             del os.environ[f'HDF5_{cenv}']
-            del os.environ[f'HDF5_{cenv}LINKER']
+            del os.environ[f'HDF5_{lenv}LINKER']
         if not self.is_found:
             return
 
@@ -140,13 +143,6 @@ def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.
             elif Path(arg).is_file():
                 self.link_args.append(arg)
 
-        # If the language is not C we need to add C as a subdependency
-        if language != 'c':
-            nkwargs = kwargs.copy()
-            nkwargs['language'] = 'c'
-            # I'm being too clever for mypy and pylint
-            self.is_found = self._add_sub_dependency(hdf5_factory(environment, for_machine, nkwargs))  # pylint: disable=no-value-for-parameter
-
     def _sanitize_version(self, ver: str) -> str:
         v = re.search(r'\s*HDF5 Version: (\d+\.\d+\.\d+)', ver)
         return v.group(1)


Test patch:
diff --git a/test cases/frameworks/25 hdf5/main.cpp b/test cases/frameworks/25 hdf5/main.cpp
--- a/test cases/frameworks/25 hdf5/main.cpp	
+++ b/test cases/frameworks/25 hdf5/main.cpp	
@@ -1,29 +1,19 @@
 #include <iostream>
-#include "hdf5.h"
+#include "H5Cpp.h"
 
 
 int main(void)
 {
-herr_t ier;
 unsigned maj, min, rel;
 
-ier = H5open();
-if (ier) {
-    std::cerr << "Unable to initialize HDF5: " << ier << std::endl;
+try {
+    H5::H5Library::open();
+    H5::H5Library::getLibVersion(maj, min, rel);
+    std::cout << "C++ HDF5 version " << maj << "." << min << "." << rel << std::endl;
+    H5::H5Library::close();
+    return EXIT_SUCCESS;
+} catch (H5::LibraryIException &e) {
+    std::cerr << "Exception caught from HDF5: " << e.getDetailMsg() << std::endl;
     return EXIT_FAILURE;
 }
-
-ier = H5get_libversion(&maj, &min, &rel);
-if (ier) {
-    std::cerr << "HDF5 did not initialize!" << std::endl;
-    return EXIT_FAILURE;
-}
-std::cout << "C++ HDF5 version " << maj << "." << min << "." << rel << std::endl;
-
-ier = H5close();
-if (ier) {
-    std::cerr << "Unable to close HDF5: " << ier << std::endl;
-    return EXIT_FAILURE;
-}
-return EXIT_SUCCESS;
 }