# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other # Spack Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (Apache-2.0 OR MIT) import shutil import sys from spack import * class Hdf5(AutotoolsPackage): """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. """ homepage = "https://support.hdfgroup.org/HDF5/" url = "https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-1.10/hdf5-1.10.1/src/hdf5-1.10.1.tar.gz" list_url = "https://support.hdfgroup.org/ftp/HDF5/releases" list_depth = 3 git = "https://bitbucket.hdfgroup.org/scm/hdffv/hdf5.git" version('develop', branch='develop') version('1.10.5', sha256='6d4ce8bf902a97b050f6f491f4268634e252a63dadd6656a1a9be5b7b7726fa8') version('1.10.4', sha256='8f60dc4dd6ab5fcd23c750d1dc5bca3d0453bdce5c8cdaf0a4a61a9d1122adb2') version('1.10.3', sha256='b600d7c914cfa80ae127cd1a1539981213fee9994ac22ebec9e3845e951d9b39') version('1.10.2', sha256='bfec1be8c366965a99812cf02ddc97e4b708c1754fccba5414d4adccdc073866') version('1.10.1', sha256='048a9d149fb99aaa1680a712963f5a78e9c43b588d0e79d55e06760ec377c172') version('1.10.0-patch1', sha256='6e78cfe32a10e6e0629393cdfddf6cfa536571efdaf85f08e35326e1b4e9eff0') version('1.10.0', sha256='81f6201aba5c30dced5dcd62f5d5477a2790fd5850e02ac514ca8bf3e2bb375a') version('1.8.21', sha256='87d8c82eba5cf766d97cd06c054f4639c1049c4adeaa3a79f77f8bd374f80f37') version('1.8.19', sha256='a4335849f19fae88c264fd0df046bc321a78c536b2548fc508627a790564dc38') version('1.8.18', sha256='cdb195ad8d9e6782acf24b2488061289f615628c2ccda8457b0a0c3fb7a8a063') version('1.8.17', sha256='d9cda297ee76ade9881c4208987939250d397bae6252d0ccb66fa7d24d67e263') version('1.8.16', sha256='ed17178abd9928a7237f30370189ba767b9e39e0db45917c2ac4665eb9cb4771') version('1.8.15', sha256='4e963216b7d32469596bc1321a8c3f6e0c278dcbbdb7be6414c63c081b34c275') version('1.8.14', sha256='1dbefeeef7f591897c632b2b090db96bb8d35ad035beaa36bc39cb2bc67e0639') version('1.8.13', sha256='82f6b38eec103b4fccfbf14892786e0c27a8135d3252d8601cf5bf20066d38c1') 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') variant('hl', default=False, description='Enable the high-level library') variant('cxx', default=False, description='Enable C++ support') variant('fortran', default=False, description='Enable Fortran support') variant('threadsafe', default=False, description='Enable thread-safe capabilities') 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)') 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('mpi', when='+mpi') # 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:') # 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. # conflicts('+threadsafe+hl') # 2. Thread safety is not guaranteed via Fortran (CXX) API, but it's # possible for a dependency tree to contain a package that uses Fortran # (CXX) API in a single thread and another one that uses low-level C-API # in multiple threads. To allow for such scenarios, we don't specify the # following conflicts. # conflicts('+threadsafe+cxx') # conflicts('+threadsafe+fortran') # 3. Parallel features are not supported via CXX API, but for the reasons # described in #2 we allow for such combination. # conflicts('+mpi+cxx') # There are known build failures with intel@18.0.1. This issue is # discussed and patch is provided at # https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/747951. patch('h5f90global-mult-obj-same-equivalence-same-common-block.patch', when='@1.10.1%intel@18') # Turn line comments into block comments to conform with pre-C99 language # standards. Versions of hdf5 after 1.8.10 don't require this patch, # either because they conform to pre-C99 or neglect to ask for pre-C99 # language standards from their compiler. The hdf5 build system adds # the -ansi cflag (run 'man gcc' for info on -ansi) for some versions # of some compilers (see hdf5-1.8.10/config/gnu-flags). The hdf5 build # system does not provide an option to disable -ansi, but since the # pre-C99 code is restricted to just five lines of line comments in # three src files, this patch accomplishes the simple task of patching the # three src files and leaves the hdf5 build system alone. patch('pre-c99-comments.patch', when='@1.8.10') # There are build errors with GCC 8, see # https://forum.hdfgroup.org/t/1-10-2-h5detect-compile-error-gcc-8-1-0-on-centos-7-2-solved/4441 patch('https://salsa.debian.org/debian-gis-team/hdf5/raw/bf94804af5f80f662cad80a5527535b3c6537df6/debian/patches/gcc-8.patch', sha256='57cee5ff1992b4098eda079815c36fc2da9b10e00a9056df054f2384c4fc7523', when='@1.10.2%gcc@8:') # 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='+mpi~cxx', sha256='b61e2f058964ad85be6ee5ecea10080bf79e73f83ff88d1fa4b602d00209da9c') filter_compiler_wrappers('h5cc', 'h5c++', 'h5fc', relative_root='bin') def url_for_version(self, version): url = "https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-{0}/hdf5-{1}/src/hdf5-{1}.tar.gz" return url.format(version.up_to(2), version) @when('@develop') def autoreconf(self, spec, prefix): autogen = Executable('./autogen.sh') autogen() @property def libs(self): """HDF5 can be queried for the following parameters: - "hl": high-level interface - "cxx": C++ APIs - "fortran": Fortran APIs :return: list of matching libraries """ query_parameters = self.spec.last_query.extra_parameters shared = '+shared' in self.spec # This map contains a translation from query_parameters # to the libraries needed query2libraries = { tuple(): ['libhdf5'], ('cxx', 'fortran', 'hl'): [ 'libhdf5hl_fortran', 'libhdf5_hl_cpp', 'libhdf5_hl', 'libhdf5_fortran', 'libhdf5', ], ('cxx', 'hl'): [ 'libhdf5_hl_cpp', 'libhdf5_hl', 'libhdf5', ], ('fortran', 'hl'): [ 'libhdf5hl_fortran', 'libhdf5_hl', 'libhdf5_fortran', 'libhdf5', ], ('hl',): [ 'libhdf5_hl', 'libhdf5', ], ('cxx', 'fortran'): [ 'libhdf5_fortran', 'libhdf5_cpp', 'libhdf5', ], ('cxx',): [ 'libhdf5_cpp', 'libhdf5', ], ('fortran',): [ 'libhdf5_fortran', 'libhdf5', ] } # Turn the query into the appropriate key key = tuple(sorted(query_parameters)) libraries = query2libraries[key] return find_libraries( libraries, root=self.prefix, shared=shared, recursive=True ) @run_before('configure') 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 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'] extra_args += ['--enable-symbols=yes'] 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') if '+szip' in self.spec: extra_args.append('--with-szlib=%s' % self.spec['szip'].prefix) else: extra_args.append('--without-szlib') 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') if '+shared' in self.spec: extra_args.append('--enable-shared') else: extra_args.append('--disable-shared') extra_args.append('--enable-static-exec') if '+pic' in self.spec: extra_args += ['%s=%s' % (f, self.compiler.pic_flag) for f in ['CFLAGS', 'CXXFLAGS', 'FCFLAGS']] if '+mpi' in self.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] if '+cxx' in self.spec: extra_args.append('CXX=%s' % self.spec['mpi'].mpicxx) if '+fortran' in self.spec: extra_args.append('FC=%s' % self.spec['mpi'].mpifc) extra_args.append('--with-zlib=%s' % self.spec['zlib'].prefix) 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('install') @on_package_attributes(run_tests=True) def check_install(self): # Build and run a small program to test the installed HDF5 library spec = self.spec print("Checking HDF5 installation...") checkdir = "spack-check" with working_dir(checkdir, create=True): source = r""" #include #include #include int main(int argc, char **argv) { unsigned majnum, minnum, relnum; herr_t herr = H5get_libversion(&majnum, &minnum, &relnum); assert(!herr); printf("HDF5 version %d.%d.%d %u.%u.%u\n", H5_VERS_MAJOR, H5_VERS_MINOR, H5_VERS_RELEASE, majnum, minnum, relnum); return 0; } """ expected = """\ HDF5 version {version} {version} """.format(version=str(spec.version.up_to(3))) with open("check.c", 'w') as f: f.write(source) if '+mpi' in spec: cc = Executable(spec['mpi'].mpicc) else: cc = Executable(self.compiler.cc) cc(*(['-c', "check.c"] + spec['hdf5'].headers.cpp_flags.split())) cc(*(['-o', "check", "check.o"] + spec['hdf5'].libs.ld_flags.split())) try: check = Executable('./check') output = check(output=str) except ProcessError: output = "" success = output == expected if not success: print("Produced output does not match expected output.") print("Expected output:") print('-' * 80) print(expected) print('-' * 80) print("Produced output:") print('-' * 80) print(output) print('-' * 80) raise RuntimeError("HDF5 install check failed") shutil.rmtree(checkdir)