xref: /petsc/config/BuildSystem/config/packages/BlasLapack.py (revision 390474f96c2cfb142235edf4f816cb7a2ce79c2a)
1from __future__ import generators
2import config.base
3import config.package
4from sourceDatabase import SourceDB
5import os
6
7class Configure(config.package.Package):
8  def __init__(self, framework):
9    config.package.Package.__init__(self, framework)
10    self.defaultPrecision    = 'double'
11    self.f2c                 = 0  # indicates either the f2cblaslapack are used or there is no Fortran compiler (and system BLAS/LAPACK is used)
12    self.has64bitindices     = 0
13    self.mkl                 = 0  # indicates BLAS/LAPACK library used is Intel MKL
14    self.mkl_spblas_h        = 0  # indicates mkl_spblas.h is found
15    self.separateBlas        = 1
16    self.required            = 1
17    self.alternativedownload = 'f2cblaslapack'
18    self.mangling            = 'unknown'
19    self.missingRoutines     = []
20    self.libDirs             = [os.path.join('lib','64'),os.path.join('lib','ia64'),os.path.join('lib','em64t'),os.path.join('lib','intel64'),'lib','64',\
21                                'ia64','em64t','intel64', os.path.join('lib','32'),os.path.join('lib','ia32'),'32','ia32','']
22
23  def setupDependencies(self, framework):
24    config.package.Package.setupDependencies(self, framework)
25    self.f2cblaslapack = framework.require('config.packages.f2cblaslapack', self)
26    self.netliblapack  = framework.require('config.packages.netlib-lapack', self)
27    self.fblaslapack   = framework.require('config.packages.fblaslapack', self)
28    self.libflame      = framework.require('config.packages.libflame', self)
29    self.blis          = framework.require('config.packages.BLIS', self)
30    self.openblas      = framework.require('config.packages.OpenBLAS', self)
31    self.flibs         = framework.require('config.packages.flibs',self)
32    self.mathlib       = framework.require('config.packages.mathlib',self)
33    self.openmp        = framework.require('config.packages.OpenMP',self)
34    self.mpi           = framework.require('config.packages.MPI',self)
35    self.deps          = [self.flibs,self.mathlib]
36    self.odeps         = [self.mpi]
37    return
38
39  def __str__(self):
40    output  = config.package.Package.__str__(self)
41    if self.has64bitindices:
42      output += '  uses 8 byte integers\n'
43    else:
44      output += '  uses 4 byte integers\n'
45    return output
46
47  def setupHelp(self, help):
48    config.package.Package.setupHelp(self,help)
49    import nargs
50    help.addArgument('BLAS/LAPACK', '-with-blas-lib=<libraries: e.g. [/Users/..../libblas.a,...]>',    nargs.ArgLibrary(None, None, 'Indicate the library(s) containing BLAS'))
51    help.addArgument('BLAS/LAPACK', '-with-lapack-lib=<libraries: e.g. [/Users/..../liblapack.a,...]>',nargs.ArgLibrary(None, None, 'Indicate the library(s) containing LAPACK'))
52    help.addArgument('BLAS/LAPACK', '-with-blaslapack-suffix=<string>',nargs.Arg(None, None, 'Indicate a suffix for BLAS/LAPACK subroutine names.'))
53    help.addArgument('BLAS/LAPACK', '-with-64-bit-blas-indices', nargs.ArgBool(None, 0, 'Try to use 64-bit integers for BLAS/LAPACK; will error if not available'))
54    help.addArgument('BLAS/LAPACK', '-known-blaslapack-mangling=<string>', nargs.ArgString(None, None, 'Indicate known name mangling for BLAS/LAPACK subroutine names (unchanged, underscore, caps)', regExp='^(unchanged|underscore|caps)$'))
55    help.addArgument('BLAS/LAPACK', '-known-blaslapack-openmp=<bool>', nargs.ArgBool(None, None, 'Indicate if BLAS/LAPACK uses OpenMP'))
56    help.addArgument('BLAS/LAPACK', '-known-64-bit-blas-indices=<bool>', nargs.ArgBool(None, None, 'Indicate if BLAS/LAPACK uses 64 bit integers\n       Should be used only when the auto-detection of 64 bit integers in BLAS/LAPACK fails'))
57    help.addArgument('BLAS/LAPACK', '-known-snrm2-returns-double=<bool>', nargs.ArgBool(None, None, 'Indicate if BLAS snrm2() returns a double'))
58    help.addArgument('BLAS/LAPACK', '-known-sdot-returns-double=<bool>', nargs.ArgBool(None, None, 'Indicate if BLAS sdot() returns a double'))
59    return
60
61  def getPrefix(self):
62    if self.compilers.fortranMangling == 'caps':
63      if self.defaultPrecision == 'single': return 'S'
64      if self.defaultPrecision == 'double': return 'D'
65      if self.defaultPrecision == '__float128': return 'Q'
66      if self.defaultPrecision == '__fp16': return 'H'
67      return 'Unknown precision'
68    else:
69      if self.defaultPrecision == 'single': return 's'
70      if self.defaultPrecision == 'double': return 'd'
71      if self.defaultPrecision == '__float128': return 'q'
72      if self.defaultPrecision == '__fp16': return 'h'
73      return 'Unknown precision'
74
75  def getType(self):
76    if self.defaultPrecision == 'single': return 'float'
77    return self.defaultPrecision
78
79  def getOtherLibs(self, foundBlas = None, blasLibrary = None, separateBlas = None):
80    if foundBlas is None:
81      foundBlas = getattr(self, "foundBlas", None)
82    if blasLibrary is None:
83      blasLibrary = getattr(self, "blasLibrary", None)
84    if separateBlas is None:
85      separateBlas = getattr(self, "separateBlas", None)
86    otherLibs = []
87    if foundBlas and separateBlas and blasLibrary:
88        otherLibs += blasLibrary
89    otherLibs += self.dlib
90    return otherLibs
91
92  def checkBlas(self, blasLibrary, otherLibs, mangling = None, routinesIn = ['dot']):
93    '''This checks the given library for the routine, dot by default'''
94    oldLibs = self.compilers.LIBS
95    if not isinstance(routinesIn, list):
96      routinesIn = [routinesIn]
97    routines = map(self.mangleBlas, routinesIn, [mangling]*len(routinesIn))
98    _, missing =  self.libraries.checkClassify(blasLibrary, routines, otherLibs = otherLibs)
99    self.compilers.LIBS = oldLibs
100    return len(missing) == 0, missing
101
102  def checkLapack(self, lapackLibrary, otherLibs, mangling = None, routinesIn = ['getrs','geev']):
103    oldLibs = self.compilers.LIBS
104    if not isinstance(routinesIn, list):
105      routinesIn = [routinesIn]
106    routines = map(self.mangleBlas, routinesIn, [mangling]*len(routinesIn))
107    _, missing = self.libraries.checkClassify(lapackLibrary, routines, otherLibs = otherLibs)
108    self.compilers.LIBS = oldLibs
109    return len(missing) == 0, missing
110
111  def checkBlasMangling(self, mangling, lapackLibrary, blasLibrary = None):
112    foundBlas, missingBlas = self.checkBlas(blasLibrary, self.getOtherLibs(), mangling, ['dot'])
113    foundLapack, missingLapack = self.checkLapack(lapackLibrary, self.getOtherLibs(foundBlas, blasLibrary), mangling, ['getrs','geev'])
114    if foundBlas and foundLapack:
115      self.logPrint('Found mangling on BLAS/LAPACK: '+mangling)
116    return (foundBlas, missingBlas, foundLapack, missingLapack)
117
118  def checkLib(self, lapackLibrary, blasLibrary = None):
119    '''Checking for BLAS and LAPACK symbols'''
120    if blasLibrary is None:
121      self.separateBlas = 0
122      blasLibrary       = lapackLibrary
123    else:
124      self.separateBlas = 1
125    if not isinstance(lapackLibrary, list):
126      lapackLibrary = [lapackLibrary]
127    if not isinstance(blasLibrary,   list):
128      blasLibrary   = [blasLibrary]
129
130    # allow a user-specified suffix to be appended to BLAS/LAPACK symbols
131    self.suffix = self.argDB.get('with-blaslapack-suffix', '')
132    # allow user to dictate which BLAS/LAPACK mangling to use (some BLAS/LAPACK libraries, like on Apple, provide several)
133    if 'known-blaslapack-mangling' in self.argDB:
134      mangling = self.argDB['known-blaslapack-mangling']
135      # check user-provided mangling, return the result regardless of success (errors are handled elsewhere)
136      (foundBlas, missingBlas, foundLapack, missingLapack) = self.checkBlasMangling(mangling, lapackLibrary, blasLibrary)
137      self.mangling = mangling
138      return (foundBlas, foundLapack)
139
140    manglings = ['unchanged', 'underscore', 'caps']
141    # if we have a Fortran compiler, check that mangling first
142    if hasattr(self.compilers, 'FC'):
143      mangling = self.compilers.fortranMangling
144      self.logPrint('Checking for Fortran name mangling "'+mangling+'" on BLAS/LAPACK')
145      (foundBlas, missingBlas, foundLapack, missingLapack) = self.checkBlasMangling(mangling, lapackLibrary, blasLibrary)
146      if not foundBlas:
147        self.logPrint('BLAS does not use Fortran name mangling "'+mangling+'", missing '+str(missingBlas))
148      if not foundLapack:
149        self.logPrint('LAPACK does not use Fortran name mangling "'+mangling+'", missing '+str(missingLapack))
150      if foundBlas and foundLapack:
151        self.logPrint('Found Fortran name mangling "'+mangling+'" on BLAS/LAPACK')
152        self.mangling = mangling
153        return (foundBlas, foundLapack)
154      if mangling in manglings:
155        manglings.remove(mangling)
156
157    for mangling in manglings:
158      self.logPrint('Checking for "'+mangling+'" name mangling on BLAS/LAPACK')
159      (foundBlas, missingBlas, foundLapack, missingLapack) = self.checkBlasMangling(mangling, lapackLibrary, blasLibrary)
160      if not foundBlas:
161        self.logPrint('BLAS does not use "'+mangling+'" name mangling, missing '+str(missingBlas))
162      if not foundLapack:
163        self.logPrint('LAPACK does not use "'+mangling+'" name mangling, missing '+str(missingLapack))
164      if foundBlas and foundLapack:
165        self.logPrint('Found "'+mangling+'" name mangling on BLAS/LAPACK')
166        self.mangling = mangling
167        return (foundBlas, foundLapack)
168
169    self.logPrint('Unknown name mangling in BLAS/LAPACK')
170    self.mangling = 'unknown'
171    return (False, False)
172
173  def generateGuesses(self):
174    # check that user has used the options properly
175    if 'with-blas-lib' in self.argDB and not 'with-lapack-lib' in self.argDB:
176      raise RuntimeError('If you use the --with-blas-lib=<lib> you must also use --with-lapack-lib=<lib> option')
177    if not 'with-blas-lib' in self.argDB and 'with-lapack-lib' in self.argDB:
178      raise RuntimeError('If you use the --with-lapack-lib=<lib> you must also use --with-blas-lib=<lib> option')
179    if 'with-blas-lib' in self.argDB and 'with-blaslapack-dir' in self.argDB:
180      raise RuntimeError('You cannot set both the library containing BLAS with --with-blas-lib=<lib>\nand the directory to search with --with-blaslapack-dir=<dir>')
181    if 'with-blaslapack-lib' in self.argDB and 'with-blaslapack-dir' in self.argDB:
182      raise RuntimeError('You cannot set both the library containing BLAS/LAPACK with --with-blaslapack-lib=<lib>\nand the directory to search with --with-blaslapack-dir=<dir>')
183
184    # Try specified BLAS/LAPACK library
185    if 'with-blaslapack-lib' in self.argDB:
186      if 'known-64-bit-blas-indices' in self.argDB:
187        if self.argDB['known-64-bit-blas-indices']:
188          known_64bit = '64'
189        else:
190          known_64bit = '32'
191      else:
192        known_64bit = 'unknown'
193      if 'known-blas-openmp' in self.argDB:
194        if self.argDB['known-blas-openmp']:
195          known_openmp = 'yes'
196        else:
197          known_openmp = 'no'
198      else:
199        known_openmp = 'unknown'
200      yield ('User specified BLAS/LAPACK library', None, self.argDB['with-blaslapack-lib'], known_64bit, known_openmp)
201      # add warning for user-specified mangling
202      warn_known_mangling = ""
203      if 'known-blaslapack-mangling' in self.argDB:
204        warn_known_mangling = 'Try running without --known-blaslapack-mangling='+self.argDB['known-blaslapack-mangling']+' to try to identify mangled names automatically\n'
205      if self.defaultPrecision == '__float128':
206        raise RuntimeError('__float128 precision requires f2c BLAS/LAPACK libraries; they are not available in '+str(self.argDB['with-blaslapack-lib'])+'; suggest --download-f2cblaslapack\n'+warn_known_mangling)
207      else:
208        raise RuntimeError('You set a value for --with-blaslapack-lib=<lib>, but '+str(self.argDB['with-blaslapack-lib'])+' cannot be used\n'+warn_known_mangling)
209    # Try specified BLAS and LAPACK libraries
210    if 'with-blas-lib' in self.argDB and 'with-lapack-lib' in self.argDB:
211      if 'known-64-bit-blas-indices' in self.argDB:
212        if self.argDB['known-64-bit-blas-indices']:
213          known_64bit = '64'
214        else:
215          known_64bit = '32'
216      else:
217        known_64bit = 'unknown'
218      if 'known-blas-openmp' in self.argDB:
219        if self.argDB['known-blas-openmp']:
220          known_openmp = 'yes'
221        else:
222          known_openmp = 'no'
223      else:
224        known_openmp = 'unknown'
225      yield ('User specified BLAS and LAPACK libraries', self.argDB['with-blas-lib'], self.argDB['with-lapack-lib'], known_64bit, known_openmp)
226      # add warning for user-specified mangling
227      warn_known_mangling = ""
228      if 'known-blaslapack-mangling' in self.argDB:
229        warn_known_mangling = 'Try running without --known-blaslapack-mangling='+self.argDB['known-blaslapack-mangling']+' to try to identify mangled names automatically\n'
230      if self.defaultPrecision == '__float128':
231        raise RuntimeError('__float128 precision requires f2c BLAS/LAPACK libraries; they are not available in '+str(self.argDB['with-blas-lib'])+' and '+str(self.argDB['with-lapack-lib'])+'; suggest --download-f2cblaslapack\n'+warn_known_mangling)
232      else:
233        raise RuntimeError('You set a value for --with-blas-lib=<lib> and --with-lapack-lib=<lib>, but '+str(self.argDB['with-blas-lib'])+' and '+str(self.argDB['with-lapack-lib'])+' cannot be used\n'+warn_known_mangling)
234
235    if self.f2cblaslapack.found:
236      self.f2c = 1
237      # TODO: use self.f2cblaslapack.libDir directly
238      libDir = os.path.join(self.f2cblaslapack.directory,'lib')
239      f2cBlas = [os.path.join(libDir,'libf2cblas.a')]
240      if self.blis.found:
241        # The real BLAS is provided by libblis, but we still need libf2cblas for aux functions needed by libf2clapack
242        f2cBlas += self.blis.lib
243      f2cLapack = [os.path.join(libDir,'libf2clapack.a')]
244      yield ('f2cblaslapack', f2cBlas, f2cLapack, '32','no')
245      yield ('f2cblaslapack', f2cBlas+['-lquadmath'], f2cLapack, '32','no')
246      raise RuntimeError('--download-f2cblaslapack libraries cannot be used')
247    if self.netliblapack.found:
248      self.f2c = 0
249      # TODO: use self.netliblapack.libDir directly
250      libDir = os.path.join(self.netliblapack.directory,'lib')
251      if self.netliblapack.cinterface:
252        yield ('netliblapack', [os.path.join(libDir,'libcblas.a'), os.path.join(libDir,'libblas.a')], [os.path.join(libDir,'liblapacke.a'), os.path.join(libDir,'liblapack.a')], '32', 'no')
253      else:
254        yield ('netliblapack', [os.path.join(libDir,'libnblas.a')], [os.path.join(libDir,'libnlapack.a')], '32', 'no')
255      raise RuntimeError('--download-netlib-lapack libraries cannot be used')
256    if self.fblaslapack.found:
257      self.f2c = 0
258      # TODO: use self.fblaslapack.libDir directly
259      libDir = os.path.join(self.fblaslapack.directory,'lib')
260      yield ('fblaslapack', os.path.join(libDir,'libfblas.a'), os.path.join(libDir,'libflapack.a'), '32','no')
261      raise RuntimeError('--download-fblaslapack libraries cannot be used')
262    if self.libflame.found:
263      self.f2c = 0
264      # TODO: use self.libflame.libDir directly
265      libDir = os.path.join(self.libflame.directory,'lib')
266      yield ('libflame', self.blis.lib, os.path.join(libDir,'libflame.a'), self.blis.known64, self.blis.usesopenmp)
267      raise RuntimeError('--download-libflame libraries cannot be used')
268    if self.blis.found:
269      self.f2c = 0
270      # TODO: Where shall we find liblapack.a?
271      yield ('BLIS', self.blis.lib, 'liblapack.a', self.blis.known64, self.blis.usesopenmp)
272    if self.openblas.found:
273      self.f2c = 0
274      self.include = self.openblas.include
275      if self.openblas.libDir:
276        yield ('OpenBLAS with full path', None, os.path.join(self.openblas.libDir,'libopenblas.a'),self.openblas.known64,self.openblas.usesopenmp)
277      else:
278        yield ('OpenBLAS', None, self.openblas.lib,self.openblas.known64,self.openblas.usesopenmp)
279      raise RuntimeError('--download-openblas libraries cannot be used')
280
281    blislib = ['libblis.a']
282    if self.openmp.found:
283      blislib.insert(0,'libblis-mt.a')
284
285    if not 'with-blaslapack-dir' in self.argDB:
286      mkl = os.getenv('MKLROOT')
287      if mkl:
288        # Since user did not select MKL specifically first try compiler defaults and only if they fail use the MKL
289        yield ('Default compiler libraries', '', '','unknown','unknown')
290        for lib in blislib:
291          for lapack in ['libflame.a','liblapack.a']:
292            for libdir in ['',os.path.join('/usr','local','lib')]:
293              if libdir:
294                lib = os.path.join(libdir,lib)
295                lapack = os.path.join(libdir,lapack)
296            yield ('BLIS/AMD-AOCL default compiler locations '+libdir,lib,lapack,'unknown','unknown')
297        yield ('OpenBLAS default compiler locations', None, 'libopenblas.a','unknown','unknown')
298        yield ('OpenBLAS default compiler locations /usr/local/lib', None, os.path.join('/usr','local','lib','libopenblas.a'),'unknown','unknown')
299        yield ('Default compiler locations', 'libblas.a', 'liblapack.a','unknown','unknown')
300        yield ('Default compiler locations /usr/local/lib', os.path.join('/usr','local','lib','libblas.a'), os.path.join('/usr','local','lib','liblapack.a'),'unknown','unknown')
301        yield ('Default compiler locations with gfortran', None, ['liblapack.a', 'libblas.a','libgfortran.a'],'unknown','unknown')
302        self.logWrite('Did not detect default BLAS and LAPACK locations so using the value of MKLROOT to search as --with-blas-lapack-dir='+mkl)
303        self.argDB['with-blaslapack-dir'] = mkl
304
305    if self.argDB['with-64-bit-blas-indices']:
306      flexiblas = 'libflexiblas64.a'
307      ILP64 = '_ilp64'
308      known = '64'
309    else:
310      flexiblas = 'libflexiblas.a'
311      ILP64 = '_lp64'
312      known = '32'
313
314    if self.openmp.found:
315      ITHREADS=['intel_thread','gnu_thread']
316      ompthread = 'yes'
317    else:
318      ITHREADS=['sequential']
319      ompthread = 'no'
320
321    # Try specified installation root
322    if 'with-blaslapack-dir' in self.argDB:
323      dir = self.argDB['with-blaslapack-dir']
324      # error if package-dir is in externalpackages
325      if os.path.realpath(dir).find(os.path.realpath(self.externalPackagesDir)) >=0:
326        fakeExternalPackagesDir = dir.replace(os.path.realpath(dir).replace(os.path.realpath(self.externalPackagesDir),''),'')
327        raise RuntimeError('Bad option: '+'--with-blaslapack-dir='+self.argDB['with-blaslapack-dir']+'\n'+
328                           fakeExternalPackagesDir+' is reserved for --download-package scratch space. \n'+
329                           'Do not install software in this location nor use software in this directory.')
330      if self.defaultPrecision == '__float128':
331        yield ('User specified installation root (F2CBLASLAPACK)', os.path.join(dir,'libf2cblas.a'), os.path.join(dir, 'libf2clapack.a'), '32','no')
332        raise RuntimeError('__float128 precision requires f2c libraries; they are not available in '+dir+'; suggest --download-f2cblaslapack\n')
333
334      if not (len(dir) > 2 and dir[1] == ':') :
335        dir = os.path.abspath(dir)
336      self.log.write('Looking for BLAS/LAPACK in user specified directory: '+dir+'\n')
337      self.log.write('Files and directories in that directory:\n'+str(os.listdir(dir))+'\n')
338
339      # Look for multi-threaded MKL for MKL_C/Pardiso
340      useCPardiso=0
341      usePardiso=0
342      if self.argDB['with-mkl_cpardiso'] or 'with-mkl_cpardiso-dir' in self.argDB or 'with-mkl_cpardiso-lib' in self.argDB:
343        useCPardiso=1
344        if self.mpi.found and hasattr(self.mpi, 'ompi_major_version'):
345          mkl_blacs_64=[['mkl_blacs_openmpi'+ILP64+'']]
346          mkl_blacs_32=[['mkl_blacs_openmpi']]
347        else:
348          mkl_blacs_64=[['mkl_blacs_intelmpi'+ILP64+''],['mkl_blacs_mpich'+ILP64+''],['mkl_blacs_sgimpt'+ILP64+''],['mkl_blacs_openmpi'+ILP64+'']]
349          mkl_blacs_32=[['mkl_blacs_intelmpi'],['mkl_blacs_mpich'],['mkl_blacs_sgimpt'],['mkl_blacs_openmpi']]
350      elif self.argDB['with-mkl_pardiso'] or 'with-mkl_pardiso-dir' in self.argDB or 'with-mkl_pardiso-lib' in self.argDB:
351        usePardiso=1
352        mkl_blacs_64=[[]]
353        mkl_blacs_32=[[]]
354      if useCPardiso or usePardiso:
355        self.logPrintBox('BLASLAPACK: Looking for multi-threaded MKL for C/Pardiso')
356        for libdir in self.libDirs:
357          if not os.path.exists(os.path.join(dir,libdir)):
358            self.logPrint('MKL Path not found.. skipping: '+os.path.join(dir,libdir))
359          else:
360            self.log.write('Files and directories in that directory:\n'+str(os.listdir(os.path.join(dir,libdir)))+'\n')
361            #  iomp5 is provided by the Intel compilers on macOS. Run source /opt/intel/bin/compilervars.sh intel64 to have it added to LIBRARY_PATH
362            #  then locate libimp5.dylib in the LIBRARY_PATH and copy it to os.path.join(dir,libdir)
363            for i in mkl_blacs_64:
364              yield ('User specified MKL-C/Pardiso Intel-Linux64', None, [os.path.join(dir,libdir,'libmkl_intel'+ILP64+'.a'),'mkl_core','mkl_intel_thread']+i+['iomp5','dl','pthread'],known,'yes')
365              yield ('User specified MKL-C/Pardiso GNU-Linux64', None, [os.path.join(dir,libdir,'libmkl_intel'+ILP64+'.a'),'mkl_core','mkl_gnu_thread']+i+['gomp','dl','pthread'],known,'yes')
366              yield ('User specified MKL-Pardiso Intel-Windows64', None, [os.path.join(dir,libdir,'mkl_core.lib'),'mkl_intel'+ILP64+'.lib','mkl_intel_thread.lib']+i+['libiomp5md.lib'],known,'yes')
367            for i in mkl_blacs_32:
368              yield ('User specified MKL-C/Pardiso Intel-Linux32', None, [os.path.join(dir,libdir,'libmkl_intel.a'),'mkl_core','mkl_intel_thread']+i+['iomp5','dl','pthread'],'32','yes')
369              yield ('User specified MKL-C/Pardiso GNU-Linux32', None, [os.path.join(dir,libdir,'libmkl_intel.a'),'mkl_core','mkl_gnu_thread']+i+['gomp','dl','pthread'],'32','yes')
370              yield ('User specified MKL-Pardiso Intel-Windows32', None, [os.path.join(dir,libdir,'mkl_core.lib'),'mkl_intel_c.lib','mkl_intel_thread.lib']+i+['libiomp5md.lib'],'32','yes')
371        return
372
373      self.log.write('Files and directories in that directory:\n'+str(os.listdir(dir))+'\n')
374      # Check MATLAB [ILP64] MKL
375      yield ('User specified MATLAB [ILP64] MKL Linux lib dir', None, [os.path.join(dir,'bin','glnxa64','mkl.so'), os.path.join(dir,'sys','os','glnxa64','libiomp5.so'), 'pthread'],'64','yes')
376      oldFlags = self.setCompilers.LDFLAGS
377      self.setCompilers.LDFLAGS += '-Wl,-rpath,'+os.path.join(dir,'bin','maci64')
378      yield ('User specified MATLAB [ILP64] MKL macOS lib dir', None, [os.path.join(dir,'bin','maci64','mkl.dylib'), os.path.join(dir,'sys','os','maci64','libiomp5.dylib'), 'pthread'],'64','yes')
379      self.setCompilers.LDFLAGS = oldFlags
380      for ITHREAD in ITHREADS:
381        yield ('User specified MKL11/12 and later', None, [os.path.join(dir,'libmkl_intel'+ILP64+'.a'),'mkl_core','mkl_'+ITHREAD,'pthread'],known,ompthread)
382      # Some new MKL 11/12 variations
383      for libdir in self.libDirs:
384        if not os.path.exists(os.path.join(dir,libdir)):
385          self.logPrint('MKL Path not found.. skipping: '+os.path.join(dir,libdir))
386        else:
387          self.log.write('Files and directories in that directory:\n'+str(os.listdir(os.path.join(dir,libdir)))+'\n')
388          for ITHREAD in ITHREADS:
389            yield ('User specified MKL11/12 Linux32', None, [os.path.join(dir,libdir,'libmkl_intel'+ILP64+'.a'),'mkl_core','mkl_'+ITHREAD,'pthread'],known,ompthread)
390            yield ('User specified MKL11/12 Linux32 for static linking (Cray)', None, ['-Wl,--start-group',os.path.join(dir,libdir,'libmkl_intel'+ILP64+'.a'),'mkl_core','mkl_'+ITHREAD,'-Wl,--end-group','pthread'],known,ompthread)
391      for libdir in self.libDirs:
392        if not os.path.exists(os.path.join(dir,libdir)):
393          self.logPrint('MKL Path not found.. skipping: '+os.path.join(dir,libdir))
394        else:
395          self.log.write('Files and directories in that directory:\n'+str(os.listdir(os.path.join(dir,libdir)))+'\n')
396          for ITHREAD in ITHREADS:
397            yield ('User specified MKL11+ Linux64', None, [os.path.join(dir,libdir,'libmkl_intel'+ILP64+'.a'),'mkl_core','mkl_'+ITHREAD,'mkl_def','pthread'],known,ompthread)
398            yield ('User specified MKL11+ Mac-64', None, [os.path.join(dir,libdir,'libmkl_intel'+ILP64+'.a'),'mkl_core','mkl_'+ITHREAD,'pthread'],known,ompthread)
399      # Older Linux MKL checks
400      yield ('User specified MKL Linux lib dir', None, [os.path.join(dir, 'libmkl_lapack.a'), 'mkl', 'guide', 'pthread'],'32','no')
401      for libdir in self.libDirs:
402        if not os.path.exists(os.path.join(dir,libdir)):
403          self.logPrint('MKL Path not found.. skipping: '+os.path.join(dir,libdir))
404        else:
405          self.log.write('Files and directories in that directory:\n'+str(os.listdir(os.path.join(dir,libdir)))+'\n')
406          yield ('User specified MKL Linux installation root', None, [os.path.join(dir,'lib',libdir,'libmkl_lapack.a'),'mkl', 'guide', 'pthread'],'32','no')
407      yield ('User specified MKL Linux-x86 lib dir', None, [os.path.join(dir, 'libmkl_lapack.a'), 'libmkl_def.a', 'guide', 'pthread'],'32','no')
408      yield ('User specified MKL Linux-x86 lib dir', None, [os.path.join(dir, 'libmkl_lapack.a'), 'libmkl_def.a', 'guide', 'vml','pthread'],'32','no')
409      yield ('User specified MKL Linux-ia64 lib dir', None, [os.path.join(dir, 'libmkl_lapack.a'), 'libmkl_ipf.a', 'guide', 'pthread'],'32','no')
410      yield ('User specified MKL Linux-em64t lib dir', None, [os.path.join(dir, 'libmkl_lapack.a'), 'libmkl_em64t.a', 'guide', 'pthread'],'32','no')
411      yield ('User specified MKL Linux-x86 installation root', None, [os.path.join(dir,'lib','32','libmkl_lapack.a'),'libmkl_def.a', 'guide', 'pthread'],'32','no')
412      yield ('User specified MKL Linux-x86 installation root', None, [os.path.join(dir,'lib','32','libmkl_lapack.a'),'libmkl_def.a', 'guide', 'vml','pthread'],'32','no')
413      yield ('User specified MKL Linux-ia64 installation root', None, [os.path.join(dir,'lib','64','libmkl_lapack.a'),'libmkl_ipf.a', 'guide', 'pthread'],'32','no')
414      yield ('User specified MKL Linux-em64t installation root', None, [os.path.join(dir,'lib','em64t','libmkl_lapack.a'),'libmkl_em64t.a', 'guide', 'pthread'],'32','no')
415      # Mac MKL check
416      yield ('User specified MKL Mac-x86 lib dir', None, [os.path.join(dir, 'libmkl_lapack.a'), 'libmkl_ia32.a', 'guide'],'32','no')
417      yield ('User specified MKL Max-x86 installation root', None, [os.path.join(dir,'Libraries','32','libmkl_lapack.a'),'libmkl_ia32.a', 'guide'],'32','no')
418      yield ('User specified MKL Max-x86 installation root', None, [os.path.join(dir,'lib','32','libmkl_lapack.a'),'libmkl_ia32.a', 'guide'],'32','no')
419      yield ('User specified MKL Mac-em64t lib dir', None, [os.path.join(dir, 'libmkl_lapack.a'), 'libmkl_intel'+ILP64+'.a', 'guide'],known,'no')
420      yield ('User specified MKL Max-em64t installation root', None, [os.path.join(dir,'Libraries','32','libmkl_lapack.a'),'libmkl_intel'+ILP64+'.a', 'guide'],'32','no')
421      yield ('User specified MKL Max-em64t installation root', None, [os.path.join(dir,'lib','32','libmkl_lapack.a'),'libmkl_intel'+ILP64+'.a', 'guide'],'32','no')
422      # Check MKL on windows
423      yield ('User specified MKL Windows lib dir', None, [os.path.join(dir, 'mkl_c_dll.lib')],'32','no')
424      yield ('User specified stdcall MKL Windows lib dir', None, [os.path.join(dir, 'mkl_s_dll.lib')],'32','no')
425      yield ('User specified ia64/em64t MKL Windows lib dir', None, [os.path.join(dir, 'mkl_dll.lib')],'32','no')
426      for ITHREAD in ITHREADS:
427        yield ('User specified MKL10-32 Windows lib dir', None, [os.path.join(dir, 'mkl_intel_c_dll.lib'),'mkl_'+ITHREAD+'_dll.lib','mkl_core_dll.lib','libiomp5md.lib'],'32',ompthread)
428        yield ('User specified MKL10-32 Windows stdcall lib dir', None, [os.path.join(dir, 'mkl_intel_s_dll.lib'),'mkl_'+ITHREAD+'_dll.lib','mkl_core_dll.lib','libiomp5md.lib'],'32',ompthread)
429        yield ('User specified MKL10-64 Windows lib dir', None, [os.path.join(dir, 'mkl_intel'+ILP64+'_dll.lib'),'mkl_'+ITHREAD+'_dll.lib','mkl_core_dll.lib','libiomp5md.lib'],known,ompthread)
430      mkldir = os.path.join(dir, 'ia32', 'lib')
431      yield ('User specified MKL Windows installation root', None, [os.path.join(mkldir, 'mkl_c_dll.lib')],'32','no')
432      yield ('User specified stdcall MKL Windows installation root', None, [os.path.join(mkldir, 'mkl_s_dll.lib')],'32','no')
433      for ITHREAD in ITHREADS:
434        yield ('User specified MKL10-32 Windows installation root', None, [os.path.join(mkldir, 'mkl_intel_c_dll.lib'),'mkl_'+ITHREAD+'_dll.lib','mkl_core_dll.lib','libiomp5md.lib'],'32',ompthread)
435        yield ('User specified MKL10-32 Windows stdcall installation root', None, [os.path.join(mkldir, 'mkl_intel_s_dll.lib'),'mkl_'+ITHREAD+'_dll.lib','mkl_core_dll.lib','libiomp5md.lib'],'32',ompthread)
436      mkldir = os.path.join(dir, 'em64t', 'lib')
437      for ITHREAD in ITHREADS:
438        yield ('User specified MKL10-64 Windows installation root', None, [os.path.join(mkldir, 'mkl_intel'+ILP64+'_dll.lib'),'mkl_'+ITHREAD+'_dll.lib','mkl_core_dll.lib','libiomp5md.lib'],known,ompthread)
439      yield ('User specified em64t MKL Windows installation root', None, [os.path.join(mkldir, 'mkl_dll.lib')],'32','no')
440      mkldir = os.path.join(dir, 'ia64', 'lib')
441      yield ('User specified ia64 MKL Windows installation root', None, [os.path.join(mkldir, 'mkl_dll.lib')],'32','no')
442      for ITHREAD in ITHREADS:
443        yield ('User specified MKL10-64 Windows installation root', None, [os.path.join(mkldir, 'mkl_intel'+ILP64+'_dll.lib'),'mkl_'+ITHREAD+'_dll.lib','mkl_core_dll.lib','libiomp5md.lib'],known,ompthread)
444      # Check AMD ACML libraries
445      yield ('User specified AMD ACML lib dir', None, os.path.join(dir,'lib','libacml.a'),'32','unknown')
446      yield ('User specified AMD ACML lib dir', None, [os.path.join(dir,'lib','libacml.a'), os.path.join(dir,'lib','libacml_mv.a')],'32','unknown')
447      yield ('User specified AMD ACML lib dir', None, os.path.join(dir,'lib','libacml_mp.a'),'32','unknown')
448      yield ('User specified AMD ACML lib dir', None, [os.path.join(dir,'lib','libacml_mp.a'), os.path.join(dir,'lib','libacml_mv.a')],'32','unknown')
449      # Check BLIS/AMD-AOCL libraries
450      for lib in blislib:
451        for lapack in ['libflame.a','liblapack.a']:
452          for libdir in [dir,os.path.join(dir,'lib')]:
453            yield ('User specified installation root BLIS/AMD-AOCL', os.path.join(libdir,lib), os.path.join(libdir,lapack), 'unknown', 'unknown')
454      # NEC
455      yield ('User specified NEC lib dir', os.path.join(dir, 'lib', 'libblas_sequential.a'), [os.path.join(dir, 'lib', 'liblapack.a'), os.path.join(dir, 'lib', 'libasl_sequential.a')], 'unknown', 'unknown')
456      yield ('User specified NEC lib dir', os.path.join(dir, 'lib', 'libblas_sequential.a'), os.path.join(dir, 'lib', 'liblapack.a'), 'unknown', 'unknown')
457      # Search for FlexiBLAS
458      for libdir in ['lib64', 'lib', '']:
459        if os.path.exists(os.path.join(dir,libdir)):
460            yield ('User specified FlexiBLAS',None,os.path.join(dir,libdir,flexiblas),known,'unknown')
461      # Search for OpenBLAS
462      for libdir in ['lib','']:
463        if os.path.exists(os.path.join(dir,libdir)):
464          yield ('User specified OpenBLAS',None,os.path.join(dir,libdir,'libopenblas.a'),'unknown','unknown')
465      # Search for atlas
466      yield ('User specified ATLAS Linux installation root', [os.path.join(dir, 'libcblas.a'),os.path.join(dir, 'libf77blas.a'), os.path.join(dir, 'libatlas.a')],  [os.path.join(dir, 'liblapack.a')],'32','no')
467      yield ('User specified ATLAS Linux installation root', [os.path.join(dir, 'libf77blas.a'), os.path.join(dir, 'libatlas.a')],  [os.path.join(dir, 'liblapack.a')],'32','no')
468
469      yield ('User specified installation root (HPUX)', os.path.join(dir, 'libveclib.a'),  os.path.join(dir, 'liblapack.a'),'32','unknown')
470      for libdir in ['lib64','lib','']:
471        if os.path.exists(os.path.join(dir,libdir)):
472          yield ('User specified installation root (F2CBLASLAPACK)', os.path.join(dir,libdir,'libf2cblas.a'), os.path.join(dir,libdir,'libf2clapack.a'),'32','no')
473      yield ('User specified installation root(NETLIB-LAPACK)', os.path.join(dir, 'libnblas.a'), os.path.join(dir, 'libnlapack.a'),'32','no')
474      yield ('User specified installation root(FBLASLAPACK)', os.path.join(dir, 'libfblas.a'),   os.path.join(dir, 'libflapack.a'),'32','no')
475      for lib in ['','lib64']:
476        yield ('User specified installation root IBM ESSL', None, os.path.join(dir, lib, 'libessl.a'),'32','unknown')
477      # Search for liblapack.a and libblas.a after the implementations with more specific name to avoid
478      # finding these in /usr/lib despite using -L<blaslapack-dir> while attempting to get a different library.
479      for libdir in ['lib64','lib','']:
480        if os.path.exists(os.path.join(dir,libdir)):
481          yield ('User specified installation root BLAS/LAPACK',os.path.join(dir,libdir,'libblas.a'),os.path.join(dir,libdir,'liblapack.a'),'unknown','unknown')
482      if hasattr(self,'checkingMKROOTautomatically'):
483        raise RuntimeError('Unable to locate working BLAS/LAPACK libraries, even tried libraries in MKLROOT '+self.argDB['with-blaslapack-dir']+'\n')
484      else:
485        raise RuntimeError('You set a value for --with-blaslapack-dir=<dir>, but '+self.argDB['with-blaslapack-dir']+' cannot be used\n')
486    if self.defaultPrecision == '__float128':
487      raise RuntimeError('__float128 precision requires f2c libraries; suggest --download-f2cblaslapack\n')
488
489    # Try compiler defaults
490    yield ('Default compiler libraries', '', '','unknown','unknown')
491    yield ('Default NEC', 'libblas_sequential.a', ['liblapack.a','libasl_sequential.a'],'unknown','unknown')
492    yield ('Default NEC', 'libblas_sequential.a', 'liblapack.a','unknown','unknown')
493    yield ('Default FlexiBLAS', None, flexiblas, known, 'unknown')
494    for lib in blislib:
495      for lapack in ['libflame.a','liblapack.a']:
496        yield ('Default BLIS/AMD-AOCL', lib, lapack,'unknown','unknown')
497    yield ('Default compiler locations', 'libblas.a', 'liblapack.a','unknown','unknown')
498    yield ('Default compiler locations (all contained in libblas)', None, 'libblas.a','unknown','unknown')
499    yield ('Default NVHPC', None, ['liblapack.a','libblas.a','libnvf.a','librt.a'],'unknown','unknown')
500    yield ('Default OpenBLAS', None, 'libopenblas.a','unknown','unknown')
501    # Intel on Mac
502    for ITHREAD in ITHREADS:
503      yield ('User specified MKL Mac-64', None, [os.path.join('/opt','intel','mkl','lib','libmkl_intel'+ILP64+'.a'),'mkl_'+ITHREAD,'mkl_core','pthread'],known,ompthread)
504    # Try Microsoft Windows location
505    for MKL_Version in [os.path.join('MKL','9.0'),os.path.join('MKL','8.1.1'),os.path.join('MKL','8.1'),os.path.join('MKL','8.0.1'),os.path.join('MKL','8.0'),'MKL72','MKL70','MKL61','MKL']:
506      mklpath = os.path.join('/cygdrive', 'c', 'Program Files', 'Intel', MKL_Version)
507      if not os.path.exists(mklpath):
508        self.logPrint('MKL Path not found.. skipping: '+mklpath)
509      else:
510        mkldir = os.path.join(mklpath, 'ia32', 'lib')
511        yield ('Microsoft Windows, Intel MKL library', None, os.path.join(mkldir,'mkl_c_dll.lib'),'32','no')
512        yield ('Microsoft Windows, Intel MKL stdcall library', None, os.path.join(mkldir,'mkl_s_dll.lib'),'32','no')
513        mkldir = os.path.join(mklpath, 'em64t', 'lib')
514        yield ('Microsoft Windows, em64t Intel MKL library', None, os.path.join(mkldir,'mkl_dll.lib'),'32','no')
515        mkldir = os.path.join(mklpath, 'ia64', 'lib')
516        yield ('Microsoft Windows, ia64 Intel MKL library', None, os.path.join(mkldir,'mkl_dll.lib'),'32','no')
517    # IRIX locations
518    yield ('IRIX Mathematics library', None, 'libcomplib.sgimath.a','32','unknown')
519    yield ('Another IRIX Mathematics library', None, 'libscs.a','32','unknown')
520    yield ('Compaq/Alpha Mathematics library', None, 'libcxml.a','32','unknown')
521    # IBM ESSL locations
522    yield ('IBM ESSL Mathematics library', None, 'libessl.a','32','unknown')
523    yield ('IBM ESSL Mathematics library for Blue Gene', None, 'libesslbg.a','32','unknown')
524    yield ('HPUX', 'libveclib.a', 'liblapack.a','unknown','unknown')
525    # /usr/local/lib
526    dir = os.path.join('/usr','local','lib')
527    yield ('Default compiler locations /usr/local/lib', os.path.join(dir,'libblas.a'), os.path.join(dir,'liblapack.a'),'unknown','unknown')
528    yield ('Default compiler locations /usr/local/lib', None, os.path.join(dir,'libopenblas.a'),'unknown','unknown')
529    yield ('Default compiler locations with gfortran', None, ['liblapack.a', 'libblas.a','libgfortran.a'],'unknown','unknown')
530    yield ('Default Atlas location',['libcblas.a','libf77blas.a','libatlas.a'],  ['liblapack.a'],'unknown','unknown')
531    yield ('Default Atlas location',['libf77blas.a','libatlas.a'],  ['liblapack.a'],'unknown','unknown')
532    yield ('Default compiler locations with G77', None, ['liblapack.a', 'libblas.a','libg2c.a'],'unknown','unknown')
533    # Try macOS location
534    dir = os.path.join('/Library', 'Frameworks', 'Intel_MKL.framework','Libraries','32')
535    yield ('macOS with Intel MKL', None, [os.path.join(dir,'libmkl_lapack.a'),'libmkl_ia32.a','libguide.a'],'32','no')
536    yield ('macOS BLAS/LAPACK library', None, os.path.join('/System', 'Library', 'Frameworks', 'vecLib.framework', 'vecLib'),'32','unknown')
537    # Sun locations; this don't currently work
538    yield ('Sun sunperf BLAS/LAPACK library', None, ['libsunperf.a','libsunmath.a'],'32','no')
539    yield ('Sun sunperf BLAS/LAPACK library', None, ['libsunperf.a','libF77.a','libM77.a','libsunmath.a'],'32','no')
540    yield ('Sun sunperf BLAS/LAPACK library', None, ['libsunperf.a','libfui.a','libfsu.a','libsunmath.a'],'32','no')
541    # Try Microsoft Windows location
542    for MKL_Version in [os.path.join('MKL','9.0'),os.path.join('MKL','8.1.1'),os.path.join('MKL','8.1'),os.path.join('MKL','8.0.1'),os.path.join('MKL','8.0'),'MKL72','MKL70','MKL61','MKL']:
543      mklpath = os.path.join('/cygdrive', 'c', 'Program Files', 'Intel', MKL_Version)
544      if not os.path.exists(mklpath):
545        self.logPrint('MKL Path not found.. skipping: '+mklpath)
546      else:
547        mkldir = os.path.join(mklpath, 'ia32', 'lib')
548        if os.path.exists(mkldir):
549          self.log.write('Files and directories in that directory:\n'+str(os.listdir(mkldir))+'\n')
550          yield ('Microsoft Windows, Intel MKL library', None, os.path.join(mkldir,'mkl_c_dll.lib'),'32','no')
551          yield ('Microsoft Windows, Intel MKL stdcall library', None, os.path.join(mkldir,'mkl_s_dll.lib'),'32','no')
552        mkldir = os.path.join(mklpath, 'em64t', 'lib')
553        if os.path.exists(mkldir):
554          self.log.write('Files and directories in that directory:\n'+str(os.listdir(mkldir))+'\n')
555          yield ('Microsoft Windows, em64t Intel MKL library', None, os.path.join(mkldir,'mkl_dll.lib'),'32','no')
556        mkldir = os.path.join(mklpath, 'ia64', 'lib')
557        if os.path.exists(mkldir):
558          self.log.write('Files and directories in that directory:\n'+str(os.listdir(mkldir))+'\n')
559          yield ('Microsoft Windows, ia64 Intel MKL library', None, os.path.join(mkldir,'mkl_dll.lib'),'32','no')
560    return
561
562  def configureLibrary(self):
563    if hasattr(self.compilers, 'FC'):
564      self.alternativedownload = 'fblaslapack'
565
566    # find working BLAS/LAPACK
567    self.foundBlas   = 0
568    self.foundLapack = 0
569    for (name, blasLibrary, lapackLibrary, known64, usesopenmp) in self.generateGuesses():
570      self.log.write('================================================================================\n')
571      self.log.write('Checking for BLAS and LAPACK in '+name+'\n')
572      (foundBlas, foundLapack) = self.executeTest(self.checkLib, [lapackLibrary, blasLibrary])
573      if foundBlas and foundLapack:
574        self.foundBlas   = 1
575        self.foundLapack = 1
576        self.known64     = known64
577        self.usesopenmp  = usesopenmp
578        if not isinstance(blasLibrary, list):
579          self.blasLibrary = [blasLibrary]
580        else:
581          self.blasLibrary = blasLibrary
582        if not isinstance(lapackLibrary, list):
583          self.lapackLibrary = [lapackLibrary]
584        else:
585          self.lapackLibrary = lapackLibrary
586        self.lib = []
587        if self.lapackLibrary[0]:
588          self.lib.extend(self.lapackLibrary)
589        if self.blasLibrary[0]:
590          self.lib.extend(self.blasLibrary)
591        self.dlib = self.lib+self.dlib
592        self.framework.packages.append(self)
593        break
594      self.include = []
595
596    # error if not found
597    if not self.foundBlas:
598      # check for split blas/blas-dev packages
599      import glob
600      blib = glob.glob('/usr/lib/libblas.*')
601      if blib != [] and not (os.path.isfile('/usr/lib/libblas.so') or os.path.isfile('/usr/lib/libblas.a')):
602        raise RuntimeError('Incomplete system BLAS install detected. Perhaps you need to install blas-dev or blas-devel package - that contains /usr/lib/libblas.so using apt or yum or equivalent package manager?')
603      if 'known-blaslapack-mangling' in self.argDB:
604        known_mangling = self.argDB['known-blaslapack-mangling']
605        raise RuntimeError('Failed to automatically detect BLAS libraries matching the mangling set with --known-blaslapack-mangling='+known_mangling+', try removing this for automatically detected mangling.')
606      if hasattr(self.compilers, 'FC') and (self.defaultPrecision != '__float128') and (self.defaultPrecision != '__fp16'):
607        pkg = 'fblaslapack'
608      else:
609        pkg = 'f2cblaslapack'
610      raise RuntimeError('Could not find a functional BLAS. Run with --with-blas-lib=<lib> to indicate the library containing BLAS.\n Or --download-'+pkg+'=1 to have one automatically downloaded and installed\n')
611    if not self.foundLapack:
612      # check for split blas/blas-dev packages
613      import glob
614      llib = glob.glob('/usr/lib/liblapack.*')
615      if llib != [] and not (os.path.isfile('/usr/lib/liblapack.so') or os.path.isfile('/usr/lib/liblapack.a')):
616        raise RuntimeError('Incomplete system LAPACK install detected. Perhaps you need to install lapack-dev or lapack-devel package - that contains /usr/lib/liblapack.so using apt or yum or equivalent package manager?')
617      if 'known-blaslapack-mangling' in self.argDB:
618        known_mangling = self.argDB['known-blaslapack-mangling']
619        raise RuntimeError('Failed to automatically detect LAPACK libraries matching the mangling set with --known-blaslapack-mangling='+known_mangling+', try removing this for automatically detected mangling.')
620      if hasattr(self.compilers, 'FC') and (self.defaultPrecision != '__float128') and (self.defaultPrecision != '__fp16'):
621        pkg = 'fblaslapack'
622      else:
623        pkg = 'f2cblaslapack'
624      raise RuntimeError('Could not find a functional LAPACK. Run with --with-lapack-lib=<lib> to indicate the library containing LAPACK.\n Or --download-'+pkg+'=1 to have one automatically downloaded and installed\n')
625
626    self.found = 1
627
628    if self.mangling == 'underscore':
629      self.addDefine('BLASLAPACK_UNDERSCORE', 1)
630    elif self.mangling == 'caps':
631      self.addDefine('BLASLAPACK_CAPS', 1)
632
633    if self.suffix != '':
634      self.addDefine('BLASLAPACK_SUFFIX', self.suffix)
635
636    if self.f2cblaslapack.found:
637      oldLibs = self.compilers.LIBS
638      routine___float128 = self.mangleBlasNoPrefix('qdot')
639      routine___fp16 = self.mangleBlasNoPrefix('hdot')
640      self.libraries.saveLog()
641      if self.defaultPrecision != '__float128':
642        found = self.libraries.check(self.blasLibrary, routine___float128, fortranMangle = 0)
643        if found:
644          self.addDefine('HAVE_F2CBLASLAPACK___FLOAT128_BINDINGS', 1)
645      if self.defaultPrecision != '__fp16':
646        found = self.libraries.check(self.blasLibrary, routine___fp16, fortranMangle = 0)
647        if found:
648          self.addDefine('HAVE_F2CBLASLAPACK___FP16_BINDINGS', 1)
649      self.logWrite(self.libraries.restoreLog())
650      self.compilers.LIBS = oldLibs
651
652    if not self.f2cblaslapack.found and not self.netliblapack.found and not self.fblaslapack.found:
653      self.executeTest(self.checkMKL)
654      if not self.mkl:
655        self.executeTest(self.checkESSL)
656        self.executeTest(self.checkPESSL)
657        self.executeTest(self.checkMissing)
658    self.executeTest(self.checklsame)
659
660    # check for shared library support
661    if self.argDB['with-shared-libraries']:
662      symbol = self.mangleBlas('geev')
663      if not self.setCompilers.checkIntoShared(symbol,self.lapackLibrary+self.getOtherLibs()):
664        raise RuntimeError('The BLAS/LAPACK libraries '+self.libraries.toStringNoDupes(self.lapackLibrary+self.getOtherLibs())+'\ncannot be used with a shared library\nEither run ./configure with --with-shared-libraries=0 or use a different BLAS/LAPACK library');
665
666    # set self.has64bitindices
667    self.executeTest(self.checkRuntimeIssues)
668    if self.mkl and self.has64bitindices:
669      self.addDefine('HAVE_MKL_INTEL_ILP64',1)
670    if self.argDB['with-64-bit-blas-indices'] and not self.has64bitindices:
671      raise RuntimeError('You requested 64-bit integer BLAS/LAPACK using --with-64-bit-blas-indices but they are not available given your other BLAS/LAPACK options')
672    if self.libraries.check(self.dlib, 'bli_thread_set_num_threads') and not self.libraries.check(self.dlib, 'flexiblas_avail'):
673      self.addDefine('HAVE_BLI_THREAD_SET_NUM_THREADS',1)
674    if self.libraries.check(self.dlib, 'openblas_set_num_threads') and not self.libraries.check(self.dlib, 'flexiblas_avail'):
675      self.addDefine('HAVE_OPENBLAS_SET_NUM_THREADS',1)
676    if self.libraries.check(self.dlib, 'APL_dgemm') and not self.libraries.check(self.dlib, 'flexiblas_avail'):
677      self.addDefine('HAVE_APPLE_ACCELERATE',1)
678
679  def checkMKL(self):
680    '''Check for Intel MKL library'''
681    self.libraries.saveLog()
682    self.include = []
683    self.defaultincludepath = False
684    if self.libraries.check(self.dlib, 'mkl_set_num_threads') and not self.libraries.check(self.dlib, 'flexiblas_avail'):
685      self.mkl = 1
686      self.addDefine('HAVE_MKL_LIBS',1)
687      '''Set include directory for mkl.h and friends'''
688      '''(the include directory is in CPATH if mklvars.sh has been sourced.'''
689      ''' if the script hasn't been sourced, we still try to pick up the include dir)'''
690      if 'with-blaslapack-include' in self.argDB:
691        incl = self.argDB['with-blaslapack-include']
692        if not isinstance(incl, list): incl = [incl]
693        self.include = incl
694      if self.checkCompile('#include "mkl_spblas.h"',''):
695        self.mkl_spblas_h = 1
696        self.logPrint('MKL mkl_spblas.h found in default include path.')
697        self.defaultincludepath = True
698      else:
699        self.logPrint('MKL include path not automatically picked up by compiler. Trying to find mkl_spblas.h...')
700        if 'with-blaslapack-dir' in self.argDB:
701          pathlist = [os.path.join(self.argDB['with-blaslapack-dir'],'include'),
702                      os.path.join(self.argDB['with-blaslapack-dir'],'..','include'),
703                      os.path.join(self.argDB['with-blaslapack-dir'],'..','..','include')]
704        elif 'with-blaslapack-include' in self.argDB:
705          pathlist = self.include
706        else:
707          pathlist = []
708        for path in pathlist:
709          if os.path.isdir(path) and self.checkInclude([path], ['mkl_spblas.h']):
710            self.include = [path]
711            self.mkl_spblas_h = 1
712            self.logPrint('MKL mkl_spblas.h found at:'+path)
713            break
714
715        if not self.mkl_spblas_h:
716          self.include = []
717          self.logPrint('Unable to find MKL include directory!')
718        else:
719          self.logPrint('MKL include path set to ' + str(self.include))
720      self.versionname    = 'INTEL_MKL_VERSION'
721      self.versioninclude = 'mkl_version.h'
722      self.versiontitle   = 'Intel MKL Version'
723      if hasattr(self,'dinclude'):
724        [self.dinclude.append(inc) for inc in self.include if inc not in self.dinclude]
725      else:
726        self.dinclude = self.include
727      self.checkVersion()
728      if self.include or self.defaultincludepath:
729        self.addDefine('HAVE_MKL_INCLUDES',1)
730        self.addDefine('HAVE_MKL_SET_NUM_THREADS',1)
731    self.logWrite(self.libraries.restoreLog())
732    return
733
734  def checkESSL(self):
735    '''Check for the IBM ESSL library'''
736    self.libraries.saveLog()
737    if self.libraries.check(self.dlib, 'iessl'):
738      self.essl = 1
739      self.addDefine('HAVE_ESSL',1)
740
741      if 'with-blaslapack-include' in self.argDB:
742        incl = self.argDB['with-blaslapack-include']
743        if not isinstance(incl, list): incl = [incl]
744      elif 'with-blaslapack-dir' in self.argDB:
745        incl = [os.path.join(self.argDB['with-blaslapack-dir'],'include')]
746      else:
747        return
748      linc = self.include + incl
749      if self.checkInclude(linc, ['essl.h']):
750        self.include = linc
751    self.logWrite(self.libraries.restoreLog())
752    return
753
754  def checkPESSL(self):
755    '''Check for the IBM PESSL library - and error out - if used instead of ESSL'''
756    self.libraries.saveLog()
757    if self.libraries.check(self.dlib, 'ipessl'):
758      self.logWrite(self.libraries.restoreLog())
759      raise RuntimeError('Cannot use PESSL instead of ESSL!')
760    self.logWrite(self.libraries.restoreLog())
761    return
762
763  def mangleBlas(self, baseName, mangling = None):
764    prefix = self.getPrefix()
765    return self.mangleBlasNoPrefix(prefix+baseName, mangling)
766
767  def mangleBlasNoPrefix(self, baseName, mangling = None):
768    if mangling is None:
769      mangling = getattr(self, 'mangling', 'unknown')
770
771    if mangling == 'underscore':
772      if not self.f2c:
773        if getattr(self.compilers, 'fortranManglingDoubleUnderscore', False) and baseName.find('_') >= 0:
774          return baseName.lower()+self.suffix+'__'
775      return baseName.lower()+self.suffix+'_'
776    elif mangling == 'unchanged':
777      return baseName.lower()+self.suffix
778    elif mangling == 'caps':
779      return baseName.upper()+self.suffix
780    else:
781      return baseName+self.suffix
782
783  def checkMissing(self):
784    '''Check for missing LAPACK routines'''
785    if self.foundLapack and hasattr(self.compilers, 'FC') and not self.f2c and self.mangling == 'unknown':
786      mangling = self.compilers.fortranMangling
787    else:
788      mangling = self.mangling
789    routines = ['gelss','gerfs','gges','hgeqz','hseqr','orgqr','ormqr','stebz',
790                'stegr','stein','steqr','stev','sytri','tgsen','trsen','trtrs','geqp3']
791    _, missing = self.checkLapack(self.lapackLibrary, self.getOtherLibs(), mangling, routines)
792    for baseName in routines:
793      if self.mangleBlas(baseName) in missing:
794        self.missingRoutines.append(baseName)
795        self.addDefine('MISSING_LAPACK_'+baseName.upper(), 1)
796
797  def checklsame(self):
798    ''' Do the BLAS/LAPACK libraries have a valid lsame() function with correct binding.'''
799    routine = 'lsame';
800    found = self.checkForRoutine(routine)
801    if not found:
802      self.addDefine('MISSING_LAPACK_'+self.mangleBlasNoPrefix(routine), 1)
803
804  def checkForRoutine(self,routine):
805    ''' used by other packages to see if a BLAS routine is available
806        This is not really correct because other packages do not (usually) know about f2cblasLapack'''
807    self.libraries.saveLog()
808    mangled_name = self.mangleBlasNoPrefix(routine)
809    ret = self.libraries.check(self.dlib,mangled_name,fortranMangle = 0)
810    self.logWrite(self.libraries.restoreLog())
811    return ret
812
813  def runTimeTest(self,name,includes,body,lib = None,nobatch=0):
814    '''Either runs a test or adds it to the batch of runtime tests'''
815    if name in self.framework.clArgDB: return self.argDB[name]
816    if self.argDB['with-batch']:
817      if nobatch:
818        raise RuntimeError('In batch mode you must provide the value for --'+name)
819      else:
820        self.framework.addBatchInclude(includes)
821        self.framework.addBatchBody(body)
822        if lib: self.framework.addBatchLib(lib)
823        if self.include: self.framework.batchIncludeDirs.extend([self.headers.getIncludeArgument(inc) for inc in self.include])
824        return None
825    else:
826      result = None
827      self.pushLanguage('C')
828      filename = 'runtimetestoutput'
829      body = '''FILE *output = fopen("'''+filename+'''","w");\n'''+body
830      if lib:
831        if not isinstance(lib, list): lib = [lib]
832        oldLibs  = self.compilers.LIBS
833        self.compilers.LIBS = self.libraries.toString(lib)+' '+self.compilers.LIBS
834      if self.checkRun(includes, body) and os.path.exists(filename):
835        f    = open(filename)
836        out  = f.read()
837        f.close()
838        os.remove(filename)
839        result = out.split("=")[1].split("'")[0]
840      self.popLanguage()
841      if lib:
842        self.compilers.LIBS = oldLibs
843      return result
844
845  def checkRuntimeIssues(self):
846    '''Determines if BLAS/LAPACK routines use 32 or 64-bit integers'''
847    if self.known64 == '64':
848      self.addDefine('HAVE_64BIT_BLAS_INDICES', 1)
849      self.has64bitindices = 1
850      self.log.write('64-bit BLAS indices based on the BLAS/LAPACK library being used\n')
851    elif self.known64 == '32':
852      self.log.write('32-bit BLAS indices based on the BLAS/LAPACK library being used\n')
853    elif 'known-64-bit-blas-indices' in self.argDB:
854      if self.argDB['known-64-bit-blas-indices']:
855        self.addDefine('HAVE_64BIT_BLAS_INDICES', 1)
856        self.has64bitindices = 1
857      else:
858        self.has64bitindices = 0
859    elif self.argDB['with-batch']:
860      self.logPrintWarning('Cannot determine if BLAS/LAPACK uses 32 or 64-bit integers \
861in batch-mode! Assuming 32-bit integers. Run with --known-64-bit-blas-indices \
862if you know they are 64-bit. Run with --known-64-bit-blas-indices=0 to remove \
863this warning message')
864      self.has64bitindices = 0
865      self.log.write('In batch mode with unknown size of BLAS/LAPACK defaulting to 32-bit\n')
866    else:
867      includes = '''#include <sys/types.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <stddef.h>\n\n'''
868      t = self.getType()
869      body     = '''extern '''+t+''' '''+self.mangleBlas('dot')+'''(const int*,const '''+t+'''*,const int *,const '''+t+'''*,const int*);
870                  '''+t+''' x1mkl[4] = {3.0,5.0,7.0,9.0};
871                  int one1mkl = 1,nmkl = 2;
872                  '''+t+''' dotresultmkl = 0;
873                  dotresultmkl = '''+self.mangleBlas('dot')+'''(&nmkl,x1mkl,&one1mkl,x1mkl,&one1mkl);
874                  fprintf(output, "-known-64-bit-blas-indices=%d",dotresultmkl != 34);'''
875      result = self.runTimeTest('known-64-bit-blas-indices',includes,body,self.dlib,nobatch=1)
876      if result is not None:
877        self.log.write('Checking for 64-bit BLAS/LAPACK indices: result ' +str(result)+'\n')
878        result = int(result)
879        if result:
880          if self.defaultPrecision == 'single':
881            self.log.write('Checking for 64-bit BLAS/LAPACK indices: special check for Apple single precision\n')
882            # On Apple single precision sdot() returns a double so we need to test that case
883            body     = '''extern double '''+self.mangleBlas('dot')+'''(const int*,const '''+t+'''*,const int *,const '''+t+'''*,const int*);
884                  '''+t+''' x1mkl[4] = {3.0,5.0,7.0,9.0};
885                  int one1mkl = 1,nmkl = 2;
886                  double dotresultmkl = 0;
887                  dotresultmkl = '''+self.mangleBlas('dot')+'''(&nmkl,x1mkl,&one1mkl,x1mkl,&one1mkl);
888                  fprintf(output, "--known-64-bit-blas-indices=%d",dotresultmkl != 34);'''
889            result = self.runTimeTest('known-64-bit-blas-indices',includes,body,self.dlib,nobatch=1)
890            result = int(result)
891        if result:
892          self.addDefine('HAVE_64BIT_BLAS_INDICES', 1)
893          self.has64bitindices = 1
894          self.log.write('Checking for 64-bit BLAS/LAPACK indices: result not equal to 1 so assuming 64-bit BLAS/LAPACK indices\n')
895      else:
896        self.addDefine('HAVE_64BIT_BLAS_INDICES', 1)
897        self.has64bitindices = 1
898        self.log.write('Checking for 64-bit BLAS/LAPACK indices: program did not return therefore assuming 64-bit BLAS/LAPACK indices\n')
899    self.log.write('Checking if sdot() returns a float or a double\n')
900    if 'known-sdot-returns-double' in self.argDB:
901      if self.argDB['known-sdot-returns-double']:
902        self.addDefine('BLASLAPACK_SDOT_RETURNS_DOUBLE', 1)
903    elif self.argDB['with-batch']:
904      self.logPrintWarning('Cannot determine if BLAS sdot() returns a float or a double \
905in batch-mode! Assuming float. Run with --known-sdot-returns-double=1 \
906if you know it returns a double (very unlikely). Run with \
907--known-sdot-returns-double=0 to remove this warning message')
908    else:
909      includes = '''#include <sys/types.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <stddef.h>\n'''
910      body     = '''extern float '''+self.mangleBlasNoPrefix('sdot')+'''(const int*,const float*,const int *,const float*,const int*);
911                  float x1[1] = {3.0};
912                  int one1 = 1;
913                  long long int ione1 = 1;
914                  float sdotresult = 0;
915                  int blasint64 = '''+str(self.has64bitindices)+''';\n
916                  if (!blasint64) {
917                       sdotresult = '''+self.mangleBlasNoPrefix('sdot')+'''(&one1,x1,&one1,x1,&one1);
918                     } else {
919                       sdotresult = '''+self.mangleBlasNoPrefix('sdot')+'''((const int*)&ione1,x1,(const int*)&ione1,x1,(const int*)&ione1);
920                     }
921                  fprintf(output, "--known-sdot-returns-double=%d",sdotresult != 9);\n'''
922      result = self.runTimeTest('known-sdot-returns-double',includes,body,self.dlib,nobatch=1)
923      if result:
924        self.log.write('Checking for sdot() return double: result ' +str(result)+'\n')
925        result = int(result)
926        if result:
927          self.addDefine('BLASLAPACK_SDOT_RETURNS_DOUBLE', 1)
928          self.log.write('Checking sdot(): Program did return with not 1 for output so assume returns double\n')
929      else:
930        self.log.write('Checking sdot(): Program did not return with output so assume returns single\n')
931    self.log.write('Checking if snrm() returns a float or a double\n')
932    if 'known-snrm2-returns-double' in self.argDB:
933      if self.argDB['known-snrm2-returns-double']:
934        self.addDefine('BLASLAPACK_SNRM2_RETURNS_DOUBLE', 1)
935    elif self.argDB['with-batch']:
936      self.logPrintWarning('Cannot determine if BLAS snrm2() returns a float or a double \
937in batch-mode! Assuming float. Run with --known-snrm2-returns-double=1 \
938if you know it returns a double (very unlikely). Run with \
939--known-snrm2-returns-double=0 to remove this warning message')
940    else:
941      includes = '''#include <sys/types.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <stddef.h>\n'''
942      body     = '''extern float '''+self.mangleBlasNoPrefix('snrm2')+'''(const int*,const float*,const int*);
943                  float x2[1] = {3.0};
944                  int one2 = 1;
945                  long long int ione2 = 1;
946                  float normresult = 0;
947                  int blasint64 = '''+str(self.has64bitindices)+''';\n
948                  if (!blasint64) {
949                       normresult = '''+self.mangleBlasNoPrefix('snrm2')+'''(&one2,x2,&one2);
950                     } else {
951                       normresult = '''+self.mangleBlasNoPrefix('snrm2')+'''((const int*)&ione2,x2,(const int*)&ione2);
952                     }
953                  fprintf(output, "--known-snrm2-returns-double=%d",normresult != 3);\n'''
954      result = self.runTimeTest('known-snrm2-returns-double',includes,body,self.dlib,nobatch=1)
955      if result:
956        self.log.write('Checking for snrm2() return double: result ' +str(result)+'\n')
957        result = int(result)
958        if result:
959          self.log.write('Checking snrm2(): Program did return with 1 for output so assume returns double\n')
960          self.addDefine('BLASLAPACK_SNRM2_RETURNS_DOUBLE', 1)
961      else:
962        self.log.write('Checking snrm2(): Program did not return with output so assume returns single\n')
963