Repository: spack/spack

Issue:
HDF5 CMake Package support
I do not see the hdf5-config.cmake file in any of my hdf5 installs.   Can we please get proper CMake installs of this package?


Discussion:
@lrknox is the maintainer of our `hdf5` package, maybe he can comment. Otherwise, PRs are always welcome! Basically, you would need to switch the base class from `AutotoolsPackage` to `CMakePackage` and replace `configure_args` with `cmake_args`. If you want to take a stab at this, I can help you out.
Related: https://github.com/spack/spack/issues/15896 . HDF5 is one of those packages that has changed their build system from autotools to cmake over the lifetime of the spack recipe.

Side note: if you're interested in the modern cmake targets provided by the CMake-installed HDF5, as of CMake 3.19  you will be able to get the targets with the FindHDF5  script (see https://gitlab.kitware.com/cmake/cmake/-/merge_requests/5129). That script can be backported to older versions of cmake if you need it inside the context of a downstream cmake project.
@sethrj I was thinking the same thing, but it looks like the oldest version of HDF5 in Spack (1.8.10) actually supports CMake, so there's no reason we can't switch.
We will lose Libtool files if we switch to CMake. I mean those `*.la` files in the `prefix/lib` that are implicitly used when we build dependent Libtool-based packages, e.g. NetCDF. It will also affect cases when we build a Libtool-based package outside of Spack and want to use Spack's installation of HDF5. I'm not saying that it will break things. It just might.
@skosukhin would that problem be solved if we also switch NetCDF to CMake? At some point in the distant future we are planning to add Windows support, in which case CMake will be necessary.
I just wanted to highlight a possible side effect. It shouldn't stop anybody from switching to CMake. If we get problems with NetCDF, we will find a way to solve them.

Resolving patch:
diff --git a/var/spack/repos/builtin/packages/hdf5/package.py b/var/spack/repos/builtin/packages/hdf5/package.py
--- a/var/spack/repos/builtin/packages/hdf5/package.py
+++ b/var/spack/repos/builtin/packages/hdf5/package.py
@@ -6,10 +6,8 @@
 import shutil
 import sys
 
-from spack.util.environment import is_system_path
 
-
-class Hdf5(AutotoolsPackage):
+class Hdf5(CMakePackage):
     """HDF5 is a data model, library, and file format for storing and managing
     data. It supports an unlimited variety of datatypes, and is designed for
     flexible and efficient I/O and for high volume and complex data.
@@ -19,20 +17,18 @@ class Hdf5(AutotoolsPackage):
     url      = "https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-1.10/hdf5-1.10.7/src/hdf5-1.10.7.tar.gz"
     list_url = "https://support.hdfgroup.org/ftp/HDF5/releases"
     list_depth = 3
-    git      = "https://bitbucket.hdfgroup.org/scm/hdffv/hdf5.git"
-    maintainers = ['lrknox']
+    git      = "https://github.com/HDFGroup/hdf5.git"
+    maintainers = ['lrknox', 'brtnfld', 'byrnHDF', 'ChristopherHogan', 'epourmal',
+                   'gheber', 'hyoklee', 'lkurz', 'soumagne']
 
     test_requires_compiler = True
 
-    # We rely on the *.la files to be removed and, therefore, do not try to make
-    # sure that they are correct. The following is a precaution against someone
-    # blindly changing the value to True, either here or in the baseclass.
-    install_libtool_archives = False
-
     version('develop', branch='develop')
+    version('develop-1.12', branch='hdf5_1_12')
+    version('develop-1.10', branch='hdf5_1_10')
+    version('develop-1.8', branch='hdf5_1_8')
 
     version('1.12.0', sha256='a62dcb276658cb78e6795dd29bf926ed7a9bc4edf6e77025cd2c689a8f97c17a')
-
     # HDF5 1.12 broke API compatibility, so we currently prefer the latest
     # 1.10 release.  packages that want later versions of HDF5 should specify,
     # e.g., depends_on("hdf5@1.12:") to get 1.12 or higher.
@@ -58,8 +54,6 @@ class Hdf5(AutotoolsPackage):
     version('1.8.12', sha256='b5cccea850096962b5fd9e96f22c4f47d2379224bb41130d9bc038bb6c37dfcb')
     version('1.8.10', sha256='4813b79c5fb8701a625b9924b8203bc7154a77f9b826ad4e034144b4056a160a')
 
-    variant('debug', default=False,
-            description='Builds a debug version of the library')
     variant('shared', default=True,
             description='Builds a shared version of the library')
 
@@ -69,32 +63,39 @@ class Hdf5(AutotoolsPackage):
     variant('java', default=False, description='Enable Java support')
     variant('threadsafe', default=False,
             description='Enable thread-safe capabilities')
-
+    variant('tools', default=True, description='Enable building tools')
     variant('mpi', default=True, description='Enable MPI support')
     variant('szip', default=False, description='Enable szip support')
     variant('pic', default=True,
             description='Produce position-independent code (for shared libs)')
-    # Build HDF5 with API compaitibility.
-    variant('api', default='none', description='choose api compatibility', values=('none', 'v114', 'v112', 'v110', 'v18', 'v16'), multi=False)
+    # Build HDF5 with API compatibility.
+    variant('api', default='default', description='Choose api compatibility for earlier version', values=('default', 'v114', 'v112', 'v110', 'v18', 'v16'), multi=False)
 
     conflicts('api=v114', when='@1.6:1.12.99', msg='v114 is not compatible with this release')
     conflicts('api=v112', when='@1.6:1.10.99', msg='v112 is not compatible with this release')
     conflicts('api=v110', when='@1.6:1.8.99', msg='v110 is not compatible with this release')
     conflicts('api=v18', when='@1.6:1.6.99', msg='v18 is not compatible with this release')
 
-    depends_on('autoconf', type='build', when='@develop')
-    depends_on('automake', type='build', when='@develop')
-    depends_on('libtool',  type='build', when='@develop')
-    depends_on('m4',       type='build', when='@develop')
+    depends_on('cmake@3.12:')
 
     depends_on('mpi', when='+mpi')
-    depends_on('java', when='+java')
+    depends_on('java', type=('build', 'run'), when='+java')
     # numactl does not currently build on darwin
     if sys.platform != 'darwin':
         depends_on('numactl', when='+mpi+fortran')
     depends_on('szip', when='+szip')
     depends_on('zlib@1.1.2:')
 
+    # The Java wrappers and associated libhdf5_java library
+    # were first available in 1.10
+    conflicts('+java', when='@:1.9')
+    # The Java wrappers cannot be built without shared libs.
+    conflicts('+java', when='~shared')
+
+    # Earlier versions of HDF5 will not correctly find szip without the patches
+    # in the <version>_cmake.patch files
+    conflicts('+szip', when='@:1.8.19,1.9.0:1.10.5')
+
     # There are several officially unsupported combinations of the features:
     # 1. Thread safety is not guaranteed via high-level C-API but in some cases
     #    it works.
@@ -138,7 +139,7 @@ class Hdf5(AutotoolsPackage):
 
     # Disable MPI C++ interface when C++ is disabled, otherwise downstream
     # libraries fail to link; see https://github.com/spack/spack/issues/12586
-    patch('h5public-skip-mpicxx.patch', when='@:1.8.21,1.10.0:1.10.5+mpi~cxx',
+    patch('h5public-skip-mpicxx.patch', when='@1.8.10:1.8.21,1.10.0:1.10.5+mpi~cxx',
           sha256='b61e2f058964ad85be6ee5ecea10080bf79e73f83ff88d1fa4b602d00209da9c')
 
     # Fixes BOZ literal constant error when compiled with GCC 10.
@@ -151,6 +152,15 @@ class Hdf5(AutotoolsPackage):
     # based on Libtool 2.4.6.
     patch('nag.mpi.libtool.patch', when='@1.8.18:%nag+fortran+mpi+shared')
 
+    patch('1.12.0_cmake.patch', when='@1.12.0',
+          sha256='e5b3bc2eecb693e88ce084dfceb35fdce68a0749945173c4bff7cf29fa81de4c')
+    patch('1.10.7_cmake.patch', when='@1.10.6:1.10.7',
+          sha256='dd9491bbe833b13929cb14b52137f7f44a539b4bfc89fc31e6e50a36e3e1b171')
+    patch('1.8.22_cmake.patch', when='@1.8.22',
+          sha256='2ca847df0f4aa24e8fe9aa7f156d48aae505a0226ca8df5d3e9b21d8087035bd')
+    patch('1.8.21_cmake.patch', when='@1.8.21',
+          sha256='b6a39255b2cc4fdf5767969a3f0cf83fc278b1750221b55ad48352a6ee3e4071')
+
     # The argument 'buf_size' of the C function 'h5fget_file_image_c' is
     # declared as intent(in) though it is modified by the invocation. As a
     # result, aggressive compilers such as Fujitsu's may do a wrong
@@ -190,11 +200,6 @@ def flag_handler(self, name, flags):
 
         return (None, None, flags)
 
-    @when('@develop')
-    def autoreconf(self, spec, prefix):
-        autogen = Executable('./autogen.sh')
-        autogen()
-
     @property
     def libs(self):
         """HDF5 can be queried for the following parameters:
@@ -202,6 +207,7 @@ def libs(self):
         - "hl": high-level interface
         - "cxx": C++ APIs
         - "fortran": Fortran APIs
+        - "java": Java APIs
 
         :return: list of matching libraries
         """
@@ -213,11 +219,12 @@ def libs(self):
         # to the libraries needed
         query2libraries = {
             tuple(): ['libhdf5'],
-            ('cxx', 'fortran', 'hl'): [
+            ('cxx', 'fortran', 'hl', 'java'): [
                 'libhdf5hl_fortran',
                 'libhdf5_hl_cpp',
                 'libhdf5_hl',
                 'libhdf5_fortran',
+                'libhdf5_java',
                 'libhdf5',
             ],
             ('cxx', 'hl'): [
@@ -247,6 +254,10 @@ def libs(self):
             ('fortran',): [
                 'libhdf5_fortran',
                 'libhdf5',
+            ],
+            ('java',): [
+                'libhdf5_java',
+                'libhdf5',
             ]
         }
 
@@ -258,104 +269,82 @@ def libs(self):
             libraries, root=self.prefix, shared=shared, recursive=True
         )
 
-    @run_before('configure')
+    @run_before('cmake')
     def fortran_check(self):
         if '+fortran' in self.spec and not self.compiler.fc:
             msg = 'cannot build a Fortran variant without a Fortran compiler'
             raise RuntimeError(msg)
 
-    def with_or_without_szip(self, activated):
-        return '--{0}-szlib'.format('with' if activated else 'without')
+    def cmake_args(self):
+        spec = self.spec
 
-    def configure_args(self):
         # Always enable this option. This does not actually enable any
         # features: it only *allows* the user to specify certain
         # combinations of other arguments. Enabling it just skips a
         # sanity check in configure, so this doesn't merit a variant.
-        extra_args = ['--enable-unsupported',
-                      '--enable-symbols=yes']
-
-        # Do not specify the prefix of zlib if it is in a system directory
-        # (see https://github.com/spack/spack/pull/21900).
-        zlib_prefix = self.spec['zlib'].prefix
-        extra_args.append('--with-zlib={0}'.format(
-            'yes' if is_system_path(zlib_prefix) else zlib_prefix))
-
-        extra_args += self.enable_or_disable('threadsafe')
-        extra_args += self.enable_or_disable('cxx')
-        extra_args += self.enable_or_disable('hl')
-        extra_args += self.enable_or_disable('fortran')
-        extra_args += self.enable_or_disable('java')
-        extra_args += self.with_or_without('szip')
-
-        api = self.spec.variants['api'].value
-        if api != 'none':
-            extra_args.append('--with-default-api-version=' + api)
-
-        if self.spec.satisfies('@1.10:'):
-            if '+debug' in self.spec:
-                extra_args.append('--enable-build-mode=debug')
-            else:
-                extra_args.append('--enable-build-mode=production')
-        else:
-            if '+debug' in self.spec:
-                extra_args.append('--enable-debug=all')
-            else:
-                extra_args.append('--enable-production')
-
-            # '--enable-fortran2003' no longer exists as of version 1.10.0
-            if '+fortran' in self.spec:
-                extra_args.append('--enable-fortran2003')
-            else:
-                extra_args.append('--disable-fortran2003')
+        args = [
+            self.define('ALLOW_UNSUPPORTED', True),
+            self.define('HDF5_ENABLE_Z_LIB_SUPPORT', True),
+            self.define_from_variant('HDF5_ENABLE_SZIP_SUPPORT', 'szip'),
+            self.define_from_variant('BUILD_SHARED_LIBS', 'shared'),
+            self.define_from_variant('HDF5_ENABLE_PARALLEL', 'mpi'),
+            self.define_from_variant('HDF5_ENABLE_THREADSAFE', 'threadsafe'),
+            self.define_from_variant('HDF5_BUILD_HL_LIB', 'hl'),
+            self.define_from_variant('HDF5_BUILD_CPP_LIB', 'cxx'),
+            self.define_from_variant('HDF5_BUILD_FORTRAN', 'fortran'),
+            self.define_from_variant('HDF5_BUILD_JAVA', 'java'),
+            self.define_from_variant('HDF5_BUILD_TOOLS', 'tools')
+        ]
 
-        if '+shared' in self.spec:
-            extra_args.append('--enable-shared')
-        else:
-            extra_args.append('--disable-shared')
-            extra_args.append('--enable-static-exec')
+        if '+szip' in spec:
+            args.append(self.define('HDF5_ENABLE_SZIP_ENCODING', True))
+
+        api = spec.variants['api'].value
+        if api != 'default':
+            args.append(self.define('DEFAULT_API_VERSION', api))
+
+        # The variable CMAKE_POSITION_INDEPENDENT_CODE is set to True by default in
+        # HDF5 Cmake code;  this should insure that it is off if '~pic' in spec. The
+        # flags that were previously set for '+pic' in the Autotools version are kept
+        # in case CMake doesn't set them as expected.  Both the 'pic' variant and the
+        # CMAKE_POSITION_INDEPENDENT_CODE variable are True by default, but '~pic'
+        # should disable them both.
+        args.append(self.define_from_variant('CMAKE_POSITION_INDEPENDENT_CODE', 'pic'))
+        if '+pic' in spec:
+            # use global spack compiler flags
+            _flags = self.compiler.cc_pic_flag
+            _flags += " " + ' '.join(spec.compiler_flags['cflags'])
+            args.append('CFLAGS={0}'.format(_flags))
+
+            if '+cxx' in spec:
+                _flags = self.compiler.cxx_pic_flag
+                _flags += " " + ' '.join(spec.compiler_flags['cxxflags'])
+                args.append('CXXFLAGS={0}'.format(_flags))
+
+            if '+fortran' in spec:
+                _flags = self.compiler.fc_pic_flag
+                _flags += " " + ' '.join(spec.compiler_flags['fflags'])
+                args.append('FCFLAGS={0}'.format(_flags))
 
         # Fujitsu Compiler does not add Fortran runtime in rpath.
-        if '+fortran %fj' in self.spec:
-            extra_args.append('LDFLAGS=-lfj90i -lfj90f -lfjsrcinfo -lelf')
+        if '+fortran %fj' in spec:
+            args.append('LDFLAGS=-lfj90i -lfj90f -lfjsrcinfo -lelf')
 
-        if '+mpi' in self.spec:
+        if '+mpi' in spec:
             # The HDF5 configure script warns if cxx and mpi are enabled
             # together. There doesn't seem to be a real reason for this, except
             # that parts of the MPI interface are not accessible via the C++
             # interface. Since they are still accessible via the C interface,
             # this is not actually a problem.
-            extra_args += ['--enable-parallel',
-                           'CC=%s' % self.spec['mpi'].mpicc]
+            args.append('CC=%s' % self.spec['mpi'].mpicc)
 
             if '+cxx' in self.spec:
-                extra_args.append('CXX=%s' % self.spec['mpi'].mpicxx)
+                args.append('CXX=%s' % self.spec['mpi'].mpicxx)
 
             if '+fortran' in self.spec:
-                extra_args.append('FC=%s' % self.spec['mpi'].mpifc)
-
-        return extra_args
-
-    @run_after('configure')
-    def patch_postdeps(self):
-        if '@:1.8.14' in self.spec:
-            # On Ubuntu14, HDF5 1.8.12 (and maybe other versions)
-            # mysteriously end up with "-l -l" in the postdeps in the
-            # libtool script.  Patch this by removing the spurious -l's.
-            filter_file(
-                r'postdeps="([^"]*)"',
-                lambda m: 'postdeps="%s"' % ' '.join(
-                    arg for arg in m.group(1).split(' ') if arg != '-l'),
-                'libtool')
-
-    @run_after('configure')
-    def patch_libtool(self):
-        """AOCC support for HDF5"""
-        if '%aocc' in self.spec:
-            filter_file(
-                r'\$wl-soname \$wl\$soname',
-                r'-fuse-ld=ld -Wl,-soname,\$soname',
-                'libtool', string=True)
+                args.append('FC=%s' % self.spec['mpi'].mpifc)
+
+        return args
 
     @run_after('install')
     @on_package_attributes(run_tests=True)


Test patch: