xref: /petsc/config/BuildSystem/config/setCompilers.py (revision ede9db9363e1fdaaa09befd664c8164883ccce80)
1from __future__ import generators
2import config.base
3import config
4import os
5import contextlib
6from functools import reduce
7from collections import namedtuple
8from collections import defaultdict
9
10# not sure how to handle this with 'self' so it's outside the class
11def noCheck(command, status, output, error):
12  return
13
14try:
15  any
16except NameError:
17  def any(lst):
18    return reduce(lambda x,y:x or y,lst,False)
19
20def _picTestIncludes(export=''):
21  return '\n'.join(['#include <stdio.h>',
22                    'int (*fprintf_ptr)(FILE*,const char*,...) = fprintf;',
23                    'int '+export+' foo(void){',
24                    '  fprintf_ptr(stdout,"hello");',
25                    '  return 0;',
26                    '}',
27                    'void bar(void){foo();}\n'])
28
29isUname_value          = False
30isLinux_value          = False
31isCygwin_value         = False
32isSolaris_value        = False
33isDarwin_value         = False
34isDarwinCatalina_value = False
35isFreeBSD_value        = False
36isARM_value            = -1
37
38class CaseInsensitiveDefaultDict(defaultdict):
39  __slots__ = ()
40
41  def update(self,*args):
42    for x in args:
43      for key,val in x.items():
44        self[key] = val
45
46  def __setitem__(self,key,val):
47    if not isinstance(key,str):
48      raise RuntimeError('must use strings as keys for {cls}'.format(cls=self.__class__))
49    # super() without args is python3 only
50    super(defaultdict,self).__setitem__(key.lower(),val)
51
52  def __missing__(self,key):
53    if not isinstance(key,str):
54      raise RuntimeError('must use strings as keys for {cls}'.format(cls=self.__class__))
55    key = key.lower()
56    if key not in self.keys():
57      self[key] = self.default_factory()
58    return self[key]
59
60def default_cxx_dialect_ranges():
61  return ('c++11','c++20')
62
63class Configure(config.base.Configure):
64  def __init__(self, framework):
65    config.base.Configure.__init__(self, framework)
66    self.headerPrefix            = ''
67    self.substPrefix             = ''
68    self.usedMPICompilers        = 0
69    self.mainLanguage            = 'C'
70    self.cxxDialectRange         = CaseInsensitiveDefaultDict(default_cxx_dialect_ranges)
71    self.cxxDialectPackageRanges = ({},{})
72    return
73
74  def __str__(self):
75    self.compilerflags = self.framework.getChild('config.compilerFlags')
76    desc = ['Compilers:']
77    if hasattr(self, 'CC'):
78      self._setupCompiler('C',desc)
79    if hasattr(self, 'CUDAC'):
80      self._setupCompiler('CUDA',desc)
81    if hasattr(self, 'HIPC'):
82      self._setupCompiler('HIP',desc)
83    if hasattr(self, 'SYCLC'):
84      self._setupCompiler('SYCL',desc)
85    if hasattr(self, 'CXX'):
86      self._setupCompiler('Cxx',desc)
87    if hasattr(self, 'FC'):
88      self._setupCompiler('FC',desc)
89    desc.append('Linkers:')
90    if hasattr(self, 'staticLinker'):
91      desc.append('  Static linker:   '+self.getSharedLinker()+' '+self.AR_FLAGS)
92    elif hasattr(self, 'sharedLinker'):
93      desc.append('  Shared linker:   '+self.getSharedLinker()+' '+self.getSharedLinkerFlags())
94    if hasattr(self, 'dynamicLinker'):
95      desc.append('  Dynamic linker:   '+self.getDynamicLinker()+' '+self.getDynamicLinkerFlags())
96      desc.append('  Libraries linked against:   '+self.LIBS)
97    return '\n'.join(desc)+'\n'
98
99  def _setupCompiler(self,compiler,desc):
100    """ Simple utility routine to minimize verbiage"""
101    clabel='  '+compiler+' '
102    if compiler == 'Cxx': clabel='  C++ '
103    if compiler == 'FC':  clabel='  Fortran '
104    self.pushLanguage(compiler)
105    desc.append(clabel+'Compiler:  '+self.getCompiler()+' '+self.getCompilerFlags())
106    if self.compilerflags.version[compiler]:
107      desc.append('    Version: '+self.compilerflags.version[compiler])
108    if not self.getLinker() == self.getCompiler():
109      desc.append(clabel+'Linker:  '+self.getLinker()+' '+self.getLinkerFlags())
110    self.popLanguage()
111    return
112
113  def setupHelp(self, help):
114    import nargs
115
116    help.addArgument('Compilers', '-with-cpp=<prog>', nargs.Arg(None, None, 'Specify the C preprocessor'))
117    help.addArgument('Compilers', '-CPP=<prog>',            nargs.Arg(None, None, 'Specify the C preprocessor'))
118    help.addArgument('Compilers', '-CPPFLAGS=<string>',     nargs.Arg(None, None, 'Specify the C only (not used for C++ or FC) preprocessor options'))
119    help.addArgument('Compilers', '-with-cc=<prog>',  nargs.Arg(None, None, 'Specify the C compiler'))
120    help.addArgument('Compilers', '-CC=<prog>',             nargs.Arg(None, None, 'Specify the C compiler'))
121    help.addArgument('Compilers', '-CFLAGS=<string>',       nargs.Arg(None, None, 'Overwrite the default PETSc C compiler flags\n\
122       Use CFLAGS+= to add to (instead of replacing) the default flags'))
123    help.addArgument('Compilers', '-CFLAGS+=<string>',      nargs.Arg(None, None, 'Add to the default PETSc C compiler flags'))
124    help.addArgument('Compilers', '-CC_LINKER_FLAGS=<string>',  nargs.Arg(None, [], 'Specify the C linker flags'))
125
126    help.addArgument('Compilers', '-CXXPP=<prog>',          nargs.Arg(None, None, 'Specify the C++ preprocessor'))
127    help.addArgument('Compilers', '-CXXPPFLAGS=<string>',   nargs.Arg(None, None, 'Specify the C++ preprocessor options'))
128    help.addArgument('Compilers', '-with-cxx=<prog>', nargs.Arg(None, None, 'Specify the C++ compiler'))
129    help.addArgument('Compilers', '-CXX=<prog>',            nargs.Arg(None, None, 'Specify the C++ compiler'))
130    help.addArgument('Compilers', '-CXXFLAGS=<string>',     nargs.Arg(None, None, 'Overwrite the default PETSc C++ compiler flags, also passed to linker\n\
131       Use CXXFLAGS+ to add to (instead of replacing) the default flags'))
132    help.addArgument('Compilers', '-CXXFLAGS+=<string>',    nargs.Arg(None, None, 'Add to the default PETSc C++ compiler flags, also passed to linker'))
133    help.addArgument('Compilers', '-CXX_CXXFLAGS=<string>', nargs.Arg(None, '',   'Specify the C++ compiler-only options, not passed to linker'))
134    help.addArgument('Compilers', '-CXX_LINKER_FLAGS=<string>',       nargs.Arg(None, [], 'Specify the C++ linker flags'))
135
136    help.addArgument('Compilers', '-FPP=<prog>',            nargs.Arg(None, None, 'Specify the Fortran preprocessor'))
137    help.addArgument('Compilers', '-FPPFLAGS=<string>',     nargs.Arg(None, None, 'Specify the Fortran preprocessor options'))
138    help.addArgument('Compilers', '-with-fc=<prog>',  nargs.Arg(None, None, 'Specify the Fortran compiler'))
139    help.addArgument('Compilers', '-FC=<prog>',             nargs.Arg(None, None, 'Specify the Fortran compiler'))
140    help.addArgument('Compilers', '-FFLAGS=<string>',       nargs.Arg(None, None, 'Overwrite the default PETSc Fortran compiler flags\n\
141       Use FFLAGS+= to add to (instead of replacing) the default flags'))
142    help.addArgument('Compilers', '-FFLAGS+=<string>',      nargs.Arg(None, None, 'Add to the default PETSc Fortran compiler flags'))
143    help.addArgument('Compilers', '-FC_LINKER_FLAGS=<string>',        nargs.Arg(None, [], 'Specify the FC linker flags'))
144
145    help.addArgument('Compilers', '-with-large-file-io=<bool>', nargs.ArgBool(None, 0, 'Allow IO with files greater than 2 GB'))
146
147    help.addArgument('Compilers', '-CUDAPP=<prog>',        nargs.Arg(None, None, 'Specify the CUDA preprocessor'))
148    help.addArgument('Compilers', '-CUDAPPFLAGS=<string>', nargs.Arg(None, None, 'Specify the CUDA preprocessor options'))
149    help.addArgument('Compilers', '-with-cudac=<prog>',    nargs.Arg(None, None, 'Specify the CUDA compiler'))
150    help.addArgument('Compilers', '-CUDAC=<prog>',         nargs.Arg(None, None, 'Specify the CUDA compiler'))
151    help.addArgument('Compilers', '-CUDAFLAGS=<string>',   nargs.Arg(None, None, 'Overwrite the PETSc default CUDA compiler flags\n\
152       Use CUDAFLAGS+= to add to (instead of replacing) the default flags'))
153    help.addArgument('Compilers', '-CUDAC_LINKER_FLAGS=<string>',        nargs.Arg(None, [], 'Specify the CUDA linker flags'))
154
155    help.addArgument('Compilers', '-HIPPP=<prog>',        nargs.Arg(None, None, 'Specify the HIP preprocessor'))
156    help.addArgument('Compilers', '-HIPPPFLAGS=<string>', nargs.Arg(None, None, 'Specify the HIP preprocessor options'))
157    help.addArgument('Compilers', '-with-hipc=<prog>',    nargs.Arg(None, None, 'Specify the HIP compiler'))
158    help.addArgument('Compilers', '-HIPC=<prog>',         nargs.Arg(None, None, 'Specify the HIP compiler'))
159    help.addArgument('Compilers', '-HIPFLAGS=<string>',   nargs.Arg(None, None, 'Overwrite the PETSc default HIP compiler flags\n\
160       Use HIPFLAGS+= to add to (instead of replacing) the default flags'))
161    help.addArgument('Compilers', '-HIPC_LINKER_FLAGS=<string>',        nargs.Arg(None, [], 'Specify the HIP linker flags'))
162
163    help.addArgument('Compilers', '-SYCLPP=<prog>',        nargs.Arg(None, None, 'Specify the SYCL preprocessor'))
164    help.addArgument('Compilers', '-SYCLPPFLAGS=<string>', nargs.Arg(None, None, 'Specify the SYCL preprocessor options'))
165    help.addArgument('Compilers', '-with-syclc=<prog>',    nargs.Arg(None, None, 'Specify the SYCL compiler'))
166    help.addArgument('Compilers', '-SYCLC=<prog>',         nargs.Arg(None, None, 'Specify the SYCL compiler'))
167    help.addArgument('Compilers', '-SYCLFLAGS=<string>',   nargs.Arg(None, None, 'Overwrite the PETSc default SYCL compiler flags\n\
168       Use SYCLLAGS+= to add to (instead of replacing) the default flags'))
169    help.addArgument('Compilers', '-SYCLC_LINKER_FLAGS=<string>',        nargs.Arg(None, '', 'Specify the SYCL linker flags'))
170
171##    help.addArgument('Compilers', '-LD=<prog>',              nargs.Arg(None, None, 'Specify the executable linker'))
172##    help.addArgument('Compilers', '-CC_LD=<prog>',           nargs.Arg(None, None, 'Specify the linker for C only'))
173##    help.addArgument('Compilers', '-CXX_LD=<prog>',          nargs.Arg(None, None, 'Specify the linker for C++ only'))
174##    help.addArgument('Compilers', '-FC_LD=<prog>',           nargs.Arg(None, None, 'Specify the linker for Fortran only'))
175    help.addArgument('Compilers', '-with-shared-ld=<prog>',  nargs.Arg(None, None, 'Specify the shared linker'))
176    help.addArgument('Compilers', '-LD_SHARED=<prog>',       nargs.Arg(None, None, 'Specify the shared linker'))
177    help.addArgument('Compilers', '-LDFLAGS=<string>',       nargs.Arg(None, '',   'Specify the linker options'))
178    help.addArgument('Compilers', '-with-ar=<prog>',                nargs.Arg(None, None,   'Specify the archiver'))
179    help.addArgument('Compilers', '-AR=<prog>',                     nargs.Arg(None, None,   'Specify the archiver flags'))
180    help.addArgument('Compilers', '-AR_FLAGS=<string>',               nargs.Arg(None, None,   'Specify the archiver flags'))
181    help.addArgument('Compilers', '-with-ranlib=<prog>',            nargs.Arg(None, None,   'Specify ranlib'))
182    help.addArgument('Compilers', '-with-pic=<bool>',               nargs.ArgBool(None, 0, 'Compile with -fPIC or equivalent flag if possible'))
183    help.addArgument('Compilers', '-sharedLibraryFlags=<string>',     nargs.Arg(None, [], 'Specify the shared library flags'))
184    help.addArgument('Compilers', '-dynamicLibraryFlags=<string>',    nargs.Arg(None, [], 'Specify the dynamic library flags'))
185    help.addArgument('Compilers', '-LIBS=<string>',          nargs.Arg(None, None, 'Specify extra libraries for all links'))
186    help.addArgument('Compilers', '-with-environment-variables=<bool>',nargs.ArgBool(None, 0, 'Use compiler variables found in environment'))
187    min_ver, max_ver   = default_cxx_dialect_ranges()
188    min_ver            = int(min_ver[2:])
189    max_ver            = int(max_ver[2:])
190    available_dialects = []
191    while min_ver <= max_ver:
192      available_dialects.append(min_ver)
193      min_ver += 3
194
195    available_dialects = ', '.join(map(str, available_dialects))
196    help.addArgument('Compilers', '-with-cxx-dialect=<dialect>',nargs.Arg(None, 'auto', 'Dialect under which to compile C++ sources. Pass "c++17" to use "-std=c++17", "gnu++17" to use "-std=gnu++17" or pass just the number (e.g. "17") to have PETSc auto-detect gnu extensions. Pass "auto" to let PETSc auto-detect everything or "0" to use the compiler"s default. Available: ({}, auto, 0)'.format(available_dialects)))
197    help.addArgument('Compilers', '-with-hip-dialect=<dialect>',nargs.Arg(None, 'auto', 'Dialect under which to compile HIP sources. If set should probably be equivalent to c++ dialect (see --with-cxx-dialect)'))
198    help.addArgument('Compilers', '-with-cuda-dialect=<dialect>',nargs.Arg(None, 'auto', 'Dialect under which to compile CUDA sources. If set should probably be equivalent to c++ dialect (see --with-cxx-dialect)'))
199    help.addArgument('Compilers', '-with-sycl-dialect=<dialect>',nargs.Arg(None, 'auto', 'Dialect under which to compile SYCL sources. If set should probably be equivalent to c++ dialect (see --with-cxx-dialect)'))
200    return
201
202  def setupDependencies(self, framework):
203    config.base.Configure.setupDependencies(self, framework)
204    self.languages = framework.require('PETSc.options.languages', self)
205    self.libraries = self.framework.getChild('config.libraries')
206    self.headers   = self.framework.getChild('config.headers')
207    return
208
209  @staticmethod
210  def isNAG(compiler, log):
211    '''Returns true if the compiler is a NAG F90 compiler'''
212    try:
213      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' -V',checkCommand = noCheck, log = log)
214      output = output + error
215      found = any([s in output for s in ['NAGWare Fortran','The Numerical Algorithms Group Ltd']])
216      if found:
217        if log: log.write('Detected NAG Fortran compiler\n')
218        return 1
219    except RuntimeError:
220      pass
221
222  @staticmethod
223  def isMINGW(compiler, log):
224    '''Returns true if the compiler is a MINGW compiler'''
225    try:
226      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' -v',checkCommand = noCheck, log = log)
227      output = output + error
228      if output.find('w64-mingw32') >= 0:
229        if log: log.write('Detected MINGW compiler\n')
230        return 1
231    except RuntimeError:
232      pass
233
234  @staticmethod
235  def isGNU(compiler, log):
236    '''Returns true if the compiler is a GNU compiler'''
237    try:
238      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --help | head -n 20 ', log = log)
239      output = output + error
240      found = (any([s in output for s in ['www.gnu.org',
241                                         'bugzilla.redhat.com',
242                                         'gcc.gnu.org',
243                                         'gcc version',
244                                         '-print-libgcc-file-name',
245                                         'passed on to the various sub-processes invoked by gcc',
246                                         'passed on to the various sub-processes invoked by cc',
247                                         'passed on to the various sub-processes invoked by gfortran',
248                                         'passed on to the various sub-processes invoked by g++',
249                                         'passed on to the various sub-processes invoked by c++',
250                                         ]])
251              and not any([s in output for s in ['Intel(R)',
252                                                 'Unrecognised option --help passed to ld', # NAG f95 compiler
253                                                 'IBM XL', # XL compiler
254                                                 ]]))
255      if not found and Configure.isCrayPEWrapper(compiler,log):
256        (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version', log = log)
257        found = any([s in output for s in ['(GCC)','GNU Fortran','gcc-','g++-']])
258      if found:
259        if log: log.write('Detected GNU compiler\n')
260        return 1
261    except RuntimeError:
262      pass
263
264  @staticmethod
265  def isClang(compiler, log):
266    '''Returns true if the compiler is a Clang/LLVM compiler'''
267    try:
268      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --help | head -n 500', log = log, logOutputflg = False)
269      output = output + error
270      found = (any([s in output for s in ['Emit Clang AST']])
271               and not any([s in output for s in ['Win32 Development Tool Front End']]))
272      if found:
273        if log: log.write('Detected CLANG compiler\n')
274        return 1
275    except RuntimeError:
276      pass
277
278  @staticmethod
279  def isHIP(compiler, log):
280    '''Returns true if the compiler is a HIP compiler'''
281    try:
282      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version', log = log)
283      output = output + error
284      if 'HIP version:' in output:
285        if log: log.write('Detected HIP compiler\n')
286        return 1
287    except RuntimeError:
288      pass
289
290  @staticmethod
291  def isOneAPI(compiler, log):
292    '''Returns true if the compiler is an Intel oneAPI compiler'''
293    try:
294      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version', log = log)
295      output = output + error
296      found = any([s in output for s in ['Intel(R) oneAPI']])
297      if found:
298        if log: log.write('Detected Intel oneAPI compiler\n')
299        return 1
300    except RuntimeError:
301      pass
302
303  @staticmethod
304  def isSYCL(compiler, log):
305    '''Returns true if the compiler is a SYCL compiler'''
306    try:
307      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version', log = log)
308      output = output + error
309      # Currently we only tested Intel oneAPI DPC++. Expand the list as more sycl compilers are available
310      found = any([s in output for s in ['oneAPI DPC++']])
311      if found:
312        if log: log.write('Detected SYCL compiler\n')
313        return 1
314    except RuntimeError:
315      pass
316
317  @staticmethod
318  def isNVCC(compiler, log):
319    '''Returns true if the compiler is a NVCC compiler'''
320    try:
321      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version', log = log)
322      output = output + error
323      if 'Cuda compiler driver' in output:
324        if log: log.write('Detected NVCC compiler\n')
325        return 1
326    except RuntimeError:
327      pass
328
329  @staticmethod
330  def isNVC(compiler, log):
331    '''Returns true if the compiler is an NVIDIA (former PGI) compiler'''
332    try:
333      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version', log = log)
334      output = output + error
335      if 'NVIDIA Compilers and Tools' in output:
336        if log: log.write('Detected NVIDIA compiler\n')
337        return 1
338    except RuntimeError:
339      pass
340
341  @staticmethod
342  def isGcc110plus(compiler, log):
343    '''returns true if the compiler is gcc-11.0.x or later'''
344    try:
345      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version', log = log)
346      output = output + error
347      import re
348      strmatch = re.match(r'gcc[-0-9]*\s+\(.*\)\s+(\d+)\.(\d+)',output)
349      if strmatch:
350        VMAJOR,VMINOR = strmatch.groups()
351        if (int(VMAJOR),int(VMINOR)) >= (11,0):
352          if log: log.write('Detected Gcc110plus compiler\n')
353          return 1
354      else:
355        if config.setCompilers.Configure.isGNU(compiler, log) and config.setCompilers.Configure.isMINGW(compiler, log):
356          (output, error, status) = config.base.Configure.executeShellCommand(compiler+' -dumpversion', log = log)
357          strmatch = re.match(r'(\d+)\.(\d+)',output)
358          if strmatch:
359            VMAJOR,VMINOR = strmatch.groups()
360            if (int(VMAJOR),int(VMINOR)) >= (11,0):
361              if log: log.write('Detected Gcc110plus compiler\n')
362              return 1
363      if log: log.write('Did not detect Gcc110plus compiler\n')
364    except RuntimeError:
365      if log: log.write('Did not detect Gcc110plus compiler due to exception\n')
366      pass
367
368  @staticmethod
369  def isGcc150plus(compiler, log):
370    '''returns true if the compiler is gcc-15.0.x or later'''
371    try:
372      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version', log = log)
373      output = output + error
374      import re
375      strmatch = re.match(r'gcc[-0-9]*\s+\(.*\)\s+(\d+)\.(\d+)',output)
376      if strmatch:
377        VMAJOR,VMINOR = strmatch.groups()
378        if (int(VMAJOR),int(VMINOR)) >= (15,0):
379          if log: log.write('Detected Gcc150plus compiler\n')
380          return 1
381      else:
382        if config.setCompilers.Configure.isGNU(compiler, log) and config.setCompilers.Configure.isMINGW(compiler, log):
383          (output, error, status) = config.base.Configure.executeShellCommand(compiler+' -dumpversion', log = log)
384          strmatch = re.match(r'(\d+)\.(\d+)',output)
385          if strmatch:
386            VMAJOR,VMINOR = strmatch.groups()
387            if (int(VMAJOR),int(VMINOR)) >= (15,0):
388              if log: log.write('Detected Gcc150plus compiler\n')
389              return 1
390      if log: log.write('Did not detect Gcc150plus compiler\n')
391    except RuntimeError:
392      if log: log.write('Did not detect Gcc150plus compiler due to exception\n')
393      pass
394
395  @staticmethod
396  def isGfortran100plus(compiler, log):
397    '''returns true if the compiler is gfortran-10.0.x or later'''
398    try:
399      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version', log = log)
400      output = output + error
401      import re
402      strmatch = re.match(r'GNU Fortran\s+\(.*\)\s+(\d+)\.(\d+)',output)
403      if strmatch:
404        VMAJOR,VMINOR = strmatch.groups()
405        if (int(VMAJOR),int(VMINOR)) >= (10,0):
406          if log: log.write('Detected GFortran100plus compiler\n')
407          return 1
408    except RuntimeError:
409      pass
410
411  @staticmethod
412  def isGfortran8plus(compiler, log):
413    '''returns true if the compiler is gfortran-8 or later'''
414    try:
415      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version', log = log)
416      output = output + error
417      import re
418      strmatch = re.match(r'GNU Fortran\s+\(.*\)\s+(\d+)\.(\d+)',output)
419      if strmatch:
420        VMAJOR,VMINOR = strmatch.groups()
421        if (int(VMAJOR),int(VMINOR)) >= (8,0):
422          if log: log.write('Detected GFortran8plus compiler\n')
423          return 1
424    except RuntimeError:
425      pass
426
427  @staticmethod
428  def isSun(compiler, log):
429    '''Returns true if the compiler is a Sun/Oracle compiler'''
430    try:
431      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' -V',checkCommand = noCheck, log = log)
432      output = output + error
433      found = any([s in output for s in [' Sun C ',' Sun C++ ', ' Sun Fortran ']])
434      if found:
435        if log: log.write('Detected Sun/Oracle compiler\n')
436        return 1
437    except RuntimeError:
438      pass
439
440  @staticmethod
441  def isIBM(compiler, log):
442    '''Returns true if the compiler is a IBM compiler'''
443    try:
444      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' -qversion', log = log)
445      output = output + error
446      if 'IBM XL' in output:
447        if log: log.write('Detected IBM compiler\n')
448        return 1
449    except RuntimeError:
450      pass
451
452  @staticmethod
453  def isIntel(compiler, log):
454    '''Returns true if the compiler is a Intel compiler'''
455    try:
456      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version', log = log)
457      output = output + error
458      if 'Intel' in output:
459        if log: log.write('Detected Intel compiler\n')
460        return 1
461    except RuntimeError:
462      pass
463
464  @staticmethod
465  def isCrayKNL(compiler, log):
466    '''Returns true if the compiler is a compiler for KNL running on a Cray'''
467    x = os.getenv('PE_PRODUCT_LIST')
468    if x and x.find('CRAYPE_MIC-KNL') > -1:
469      if log: log.write('Detected Cray KNL compiler\n')
470      return 1
471
472  @staticmethod
473  def isCray(compiler, log):
474    '''Returns true if the compiler is a Cray compiler'''
475    try:
476      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' -V', log = log)
477      output = output + error
478      found = any([s in output for s in ['Cray C ','Cray Standard C','Cray C++ ','Cray Fortran ']])
479      if found:
480        if log: log.write('Detected Cray compiler\n')
481        return 1
482    except RuntimeError:
483      pass
484
485  @staticmethod
486  def isCrayPEWrapper(compiler, log):
487    '''Returns true if the compiler is a Cray Programming Environment (PE) compiler wrapper'''
488    try:
489      # Cray PE compiler wrappers (e.g., cc) when invoked with --version option will complain when CRAY_CPU_TARGET is set to erroneous value, but Cray raw compilers (e.g., craycc) won't. So use this behavior to differentiate cc from craycc.
490      canary_value = '5dde31d2'
491      (output, error, status) = config.base.Configure.executeShellCommand(
492        'CRAY_CPU_TARGET="%s" %s --version' % (canary_value, compiler),
493        checkCommand=config.base.Configure.passCheckCommand,
494        log=log,
495      )
496      output = output + error
497      if output.find(canary_value) >= 0:
498        if log:
499          log.write('Detected Cray PE compiler wrapper\n')
500        return 1
501    except RuntimeError:
502      pass
503
504  @staticmethod
505  def isCrayVector(compiler, log):
506    '''Returns true if the compiler is a Cray compiler for a Cray Vector system'''
507    try:
508      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' -VV', log = log)
509      output = output + error
510      if not status and output.find('x86') >= 0:
511        return 0
512      elif not status:
513        if log: log.write('Detected Cray vector compiler\n')
514        return 1
515    except RuntimeError:
516      pass
517
518  @staticmethod
519  def isPGI(compiler, log):
520    '''Returns true if the compiler is a PGI compiler'''
521    try:
522      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' -V',checkCommand = noCheck, log = log)
523      output = output + error
524      found = any([s in output for s in ['The Portland Group','PGI Compilers and Tools']])
525      if found:
526        if log: log.write('Detected PGI compiler\n')
527        return 1
528    except RuntimeError:
529      pass
530
531  @staticmethod
532  def isNEC(compiler, log):
533    '''Returns true if the compiler is a NEC compiler'''
534    try:
535      (output, error, status) = config.base.Configure.executeShellCommand(compiler+' --version',checkCommand = noCheck, log = log)
536      output = output + error
537      if output.find('NEC Corporation') >= 0:
538        if log: log.write('Detected NEC compiler\n')
539        return 1
540    except RuntimeError:
541      pass
542
543  @classmethod
544  def isWindows(cls, compiler, log):
545    '''Returns true if the compiler is a Windows compiler'''
546    if cls.isCygwin(log):
547      compiler = os.path.basename(compiler)
548      if compiler.startswith('win32fe'):
549        if log: log.write('Detected Microsoft Windows native compiler\n')
550        return 1
551    if log: log.write('Detected Non-Microsoft Windows native compiler\n')
552    return 0
553
554  @classmethod
555  def isMSVC(cls, compiler, log):
556    """
557    Returns true if the compiler is MSVC. Does not distinguish between raw MSVC and win32fe + MSVC
558    """
559    output, error, _ = cls.executeShellCommand(compiler + ' --version', checkCommand=noCheck, log=log)
560    output           = '\n'.join((output, error)).casefold()
561    found            = all(
562      sub.casefold() in output for sub in ('microsoft', 'c/c++ optimizing compiler')
563    )
564    if log:
565      log.write('Detected MSVC\n' if found else 'Did not detect MSVC\n')
566    return int(found)
567
568  @staticmethod
569  def isSolarisAR(ar, log):
570    '''Returns true AR is solaris'''
571    try:
572      (output, error, status) = config.base.Configure.executeShellCommand(ar + ' -V',checkCommand = noCheck, log = log)
573      output = output + error
574      if 'Software Generation Utilities' in output:
575        return 1
576    except RuntimeError:
577      pass
578
579  @staticmethod
580  def isAIXAR(ar, log):
581    '''Returns true AR is AIX'''
582    try:
583      (output, error, status) = config.base.Configure.executeShellCommand(ar + ' -V',checkCommand = noCheck, log = log)
584      output = output + error
585      if output.find('[-X{32|64|32_64|d64|any}]') >= 0:
586        return 1
587    except RuntimeError:
588      pass
589
590  @staticmethod
591  def isUname(log):
592    global isLinux_value,isCygwin_value,isSolaris_value,isDarwin_value,isDarwinCatalina_value,isFreeBSD_value,isUname_value
593    isUname_value = True
594    (output, error, status) = config.base.Configure.executeShellCommand('uname -s', log = log)
595    if not status:
596      output = output.lower().strip()
597      if output.find('linux') >= 0:
598        if log: log.write('Detected Linux OS')
599        isLinux_value = True
600        return
601      if output.find('cygwin') >= 0:
602        if log: log.write('Detected Cygwin')
603        isCygwin_value = True
604        return
605      if output.find('sunos') >= 0:
606        if log: log.write('Detected Solaris')
607        isSolaris_value = True
608        return
609      if output.find('darwin') >= 0:
610        if log: log.write('Detected Darwin')
611        isDarwin_value = True
612        import platform
613        try:
614          v = tuple([int(a) for a in platform.mac_ver()[0].split('.')])
615          if v >= (10,15,0):
616            if log: log.write('Detected Darwin/macOS Catalina OS\n')
617            isDarwinCatalina_value = True
618        except:
619          if log: log.write('macOS version detecton failed!\n')
620          pass
621      if output.find('freebsd') >= 0:
622        if log: log.write('Detected FreeBSD')
623        isFreeBSD_value = True
624        return
625
626  @staticmethod
627  def isLinux(log):
628    '''Returns true if system is linux'''
629    global isUname_value,isLinux_value
630    if not isUname_value: config.setCompilers.Configure.isUname(log)
631    return isLinux_value
632
633  @staticmethod
634  def isCygwin(log):
635    '''Returns true if system is Cygwin'''
636    global isUname_value,isCygwin_value
637    if not isUname_value: config.setCompilers.Configure.isUname(log)
638    return isCygwin_value
639
640  @staticmethod
641  def isSolaris(log):
642    '''Returns true if system is Solaris'''
643    global isUname_value,sSolaris_value
644    if not isUname_value: config.setCompilers.Configure.isUname(log)
645    return isSolaris_value
646
647  @staticmethod
648  def isDarwin(log):
649    '''Returns true if system is Dwarwin'''
650    global isUname_value,sDarwin_value
651    if not isUname_value: config.setCompilers.Configure.isUname(log)
652    return isDarwin_value
653
654  @staticmethod
655  def isDarwinCatalina(log):
656    '''Returns true if system is Dwarwin Catalina'''
657    global isUname_value,isDarwinCatalina_value
658    if not isUname_value: config.setCompilers.Configure.isUname(log)
659    return isDarwinCatalina_value
660
661  @staticmethod
662  def isFreeBSD(log):
663    '''Returns true if system is FreeBSD'''
664    global isUname_value,isFreeBSD_value
665    if not isUname_value: config.setCompilers.Configure.isUname(log)
666    return isFreeBSD_value
667
668  @staticmethod
669  def isARM(log):
670    '''Returns true if system is processor-type is ARM'''
671    global isARM_value
672    if isARM_value == -1:
673       (output, error, status) = config.base.Configure.executeShellCommand('uname -m', log = log)
674       if not status and (output.lower().strip().startswith('arm') or output.lower().strip().startswith('aarch')):
675         if log: log.write('Detected ARM processor\n\n')
676         isARM_value = True
677       else:
678         isARM_value = False
679    return isARM_value
680
681  @staticmethod
682  def addLdPath(path):
683    if 'LD_LIBRARY_PATH' in os.environ:
684      ldPath=os.environ['LD_LIBRARY_PATH']
685    else:
686      ldPath=''
687    if ldPath == '': ldPath = path
688    else: ldPath += ':' + path
689    os.environ['LD_LIBRARY_PATH'] = ldPath
690    return
691
692  def useMPICompilers(self):
693    if ('with-cc' in self.argDB and self.argDB['with-cc'] != '0') or 'CC' in self.argDB:
694      return 0
695    if ('with-cxx' in self.argDB and self.argDB['with-cxx'] != '0') or 'CXX' in self.argDB:
696      return 0
697    if ('with-fc' in self.argDB and self.argDB['with-fc'] != '0') or 'FC' in self.argDB:
698      return 0
699    if self.argDB['download-mpich'] or self.argDB['download-openmpi']:
700      return 0
701    if 'with-mpi-include' in self.argDB and self.argDB['with-mpi-include']:
702      return 0;
703    if 'with-mpi' in self.argDB and self.argDB['with-mpi'] and self.argDB['with-mpi-compilers']:
704      return 1
705    return 0
706
707  def checkInitialFlags(self):
708    '''Initialize the compiler and linker flags'''
709    for language in ['C', 'CUDA', 'HIP', 'SYCL', 'Cxx', 'FC']:
710      self.pushLanguage(language)
711      for flagsArg in [config.base.Configure.getCompilerFlagsName(language)]:
712        if flagsArg in self.argDB:
713          self.logPrintWarning('You are overwriting the standard PETSc compiler flags with ' + flagsArg + '="' + self.argDB[flagsArg] + '". Are you sure you want to do this? Generally it is best to let PETSc ./configure determine the flags. You can use ' + flagsArg + '+="' + self.argDB[flagsArg] + '" to provide additional compiler flags instead of overwriting those used by PETSc.')
714      for flagsArg in [config.base.Configure.getCompilerFlagsName(language), config.base.Configure.getCompilerFlagsName(language, 1), config.base.Configure.getLinkerFlagsName(language)]:
715        if flagsArg in self.argDB: setattr(self, flagsArg, self.argDB[flagsArg])
716        else: setattr(self, flagsArg, '')
717        self.logPrint('Initialized '+flagsArg+' to '+str(getattr(self, flagsArg)))
718      self.popLanguage()
719    for flagsArg in ['CPPFLAGS', 'FPPFLAGS', 'CUDAPPFLAGS', 'CXXPPFLAGS', 'HIPPPFLAGS', 'SYCLPPFLAGS']:
720      if flagsArg in self.argDB: setattr(self, flagsArg, self.argDB[flagsArg])
721      else: setattr(self, flagsArg, '')
722      self.logPrint('Initialized '+flagsArg+' to '+str(getattr(self, flagsArg)))
723    # SYCLC_LINKER_FLAGS is init'ed above in the "for language" loop.
724    # FIXME: these linker flags are init'ed as a list, while others are init'ed as a string. Need to make them consistent.
725    for flagsArg in ['CC_LINKER_FLAGS', 'CXX_LINKER_FLAGS', 'FC_LINKER_FLAGS', 'CUDAC_LINKER_FLAGS', 'HIPC_LINKER_FLAGS', 'sharedLibraryFlags', 'dynamicLibraryFlags']:
726      if isinstance(self.argDB[flagsArg],str): val = [self.argDB[flagsArg]]
727      else: val = self.argDB[flagsArg]
728      setattr(self, flagsArg, val)
729      self.logPrint('Initialized '+flagsArg+' to '+str(getattr(self, flagsArg)))
730    if 'LIBS' in self.argDB:
731      self.LIBS = self.argDB['LIBS']
732    else:
733      self.LIBS = ''
734    return
735
736  def checkDeviceHostCompiler(self,language):
737    """Set the host compiler (HC) of the device compiler (DC) to the HC unless the DC already explicitly sets its HC. This may be needed if the default HC used by the DC is ancient and PETSc uses a different HC (e.g., through --with-cxx=...)."""
738    if language.upper() == 'CUDA':
739      setHostFlag = '-ccbin'
740    else:
741      raise NotImplementedError
742    with self.Language(language):
743      if setHostFlag in self.getCompilerFlags():
744        # don't want to override this if it is already set
745        return
746    if not hasattr(self,'CXX'):
747        return
748    compilerName = self.getCompiler(lang='Cxx')
749    hostCCFlag   = '{shf} {cc}'.format(shf=setHostFlag,cc=compilerName)
750    with self.Language(language):
751      self.logPrint(' '.join(('checkDeviceHostCompiler: checking',language,'accepts host compiler',compilerName)))
752      try:
753        self.addCompilerFlag(hostCCFlag)
754      except RuntimeError:
755        pass
756    return
757
758  def checkCxxDialect(self, language, isGNUish=False):
759    """Determine the CXX dialect supported by the compiler (language) [and corresponding compiler option - if any].
760
761    isGNUish indicates if the compiler is gnu compliant (i.e. clang).
762    -with-<lang>-dialect can take options:
763      auto: use highest supported dialect configure can determine
764      [[c|gnu][xx|++]]23: not yet supported
765      [[c|gnu][xx|++]]20: gnu++20 or c++20
766      [[c|gnu][xx|++]]17: gnu++17 or c++17
767      [[c|gnu][xx|++]]14: gnu++14 or c++14
768      [[c|gnu][xx|++]]11: gnu++11 or c++11
769      0: disable CxxDialect check and use compiler default
770
771    On return this function sets the following values:
772    - if needed, appends the relevant CXX dialect flag to <lang> compiler flags
773    - self.cxxDialectRange = (minSupportedDialect,maxSupportedDialect) (e.g. ('c++11','c++14'))
774    - self.addDefine('HAVE_{LANG}_DIALECT_CXX{DIALECT_NUM}',1) for every supported dialect
775    - self.lang+'dialect' = 'c++'+maxDialectNumber (e.g. 'c++14') but ONLY if the user
776      specifically requests a dialect version, otherwise this is not set
777
778    Raises a config.base.ConfigureSetupError if:
779    - The user has set both the --with-dialect=[...] configure options and -std=[...] in their
780      compiler flags
781    - The combination of specifically requested packages cannot all be compiled with the same flag
782    - An unknown C++ dialect is provided
783
784    The config.base.ConfigureSetupErrors are NOT meant to be caught, as they are fatal errors
785    on part of the user
786
787    Raises a RuntimeError (which may be caught) if:
788    - The compiler does not support at minimum -std=c++11
789    """
790    from config.base import ConfigureSetupError
791    import textwrap
792
793    def includes11():
794      return textwrap.dedent(
795        """
796        #include <memory> // c++11 includes
797        #include <random>
798        #include <complex>
799        #include <iostream>
800        #include <algorithm>
801        template<class T> void ignore(const T&) { } // silence unused variable warnings
802        class valClass {
803        public:
804          int i;
805          valClass() { i = 3; }
806          valClass(int x) : i(x) { }
807        };
808        class MoveSemantics {
809          std::unique_ptr<valClass> _member;
810        public:
811          MoveSemantics(int val = 4) : _member(new valClass(val)) { }
812          MoveSemantics& operator=(MoveSemantics &&other) noexcept = default;
813        };
814        template<typename T> constexpr T Cubed( T x ) { return x*x*x; }
815        auto trailing(int x) -> int { return x+2; }
816        enum class Shapes : int {SQUARE,CIRCLE};
817        template<class ... Types> struct Tuple { };
818        using PetscErrorCode = int;
819        """
820      )
821
822    def body11():
823      return textwrap.dedent(
824        """
825        valClass cls = valClass(); // c++11 body; // value initialization
826        int i = cls.i;             // i is not declared const
827        const int& rci = i;        // but rci is
828        const_cast<int&>(rci) = 4;
829        constexpr int big_value = 1234;
830        decltype(big_value) ierr = big_value;
831        auto ret = trailing(ierr);
832        MoveSemantics bob;
833        MoveSemantics alice;
834        alice = std::move(bob);ignore(alice);
835        Tuple<> t0;ignore(t0);
836        Tuple<long> t1;ignore(t1);
837        Tuple<int,float> t2;ignore(t2);
838        std::random_device rd;
839        std::mt19937 mt(rd());
840        std::normal_distribution<double> dist(0,1);
841        const double x = dist(mt);
842        std::cout << x << ret << std::endl;
843        std::vector<std::unique_ptr<double>> vector;
844        std::sort(vector.begin(), vector.end(), [](std::unique_ptr<double> &a, std::unique_ptr<double> &b) { return *a < *b; });
845        std::size_t alignment = 0, size = 0, space;
846        void* ptr2 = nullptr;
847        std::align(alignment, size, ptr2, space);
848        """
849      )
850
851    def includes14():
852      return ''.join((includes11(),textwrap.dedent(
853        """
854        #include <type_traits> // c++14 includes
855        template<class T> constexpr T pi = T(3.1415926535897932385L);  // variable template
856        """
857        )))
858
859    def body14():
860      return ''.join((body11(),textwrap.dedent(
861        """
862        auto ptr = std::make_unique<int>();  // c++14 body
863        *ptr = 1;
864        std::cout << pi<double> << std::endl;
865        constexpr const std::complex<double> const_i(0.0,1.0);
866        auto lambda = [](auto x, auto y) { return x + y; };
867        std::cout << lambda(3,4) << std::real(const_i) << std::endl;
868        """
869      )))
870
871    def includes17():
872      return ''.join((includes14(),textwrap.dedent(
873        """
874        #include <string_view> // c++17 includes
875        #include <any>
876        #include <optional>
877        #include <variant>
878        #include <tuple>
879        #include <new>
880        std::align_val_t dummy;
881        [[nodiscard]] int nodiscardFunc() { return 0; }
882        struct S2 {
883          static inline int var = 8675309; // static inline member variables since c++17
884          void f(int i);
885        };
886        void S2::f(int i) {
887          // until c++17: Error: invalid syntax
888          // since c++17: OK: captures the enclosing S2 by copy
889          auto lmbd = [=, *this] { std::cout << i << " " << this->var << std::endl; };
890          lmbd();
891        }
892        std::tuple<double, int, char> foobar() { return {3.8, 0, 'x'}; }
893        """
894      )))
895
896    def body17():
897      return ''.join((body14(),textwrap.dedent(
898        """
899        std::variant<int,float> v,w;  // c++17 body
900        v = 42;               // v contains int
901        int ivar = std::get<int>(v);
902        w = std::get<0>(v);   // same effect as the previous line
903        w = v;                // same effect as the previous line
904        S2 foo; foo.f(ivar);
905        if constexpr (std::is_arithmetic_v<int>) std::cout << "c++17" << std::endl;
906        typedef std::integral_constant<Shapes,Shapes::SQUARE> squareShape;
907        static_assert(std::is_same_v<squareShape,squareShape>); // static_assert with no message since c++17
908        auto val = nodiscardFunc();ignore(val);
909        const auto [ab, cd, ef] = foobar(); // structured binding
910        """
911      )))
912
913    def includes20():
914      return ''.join((includes17(),textwrap.dedent(
915        """
916        #include <compare> // c++20 includes
917        #include <concepts>
918        consteval int sqr_cpp20(int n) { return n*n; }
919        constexpr auto r = sqr_cpp20(10);
920        static_assert(r == 100);
921        const char *g_cpp20() { return "dynamic initialization"; }
922        constexpr const char *f_cpp20(bool p) { return p ? "constant initializer" : g_cpp20(); }
923        constinit const char *cinit_c = f_cpp20(true); // OK
924        // Declaration of the concept "Hashable", which is satisfied by any type 'T'
925        // such that for values 'a' of type 'T', the expression std::hash<T>{}(a)
926        // compiles and its result is convertible to std::size_t
927        template <typename T>
928        concept Hashable = requires(T a) { { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>; };
929        struct meow {};
930        template <Hashable T> // Constrained C++20 function template:
931        void f_concept(T) {}
932        void abbrev_f1(auto); // same as template<class T> void abbrev_f1(T)
933        void abbrev_f4(const std::destructible auto*, std::floating_point auto&); // same as template<C3 T, C4 U> void abbrev_f4(const T*, U&);
934        template<> void abbrev_f4<int>(const int*, const double&); // specialization of abbrev_f4<int, const double> (since C++20)
935        """
936      )))
937
938    def body20():
939      return ''.join((body17(),textwrap.dedent(
940        """
941        ignore(cinit_c); // c++20 body
942        using std::operator""s; f_concept("abc"s);
943        """
944      )))
945
946    isGNUish     = bool(isGNUish)
947    lang,LANG    = language.lower(),language.upper()
948    compiler     = self.getCompiler(lang=language)
949    if self.isMSVC(compiler, self.log):
950      stdflag_base = '-std:'
951    elif self.isWindows(compiler, self.log) and self.isIntel(compiler, self.log):
952      stdflag_base = '-Qstd='
953    else:
954      stdflag_base = '-std='
955    DialectFlags = namedtuple('DialectFlags',['standard','gnu'])
956    BaseFlags    = DialectFlags(standard=stdflag_base+'c++',gnu=stdflag_base+'gnu++')
957    self.logPrint('checkCxxDialect: checking C++ dialect version for language "{lang}" using compiler "{compiler}"'.format(lang=LANG,compiler=compiler))
958    self.logPrint('checkCxxDialect: PETSc believes compiler ({compiler}) {isgnuish} gnu-ish'.format(compiler=compiler,isgnuish='IS' if isGNUish else 'is NOT'))
959
960    # if we have done this before the flag may have been inserted (by us) into the
961    # compiler flags, so we shouldn't yell at the user for having it in there, nor should
962    # we treat it as explicitly being set. If we have the attribute, it is either True or
963    # False
964    setPreviouslyAttrName   = lang+'dialect_set_explicitly__'
965    previouslySetExplicitly = getattr(self,setPreviouslyAttrName,None)
966    assert previouslySetExplicitly in (True,False,None)
967    processedBefore = previouslySetExplicitly is not None
968    self.logPrint('checkCxxDialect: PETSc believes that we {pbefore} processed {compiler} before'.format(pbefore='HAVE' if processedBefore else 'have NOT',compiler=compiler))
969
970    # configure value
971    useFlag         = True
972    configureArg    = lang.join(['with-','-dialect'])
973    withLangDialect = self.argDB.get(configureArg).upper().replace('X','+')
974    if withLangDialect in ('','0','NONE'):
975      self.logPrint(
976        'checkCxxDialect: user has requested NO cxx dialect, we\'ll check but not add the flag'
977      )
978      withLangDialect = 'NONE'
979      useFlag         = False # we still do the checks, just not add the flag in the end
980    self.logPrint('checkCxxDialect: configure option after sanitization: --{opt}={val}'.format(opt=configureArg,val=withLangDialect))
981
982    # check the configure argument
983    if withLangDialect.startswith('GNU'):
984      allowedBaseFlags = [BaseFlags.gnu]
985    elif withLangDialect.startswith('C++'):
986      allowedBaseFlags = [BaseFlags.standard]
987    elif withLangDialect == 'NONE':
988      allowedBaseFlags = ['(NO FLAG)']
989    else:
990      # if we are here withLangDialect is either AUTO or e.g. 14
991      allowedBaseFlags = [BaseFlags.gnu,BaseFlags.standard] if isGNUish else [BaseFlags.standard]
992
993    Dialect  = namedtuple('Dialect',['num','includes','body'])
994    dialects = (
995      Dialect(num='11',includes=includes11(),body=body11()),
996      Dialect(num='14',includes=includes14(),body=body14()),
997      Dialect(num='17',includes=includes17(),body=body17()),
998      Dialect(num='20',includes=includes20(),body=body20()),
999      Dialect(num='23',includes=includes20(),body=body20()) # no c++23 checks yet
1000    )
1001
1002    # search compiler flags to see if user has set the c++ standard from there
1003    with self.Language(language):
1004      allFlags = tuple(self.getCompilerFlags().strip().split())
1005    langDialectFromFlags = tuple(f for f in allFlags for flg in BaseFlags if f.startswith(flg))
1006    if len(langDialectFromFlags):
1007      sanitized = langDialectFromFlags[-1].lower().replace(stdflag_base,'')
1008      if not processedBefore:
1009        # check that we didn't set the compiler flag ourselves before we yell at the user
1010        if withLangDialect != 'AUTO':
1011          # user has set both flags
1012          errorMessage = 'Competing or duplicate C++ dialect flags, have specified {flagdialect} in compiler ({compiler}) flags and used configure option {opt}'.format(flagdialect=langDialectFromFlags,compiler=compiler,opt='--'+configureArg+'='+withLangDialect.lower())
1013          raise ConfigureSetupError(errorMessage)
1014        mess = 'Explicitly setting C++ dialect in compiler flags may not be optimal. Use ./configure --{opt}={sanitized} if you really want to use that value, otherwise remove {flag} from compiler flags and omit --{opt}=[...] from configure to have PETSc automatically detect the most appropriate flag for you'.format(opt=configureArg,sanitized=sanitized,flag=langDialectFromFlags[-1])
1015        self.logPrintWarning(mess)
1016
1017      # the user has already set the flag in their options, no need to set it a second time
1018      useFlag          = False
1019      # set the dialect to whatever was in the users compiler flags
1020      withLangDialect  = sanitized
1021      # if we have processed before, then the flags will be the ones we set, so it's best
1022      # to just keep the allowedBaseFlags general
1023      if not processedBefore:
1024        allowedBaseFlags = [
1025          BaseFlags.gnu if withLangDialect.startswith('gnu') else BaseFlags.standard
1026        ]
1027
1028    # delete any previous defines (in case we are doing this again)
1029    for dlct in dialects:
1030      self.delDefine('HAVE_{lang}_DIALECT_CXX{ver}'.format(lang=LANG,ver=dlct.num))
1031
1032    if withLangDialect in {'AUTO','NONE'}:
1033      # see top of file
1034      dialectNumStr = default_cxx_dialect_ranges()[1]
1035      explicit      = withLangDialect == 'NONE' # NONE is explicit but AUTO is not
1036    else:
1037      # we can stop shouting now
1038      dialectNumStr = withLangDialect = withLangDialect.lower()
1039      # if we have done this before, then previouslySetExplicitly holds the previous
1040      # explicit value
1041      explicit      = previouslySetExplicitly if processedBefore else True
1042      max_sup_ver   = default_cxx_dialect_ranges()[1].replace('c++','')
1043      max_unsup_ver = str(int(max_sup_ver) + 3)
1044      if withLangDialect.endswith(max_unsup_ver):
1045        mess = 'C++{unsup_ver} is not yet fully supported, PETSc only tests up to C++{maxver}. Remove -std=[...] from compiler flags and/or omit --{opt}=[...] from configure to have PETSc automatically detect the most appropriate flag for you'.format(unsup_ver=max_unsup_ver,maxver=max_sup_ver,opt=configureArg)
1046        self.logPrintWarning(mess)
1047
1048    minDialect,maxDialect = 0,-1
1049    for i,dialect in enumerate(dialects):
1050      if dialectNumStr.endswith(dialect.num):
1051        maxDialect = i
1052        break
1053
1054    if maxDialect == -1:
1055      try:
1056        ver = int(withLangDialect[-2:])
1057      except ValueError:
1058        ver = 9e9
1059      minver = int(dialects[0].num)
1060      if ver in {89, 98} or ver < minver:
1061        mess = 'PETSc requires at least C++{} when using {}'.format(minver, language.replace('x', '+'))
1062      else:
1063        mess = 'Unknown C++ dialect: {}'.format(withLangDialect)
1064      if explicit:
1065        mess += ' (you have explicitly requested --{}={}). Remove this flag and let configure choose the most appropriate flag for you.'.format(configureArg, withLangDialect)
1066        # If the user explicitly requested the dialect throw CSE (which is NOT meant to be
1067        # caught) as this indicates a user error
1068        raise ConfigureSetupError(mess)
1069      raise RuntimeError(mess)
1070    self.logPrint('checkCxxDialect: dialect {dlct} has been {expl} selected for {lang}'.format(dlct=withLangDialect,expl='EXPLICITLY' if explicit else 'NOT explicitly',lang=LANG))
1071
1072    def checkPackageRange(packageRanges,kind,dialectIdx):
1073      if kind == 'upper':
1074        boundFunction   = min
1075        compareFunction = lambda x,y: x[-2:] > y[-2:]
1076      elif kind == 'lower':
1077        boundFunction   = max
1078        compareFunction = lambda x,y: x == 'NONE' or x[-2:] < y[-2:]
1079      else:
1080        raise ValueError('unknown bound type',kind)
1081
1082      # Check that we have a sane upper bound on the dialect
1083      if len(packageRanges.keys()):
1084        packageBound = boundFunction(packageRanges.keys()).lower()
1085        startDialect = withLangDialect if explicit else dialects[dialectIdx].num
1086        if compareFunction(startDialect,packageBound):
1087          packageBlame = '\n'.join('\t- '+s for s in packageRanges[packageBound])
1088          # if using NONE startDialect will be highest possible dialect
1089          if explicit and startDialect != 'NONE':
1090            # user asked for a dialect, they'll probably want to know why it doesn't work
1091            errorMessage = '\n'.join((
1092              'Explicitly requested {lang} dialect {dlct} but package(s):',
1093              packageBlame.replace('\t',''),
1094              'Has {kind} bound of -std={packdlct}'
1095            )).format(lang=LANG,dlct=withLangDialect,kind=kind,packdlct=packageBound)
1096            raise ConfigureSetupError(errorMessage)
1097          # if not explicit, we can just silently log the discrepancy instead
1098          self.logPrint('\n'.join((
1099            'checkCxxDialect: had {lang} dialect {dlct} as {kind} bound but package(s):',
1100            packageBlame,
1101            '\tHas {kind} bound of -std={packdlct}, using package requirement -std={packdlct}'
1102          )).format(lang=LANG,dlct=startDialect,kind=kind,packdlct=packageBound))
1103          try:
1104            dialectIdx = [i for i,d in enumerate(dialects) if packageBound.endswith(d.num)][0]
1105          except IndexError:
1106            mess = 'Could not find a dialect number that matches the package bounds: {}'.format(
1107              packageRanges
1108            )
1109            raise ConfigureSetupError(mess)
1110      return dialectIdx
1111
1112    maxDialect = checkPackageRange(self.cxxDialectPackageRanges[1],'upper',maxDialect)
1113    minDialect = checkPackageRange(self.cxxDialectPackageRanges[0],'lower',minDialect)
1114
1115    # if the user asks for a particular version we should pin that version
1116    if withLangDialect not in ('NONE','AUTO') and explicit:
1117      minDialect = maxDialect
1118
1119    # compile a list of all the flags we will test in descending order, for example
1120    # -std=gnu++17
1121    # -std=c++17
1122    # -std=gnu++14
1123    # ...
1124    flagPool = [(''.join((b,d.num)),d) for d in reversed(dialects[minDialect:maxDialect+1]) for b in allowedBaseFlags]
1125
1126    self.logPrint(
1127      '\n'.join(['checkCxxDialect: Have potential flag pool:']+['\t   - '+f for f,_ in flagPool])
1128    )
1129    assert len(flagPool)
1130    with self.Language(language):
1131      for index,(flag,dlct) in enumerate(flagPool):
1132        self.logPrint(' '.join(('checkCxxDialect: checking CXX',dlct.num,'for',lang,'with',flag)))
1133        # test with flag
1134        try:
1135          if useFlag:
1136            # needs compilerOnly = True as we need to keep the flag out of the linker flags
1137            self.addCompilerFlag(flag,includes=dlct.includes,body=dlct.body,compilerOnly=True)
1138            if language == 'SYCL': self.insertPreprocessorFlag(flag) # Workaround for a build error on Aurora. We should add Cxx dialect to preprocessor flags to all languages
1139          elif not self.checkCompile(includes=dlct.includes,body=dlct.body):
1140            raise RuntimeError # to mimic addCompilerFlag
1141        except RuntimeError:
1142          # failure, flag is discarded, but first check we haven't run out of flags
1143          if index == len(flagPool)-1:
1144            # compiler does not support the minimum required c++ dialect
1145            base_mess = '\n'.join((
1146              '{lang} compiler ({compiler}) appears non-compliant with C++{ver} or didn\'t accept:',
1147              '\n'.join(
1148                '- '+(f[:-2] if f.startswith('(NO FLAG)') else f) for f,_ in flagPool[:index+1]
1149              ),
1150              '' # for extra newline at the end
1151            ))
1152            if withLangDialect in ('NONE','AUTO'):
1153              packDialects = self.cxxDialectPackageRanges[0]
1154              if packDialects.keys():
1155                # it's a packages fault we can't try the next dialect
1156                minPackDialect = max(packDialects.keys())
1157                base_mess      = '\n'.join((
1158                  'Using {lang} dialect C++{ver} as lower bound due to package(s):',
1159                  '\n'.join('- '+s for s in packDialects[minPackDialect]),
1160                  ' '.join(('But',base_mess))
1161                ))
1162                dialectNum = minPackDialect[-2:]
1163                explicit   = True
1164              else:
1165                assert flag.endswith(dialects[0].num)
1166                # it's the compilers fault we can't try the next dialect
1167                dialectNum = dialects[0].num
1168            else:
1169              # if nothing else then it's because the user requested a particular version
1170              dialectNum = dialectNumStr
1171              base_mess  = '\n'.join((
1172                base_mess,
1173                'Note, you have explicitly requested --{}={}. If you do not need C++{ver}, then remove this flag and let configure choose the most appropriate flag for you.'
1174                '\nIf you DO need it, then (assuming your compiler isn\'t just old) try consulting your compilers user manual. There may be other flags (e.g. \'--gcc-toolchain\') you must pass to enable C++{ver}'.format(configureArg, withLangDialect, ver='{ver}')
1175              ))
1176            if dialectNum.isdigit():
1177              ver = dialectNum
1178            else:
1179              ver = dialectNum.casefold().replace('c++', '').replace('gnu++', '')
1180            mess = base_mess.format(lang=language.replace('x','+'),compiler=compiler,ver=ver)
1181            if explicit:
1182              # if the user explicitly set the version, then this is a hard error
1183              raise ConfigureSetupError(mess)
1184            raise RuntimeError(mess)
1185        else:
1186          # success
1187          self.cxxDialectRange[language] = ('c++'+dialects[minDialect].num,'c++'+dlct.num)
1188          if not useFlag:
1189            compilerFlags = self.getCompilerFlags()
1190            if compilerFlags.count(flag) > 1:
1191              errorMessage = '\n'.join((
1192                'We said we wouldn\'t add the flag yet the flag has been mysteriously added!!:',
1193                compilerFlags
1194              ))
1195              raise ConfigureSetupError(errorMessage)
1196          self.logPrint('checkCxxDialect: success using {flag} for {lang} dialect C++{ver}, set new cxxDialectRange: {drange}'.format(flag=flag,lang=language,ver=dlct.num,drange=self.cxxDialectRange[language]))
1197          break # flagPool loop
1198
1199    # this loop will also set maxDialect for the setattr below
1200    for maxDialect,dlct in enumerate(dialects):
1201      if dlct.num > flag[-2:]:
1202        break
1203      self.addDefine('HAVE_{lang}_DIALECT_CXX{ver}'.format(lang=LANG,ver=dlct.num),1)
1204
1205    if explicit:
1206      # if we don't use the flag we shouldn't set this attr because its existence implies
1207      # a particular dialect is *chosen*
1208      setattr(self,lang+'dialect','c++'+dialects[maxDialect-1].num)
1209    setattr(self,setPreviouslyAttrName,explicit)
1210    return
1211
1212  def checkCompiler(self, language, linkLanguage=None,includes = '', body = '', cleanup = 1, codeBegin = None, codeEnd = None):
1213    """Check that the given compiler is functional, and if not raise an exception"""
1214    with self.Language(language):
1215      compiler = self.getCompiler()
1216      if not self.checkCompile(includes=includes,body=body,cleanup=cleanup,codeBegin=codeBegin,codeEnd=codeEnd):
1217        msg = 'Cannot compile {} with {}.'.format(language,compiler)
1218        raise RuntimeError(msg)
1219
1220      if language.upper() in {'CUDA','HIP','SYCL'}:
1221        # do not check CUDA/HIP/SYCL linkers since they are never used (assumed for now)
1222        return
1223      if not self.checkLink(linkLanguage=linkLanguage,includes=includes,body=body):
1224        msg = 'Cannot compile/link {} with {}.'.format(language,compiler)
1225        msg = '\nIf the above linker messages do not indicate failure of the compiler you can rerun with the option --ignoreLinkOutput=1'
1226        raise RuntimeError(msg)
1227      oldlibs     = self.LIBS
1228      compilerObj = self.framework.getCompilerObject(linkLanguage if linkLanguage else language)
1229      if not hasattr(compilerObj,'linkerrorcodecheck'):
1230        self.LIBS += ' -lpetsc-ufod4vtr9mqHvKIQiVAm'
1231        if self.checkLink(linkLanguage=linkLanguage):
1232          self.LIBS = oldlibs
1233          msg = '\n'.join((
1234            '{lang} compiler {cmp} is broken! It is returning no errors for a failed link! Either',
1235            '1) switch to another compiler suite',
1236            '2) report this entire error message to your compiler/linker suite vendor'
1237          )).format(lang=language,cmp=compiler)
1238          raise RuntimeError(msg)
1239        self.LIBS = oldlibs
1240        compilerObj.linkerrorcodecheck = 1
1241      if not self.argDB['with-batch']:
1242        if not self.checkRun(linkLanguage=linkLanguage):
1243          msg = '\n'.join((
1244            'Cannot run executables created with {language}. If this machine uses a batch system ',
1245            'to submit jobs you will need to configure using ./configure with the additional option  --with-batch. ',
1246            'Otherwise there is problem with the compilers. Can you compile and run code with your compiler \'{compiler}\'?'
1247          )).format(language=language,compiler=compiler)
1248          if self.isIntel(compiler,self.log):
1249            msg = '\n'.join((msg,'See https://petsc.org/release/faq/#error-libimf'))
1250          raise OSError(msg) # why OSError?? it isn't caught anywhere in here?
1251    return
1252
1253  def crayCrossCompiler(self,compiler):
1254    import script
1255    '''For Cray Intel KNL systems returns the underlying compiler line used by the wrapper compiler if is for KNL systems'''
1256    '''This removes all the KNL specific options allowing the generated binary to run on the front-end'''
1257    '''This is needed by some build systems include HDF5 that insist on running compiled programs during the configure and'''
1258    '''make process. This does not work for the Cray compiler module, only intel and gcc'''
1259
1260    (output,error,status) = self.executeShellCommand(compiler+' -craype-verbose',checkCommand = script.Script.passCheckCommand,log=self.log)
1261    output = output.split()
1262    if output[0].strip().startswith('driver'): return ''
1263    newoutput = [output[0]]
1264    cross = 0
1265    for i in output[1:-1]:
1266      if i.find('mic') > -1 or i.find('knl') > -1 or i.find('KNL') > -1:
1267        cross = 1
1268        continue
1269      if i.startswith('-L') or i.startswith('-l') or i.startswith('-Wl'):
1270        continue
1271      newoutput.append(i)
1272    if cross:
1273      return ' '.join(newoutput)
1274    return ''
1275
1276  def crayCrossLIBS(self,compiler):
1277    import script
1278    '''For Cray Intel KNL systems returns the underlying linker options used by the wrapper compiler if is for KNL systems'''
1279    (output,error,status) = self.executeShellCommand(compiler+' -craype-verbose',checkCommand = script.Script.passCheckCommand,log=self.log)
1280    output = output.split()
1281    newoutput = []
1282    cross = 0
1283    for i in output[1:-1]:
1284      if i.find('mic') > -1 or i.find('knl') > -1 or i.find('KNL') > -1:
1285        cross = 1
1286        continue
1287      if i.find('darshan') > -1:
1288        cross = 1
1289        continue
1290      if i.find('static') > -1:
1291        continue
1292      if i.startswith('-I') or i.startswith('-D'):
1293        continue
1294      # the math libraries are not needed by external packages and cause errors in HDF5 with libgfortran.so.4 => not found
1295      if i.startswith('-lsci_gnu'):
1296        continue
1297      newoutput.append(i)
1298    if cross:
1299      return ' '.join(newoutput)
1300    return ''
1301
1302  def generateCCompilerGuesses(self):
1303    '''Determine the C compiler '''
1304    if hasattr(self, 'CC'):
1305      yield self.CC
1306      if self.argDB['download-mpich']: mesg ='with downloaded MPICH'
1307      elif self.argDB['download-openmpi']: mesg ='with downloaded Open MPI'
1308      else: mesg = ''
1309      raise RuntimeError('Error '+mesg+': '+self.mesg)
1310    elif 'with-cc' in self.argDB:
1311      yield self.argDB['with-cc']
1312      raise RuntimeError('C compiler you provided with -with-cc='+self.argDB['with-cc']+' cannot be found or does not work.'+'\n'+self.mesg)
1313    elif 'CC' in self.argDB:
1314      yield self.argDB['CC']
1315      raise RuntimeError('C compiler you provided with -CC='+self.argDB['CC']+' cannot be found or does not work.'+'\n'+self.mesg)
1316    elif self.useMPICompilers() and 'with-mpi-dir' in self.argDB and os.path.isdir(os.path.join(self.argDB['with-mpi-dir'], 'bin')):
1317      self.usedMPICompilers = 1
1318      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpincc')
1319      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpiicc')
1320      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpicc')
1321      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpcc')
1322      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'hcc')
1323      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpcc_r')
1324      self.usedMPICompilers = 0
1325      raise RuntimeError('MPI compiler wrappers in '+self.argDB['with-mpi-dir']+'/bin cannot be found or do not work. See https://petsc.org/release/faq/#invalid-mpi-compilers')
1326    else:
1327      if self.useMPICompilers() and 'with-mpi-dir' in self.argDB:
1328      # if it gets here this means that self.argDB['with-mpi-dir']/bin does not exist so we should not search for MPI compilers
1329      # that is we are turning off the self.useMPICompilers()
1330        self.logPrintWarning(os.path.join(self.argDB['with-mpi-dir'], 'bin')+ ' dir does not exist! Skipping check for MPI compilers due to potentially incorrect --with-mpi-dir option. Suggest using --with-cc=/path/to/mpicc option instead')
1331
1332        self.argDB['with-mpi-compilers'] = 0
1333      if self.useMPICompilers():
1334        self.usedMPICompilers = 1
1335        cray = os.getenv('CRAYPE_DIR')
1336        if cray:
1337          cross_cc = self.crayCrossCompiler('cc')
1338          if cross_cc:
1339            self.cross_cc = cross_cc
1340            self.log.write('Cray system using C cross compiler:'+cross_cc+'\n')
1341            self.cross_LIBS = self.crayCrossLIBS('cc')
1342            self.log.write('Cray system using C cross LIBS:'+self.cross_LIBS+'\n')
1343          yield 'cc'
1344          if cross_cc:
1345            delattr(self, 'cross_cc')
1346            delattr(self, 'cross_LIBS')
1347        yield 'mpincc'
1348        yield 'mpicc'
1349        yield 'mpiicc'
1350        yield 'mpcc_r'
1351        yield 'mpcc'
1352        yield 'mpxlc'
1353        yield 'hcc'
1354        self.usedMPICompilers = 0
1355      yield 'ncc'
1356      yield 'gcc'
1357      yield 'clang'
1358      yield 'icc'
1359      yield 'cc'
1360      yield 'xlc'
1361      path = os.path.join(os.getcwd(),'lib','petsc','win32fe','bin')
1362      yield os.path.join(path,'win32fe_icl')
1363      yield os.path.join(path,'win32fe_cl')
1364      yield 'pgcc'
1365    return
1366
1367  def showMPIWrapper(self,compiler):
1368    if os.path.basename(compiler).startswith('mpi'):
1369      self.logPrint(' MPI compiler wrapper '+compiler+' failed to compile')
1370      try:
1371        output = self.executeShellCommand(compiler + ' -show', log = self.log)[0]
1372      except RuntimeError:
1373        self.logPrint('-show option failed for MPI compiler wrapper '+compiler)
1374    self.logPrint(' MPI compiler wrapper '+compiler+' is likely incorrect.\n  Use --with-mpi-dir to indicate an alternate MPI.')
1375
1376  def checkCCompiler(self):
1377    import re
1378    '''Locate a functional C compiler'''
1379    if 'with-cc' in self.argDB and self.argDB['with-cc'] == '0':
1380      raise RuntimeError('A functional C compiler is necessary for configure, cannot use --with-cc=0')
1381    self.mesg = ''
1382    for compiler in self.generateCCompilerGuesses():
1383      try:
1384        if self.getExecutable(compiler, resultName = 'CC'):
1385          self.checkCompiler('C')
1386          break
1387      except RuntimeError as e:
1388        self.mesg = str(e)
1389        self.logPrint('Error testing C compiler: '+str(e))
1390        self.showMPIWrapper(compiler)
1391        self.delMakeMacro('CC')
1392        del self.CC
1393    if not hasattr(self, 'CC'):
1394      raise RuntimeError('Could not locate a functional C compiler')
1395    try:
1396      (output,error,status) = self.executeShellCommand(self.CC+' --version', log = self.log)
1397    except:
1398      pass
1399    else:
1400      if self.isDarwin(self.log) and self.isARM(self.log) and output.find('x86_64-apple-darwin') > -1:
1401        raise RuntimeError('Running on a macOS ARM system but your compilers are configured for Intel processors\n' + output + '\n')
1402
1403    (output, error, status) = config.base.Configure.executeShellCommand(self.CC+' -v | head -n 20', log = self.log)
1404    output = output + error
1405    if '(gcc version 4.8.5 compatibility)' in output or re.match('^Selected GCC installation:.*4.8.5$', output):
1406       self.logPrintWarning('Intel compiler being used with gcc 4.8.5 compatibility, failures may occur. Recommend having a newer gcc version in your path.')
1407    if os.path.basename(self.CC).startswith('mpi'):
1408       self.logPrint('Since MPI c compiler starts with mpi, force searches for other compilers to only look for MPI compilers\n')
1409       self.argDB['with-mpi-compilers'] = 1
1410    return
1411
1412  def generateCPreprocessorGuesses(self):
1413    '''Determines the C preprocessor from --with-cpp, then CPP, then the C compiler'''
1414    if 'with-cpp' in self.argDB:
1415      yield self.argDB['with-cpp']
1416    elif 'CPP' in self.argDB:
1417      yield self.argDB['CPP']
1418    else:
1419      yield self.CC+' -E'
1420      yield self.CC+' --use cpp32'
1421    return
1422
1423  def checkCPreprocessor(self):
1424    '''Locate a functional C preprocessor'''
1425    with self.Language('C'):
1426      for compiler in self.generateCPreprocessorGuesses():
1427        try:
1428          if self.getExecutable(compiler, resultName = 'CPP'):
1429            if not self.checkPreprocess('#include <stdlib.h>\n'):
1430              raise RuntimeError('Cannot preprocess C with '+self.CPP+'.')
1431            return
1432        except RuntimeError as e:
1433          self.logPrint(str(e))
1434    raise RuntimeError('Cannot find a C preprocessor')
1435    return
1436
1437  def generateCUDACompilerGuesses(self):
1438    '''Determine the CUDA compiler using CUDAC, then --with-cudac
1439       - Any given category can be excluded'''
1440    if hasattr(self, 'CUDAC'):
1441      yield self.CUDAC
1442      raise RuntimeError('Error: '+self.mesg)
1443    elif 'with-cudac' in self.argDB:
1444      yield self.argDB['with-cudac']
1445      raise RuntimeError('CUDA compiler you provided with -with-cudac='+self.argDB['with-cudac']+' cannot be found or does not work.'+'\n'+self.mesg)
1446    elif 'CUDAC' in self.argDB:
1447      yield self.argDB['CUDAC']
1448      raise RuntimeError('CUDA compiler you provided with -CUDAC='+self.argDB['CUDAC']+' cannot be found or does not work.'+'\n'+self.mesg)
1449    elif 'with-cuda-dir' in self.argDB:
1450      nvccPath = os.path.join(self.argDB['with-cuda-dir'], 'bin','nvcc')
1451      yield nvccPath
1452    else:
1453      yield 'nvcc'
1454      yield os.path.join('/Developer','NVIDIA','CUDA-6.5','bin','nvcc')
1455      yield os.path.join('/usr','local','cuda','bin','nvcc')
1456      yield 'clang'
1457    return
1458
1459  def checkCUDACompiler(self):
1460    '''Locate a functional CUDA compiler'''
1461    self.mesg = ''
1462    for compiler in self.generateCUDACompilerGuesses():
1463      try:
1464        if self.getExecutable(compiler, resultName = 'CUDAC'):
1465          self.checkCompiler('CUDA')
1466          # Put version info into the log
1467          self.executeShellCommand(self.CUDAC+' --version', log = self.log)
1468          break
1469      except RuntimeError as e:
1470        self.mesg = str(e)
1471        self.logPrint('Error testing CUDA compiler: '+str(e))
1472        self.delMakeMacro('CUDAC')
1473        del self.CUDAC
1474    return
1475
1476  def generateCUDAPreprocessorGuesses(self):
1477    '''Determines the CUDA preprocessor from --with-cudapp, then CUDAPP, then the CUDA compiler'''
1478    if 'with-cudacpp' in self.argDB:
1479      yield self.argDB['with-cudapp']
1480    elif 'CUDAPP' in self.argDB:
1481      yield self.argDB['CUDAPP']
1482    else:
1483      if hasattr(self, 'CUDAC'):
1484        yield self.CUDAC+' -E'
1485    return
1486
1487  def checkCUDAPreprocessor(self):
1488    '''Locate a functional CUDA preprocessor'''
1489    with self.Language('CUDA'):
1490      for compiler in self.generateCUDAPreprocessorGuesses():
1491        try:
1492          if self.getExecutable(compiler, resultName = 'CUDAPP'):
1493            if not self.checkPreprocess('#include <stdlib.h>\n__global__ void testFunction() {return;};'):
1494              raise RuntimeError('Cannot preprocess CUDA with '+self.CUDAPP+'.')
1495            return
1496        except RuntimeError as e:
1497          self.logPrint(str(e))
1498    return
1499
1500  def generateHIPCompilerGuesses(self):
1501    '''Determine the HIP compiler using HIPC, then --with-hipc
1502       - Any given category can be excluded'''
1503    if hasattr(self, 'HIPC'):
1504      yield self.HIPC
1505      raise RuntimeError('Error: '+self.mesg)
1506    elif 'with-hipc' in self.argDB:
1507      yield self.argDB['with-hipc']
1508      raise RuntimeError('HIPC compiler you provided with -with-hipc='+self.argDB['with-hipc']+' cannot be found or does not work.'+'\n'+self.mesg)
1509    elif 'HIPC' in self.argDB:
1510      yield self.argDB['HIPC']
1511      raise RuntimeError('HIP compiler you provided with -HIPC='+self.argDB['HIPC']+' cannot be found or does not work.'+'\n'+self.mesg)
1512    elif 'with-hip-dir' in self.argDB:
1513      hipPath = os.path.join(self.argDB['with-hip-dir'], 'bin','hipcc')
1514      yield hipPath
1515    else:
1516      yield 'hipcc'
1517      yield os.path.join('opt','rocm','bin','hipcc')
1518    return
1519
1520  def checkHIPCompiler(self):
1521    '''Locate a functional HIP compiler'''
1522    self.mesg = 'in generateHIPCompilerGuesses'
1523    for compiler in self.generateHIPCompilerGuesses():
1524      try:
1525        if self.getExecutable(compiler, resultName = 'HIPC'):
1526          self.checkCompiler('HIP')
1527          # Put version info into the log
1528          self.executeShellCommand(self.HIPC+' --version', log = self.log)
1529          break
1530      except RuntimeError as e:
1531        self.mesg = str(e)
1532        self.logPrint('Error testing HIP compiler: '+str(e))
1533        self.delMakeMacro('HIPC')
1534        del self.HIPC
1535    return
1536
1537  def generateHIPPreprocessorGuesses(self):
1538    '''Determines the HIP preprocessor from --with-hippp, then HIPPP, then the HIP compiler'''
1539    if 'with-hipcpp' in self.argDB:
1540      yield self.argDB['with-hippp']
1541    elif 'HIPPP' in self.argDB:
1542      yield self.argDB['HIPPP']
1543    else:
1544      if hasattr(self, 'HIPC'):
1545        yield self.HIPC+' -E'
1546    return
1547
1548  def checkHIPPreprocessor(self):
1549    '''Locate a functional HIP preprocessor'''
1550    with self.Language('HIP'):
1551      for compiler in self.generateHIPPreprocessorGuesses():
1552        try:
1553          if self.getExecutable(compiler, resultName = 'HIPPP'):
1554            if not self.checkPreprocess('#include <stdlib.h>\n__global__ void testFunction() {return;};'):
1555              raise RuntimeError('Cannot preprocess HIP with '+self.HIPPP+'.')
1556            return
1557        except RuntimeError as e:
1558          self.logPrint(str(e))
1559    return
1560
1561  def generateSYCLCompilerGuesses(self):
1562    '''Determine the SYCL compiler using SYCLC, then --with-syclc
1563       - Any given category can be excluded'''
1564    if hasattr(self, 'SYCLC'):
1565      yield self.SYCLC
1566      raise RuntimeError('Error: '+self.mesg)
1567    elif 'with-syclc' in self.argDB:
1568      yield self.argDB['with-syclc']
1569      raise RuntimeError('SYCLC compiler you provided with -with-syclxx='+self.argDB['with-syclxx']+' cannot be found or does not work.'+'\n'+self.mesg)
1570    elif 'SYCLC' in self.argDB:
1571      yield self.argDB['SYCLC']
1572      raise RuntimeError('SYCLC compiler you provided with -SYCLC='+self.argDB['SYCLC']+' cannot be found or does not work.'+'\n'+self.mesg)
1573    elif 'with-sycl-dir' in self.argDB:
1574      syclPath = os.path.join(self.argDB['with-sycl-dir'], 'bin','dpcpp')
1575      yield syclPath
1576    return
1577
1578  def checkSYCLCompiler(self):
1579    '''Locate a functional SYCL compiler'''
1580    self.mesg = 'in generateSYCLCompilerGuesses'
1581    for compiler in self.generateSYCLCompilerGuesses():
1582      try:
1583        if self.getExecutable(compiler, resultName = 'SYCLC'):
1584          self.checkCompiler('SYCL')
1585          # Put version info into the log
1586          self.executeShellCommand(self.SYCLC+' --version', log = self.log)
1587          break
1588      except RuntimeError as e:
1589        self.mesg = str(e)
1590        self.delMakeMacro('SYCLC')
1591        del self.SYCLC
1592    return
1593
1594  def generateSYCLPreprocessorGuesses(self):
1595    '''Determines the SYCL preprocessor from --with-syclpp, then SYCLPP, then the SYCL compiler'''
1596    if 'with-syclpp' in self.argDB:
1597      yield self.argDB['with-syclpp']
1598    elif 'SYCLPP' in self.argDB:
1599      yield self.argDB['SYCLPP']
1600    else:
1601      if hasattr(self, 'SYCLC'):
1602        yield self.SYCLC +' -E'
1603    return
1604
1605  def checkSYCLPreprocessor(self):
1606    '''Locate a functional SYCL preprocessor'''
1607    with self.Language('SYCL'):
1608      for compiler in self.generateSYCLPreprocessorGuesses():
1609        try:
1610          if self.getExecutable(compiler, resultName = 'SYCLPP'):
1611            if not self.checkPreprocess('#include <sycl/sycl.hpp>\n void testFunction() {return;};'):
1612              raise RuntimeError('Cannot preprocess SYCL with '+self.SYCLPP+'.')
1613            return
1614        except RuntimeError as e:
1615          self.logPrint(str(e))
1616    return
1617
1618  def generateCxxCompilerGuesses(self):
1619    '''Determine the Cxx compiler'''
1620
1621    if hasattr(self, 'CXX'):
1622      yield self.CXX
1623      if self.argDB['download-mpich']: mesg ='with downloaded MPICH'
1624      elif self.argDB['download-openmpi']: mesg ='with downloaded Open MPI'
1625      else: mesg = ''
1626      raise RuntimeError('Error '+mesg+': '+self.mesg)
1627    elif 'with-c++' in self.argDB:
1628      raise RuntimeError('Keyword --with-c++ is WRONG, use --with-cxx')
1629    if 'with-CC' in self.argDB:
1630      raise RuntimeError('Keyword --with-CC is WRONG, use --with-cxx')
1631
1632    if 'with-cxx' in self.argDB:
1633      if self.argDB['with-cxx'] == 'gcc': raise RuntimeError('Cannot use C compiler gcc as the C++ compiler passed in with --with-cxx')
1634      yield self.argDB['with-cxx']
1635      raise RuntimeError('C++ compiler you provided with -with-cxx='+self.argDB['with-cxx']+' cannot be found or does not work.'+'\n'+self.mesg)
1636    elif 'CXX' in self.argDB:
1637      yield self.argDB['CXX']
1638      raise RuntimeError('C++ compiler you provided with -CXX='+self.argDB['CXX']+' cannot be found or does not work.'+'\n'+self.mesg)
1639    elif self.usedMPICompilers and 'with-mpi-dir' in self.argDB and os.path.isdir(os.path.join(self.argDB['with-mpi-dir'], 'bin')):
1640      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpinc++')
1641      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpiicpc')
1642      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpicxx')
1643      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'hcp')
1644      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpic++')
1645      if not Configure.isDarwin(self.log):
1646        yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpiCC')
1647      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpCC_r')
1648      raise RuntimeError('bin/<mpiCC,mpicxx,hcp,mpCC_r> you provided with -with-mpi-dir='+self.argDB['with-mpi-dir']+' cannot be found or does not work. See https://petsc.org/release/faq/#invalid-mpi-compilers')
1649    else:
1650      if self.usedMPICompilers:
1651        # TODO: Should only look for the MPI CXX compiler related to the found MPI C compiler
1652        cray = os.getenv('CRAYPE_DIR')
1653        if cray:
1654          cross_CC = self.crayCrossCompiler('CC')
1655          if cross_CC:
1656            self.cross_CC = cross_CC
1657            self.log.write('Cray system using C++ cross compiler:'+cross_CC+'\n')
1658          yield 'CC'
1659          if cross_CC: delattr(self, 'cross_CC')
1660        yield 'mpinc++'
1661        yield 'mpicxx'
1662        yield 'mpiicpc'
1663        yield 'mpCC_r'
1664        if not Configure.isDarwin(self.log):
1665          yield 'mpiCC'
1666        yield 'mpic++'
1667        yield 'mpCC'
1668        yield 'mpxlC'
1669      else:
1670        #attempt to match c++ compiler with c compiler
1671        if self.CC.find('win32fe_cl') >= 0:
1672          yield self.CC
1673        elif self.CC.find('win32fe_icl') >= 0:
1674          yield self.CC
1675        elif self.CC == 'gcc':
1676          yield 'g++'
1677        elif self.CC == 'clang':
1678          yield 'clang++'
1679        elif self.CC == 'icc':
1680          yield 'icpc'
1681        elif self.CC == 'xlc':
1682          yield 'xlC'
1683        elif self.CC == 'ncc':
1684          yield 'nc++'
1685        yield 'g++'
1686        yield 'clang++'
1687        yield 'c++'
1688        yield 'icpc'
1689        yield 'CC'
1690        yield 'cxx'
1691        yield 'cc++'
1692        yield 'xlC'
1693        yield 'ccpc'
1694        path = os.path.join(os.getcwd(),'lib','petsc','win32fe','bin')
1695        yield os.path.join(path,'win32fe_icl')
1696        yield os.path.join(path,'win32fe_cl')
1697        yield 'pgCC'
1698        yield 'CC'
1699    return
1700
1701  def checkCxxCompiler(self):
1702    '''Locate a functional Cxx compiler'''
1703    self.mesg = ''
1704    for compiler in self.generateCxxCompilerGuesses():
1705      # Determine an acceptable extensions for the C++ compiler
1706      for ext in ['.cc', '.cpp', '.C']:
1707        self.framework.getCompilerObject('Cxx').sourceExtension = ext
1708        try:
1709          if self.getExecutable(compiler, resultName = 'CXX'):
1710            self.checkCompiler('Cxx')
1711            break
1712        except RuntimeError as e:
1713          self.mesg = str(e)
1714          self.logPrint('Error testing C++ compiler: '+str(e))
1715          self.showMPIWrapper(compiler)
1716          self.delMakeMacro('CXX')
1717          del self.CXX
1718      if hasattr(self, 'CXX'):
1719        try:
1720          self.executeShellCommand(self.CXX+' --version', log = self.log)
1721        except:
1722          pass
1723        break
1724    return
1725
1726  def generateCxxPreprocessorGuesses(self):
1727    '''Determines the Cxx preprocessor from CXXPP, then --with-cxxpp, then the Cxx compiler'''
1728    if 'with-cxxpp' in self.argDB:
1729      yield self.argDB['with-cxxpp']
1730    elif 'CXXPP' in self.argDB:
1731      yield self.argDB['CXXPP']
1732    else:
1733      yield self.CXX+' -E'
1734      yield self.CXX+' --use cpp32'
1735    return
1736
1737  def checkCxxPreprocessor(self):
1738    '''Locate a functional Cxx preprocessor'''
1739    if not hasattr(self,'CXX'): # pointless, it is checked already
1740      return
1741    with self.Language('Cxx'):
1742      for compiler in self.generateCxxPreprocessorGuesses():
1743        try:
1744          if self.getExecutable(compiler, resultName = 'CXXPP'):
1745            if not self.checkPreprocess('#include <cstdlib>\n'):
1746              raise RuntimeError('Cannot preprocess Cxx with '+self.CXXPP+'.')
1747            break
1748        except RuntimeError as e:
1749          self.logPrint(str(e))
1750          if os.path.basename(self.CXXPP) in ['mpicxx', 'mpiCC']:
1751            self.logPrint('MPI installation '+self.getCompiler()+' is likely incorrect.\n  Use --with-mpi-dir to indicate an alternate MPI')
1752          self.delMakeMacro('CXXPP')
1753          del self.CXXPP
1754    return
1755
1756  def generateFortranCompilerGuesses(self):
1757    '''Determine the Fortran compiler'''
1758
1759    if hasattr(self, 'FC'):
1760      yield self.FC
1761      if self.argDB['download-mpich']: mesg ='with downloaded MPICH'
1762      elif self.argDB['download-openmpi']: mesg ='with downloaded Open MPI'
1763      else: mesg = ''
1764      raise RuntimeError('Error '+mesg+': '+self.mesg)
1765    elif 'with-fc' in self.argDB:
1766      yield self.argDB['with-fc']
1767      raise RuntimeError('Fortran compiler you provided with --with-fc='+self.argDB['with-fc']+' cannot be found or does not work.'+'\n'+self.mesg)
1768    elif 'FC' in self.argDB:
1769      yield self.argDB['FC']
1770      yield self.argDB['FC']
1771      raise RuntimeError('Fortran compiler you provided with -FC='+self.argDB['FC']+' cannot be found or does not work.'+'\n'+self.mesg)
1772    elif self.usedMPICompilers and 'with-mpi-dir' in self.argDB and os.path.isdir(os.path.join(self.argDB['with-mpi-dir'], 'bin')):
1773      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpinfort')
1774      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpiifort')
1775      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpif90')
1776      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpf90')
1777      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpxlf95_r')
1778      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpxlf90_r')
1779      yield os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpxlf_r')
1780      if os.path.isfile(os.path.join(self.argDB['with-mpi-dir'], 'bin', 'mpif90')):
1781        raise RuntimeError('bin/mpif90 you provided with --with-mpi-dir='+self.argDB['with-mpi-dir']+' cannot be found or does not work.\nRun with --with-fc=0 if you wish to use this MPI and disable Fortran. See https://petsc.org/release/faq/#invalid-mpi-compilers')
1782    else:
1783      if self.usedMPICompilers:
1784        # TODO: Should only look for the MPI Fortran compiler related to the found MPI C compiler
1785        cray = os.getenv('CRAYPE_DIR')
1786        if cray:
1787          cross_fc = self.crayCrossCompiler('ftn')
1788          if cross_fc:
1789            self.cross_fc = cross_fc
1790            self.log.write('Cray system using Fortran cross compiler:'+cross_fc+'\n')
1791          yield 'ftn'
1792          if cross_fc: delattr(self, 'cross_fc')
1793        yield 'mpinfort'
1794        yield 'mpif90'
1795        yield 'mpiifort'
1796        yield 'mpxlf_r'
1797        yield 'mpxlf'
1798        yield 'mpf90'
1799      else:
1800        path = os.path.join(os.getcwd(),'lib','petsc','win32fe','bin')
1801        #attempt to match fortran compiler with c compiler
1802        if self.CC == 'gcc':
1803          yield 'gfortran'
1804        elif self.CC == 'clang':
1805          yield 'gfortran'
1806        elif self.CC == 'icc':
1807          yield 'ifort'
1808        elif self.CC == 'xlc':
1809          yield 'xlf90'
1810          yield 'xlf'
1811        elif self.CC == 'ncc':
1812          yield 'nfort'
1813        elif self.CC.find('win32fe_icl') >= 0:
1814          yield os.path.join(path,'win32fe_ifort')
1815        yield 'gfortran'
1816        yield 'egfortran' # On OpenBSD, the GFortran executable is named egfortran, https://fortran-lang.org/learn/os_setup/install_gfortran/#openbsd
1817        yield 'g95'
1818        yield 'xlf90'
1819        yield 'xlf'
1820        yield 'f90'
1821        yield 'lf95'
1822        yield os.path.join(path,'win32fe_ifort')
1823        yield 'ifort'
1824        yield 'ifc'
1825        yield 'pgf90'
1826        yield 'f95'
1827        yield 'f90'
1828    return
1829
1830  def checkFortranCompiler(self):
1831    '''Locate a functional Fortran compiler'''
1832    if 'with-fc' in self.argDB and self.argDB['with-fc'] == '0':
1833      if 'FC' in self.argDB:
1834        del self.argDB['FC']
1835      return
1836    self.mesg = ''
1837    for compiler in self.generateFortranCompilerGuesses():
1838      try:
1839        if self.getExecutable(compiler, resultName = 'FC'):
1840          self.checkCompiler('FC')
1841          break
1842      except RuntimeError as e:
1843        self.mesg = str(e)
1844        self.logPrint('Error testing Fortran compiler: '+str(e))
1845        self.showMPIWrapper(compiler)
1846        self.delMakeMacro('FC')
1847        del self.FC
1848    if hasattr(self, 'FC'):
1849      try:
1850        self.executeShellCommand(self.FC+' --version', log = self.log)
1851      except:
1852        pass
1853    return
1854
1855  def generateFortranPreprocessorGuesses(self):
1856    '''Determines the Fortran preprocessor from FPP, then --with-fpp, then the Fortran compiler'''
1857    if 'with-fpp' in self.argDB:
1858      yield self.argDB['with-fpp']
1859    elif 'FPP' in self.argDB:
1860      yield self.argDB['FPP']
1861    else:
1862      yield self.FC+' -E'
1863      yield self.FC+' --use cpp32'
1864    return
1865
1866  def checkFortranPreprocessor(self):
1867    '''Locate a functional Fortran preprocessor'''
1868    if not hasattr(self, 'FC'):
1869      return
1870    with self.Language('FC'):
1871      for compiler in self.generateFortranPreprocessorGuesses():
1872        try:
1873          if self.getExecutable(compiler, resultName = 'FPP'):
1874            if not self.checkPreprocess('#define foo 10\n'):
1875              raise RuntimeError('Cannot preprocess Fortran with '+self.FPP+'.')
1876            break
1877        except RuntimeError as e:
1878          self.logPrint(str(e))
1879          if os.path.basename(self.FPP) in ['mpif90']:
1880            self.logPrint('MPI installation '+self.getCompiler()+' is likely incorrect.\n  Use --with-mpi-dir to indicate an alternate MPI')
1881          self.delMakeMacro('FPP')
1882          del self.FPP
1883    return
1884
1885  def checkFortranComments(self):
1886    '''Make sure fortran comment "!" works'''
1887    self.pushLanguage('FC')
1888    if not self.checkCompile('! comment'):
1889      raise RuntimeError(self.getCompiler()+' cannot process fortran comments.')
1890    self.logPrint('Fortran comments can use ! in column 1')
1891    self.popLanguage()
1892    return
1893
1894  def containsInvalidFlag(self, output):
1895    '''If the output contains evidence that an invalid flag was used, return True'''
1896    substrings = ('unknown argument', 'ignoring unsupported linker flag', 'unrecognized command line option','unrecognised command line option',
1897                  'unrecognized option','unrecognised option','not recognized',
1898                  'not recognised','unknown option','unknown warning option',
1899                  'unknown flag','unknown switch','ignoring option','ignored','argument unused',
1900                  'unsupported command line options encountered',
1901                  'not supported','is unsupported and will be skipped','illegal option',
1902                  'invalid option','invalid suboption','bad ',' option','petsc error',
1903                  'unbekannte option','linker input file unused because linking not done',
1904                  'warning: // comments are not allowed in this language',
1905                  'no se reconoce la opci','non reconnue','warning: unsupported linker arg:','ignoring unknown option')
1906    outlo = output.lower()
1907    return any(sub.lower() in outlo for sub in substrings)
1908
1909  def containsInvalidLinkerFlag(self, output):
1910    '''If the output contains evidence that an invalid flag was used, return True'''
1911    substrings = ('unknown argument', 'ignoring unsupported linker flag', 'unrecognized command line option','unrecognised command line option',
1912                  'unrecognized option','unrecognised option','unknown option',
1913                  'unknown flag','unsupported command line options encountered',
1914                  'not supported','is unsupported and will be skipped','illegal option',
1915                  'invalid option','invalid suboption',
1916                  'unbekannte option',
1917                  'warning: -commons use_dylibs is no longer supported, using error treatment instead',
1918                  'warning: -bind_at_load is deprecated on macOS',
1919                  'no se reconoce la opci','non reconnue','warning: unsupported linker arg:','ignoring unknown option')
1920    outlo = output.lower()
1921    return any(sub.lower() in outlo for sub in substrings)
1922
1923  def checkCompilerFlag(self, flag, includes = '', body = '', compilerOnly = 0):
1924    '''Determine whether the compiler accepts the given flag'''
1925    flagsArg = self.getCompilerFlagsArg(compilerOnly)
1926    oldFlags = getattr(self, flagsArg)
1927    setattr(self, flagsArg, oldFlags+' '+flag)
1928    (output, error, status) = self.outputCompile(includes, body)
1929    output = self.filterCompileOutput(output+'\n'+error,flag=flag)
1930    self.logPrint('Output from compiling with '+oldFlags+' '+flag+'\n'+output)
1931    setattr(self, flagsArg, oldFlags)
1932    # Please comment each entry and provide an example line
1933    if status:
1934      self.logPrint('Rejecting compiler flag '+flag+' due to nonzero status from link')
1935      return False
1936    elif self.containsInvalidFlag(output):
1937      self.logPrint('Rejecting compiler flag '+flag+' due to \n'+output)
1938      return False
1939    return True
1940
1941  def insertCompilerFlag(self, flag, compilerOnly):
1942    '''DANGEROUS: Put in the compiler flag without checking'''
1943    if not flag: return
1944    flagsArg = self.getCompilerFlagsArg(compilerOnly)
1945    setattr(self, flagsArg, getattr(self, flagsArg)+' '+flag)
1946    self.log.write('Added to '+self.language[-1]+' compiler flag '+flagsArg+': '+flag+'\n')
1947    return
1948
1949  def addCompilerFlag(self, flag, includes = '', body = '', extraflags = '', compilerOnly = 0):
1950    '''Determine whether the compiler accepts the given flag, and add it if valid, otherwise throw an exception'''
1951    if self.checkCompilerFlag(flag+' '+extraflags, includes, body, compilerOnly):
1952      self.insertCompilerFlag(flag, compilerOnly)
1953      return
1954    raise RuntimeError('Bad compiler flag: '+flag)
1955
1956  def insertPreprocessorFlag(self, flag):
1957    '''DANGEROUS: Put in the preprocessor flag without checking'''
1958    if not flag: return
1959    flagsArg = self.getPreprocessorFlagsArg()
1960    setattr(self, flagsArg, getattr(self, flagsArg)+' '+flag)
1961    self.log.write('Added to '+self.language[-1]+' preprocessor flag '+flagsArg+': '+flag+'\n')
1962    return
1963
1964  @contextlib.contextmanager
1965  def extraCompilerFlags(self, extraFlags, lang = None, **kwargs):
1966    assert isinstance(extraFlags,(list,tuple)), "extraFlags must be either a list or tuple"
1967    if lang:
1968      self.pushLanguage(lang)
1969    flagsArg  = self.getCompilerFlagsArg()
1970    oldCompilerFlags = getattr(self,flagsArg)
1971    skipFlags = []
1972    try:
1973      for i,flag in enumerate(extraFlags):
1974        try:
1975          self.addCompilerFlag(flag, **kwargs)
1976        except RuntimeError:
1977          skipFlags.append((i,flag))
1978      yield skipFlags
1979    finally:
1980      # This last finally is a bit of deep magic, it makes it so that if the code in the
1981      # resulting yield throws some unrelated exception which is meant to be caught
1982      # outside this ctx manager then the flags and languages are still reset
1983      if lang:
1984        oldLang = self.popLanguage()
1985      setattr(self,flagsArg,oldCompilerFlags)
1986
1987  def checkPragma(self):
1988    '''Check for all available applicable languages whether they complain (including warnings!) about potentially unknown pragmas'''
1989    usePragma = {}
1990    langMap = {'C':'CC','Cxx':'CXX','CUDA':'CUDAC','HIP':'HIPC','SYCL':'SYCLC'}
1991    for lang in langMap:
1992      if hasattr(self,langMap[lang]):
1993        usePragma[lang] = False
1994    for lang in usePragma.keys():
1995      with self.Language(lang):
1996        with self.extraCompilerFlags(['-Wunknown-pragmas']) as skipFlags:
1997          if not skipFlags:
1998            usePragma[lang] = self.checkCompile('#pragma GCC poison TEST')
1999    if all(usePragma.values()): self.framework.enablepoison = True
2000    return
2001
2002  def generatePICGuesses(self):
2003    if self.language[-1] == 'CUDA':
2004      yield '-Xcompiler -fPIC'
2005      yield '-fPIC'
2006      return
2007    if config.setCompilers.Configure.isGNU(self.getCompiler(), self.log) or config.setCompilers.Configure.isClang(self.getCompiler(), self.log) or config.setCompilers.Configure.isIntel(self.getCompiler(), self.log):
2008      PICFlags = ['-fPIC']
2009    elif config.setCompilers.Configure.isIBM(self.getCompiler(), self.log):
2010      PICFlags = ['-qPIC']
2011    else:
2012      PICFlags = ['-PIC','-qPIC','-KPIC','-fPIC','-fpic']
2013    try:
2014      output = self.executeShellCommand(self.getCompiler() + ' -show', log = self.log)[0]
2015    except:
2016      self.logPrint('Skipping checking MPI compiler command for PIC flag since MPI compiler -show causes an exception so is likely not an MPI compiler')
2017      output = ''
2018    output = output + ' ' + getattr(self, self.getCompilerFlagsArg(1)) + ' '
2019    # Try without specific PIC flag only if the MPI compiler or user compiler flag already provides a PIC option
2020    for i in PICFlags:
2021      if output.find(' '+i+' ') > -1:
2022        self.logPrint('Trying no specific compiler flag for PIC code since MPI compiler or current flags seem to provide such a flag with '+i)
2023        yield ''
2024        break
2025    for i in PICFlags:
2026      yield i
2027    yield ''
2028
2029  def checkPIC(self):
2030    '''Determine the PIC option for each compiler'''
2031    self.usePIC = 0
2032    useSharedLibraries = 'with-shared-libraries' in self.argDB and self.argDB['with-shared-libraries']
2033    myLanguage = self.language[-1]
2034    if not self.argDB['with-pic'] and not useSharedLibraries:
2035      self.logPrint("Skip checking PIC options on user request")
2036      return
2037    if self.argDB['with-pic'] and not useSharedLibraries:
2038      # this is a flaw in configure; it is a legitimate use case where PETSc is built with PIC flags but not shared libraries
2039      # to fix it the capability to build shared libraries must be enabled in configure if --with-pic=true even if shared libraries are off and this
2040      # test must use that capability instead of using the default shared library build in that case which is static libraries
2041      raise RuntimeError("Cannot determine compiler PIC flags if shared libraries is turned off\nEither run using --with-shared-libraries or --with-pic=0 and supply the compiler PIC flag via CFLAGS, CXXFLAGS, and FCFLAGS\n")
2042    if self.sharedLibraries and self.mainLanguage == 'C': languages = []
2043    else: languages = ['C']
2044    langMap = {'FC':'FC','Cxx':'CXX','CUDA':'CUDAC','HIP':'HIPC','SYCL':'SYCLC'}
2045    for language in langMap:
2046      if hasattr(self,langMap[language]): languages.append(language)
2047    for language in languages:
2048      self.pushLanguage(language)
2049      if language in ['C','Cxx','CUDA','HIP','SYCL']:
2050        includeLine = _picTestIncludes()
2051      else:
2052        includeLine = '      function foo(a)\n      real:: a,x,bar\n      common /xx/ x\n      x=a\n      foo = bar(x)\n      end\n'
2053      compilerFlagsArg = self.getCompilerFlagsArg(1) # compiler only
2054      oldCompilerFlags = getattr(self, compilerFlagsArg)
2055      for testFlag in self.generatePICGuesses():
2056        if testFlag:
2057          self.logPrint('Trying '+language+' compiler flag '+testFlag+' for PIC code')
2058        else:
2059          self.logPrint('Trying '+language+' for PIC code without any compiler flag')
2060        acceptedPIC = 1
2061        try:
2062          self.addCompilerFlag(testFlag, compilerOnly = 1)
2063          acceptedPIC = self.checkLink(includes = includeLine, body = None, codeBegin = '', codeEnd = '', cleanup = 1, shared = 1, linkLanguage = myLanguage)
2064        except RuntimeError:
2065          acceptedPIC = 0
2066        if not acceptedPIC:
2067          self.logPrint('Rejected '+language+' compiler flag '+testFlag+' because shared linker cannot handle it')
2068          setattr(self, compilerFlagsArg, oldCompilerFlags)
2069          continue
2070        if testFlag:
2071          self.logPrint('Accepted '+language+' compiler flag '+testFlag+' for PIC code')
2072        else:
2073          self.logPrint('Accepted '+language+' PIC code without compiler flag')
2074        self.isPIC = 1
2075        break
2076      self.popLanguage()
2077    return
2078
2079  def checkKandRFlags(self):
2080    '''Check C compiler flags that allow compiling K and R code (needed for some external packages)'''
2081    self.KandRFlags = []
2082    with self.Language('C'):
2083      if config.setCompilers.Configure.isGNU(self.getCompiler(), self.log) or config.setCompilers.Configure.isClang(self.getCompiler(), self.log):
2084        for f in ['-Wno-implicit-int', '-Wno-int-conversion', '-Wno-implicit-function-declaration', '-Wno-deprecated-non-prototype', '-fno-common']:
2085          if self.checkCompilerFlag(f, compilerOnly = 1):
2086            self.KandRFlags.append(f)
2087
2088  def checkLargeFileIO(self):
2089    '''check for large file support with 64-bit offset'''
2090    if not self.argDB['with-large-file-io']:
2091      return
2092    languages = ['C']
2093    if hasattr(self, 'CXX'):
2094      languages.append('Cxx')
2095    for language in languages:
2096      self.pushLanguage(language)
2097      if self.checkCompile('#include <unistd.h>','#ifndef _LFS64_LARGEFILE \n#error no largefile defines \n#endif'):
2098        try:
2099          self.addCompilerFlag('-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64',compilerOnly=1)
2100        except RuntimeError as e:
2101          self.logPrint('Error adding ' +language+ ' flags -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64')
2102      else:
2103        self.logPrint('Rejected ' +language+ ' flags -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64')
2104      self.popLanguage()
2105    return
2106
2107  def getArchiverFlags(self, archiver):
2108    prog = os.path.basename(archiver).split(' ')[0]
2109    flag = ''
2110    if 'AR_FLAGS' in self.argDB:
2111      flag = self.argDB['AR_FLAGS']
2112    elif prog.endswith('ar'):
2113      flag = 'cr'
2114    elif os.path.basename(archiver).endswith('_lib'):
2115      flag = '-a'
2116    if prog.endswith('ar') and not (self.isSolarisAR(prog, self.log) or self.isAIXAR(prog, self.log)):
2117      self.FAST_AR_FLAGS = 'Scq'
2118    else:
2119      self.FAST_AR_FLAGS = flag
2120    self.framework.addMakeMacro('FAST_AR_FLAGS',self.FAST_AR_FLAGS )
2121    return flag
2122
2123  def generateArchiverGuesses(self):
2124    defaultAr = None
2125    if 'with-ar' in self.argDB:
2126      defaultAr = self.argDB['with-ar']
2127    envAr = None
2128    if 'AR' in self.argDB:
2129      envAr = self.argDB['AR']
2130    defaultRanlib = None
2131    if 'with-ranlib' in self.argDB:
2132      defaultRanlib = self.argDB['with-ranlib']
2133    envRanlib = None
2134    if 'RANLIB' in self.argDB:
2135      envRanlib = self.argDB['RANLIB']
2136    if defaultAr and defaultRanlib:
2137      yield(defaultAr,self.getArchiverFlags(defaultAr),defaultRanlib)
2138      raise RuntimeError('The archiver set --with-ar="'+defaultAr+'" is broken or incompatible with the ranlib set --with-ranlib="'+defaultRanlib+'".')
2139    if defaultAr and envRanlib:
2140      yield(defaultAr,self.getArchiverFlags(defaultAr),envRanlib)
2141      raise RuntimeError('The archiver set --with-ar="'+defaultAr+'" is broken or incompatible with the ranlib set (perhaps in your environment) -RANLIB="'+envRanlib+'".')
2142    if envAr and defaultRanlib:
2143      yield(envAr,self.getArchiverFlags(envAr),defaultRanlib)
2144      raise RuntimeError('The archiver set --AR="'+envAr+'" is broken or incompatible with the ranlib set --with-ranlib="'+defaultRanlib+'".')
2145    if envAr and envRanlib:
2146      yield(envAr,self.getArchiverFlags(envAr),envRanlib)
2147      raise RuntimeError('The archiver set --AR="'+envAr+'" is broken or incompatible with the ranlib set (perhaps in your environment) -RANLIB="'+envRanlib+'".')
2148    if defaultAr:
2149      yield (defaultAr,self.getArchiverFlags(defaultAr),'ranlib')
2150      yield (defaultAr,self.getArchiverFlags(defaultAr),'true')
2151      raise RuntimeError('You set a value for --with-ar='+defaultAr+'", but '+defaultAr+' cannot be used\n')
2152    if envAr:
2153      yield (envAr,self.getArchiverFlags(envAr),'ranlib')
2154      yield (envAr,self.getArchiverFlags(envAr),'true')
2155      raise RuntimeError('You set a value for -AR="'+envAr+'" (perhaps in your environment), but '+envAr+' cannot be used\n')
2156    if defaultRanlib:
2157      yield ('ar',self.getArchiverFlags('ar'),defaultRanlib)
2158      path = os.path.join(os.getcwd(),'lib','petsc','bin')
2159      war  = os.path.join(path,'win32fe_lib')
2160      yield (war,self.getArchiverFlags(war),defaultRanlib)
2161      raise RuntimeError('You set --with-ranlib="'+defaultRanlib+'", but '+defaultRanlib+' cannot be used\n')
2162    if envRanlib:
2163      yield ('ar',self.getArchiverFlags('ar'),envRanlib)
2164      path = os.path.join(os.getcwd(),'lib','petsc','bin')
2165      war  = os.path.join(path,'win32fe_lib')
2166      yield (war,self.getArchiverFlags('war'),envRanlib)
2167      raise RuntimeError('You set -RANLIB="'+envRanlib+'" (perhaps in your environment), but '+defaultRanlib+' cannot be used\n')
2168    if config.setCompilers.Configure.isWindows(self.getCompiler(), self.log):
2169      path = os.path.join(os.getcwd(),'lib','petsc','bin')
2170      war  = os.path.join(path,'win32fe_lib')
2171      yield (war,self.getArchiverFlags(war),'true')
2172    yield ('ar',self.getArchiverFlags('ar'),'ranlib -c')
2173    yield ('ar',self.getArchiverFlags('ar'),'ranlib')
2174    yield ('ar',self.getArchiverFlags('ar'),'true')
2175    # IBM with 64-bit pointers
2176    yield ('ar','-X64 '+self.getArchiverFlags('ar'),'ranlib -c')
2177    yield ('ar','-X64 '+self.getArchiverFlags('ar'),'ranlib')
2178    yield ('ar','-X64 '+self.getArchiverFlags('ar'),'true')
2179    return
2180
2181  def checkArchiver(self):
2182    '''Check that the archiver exists and can make a library usable by the compiler'''
2183    objName    = os.path.join(self.tmpDir, 'conf1.o')
2184    arcUnix    = os.path.join(self.tmpDir, 'libconf1.a')
2185    arcWindows = os.path.join(self.tmpDir, 'libconf1.lib')
2186    def checkArchive(command, status, output, error):
2187      if error:
2188        error = error.splitlines()
2189        error = [s for s in error if not (s.find('unsupported GNU_PROPERTY_TYPE') >= 0 and s.find('warning:') >= 0)]
2190        error = [s for s in error if s.find("xiar: executing 'ar'") < 0]
2191        if error: error = '\n'.join(error)
2192        else: error = ''
2193      if error or status:
2194        self.logError('archiver', status, output, error)
2195        if os.path.isfile(objName):
2196          os.remove(objName)
2197        raise RuntimeError('Archiver is not functional')
2198      return
2199    def checkRanlib(command, status, output, error):
2200      if error or status:
2201        self.logError('ranlib', status, output, error)
2202        if os.path.isfile(arcUnix):
2203          os.remove(arcUnix)
2204        raise RuntimeError('Ranlib is not functional with your archiver.  Try --with-ranlib=true if ranlib is unnecessary.')
2205      return
2206    oldLibs = self.LIBS
2207    self.pushLanguage('C')
2208    for (archiver, arflags, ranlib) in self.generateArchiverGuesses():
2209      if not self.checkCompile('', 'int foo(int a) {\n  return a+1;\n}\n\n', cleanup = 0, codeBegin = '', codeEnd = ''):
2210        raise RuntimeError('Compiler is not functional')
2211      if os.path.isfile(objName):
2212        os.remove(objName)
2213      os.rename(self.compilerObj, objName)
2214      if self.getExecutable(archiver, getFullPath = 1, resultName = 'AR'):
2215        if self.getExecutable(ranlib, getFullPath = 1, resultName = 'RANLIB'):
2216          arext = 'a'
2217          try:
2218            (output, error, status) = config.base.Configure.executeShellCommand(self.AR+' '+arflags+' '+arcUnix+' '+objName, checkCommand = checkArchive, log = self.log)
2219            (output, error, status) = config.base.Configure.executeShellCommand(self.RANLIB+' '+arcUnix, checkCommand = checkRanlib, log = self.log)
2220          except RuntimeError as e:
2221            self.logPrint(str(e))
2222            continue
2223          self.LIBS = '-L'+self.tmpDir+' -lconf1 ' + oldLibs
2224          success =  self.checkLink('extern int foo(int);', '  int b = foo(1);  (void)b')
2225          os.rename(arcUnix, arcWindows)
2226          if not success:
2227            arext = 'lib'
2228            success = self.checkLink('extern int foo(int);', '  int b = foo(1);  (void)b')
2229            os.remove(arcWindows)
2230            if success:
2231              break
2232          else:
2233            os.remove(arcWindows)
2234            break
2235      else:
2236        if os.path.isfile(objName):
2237          os.remove(objName)
2238        self.LIBS = oldLibs
2239        self.popLanguage()
2240        if 'with-ar' in self.argDB:
2241          raise RuntimeError('Archiver set with --with-ar='+self.argDB['with-ar']+' does not exist')
2242        else:
2243          raise RuntimeError('Could not find a suitable archiver.  Use --with-ar to specify an archiver.')
2244    self.AR_FLAGS      = arflags
2245    self.AR_LIB_SUFFIX = arext
2246    self.framework.addMakeMacro('AR_FLAGS', self.AR_FLAGS)
2247    self.addMakeMacro('AR_LIB_SUFFIX', self.AR_LIB_SUFFIX)
2248    os.remove(objName)
2249    self.LIBS = oldLibs
2250    self.popLanguage()
2251    return
2252
2253  def checkArchiverRecipeArgfile(self):
2254    '''Checks if AR handles @ notation'''
2255    def checkArchiverArgfile(command, status, output, error):
2256      if error or status:
2257        self.logError('archiver', status, output, error)
2258        if os.path.isfile(objName):
2259          os.remove(objName)
2260        raise RuntimeError('ArchiverArgfile error')
2261      return
2262    oldDir = os.getcwd()
2263    os.chdir(self.tmpDir)
2264    try:
2265      objName = 'checkRecipeArgfile.o'
2266      obj = open(objName, 'a').close()
2267      argsName = 'checkRecipeArgfile.args'
2268      args = open(argsName, 'a')
2269      args.write(objName)
2270      args.close()
2271      archiveName = 'checkRecipeArgfile.'+self.AR_LIB_SUFFIX
2272      (output, error, status) = config.base.Configure.executeShellCommand(self.AR+' '+self.AR_FLAGS+' '+archiveName+' @'+argsName,checkCommand = checkArchiverArgfile, log = self.log)
2273      os.remove(objName)
2274      os.remove(argsName)
2275      os.remove(archiveName)
2276      if not status:
2277        self.framework.addMakeMacro('AR_ARGFILE','yes')
2278    except RuntimeError:
2279      pass
2280    os.chdir(oldDir)
2281
2282  def setStaticLinker(self):
2283    language = self.language[-1]
2284    return self.framework.setSharedLinkerObject(language, self.framework.getLanguageModule(language).StaticLinker(self.argDB))
2285
2286  def generateSharedLinkerGuesses(self):
2287    if not self.argDB['with-shared-libraries']:
2288      self.setStaticLinker()
2289      self.staticLinker = self.AR
2290      self.staticLibraries = 1
2291      self.LDFLAGS = ''
2292      yield (self.AR, [], self.AR_LIB_SUFFIX)
2293      raise RuntimeError('Archiver failed static link check')
2294    if 'with-shared-ld' in self.argDB:
2295      yield (self.argDB['with-shared-ld'], [], 'so')
2296    if 'LD_SHARED' in self.argDB:
2297      yield (self.argDB['LD_SHARED'], [], 'so')
2298    if Configure.isDarwin(self.log):
2299      if 'with-shared-ld' in self.argDB:
2300        yield (self.argDB['with-shared-ld'], ['-dynamiclib', '-undefined dynamic_lookup', '-no_compact_unwind'], 'dylib')
2301      if hasattr(self, 'CXX') and self.mainLanguage == 'Cxx':
2302        yield (self.CXX, ['-dynamiclib', '-undefined dynamic_lookup', '-no_compact_unwind'], 'dylib')
2303      yield (self.CC, ['-dynamiclib', '-undefined dynamic_lookup', '-no_compact_unwind'], 'dylib')
2304    if hasattr(self, 'CXX') and self.mainLanguage == 'Cxx':
2305      # C++ compiler default
2306      if config.setCompilers.Configure.isIBM(self.CXX, self.log):
2307        yield (self.CXX, ['-qmkshrobj'], 'so')
2308      yield (self.CXX, ['-shared'], 'so')
2309      yield (self.CXX, ['-dynamic'], 'so')
2310      yield (self.CC, ['-shared'], 'dll')
2311    # C compiler default
2312      if config.setCompilers.Configure.isIBM(self.CC, self.log):
2313        yield (self.CC, ['-qmkshrobj'], 'so')
2314    yield (self.CC, ['-shared'], 'so')
2315    yield (self.CC, ['-dynamic'], 'so')
2316    yield (self.CC, ['-shared'], 'dll')
2317    # Windows default
2318    if self.CC.find('win32fe') >=0:
2319      if hasattr(self, 'CXX') and self.mainLanguage == 'Cxx':
2320        yield (self.CXX, ['-LD'], 'dll')
2321      yield (self.CC, ['-LD'], 'dll')
2322    # Solaris default
2323    if Configure.isSolaris(self.log):
2324      if hasattr(self, 'CXX') and self.mainLanguage == 'Cxx':
2325        yield (self.CXX, ['-G'], 'so')
2326      yield (self.CC, ['-G'], 'so')
2327    # If user does not explicitly enable shared-libraries - disable shared libraries and default to static linker
2328    if not 'with-shared-libraries' in self.framework.clArgDB:
2329      self.argDB['with-shared-libraries'] = 0
2330      self.setStaticLinker()
2331      self.staticLinker = self.AR
2332      self.staticLibraries = 1
2333      self.LDFLAGS = ''
2334      yield (self.AR, [], self.AR_LIB_SUFFIX)
2335    raise RuntimeError('Exhausted all shared linker guesses. Could not determine how to create a shared library!')
2336
2337  def checkSharedLinker(self):
2338    '''Check that the linker can produce shared libraries'''
2339    self.sharedLibraries = 0
2340    self.staticLibraries = 0
2341    for linker, flags, ext in self.generateSharedLinkerGuesses():
2342      self.logPrint('Checking shared linker '+linker+' using flags '+str(flags))
2343      if self.getExecutable(linker, resultName = 'LD_SHARED'):
2344        for picFlag in self.generatePICGuesses():
2345          self.logPrint('Trying '+self.language[-1]+' compiler flag '+picFlag)
2346          compilerFlagsArg = self.getCompilerFlagsArg(1) # compiler only
2347          oldCompilerFlags = getattr(self, compilerFlagsArg)
2348          accepted = 1
2349          try:
2350            self.addCompilerFlag(picFlag,compilerOnly=1)
2351          except RuntimeError:
2352            accepted = 0
2353          if accepted:
2354            goodFlags = list(filter(self.checkLinkerFlag, flags))
2355            self.sharedLinker = self.LD_SHARED
2356            self.sharedLibraryFlags = goodFlags
2357            self.sharedLibraryExt = ext
2358            if ext == 'dll':
2359              dllexport = '__declspec(dllexport) '
2360              dllimport = '__declspec(dllimport) '
2361            else:
2362              dllexport = ''
2363              dllimport = ''
2364            # using printf appears to correctly identify non-pic code on X86_64
2365            if self.checkLink(includes = _picTestIncludes(dllexport), codeBegin = '', codeEnd = '', cleanup = 0, shared = 1):
2366              oldLib  = self.linkerObj
2367              oldLibs = self.LIBS
2368              self.LIBS += ' -L'+self.tmpDir+' -lconftest'
2369              accepted = self.checkLink(includes = dllimport+'int foo(void);', body = 'int ret = foo();\nif (ret) {}\n')
2370              os.remove(oldLib)
2371              self.LIBS = oldLibs
2372              if accepted:
2373                self.sharedLibraries = 1
2374                self.logPrint('Using shared linker '+self.sharedLinker+' with flags '+str(self.sharedLibraryFlags)+' and library extension '+self.sharedLibraryExt)
2375                break
2376          self.logPrint('Rejected '+self.language[-1]+' compiler flag '+picFlag+' because it was not compatible with shared linker '+linker+' using flags '+str(flags))
2377          setattr(self, compilerFlagsArg, oldCompilerFlags)
2378        if os.path.isfile(self.linkerObj): os.remove(self.linkerObj)
2379        if self.sharedLibraries: break
2380        self.delMakeMacro('LD_SHARED')
2381        del self.LD_SHARED
2382        if hasattr(self,'sharedLinker'): del self.sharedLinker
2383    return
2384
2385  def checkLinkerFlag(self, flag):
2386    '''Determine whether the linker accepts the given flag'''
2387    flagsArg = self.getLinkerFlagsArg()
2388    oldFlags = getattr(self, flagsArg)
2389    setattr(self, flagsArg, oldFlags+' '+flag)
2390    (output, status) = self.outputLink('', '')
2391    valid = 1
2392    if status:
2393      valid = 0
2394      self.logPrint('Rejecting linker flag '+flag+' due to nonzero status from link')
2395    if self.containsInvalidLinkerFlag(output):
2396      valid = 0
2397      self.logPrint('Rejecting '+self.language[-1]+' linker flag '+flag+' due to \n'+output)
2398    if valid:
2399      self.logPrint('Valid '+self.language[-1]+' linker flag '+flag)
2400    setattr(self, flagsArg, oldFlags)
2401    return valid
2402
2403  def addLinkerFlag(self, flag):
2404    '''Determine whether the linker accepts the given flag, and add it if valid, otherwise throw an exception'''
2405    if self.checkLinkerFlag(flag):
2406      flagsArg = self.getLinkerFlagsArg()
2407      setattr(self, flagsArg, getattr(self, flagsArg)+' '+flag)
2408      return
2409    raise RuntimeError('Bad linker flag: '+flag)
2410
2411  def checkLinkerMac(self):
2412    '''Tests some Apple Mac specific linker flags'''
2413    self.addDefine('USING_DARWIN', 1)
2414    langMap = {'C':'CC','FC':'FC','Cxx':'CXX','CUDA':'CUDAC','HIP':'HIPC','SYCL':'SYCLC'}
2415    languages = ['C']
2416    if hasattr(self, 'CXX'):
2417      languages.append('Cxx')
2418    if hasattr(self, 'FC'):
2419      languages.append('FC')
2420    ldTestFlags = ['-Wl,-bind_at_load', '-Wl,-commons,use_dylibs', '-Wl,-search_paths_first', '-Wl,-no_compact_unwind']
2421    if self.LDFLAGS.find('-Wl,-ld_classic') < 0:
2422      ldTestFlags.append('-Wl,-no_warn_duplicate_libraries')
2423    for language in languages:
2424      self.pushLanguage(language)
2425      for testFlag in ldTestFlags:
2426        if self.checkLinkerFlag(testFlag):
2427          # expand to CC_LINKER_FLAGS or CXX_LINKER_FLAGS or FC_LINKER_FLAGS
2428          linker_flag_var = langMap[language]+'_LINKER_FLAGS'
2429          val = getattr(self,linker_flag_var)
2430          val.append(testFlag)
2431          setattr(self,linker_flag_var,val)
2432          self.logPrint('Accepted macOS linker flag ' + testFlag)
2433        else:
2434          self.logPrint('Rejected macOS linker flag ' + testFlag)
2435      self.popLanguage()
2436    return
2437
2438  def checkLinkerWindows(self):
2439    '''Turns off linker warning about unknown .o files extension'''
2440    langMap = {'C':'CC','FC':'FC','Cxx':'CXX','CUDA':'CUDAC','HIP':'HIPC','SYCL':'SYCLC'}
2441    languages = ['C']
2442    if hasattr(self, 'CXX'):
2443      languages.append('Cxx')
2444    for language in languages:
2445      self.pushLanguage(language)
2446      for testFlag in ['-Qwd10161']:  #Warning for Intel icl,  there appear to be no way to remove warnings with Microsoft cl
2447        if self.checkLinkerFlag(testFlag):
2448          # expand to CC_LINKER_FLAGS or CXX_LINKER_FLAGS or FC_LINKER_FLAGS
2449          linker_flag_var = langMap[language]+'_LINKER_FLAGS'
2450          val = getattr(self,linker_flag_var)
2451          val.append(testFlag)
2452          setattr(self,linker_flag_var,val)
2453      self.popLanguage()
2454    return
2455
2456  def checkSharedLinkerPaths(self):
2457    '''Determine the shared linker path options
2458       - IRIX: -rpath
2459       - Linux, OSF: -Wl,-rpath,
2460       - Solaris: -R
2461       - FreeBSD: -Wl,-R,'''
2462    languages = ['C']
2463    if hasattr(self, 'CXX'):
2464      languages.append('Cxx')
2465    if hasattr(self, 'FC'):
2466      languages.append('FC')
2467    if hasattr(self, 'CUDAC'):
2468      languages.append('CUDA')
2469    if hasattr(self, 'HIPC'):
2470      languages.append('HIP')
2471    if hasattr(self, 'SYCLC'):
2472      languages.append('SYCL')
2473    for language in languages:
2474      flag = '-L'
2475      self.pushLanguage(language)
2476      if Configure.isCygwin(self.log):
2477        self.logPrint('Cygwin detected! disabling -rpath test.')
2478        testFlags = []
2479      # test '-R' before '-rpath' as sun compilers [c,fortran] don't give proper errors with wrong options.
2480      elif not Configure.isDarwin(self.log):
2481        testFlags = ['-Wl,-rpath,', '-R','-rpath ' , '-Wl,-R,']
2482      else:
2483        testFlags = ['-Wl,-rpath,']
2484      # test '-R' before '-Wl,-rpath' for SUN compilers [as cc on linux accepts -Wl,-rpath, but  f90 & CC do not.
2485      if self.isSun(self.framework.getCompiler(), self.log):
2486        testFlags.insert(0,'-R')
2487      for testFlag in testFlags:
2488        self.logPrint('Trying '+language+' linker flag '+testFlag)
2489        if self.checkLinkerFlag(testFlag+os.path.abspath(os.getcwd())):
2490          flag = testFlag
2491          break
2492        else:
2493          self.logPrint('Rejected '+language+' linker flag '+testFlag)
2494      self.popLanguage()
2495      setattr(self, language+'SharedLinkerFlag', flag)
2496    return
2497
2498  def checkLibC(self):
2499    '''Test whether we need to explicitly include libc in shared linking
2500       - Mac OSX requires an explicit reference to libc for shared linking'''
2501    self.explicitLibc = None
2502    if self.staticLibraries:
2503      return
2504    tmpCompilerDefines   = self.compilerDefines
2505    self.compilerDefines = ''
2506    code = '#include <stdlib.h> \nint foo(void) {void *chunk = malloc(31); free(chunk); return 0;}\n'
2507    if self.checkLink(includes = code, codeBegin = '', codeEnd = '', shared = 1):
2508      self.logPrint('Shared linking does not require an explicit libc reference')
2509      self.compilerDefines = tmpCompilerDefines
2510      return
2511    oldLibs = self.LIBS
2512    self.LIBS += '-lc '
2513    if self.checkLink(includes = code, codeBegin = '', codeEnd = '', shared = 1):
2514      self.logPrint('Shared linking requires an explicit libc reference')
2515      self.compilerDefines = tmpCompilerDefines
2516      self.explicitLibc = ['libc.so']
2517      return
2518    self.LIBS = oldLibs
2519    self.compilerDefines = tmpCompilerDefines
2520    self.logPrintWarning('Shared linking may not function on this architecture')
2521    self.staticLibrary=1
2522    self.sharedLibrary=0
2523
2524  def generateDynamicLinkerGuesses(self):
2525    if 'with-dynamic-ld' in self.argDB:
2526      yield (self.argDB['with-dynamic-ld'], [], 'so')
2527    # Mac OSX
2528    if Configure.isDarwin(self.log):
2529      if 'with-dynamic-ld' in self.argDB:
2530        yield (self.argDB['with-dynamic-ld'], ['-dynamiclib -undefined dynamic_lookup'], 'dylib')
2531      if hasattr(self, 'CXX') and self.mainLanguage == 'Cxx':
2532        yield (self.CXX, ['-dynamiclib -undefined dynamic_lookup'], 'dylib')
2533      yield (self.CC, ['-dynamiclib -undefined dynamic_lookup'], 'dylib')
2534    # Shared default
2535    if hasattr(self, 'sharedLinker'):
2536      yield (self.sharedLinker, self.sharedLibraryFlags, 'so')
2537    # C++ Compiler default
2538    if hasattr(self, 'CXX') and self.mainLanguage == 'Cxx':
2539      yield (self.CXX, ['-shared'], 'so')
2540    # C Compiler default
2541    yield (self.CC, ['-shared'], 'so')
2542    self.logPrint('Unable to find working dynamic linker')
2543
2544  def checkDynamicLinker(self):
2545    '''Check that the linker can dynamically load shared libraries'''
2546    self.dynamicLibraries = 0
2547    if not self.headers.check('dlfcn.h'):
2548      self.logPrint('Dynamic loading disabled since dlfcn.h was missing')
2549      return
2550    self.libraries.saveLog()
2551    if not self.libraries.check('', ['dlopen', 'dlsym', 'dlclose']):
2552      if not self.libraries.add('dl', ['dlopen', 'dlsym', 'dlclose']):
2553        self.logWrite(self.libraries.restoreLog())
2554        self.logPrint('Dynamic linking disabled since functions dlopen(), dlsym(), and dlclose() were not found')
2555        return
2556    self.logWrite(self.libraries.restoreLog())
2557    for linker, flags, ext in self.generateDynamicLinkerGuesses():
2558      self.logPrint('Checking dynamic linker '+linker+' using flags '+str(flags))
2559      if self.getExecutable(linker, resultName = 'dynamicLinker'):
2560        flagsArg = self.getLinkerFlagsArg()
2561        goodFlags = list(filter(self.checkLinkerFlag, flags))
2562        self.dynamicLibraryFlags = goodFlags
2563        self.dynamicLibraryExt = ext
2564        testMethod = 'foo'
2565        if self.checkLink(includes = '#include <stdio.h>\nint '+testMethod+'(void) {printf("test");return 0;}\n', codeBegin = '', codeEnd = '', cleanup = 0, shared = 'dynamic'):
2566          oldLib  = self.linkerObj
2567          code = '''
2568void *handle = dlopen("%s", 0);
2569int (*foo)(void) = (int (*)(void)) dlsym(handle, "foo");
2570
2571if (!foo) {
2572  printf("Could not load symbol\\n");
2573  return -1;
2574}
2575if ((*foo)()) {
2576  printf("Invalid return from foo()\\n");
2577  return -1;
2578}
2579if (dlclose(handle)) {
2580  printf("Could not close library\\n");
2581  return -1;
2582}
2583''' % oldLib
2584          if self.checkLink(includes = '#include <dlfcn.h>\n#include <stdio.h>', body = code):
2585            self.dynamicLibraries = 1
2586            self.logPrint('Using dynamic linker '+self.dynamicLinker+' with flags '+str(self.dynamicLibraryFlags)+' and library extension '+self.dynamicLibraryExt)
2587            os.remove(oldLib)
2588            break
2589        if os.path.isfile(self.linkerObj): os.remove(self.linkerObj)
2590        del self.dynamicLinker
2591    return
2592
2593  def output(self):
2594    '''Output module data as defines and substitutions'''
2595    if hasattr(self, 'CC'):
2596      self.addSubstitution('CC', self.CC)
2597      self.addSubstitution('CFLAGS', self.CFLAGS)
2598      self.addMakeMacro('CC_LINKER_SLFLAG', self.CSharedLinkerFlag)
2599    if hasattr(self, 'CPP'):
2600      self.addSubstitution('CPP', self.CPP)
2601      self.addSubstitution('CPPFLAGS', self.CPPFLAGS)
2602    if hasattr(self, 'CUDAC'):
2603      self.addSubstitution('CUDAC', self.CUDAC)
2604      self.addSubstitution('CUDAFLAGS', self.CUDAFLAGS)
2605    if hasattr(self, 'CUDAPP'):
2606      self.addSubstitution('CUDAPP', self.CUDAPP)
2607      self.addSubstitution('CUDAPPFLAGS', self.CUDAPPFLAGS)
2608    if hasattr(self, 'HIPC'):
2609      self.addSubstitution('HIPC', self.HIPC)
2610      self.addSubstitution('HIPFLAGS', self.HIPFLAGS)
2611    if hasattr(self, 'HIPPP'):
2612      self.addSubstitution('HIPPP', self.HIPPP)
2613      self.addSubstitution('HIPPPFLAGS', self.HIPPPFLAGS)
2614    if hasattr(self, 'SYCLC'):
2615      self.addSubstitution('SYCLC', self.SYCLC)
2616      self.addSubstitution('SYCLFLAGS', self.SYCLFLAGS)
2617    if hasattr(self, 'SYCLPP'):
2618      self.addSubstitution('SYCLPP', self.SYCLPP)
2619      self.addSubstitution('SYCLPPFLAGS', self.SYCLPPFLAGS)
2620    if hasattr(self, 'CXX'):
2621      self.addSubstitution('CXX', self.CXX)
2622      self.addSubstitution('CXX_CXXFLAGS', self.CXX_CXXFLAGS)
2623      self.addSubstitution('CXXFLAGS', self.CXXFLAGS)
2624      self.addSubstitution('CXX_LINKER_SLFLAG', self.CxxSharedLinkerFlag)
2625    else:
2626      self.addSubstitution('CXX', '')
2627    if hasattr(self, 'CXXPP'):
2628      self.addSubstitution('CXXPP', self.CXXPP)
2629      self.addSubstitution('CXXPPFLAGS', self.CXXPPFLAGS)
2630    if hasattr(self, 'FC'):
2631      self.addSubstitution('FC', self.FC)
2632      self.addSubstitution('FFLAGS', self.FFLAGS)
2633      self.addMakeMacro('FC_LINKER_SLFLAG', self.FCSharedLinkerFlag)
2634    else:
2635      self.addSubstitution('FC', '')
2636    self.addSubstitution('LDFLAGS', self.LDFLAGS)
2637    if hasattr(self, 'FPP'):
2638      self.addSubstitution('FPP', self.FPP)
2639      self.addSubstitution('FPPFLAGS', self.FPPFLAGS)
2640    self.addSubstitution('LIBS', self.LIBS)
2641    if hasattr(self, 'sharedLibraryFlags'):
2642      self.addSubstitution('SHARED_LIBRARY_FLAG', ' '.join(self.sharedLibraryFlags))
2643    else:
2644      self.addSubstitution('SHARED_LIBRARY_FLAG','')
2645    return
2646
2647  def updateMPICompilers(self, mpicc, mpicxx, mpifc):
2648    '''Reset compilers by an external module aka MPI'''
2649    self.CC = mpicc
2650    self.delMakeMacro("CC")
2651
2652    if hasattr(self, 'CXX'):
2653      self.CXX = mpicxx
2654      self.delMakeMacro("CXX")
2655
2656    if hasattr(self, 'FC'):
2657      self.FC = mpifc
2658      self.delMakeMacro("FC")
2659
2660    self.configure()
2661    self.usedMPICompilers=1
2662    return
2663
2664  def checkMPICompilerOverride(self):
2665    '''Check if --with-mpi-dir is used along with CC CXX or FC compiler options.
2666    This usually prevents mpi compilers from being used - so issue a warning'''
2667
2668    if 'with-mpi-dir' in self.argDB and self.argDB['with-mpi-compilers']:
2669      optcplrs = [(['with-cc','CC'],['mpincc','mpiicc','mpicc','mpcc','hcc','mpcc_r']),
2670              (['with-fc','FC'],['mpinfort','mpiifort','mpif90','mpxlf95_r','mpxlf90_r','mpxlf_r','mpf90']),
2671              (['with-cxx','CXX'],['mpinc++','mpiicpc','mpicxx','hcp','mpic++','mpiCC','mpCC_r'])]
2672      for opts,cplrs in optcplrs:
2673        for opt in opts:
2674          if (opt in self.argDB  and self.argDB[opt] != '0'):
2675            # check if corresponding mpi wrapper exists
2676            for cplr in cplrs:
2677              for mpicplr in [os.path.join(self.argDB['with-mpi-dir'], 'bin', cplr),os.path.join(self.argDB['with-mpi-dir'], 'intel64', 'bin', cplr)]:
2678                if os.path.exists(mpicplr):
2679                  msg = '--'+opt+'='+self.argDB[opt]+' is specified along with --with-mpi-dir='+self.argDB['with-mpi-dir']+' which implies using '+mpicplr+'.\n\
2680  configure is confused and does not know which compiler to select and use! Please specify either [mpi] compilers or --with-mpi-dir - but not both!\n\
2681  In most cases, specifying --with-mpi-dir - and not explicitly listing compilers could be preferable.'
2682                  raise RuntimeError(msg)
2683    return
2684
2685  def requireMpiLdPath(self):
2686    '''Open MPI wrappers require LD_LIBRARY_PATH set'''
2687    if 'with-mpi-dir' in self.argDB:
2688      libdir = os.path.join(self.argDB['with-mpi-dir'], 'lib')
2689      if os.path.exists(os.path.join(libdir,'libopen-rte.so')):
2690        Configure.addLdPath(libdir)
2691        self.logPrint('Adding to LD_LIBRARY_PATH '+libdir)
2692    return
2693
2694  def resetEnvCompilers(self):
2695    '''Remove compilers from the shell environment so they do not interfere with testing'''
2696    ignoreEnvCompilers = ['CC','CXX','FC','F77','F90']
2697    ignoreEnv = ['CFLAGS','CXXFLAGS','FCFLAGS','FFLAGS','F90FLAGS','CPP','CPPFLAGS','CXXPP','CXXPPFLAGS','LDFLAGS','LIBS','MPI_DIR','RM','MAKEFLAGS','AR','RANLIB']
2698    for envVal in ignoreEnvCompilers + ignoreEnv:
2699      if envVal in os.environ:
2700        msg = 'Found environment variable: %s=%s. ' % (envVal, os.environ[envVal])
2701        if envVal in self.framework.clArgDB or (envVal in ignoreEnvCompilers and 'with-'+envVal.lower() in self.framework.clArgDB):
2702          self.logPrintWarning(msg+"Ignoring it, since it's also set on command line")
2703          del os.environ[envVal]
2704        elif self.argDB['with-environment-variables']:
2705          self.logPrintWarning(msg+'Using it! Use "./configure --disable-environment-variables" to NOT use the environmental variables')
2706        else:
2707          self.logPrintWarning(msg+'Ignoring it! Use "./configure %s=$%s" if you really want to use this value' % (envVal,envVal))
2708          del os.environ[envVal]
2709    return
2710
2711  def checkEnvCompilers(self):
2712    '''Set configure compilers from the environment, from -with-environment-variables'''
2713    if 'with-environment-variables' in self.framework.clArgDB:
2714      envVarChecklist = ['CC','CFLAGS','CXX','CXXFLAGS','FC','FCFLAGS','F77','FFLAGS','F90','F90FLAGS','CPP','CPPFLAGS','CXXPP','CXXPPFLAGS','LDFLAGS','LIBS','MPI_DIR','RM','MAKEFLAGS','AR']
2715      for ev in envVarChecklist:
2716        if ev in os.environ:
2717          self.argDB[ev] = os.environ[ev]
2718
2719    # abort if FCFLAGS and FFLAGS both set, but to different values
2720    if 'FFLAGS' in self.argDB and 'FCFLAGS' in self.argDB:
2721      if self.argDB['FCFLAGS'] != self.argDB['FFLAGS']:
2722        raise RuntimeError('FCFLAGS and FFLAGS are both set, but with different values (FCFLAGS=%s, FFLAGS=%s)'%(self.argDB['FCFLAGS'],self.argDB['FFLAGS']))
2723    return
2724
2725  def checkIntoShared(self,symbol,lib):
2726    '''Check that a given library can be linked into a shared library'''
2727    import sys
2728    if not self.checkCompile(includes = 'char *'+symbol+'(void);\n',body = 'return '+symbol+'();\n', cleanup = 0, codeBegin = 'char* testroutine(void){', codeEnd = '}'):
2729      raise RuntimeError('Unable to compile test file with symbol: '+symbol)
2730    oldLibs = self.LIBS
2731    self.LIBS = self.libraries.toStringNoDupes(lib) + ' '+self.LIBS
2732    ret = self.checkLink(includes = 'char *'+symbol+'(void);\n',body = 'return '+symbol+'();\n', cleanup = 0, codeBegin = 'char* testroutine(void){', codeEnd = '}',shared =1)
2733    self.LIBS = oldLibs
2734    return ret
2735
2736  def checkAtFileOption(self):
2737    '''Check if linker supports @file option'''
2738    optfile = os.path.join(self.tmpDir,'optfile')
2739    with open(optfile,'w') as fd:
2740      fd.write(str(self.getCompilerFlags()))
2741    if self.checkLinkerFlag('@'+optfile):
2742      self.framework.addMakeMacro('PCC_AT_FILE',1)
2743    else:
2744      self.logPrint('@file option test failed!')
2745    return
2746
2747  def configure(self):
2748    self.mainLanguage = self.languages.clanguage
2749    self.executeTest(self.resetEnvCompilers)
2750    self.executeTest(self.checkEnvCompilers)
2751    self.executeTest(self.checkMPICompilerOverride)
2752    self.executeTest(self.requireMpiLdPath)
2753    self.executeTest(self.checkInitialFlags)
2754    if hasattr(self.framework,'conda_active'):
2755      self.framework.additional_error_message = 'Conda may be causing this compiling/linking problem, consider turning off Conda.'
2756    self.executeTest(self.checkCCompiler)
2757    self.executeTest(self.checkCPreprocessor)
2758
2759    for LANG in ['Cxx','CUDA','HIP','SYCL']:
2760      compilerName = LANG.upper() if LANG == 'Cxx' else LANG+'C'
2761      argdbName    = 'with-' + compilerName.casefold()
2762      argdbVal     = self.argDB.get(argdbName)
2763      if argdbVal == '0':
2764        # compiler was explicitly disabled, i.e. --with-cxx=0
2765        COMPILER_NAME = compilerName.upper()
2766        if COMPILER_NAME in self.argDB:
2767          del self.argDB[COMPILER_NAME]
2768          continue
2769      else:
2770        self.executeTest(getattr(self,LANG.join(('check','Compiler'))))
2771        try:
2772          self.executeTest(self.checkDeviceHostCompiler,args=[LANG])
2773        except NotImplementedError:
2774          pass
2775        if hasattr(self,compilerName):
2776          compiler = self.getCompiler(lang=LANG)
2777          isGNUish = self.isGNU(compiler,self.log) or self.isClang(compiler,self.log)
2778          try:
2779            self.executeTest(self.checkCxxDialect,args=[LANG],kargs={'isGNUish':isGNUish})
2780          except RuntimeError as e:
2781            self.mesg = str(e)
2782            if argdbVal is not None:
2783              # user explicitly enabled a compiler, e.g. --with-cxx=clang++, so the fact
2784              # that it does not work is an immediate problem
2785              self.mesg += '\n'.join((
2786                '',
2787                'Note, you have explicitly requested --{}={}. If you don\'t need {}, or that specific compiler, remove this flag -- configure may be able to find a more suitable compiler automatically.',
2788                'If you DO need the above, then consult your compilers user manual. It\'s possible you may need to add additional flags (or perhaps load additional modules) to enable compliance'
2789              )).format(argdbName, argdbVal, LANG.replace('x', '+'))
2790              raise config.base.ConfigureSetupError(self.mesg)
2791            self.logPrint(' '.join(('Error testing',LANG,'compiler:',self.mesg)))
2792            self.delMakeMacro(compilerName)
2793            delattr(self,compilerName)
2794          else:
2795            self.executeTest(getattr(self,LANG.join(('check','Preprocessor'))))
2796    self.executeTest(self.checkFortranCompiler)
2797    if hasattr(self, 'FC'):
2798      self.executeTest(self.checkFortranPreprocessor)
2799      self.executeTest(self.checkFortranComments)
2800    self.executeTest(self.checkLargeFileIO)
2801    self.executeTest(self.checkArchiver)
2802    self.executeTest(self.checkArchiverRecipeArgfile)
2803    self.executeTest(self.checkSharedLinker)
2804    if Configure.isDarwin(self.log):
2805      self.executeTest(self.checkLinkerMac)
2806    if Configure.isCygwin(self.log):
2807      self.executeTest(self.checkLinkerWindows)
2808    self.executeTest(self.checkPIC)
2809    self.executeTest(self.checkKandRFlags)
2810    self.executeTest(self.checkSharedLinkerPaths)
2811    self.executeTest(self.checkLibC)
2812    self.executeTest(self.checkDynamicLinker)
2813    if hasattr(self.framework,'conda_active'):
2814      del self.framework.additional_error_message
2815
2816    self.executeTest(self.checkPragma)
2817    self.executeTest(self.checkAtFileOption)
2818    self.executeTest(self.output)
2819    return
2820
2821  def no_configure(self):
2822    if self.staticLibraries:
2823      self.setStaticLinker()
2824    return
2825