1# Copyright 2013-2019 The Meson development team 2 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6 7# http://www.apache.org/licenses/LICENSE-2.0 8 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15# This file contains the detection logic for miscellaneous external dependencies. 16 17import subprocess 18import shutil 19from pathlib import Path 20 21from .. import mlog 22from ..mesonlib import split_args, listify 23from .base import (DependencyException, DependencyMethods, ExternalDependency, ExternalProgram, 24 PkgConfigDependency) 25 26class HDF5Dependency(ExternalDependency): 27 28 def __init__(self, environment, kwargs): 29 language = kwargs.get('language', 'c') 30 super().__init__('hdf5', environment, kwargs, language=language) 31 kwargs['required'] = False 32 kwargs['silent'] = True 33 self.is_found = False 34 methods = listify(self.methods) 35 36 if language not in ('c', 'cpp', 'fortran'): 37 raise DependencyException('Language {} is not supported with HDF5.'.format(language)) 38 39 if set([DependencyMethods.AUTO, DependencyMethods.PKGCONFIG]).intersection(methods): 40 pkgconfig_files = ['hdf5', 'hdf5-serial'] 41 PCEXE = shutil.which('pkg-config') 42 if PCEXE: 43 # some distros put hdf5-1.2.3.pc with version number in .pc filename. 44 ret = subprocess.run([PCEXE, '--list-all'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, 45 universal_newlines=True) 46 if ret.returncode == 0: 47 for pkg in ret.stdout.split('\n'): 48 if pkg.startswith(('hdf5')): 49 pkgconfig_files.append(pkg.split(' ', 1)[0]) 50 pkgconfig_files = list(set(pkgconfig_files)) # dedupe 51 52 for pkg in pkgconfig_files: 53 pkgdep = PkgConfigDependency(pkg, environment, kwargs, language=self.language) 54 if not pkgdep.found(): 55 continue 56 57 self.compile_args = pkgdep.get_compile_args() 58 # some broken pkgconfig don't actually list the full path to the needed includes 59 newinc = [] 60 for arg in self.compile_args: 61 if arg.startswith('-I'): 62 stem = 'static' if kwargs.get('static', False) else 'shared' 63 if (Path(arg[2:]) / stem).is_dir(): 64 newinc.append('-I' + str(Path(arg[2:]) / stem)) 65 self.compile_args += newinc 66 67 # derive needed libraries by language 68 pd_link_args = pkgdep.get_link_args() 69 link_args = [] 70 for larg in pd_link_args: 71 lpath = Path(larg) 72 # some pkg-config hdf5.pc (e.g. Ubuntu) don't include the commonly-used HL HDF5 libraries, 73 # so let's add them if they exist 74 # additionally, some pkgconfig HDF5 HL files are malformed so let's be sure to find HL anyway 75 if lpath.is_file(): 76 hl = [] 77 if language == 'cpp': 78 hl += ['_hl_cpp', '_cpp'] 79 elif language == 'fortran': 80 hl += ['_hl_fortran', 'hl_fortran', '_fortran'] 81 hl += ['_hl'] # C HL library, always needed 82 83 suffix = '.' + lpath.name.split('.', 1)[1] # in case of .dll.a 84 for h in hl: 85 hlfn = lpath.parent / (lpath.name.split('.', 1)[0] + h + suffix) 86 if hlfn.is_file(): 87 link_args.append(str(hlfn)) 88 # HDF5 C libs are required by other HDF5 languages 89 link_args.append(larg) 90 else: 91 link_args.append(larg) 92 93 self.link_args = link_args 94 self.version = pkgdep.get_version() 95 self.is_found = True 96 self.pcdep = pkgdep 97 return 98 99 if DependencyMethods.AUTO in methods: 100 wrappers = {'c': 'h5cc', 'cpp': 'h5c++', 'fortran': 'h5fc'} 101 comp_args = [] 102 link_args = [] 103 # have to always do C as well as desired language 104 for lang in set([language, 'c']): 105 prog = ExternalProgram(wrappers[lang], silent=True) 106 if not prog.found(): 107 return 108 cmd = prog.get_command() + ['-show'] 109 p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, timeout=15) 110 if p.returncode != 0: 111 mlog.debug('Command', mlog.bold(cmd), 'failed to run:') 112 mlog.debug(mlog.bold('Standard output\n'), p.stdout) 113 mlog.debug(mlog.bold('Standard error\n'), p.stderr) 114 return 115 args = split_args(p.stdout) 116 for arg in args[1:]: 117 if arg.startswith(('-I', '-f', '-D')) or arg == '-pthread': 118 comp_args.append(arg) 119 elif arg.startswith(('-L', '-l', '-Wl')): 120 link_args.append(arg) 121 elif Path(arg).is_file(): 122 link_args.append(arg) 123 self.compile_args = comp_args 124 self.link_args = link_args 125 self.is_found = True 126 return 127 128 @staticmethod 129 def get_methods(): 130 return [DependencyMethods.AUTO, DependencyMethods.PKGCONFIG] 131