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