1# Copyright 2013-2020 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 15from pathlib import Path 16import functools 17import os 18import typing as T 19 20from .base import CMakeDependency, DependencyMethods, PkgConfigDependency 21from .base import factory_methods, DependencyException 22 23if T.TYPE_CHECKING: 24 from ..environment import Environment, MachineChoice 25 from .base import DependencyType 26 27 28@factory_methods({DependencyMethods.PKGCONFIG, DependencyMethods.CMAKE}) 29def scalapack_factory(env: 'Environment', for_machine: 'MachineChoice', 30 kwargs: T.Dict[str, T.Any], 31 methods: T.List[DependencyMethods]) -> T.List['DependencyType']: 32 candidates = [] 33 34 if DependencyMethods.PKGCONFIG in methods: 35 mkl = 'mkl-static-lp64-iomp' if kwargs.get('static', False) else 'mkl-dynamic-lp64-iomp' 36 candidates.append(functools.partial( 37 MKLPkgConfigDependency, mkl, env, kwargs)) 38 39 for pkg in ['scalapack-openmpi', 'scalapack']: 40 candidates.append(functools.partial( 41 PkgConfigDependency, pkg, env, kwargs)) 42 43 if DependencyMethods.CMAKE in methods: 44 candidates.append(functools.partial( 45 CMakeDependency, 'Scalapack', env, kwargs)) 46 47 return candidates 48 49 50class MKLPkgConfigDependency(PkgConfigDependency): 51 52 """PkgConfigDependency for Intel MKL. 53 54 MKL's pkg-config is pretty much borked in every way. We need to apply a 55 bunch of fixups to make it work correctly. 56 """ 57 58 def __init__(self, name: str, env: 'Environment', kwargs: T.Dict[str, T.Any], 59 language: T.Optional[str] = None): 60 _m = os.environ.get('MKLROOT') 61 self.__mklroot = Path(_m).resolve() if _m else None 62 63 # We need to call down into the normal super() method even if we don't 64 # find mklroot, otherwise we won't have all of the instance variables 65 # initialized that meson expects. 66 super().__init__(name, env, kwargs, language=language) 67 68 # Doesn't work with gcc on windows, but does on Linux 69 if (not self.__mklroot or (env.machines[self.for_machine].is_windows() 70 and self.clib_compiler.id == 'gcc')): 71 self.is_found = False 72 73 # This can happen either because we're using GCC, we couldn't find the 74 # mklroot, or the pkg-config couldn't find it. 75 if not self.is_found: 76 return 77 78 assert self.version != '', 'This should not happen if we didn\'t return above' 79 80 if self.version == 'unknown': 81 # At least by 2020 the version is in the pkg-config, just not with 82 # the correct name 83 v = self.get_variable(pkgconfig='Version', default_value='') 84 85 if not v and self.__mklroot: 86 try: 87 v = ( 88 self.__mklroot.as_posix() 89 .split('compilers_and_libraries_')[1] 90 .split('/', 1)[0] 91 ) 92 except IndexError: 93 pass 94 95 if v: 96 self.version = v 97 98 def _set_libs(self): 99 super()._set_libs() 100 101 if self.env.machines[self.for_machine].is_windows(): 102 suffix = '.lib' 103 elif self.static: 104 suffix = '.a' 105 else: 106 suffix = '' 107 libdir = self.__mklroot / 'lib/intel64' 108 109 if self.clib_compiler.id == 'gcc': 110 for i, a in enumerate(self.link_args): 111 # only replace in filename, not in directory names 112 parts = list(os.path.split(a)) 113 if 'mkl_intel_lp64' in parts[-1]: 114 parts[-1] = parts[-1].replace('intel', 'gf') 115 self.link_args[i] = '/' + os.path.join(*parts) 116 # MKL pkg-config omits scalapack 117 # be sure "-L" and "-Wl" are first if present 118 i = 0 119 for j, a in enumerate(self.link_args): 120 if a.startswith(('-L', '-Wl')): 121 i = j + 1 122 elif j > 3: 123 break 124 if self.env.machines[self.for_machine].is_windows() or self.static: 125 self.link_args.insert( 126 i, str(libdir / ('mkl_scalapack_lp64' + suffix)) 127 ) 128 self.link_args.insert( 129 i + 1, str(libdir / ('mkl_blacs_intelmpi_lp64' + suffix)) 130 ) 131 else: 132 self.link_args.insert(i, '-lmkl_scalapack_lp64') 133 self.link_args.insert(i + 1, '-lmkl_blacs_intelmpi_lp64') 134 135 def _set_cargs(self): 136 env = None 137 if self.language == 'fortran': 138 # gfortran doesn't appear to look in system paths for INCLUDE files, 139 # so don't allow pkg-config to suppress -I flags for system paths 140 env = os.environ.copy() 141 env['PKG_CONFIG_ALLOW_SYSTEM_CFLAGS'] = '1' 142 ret, out, err = self._call_pkgbin([ 143 '--cflags', self.name, 144 '--define-variable=prefix=' + self.__mklroot.as_posix()], 145 env=env) 146 if ret != 0: 147 raise DependencyException('Could not generate cargs for %s:\n%s\n' % 148 (self.name, err)) 149 self.compile_args = self._convert_mingw_paths(self._split_args(out)) 150