1''' 2config.base.Configure is the base class for all configure objects. It handles several types of interaction: 3 4Framework hooks 5--------------- 6 7 The Framework will first instantiate the object and call setupDependencies(). All require() 8 calls should be made in that method. 9 10 The Framework will then call configure(). If it succeeds, the object will be marked as configured. 11 12Generic test execution 13---------------------- 14 15 All configure tests should be run using 16 17 executeTest() 18 19which formats the output and adds metadata for the log. 20 21Preprocessing, Compiling, Linking, and Running 22---------------------------------------------- 23 24 Two forms of this check are provided for each operation. The first is an "output" form which is 25intended to provide the status and complete output of the command. The second, or "check" form will 26return a success or failure indication based upon the status and output. 27 28 outputPreprocess(), checkPreprocess(), preprocess() 29 outputCompile(), checkCompile() 30 outputLink(), checkLink() 31 outputRun(), checkRun() 32 33 The language used for these operation is managed with a stack, similar to autoconf. 34 35 pushLanguage(), popLanguage() 36 37 We also provide special forms used to check for valid compiler and linker flags, optionally adding 38them to the defaults. 39 40 checkCompilerFlag(), addCompilerFlag() 41 checkLinkerFlag(), addLinkerFlag() 42 43Finding Executables 44------------------- 45 46 getExecutable(), getExecutables(), checkExecutable() 47 48Output 49------ 50 51 addDefine(), addSubstitution(), addArgumentSubstitution(), addTypedef(), addPrototype() 52 addMakeMacro(), addMakeRule() 53 54 The object may define a headerPrefix member, which will be appended, followed 55by an underscore, to every define which is output from it. Similarly, a substPrefix 56can be defined which applies to every substitution from the object. Typedefs and 57function prototypes are placed in a separate header in order to accommodate languages 58such as Fortran whose preprocessor can sometimes fail at these statements. 59''' 60import script 61 62import os 63import time 64import contextlib 65 66class ConfigureSetupError(Exception): 67 pass 68 69class Configure(script.Script): 70 def __init__(self, framework, tmpDir = None): 71 script.Script.__init__(self, framework.clArgs, framework.argDB) 72 self.framework = framework 73 self.defines = {} 74 self.makeRules = {} 75 self.makeMacros = {} 76 self.typedefs = {} 77 self.prototypes = {} 78 self.subst = {} 79 self.argSubst = {} 80 self.language = [] 81 if not tmpDir is None: 82 self.tmpDir = tmpDir 83 try: 84 # The __init__ method may be called to reinitialize in the future (e.g., 85 # updateCompilers()) and will need to be re-setup in that case. 86 delattr(self, '_setup') 87 except AttributeError: 88 pass 89 return 90 91 def setup(self): 92 if hasattr(self, '_setup'): 93 return 94 script.Script.setup(self) 95 self._setup = 1 96 self.pushLanguage('C') 97 98 def getTmpDir(self): 99 if not hasattr(self, '_tmpDir'): 100 self._tmpDir = os.path.join(self.framework.tmpDir, self.__module__) 101 if not os.path.isdir(self._tmpDir): os.mkdir(self._tmpDir) 102 return self._tmpDir 103 def setTmpDir(self, temp): 104 if hasattr(self, '_tmpDir'): 105 if os.path.isdir(self._tmpDir): 106 import shutil 107 shutil.rmtree(self._tmpDir) 108 if temp is None: 109 delattr(self, '_tmpDir') 110 if not temp is None: 111 self._tmpDir = temp 112 return 113 tmpDir = property(getTmpDir, setTmpDir, doc = 'Temporary directory for test byproducts') 114 115 def __str__(self): 116 return '' 117 118 def logError(self, component, status, output, error): 119 if status: 120 exitstr = ' exit code ' + str(status) 121 else: 122 exitstr = 'exit code 0' 123 self.logWrite('Possible ERROR while running %s:%s\n' % (component, exitstr)) 124 if output: 125 self.logWrite('stdout:\n' + output) 126 if error: 127 self.logWrite('stderr:\n' + error) 128 129 def executeTest(self, test, args = [], kargs = {}): 130 '''Prints the function and class information for the test and then runs the test''' 131 import time 132 133 self.logPrintDivider() 134 self.logPrint('TESTING: '+str(test.__func__.__name__)+' from '+str(test.__self__.__class__.__module__)+'('+str(test.__func__.__code__.co_filename)+':'+str(test.__func__.__code__.co_firstlineno)+')', debugSection = 'screen', indent = 0) 135 if test.__doc__: self.logWrite(' '+test.__doc__+'\n') 136 #t = time.time() 137 if not isinstance(args, list): args = [args] 138 ret = test(*args,**kargs) 139 #self.logPrint(' TIME: '+str(time.time() - t)+' sec', debugSection = 'screen', indent = 0) 140 return ret 141 142 def printTest(self, test): 143 '''Prints the function and class information for a test''' 144 self.logPrintDivider() 145 self.logPrint('TESTING: '+str(test.__func__.__name__)+' from '+str(test.__self__.__class__.__module__)+'('+str(test.__func__.__code__.co_filename)+':'+str(test.__func__.__code__.co_firstlineno)+')', debugSection = 'screen', indent = 0) 146 if test.__doc__: self.logWrite(' '+test.__doc__+'\n') 147 148 ################################# 149 # Define and Substitution Supported 150 def addMakeRule(self, name, dependencies, rule = []): 151 '''Designate that "name" should be rule in the makefile header (bmake file)''' 152 self.logPrint('Defined make rule "'+name+'" with dependencies "'+str(dependencies)+'" and code '+str(rule)) 153 if not isinstance(rule,list): rule = [rule] 154 self.makeRules[name] = [dependencies,rule] 155 return 156 157 def addMakeMacro(self, name, value): 158 '''Designate that "name" should be defined to "value" in the makefile header (bmake file)''' 159 self.logPrint('Defined make macro "'+name+'" to "'+str(value)+'"') 160 self.makeMacros[name] = value 161 return 162 163 def getMakeMacro(self, name): 164 return self.makeMacros.get(name) 165 166 def delMakeMacro(self, name): 167 '''Designate that "name" should be deleted (never put in) configuration header''' 168 self.logPrint('Deleting "'+name+'"') 169 if name in self.makeMacros: del self.makeMacros[name] 170 return 171 172 def addDefine(self, name, value): 173 '''Designate that "name" should be defined to "value" in the configuration header''' 174 self.logPrint('Defined "'+name+'" to "'+str(value)+'"') 175 self.defines[name] = value 176 return 177 178 def delDefine(self, name): 179 '''Designate that "name" should be deleted (never put in) configuration header''' 180 if name in self.defines: 181 self.logPrint('Deleting "'+name+'"') 182 del self.defines[name] 183 return 184 185 def addTypedef(self, name, value): 186 '''Designate that "name" should be typedefed to "value" in the configuration header''' 187 self.logPrint('Typedefed "'+name+'" to "'+str(value)+'"') 188 self.typedefs[value] = name 189 return 190 191 def addPrototype(self, prototype, language = 'All'): 192 '''Add a missing function prototype 193 - The language argument defaults to "All" 194 - Other language choices are C, Cxx, extern C''' 195 self.logPrint('Added prototype '+prototype+' to language '+language) 196 language = language.replace('+', 'x') 197 if not language in self.prototypes: 198 self.prototypes[language] = [] 199 self.prototypes[language].append(prototype) 200 return 201 202 def addSubstitution(self, name, value): 203 '''Designate that "@name@" should be replaced by "value" in all files which experience substitution''' 204 self.logPrint('Substituting "'+name+'" with "'+str(value)+'"') 205 self.subst[name] = value 206 return 207 208 def addArgumentSubstitution(self, name, arg): 209 '''Designate that "@name@" should be replaced by "arg" in all files which experience substitution''' 210 self.logPrint('Substituting "'+name+'" with '+str(arg)+'('+str(self.argDB[arg])+')') 211 self.argSubst[name] = arg 212 return 213 214 ################ 215 # Program Checks 216 def checkExecutable(self, dir, name): 217 prog = os.path.join(dir, name) 218 # also strip any \ before spaces, braces, so that we can specify paths the way we want them in makefiles. 219 prog = prog.replace(r'\ ',' ').replace(r'\(','(').replace(r'\)',')') 220 found = 0 221 self.logWrite(' Checking for program '+prog+'...') 222 if os.path.isfile(prog) and os.access(prog, os.X_OK): 223 found = 1 224 self.logWrite('found\n') 225 else: 226 self.logWrite('not found\n') 227 return found 228 229 def getExecutable(self, names, path = [], getFullPath = 0, useDefaultPath = 0, resultName = '', setMakeMacro = 1): 230 '''Search for an executable in the list names 231 - Each name in the list is tried for each entry in the path until a name is located, then it stops 232 - If found, the path is attached to self as an attribute named "name", or "resultName" if given 233 - By default, a make macro "resultName" will hold the path''' 234 found = 0 235 if isinstance(names,str) and names.startswith('/'): 236 path = os.path.dirname(names) 237 names = os.path.basename(names) 238 239 if isinstance(names, str): 240 names = [names] 241 if isinstance(path, str): 242 path = path.split(os.path.pathsep) 243 if not len(path): 244 useDefaultPath = 1 245 246 def getNames(name, resultName): 247 import re 248 prog = re.match(r'(.*?)(?<!\\)(\s.*)',name) 249 if prog: 250 name = prog.group(1) 251 options = prog.group(2) 252 else: 253 options = '' 254 if not resultName: 255 varName = name 256 else: 257 varName = resultName 258 return name, options, varName 259 260 varName = names[0] 261 varPath = '' 262 for d in path: 263 for name in names: 264 name, options, varName = getNames(name, resultName) 265 if self.checkExecutable(d, name): 266 found = 1 267 getFullPath = 1 268 varPath = d 269 break 270 if found: break 271 if useDefaultPath and not found: 272 for d in os.environ['PATH'].split(os.path.pathsep): 273 for name in names: 274 name, options, varName = getNames(name, resultName) 275 if self.checkExecutable(d, name): 276 found = 1 277 varPath = d 278 break 279 if found: break 280 if not found: 281 dirs = self.argDB['with-executables-search-path'] 282 if not isinstance(dirs, list): dirs = dirs.split(os.path.pathsep) 283 for d in dirs: 284 for name in names: 285 name, options, varName = getNames(name, resultName) 286 if self.checkExecutable(d, name): 287 found = 1 288 getFullPath = 1 289 varPath = d 290 break 291 if found: break 292 293 if found: 294 if getFullPath: 295 setattr(self, varName, os.path.abspath(os.path.join(varPath, name))+options) 296 else: 297 setattr(self, varName, name+options) 298 if setMakeMacro: 299 self.addMakeMacro(varName.upper(), getattr(self, varName)) 300 else: 301 def logPrintFilesInPath(path): 302 for d in path: 303 try: 304 self.logWrite(' '+d+': '+' '.join(os.listdir(d))+'\n') 305 except Exception as e: 306 self.logWrite(' Warning accessing '+d+' gives errors: '+str(e)+'\n') 307 return 308 if path: 309 self.logWrite(' Unable to find programs: %s in listing of the specific search path: %s\n' % (names, path)) 310 logPrintFilesInPath(path) 311 return found 312 313 def getExecutables(self, names, path = '', getFullPath = 0, useDefaultPath = 0, resultName = ''): 314 '''Search for an executable in the list names 315 - The full path given is searched for each name in turn 316 - If found, the path is stored in the variable "name", or "resultName" if given''' 317 for name in names: 318 if self.getExecutable(name, path = path, getFullPath = getFullPath, useDefaultPath = useDefaultPath, resultName = resultName): 319 return name 320 return None 321 322 ############################################### 323 # Preprocessor, Compiler, and Linker Operations 324 def pushLanguage(self, language): 325 if language == 'C++': language = 'Cxx' 326 self.language.append(language) 327 return self.language[-1] 328 329 def popLanguage(self): 330 self.language.pop() 331 return self.language[-1] 332 333 @contextlib.contextmanager 334 def Language(self, lang): 335 if lang is None: 336 yield 337 else: 338 try: 339 yield self.pushLanguage(lang) 340 finally: 341 self.popLanguage() 342 343 def getHeaders(self): 344 self.compilerDefines = os.path.join(self.tmpDir, 'confdefs.h') 345 self.compilerFixes = os.path.join(self.tmpDir, 'conffix.h') 346 return 347 348 def getPreprocessor(self): 349 self.getHeaders() 350 preprocessor = self.framework.getPreprocessorObject(self.language[-1]) 351 preprocessor.checkSetup() 352 return preprocessor.getProcessor() 353 354 def getCompiler(self, lang=None): 355 with self.Language(lang): 356 self.getHeaders() 357 compiler = self.framework.getCompilerObject(self.language[-1]) 358 compiler.checkSetup() 359 self.compilerSource = os.path.join(self.tmpDir, 'conftest'+compiler.sourceExtension) 360 self.compilerObj = os.path.join(self.tmpDir, compiler.getTarget(self.compilerSource)) 361 return compiler.getProcessor() 362 363 def getCompilerFlags(self): 364 return self.framework.getCompilerObject(self.language[-1]).getFlags() 365 366 def getLinker(self): 367 self.getHeaders() 368 linker = self.framework.getLinkerObject(self.language[-1]) 369 linker.checkSetup() 370 self.linkerSource = os.path.join(self.tmpDir, 'conftest'+linker.sourceExtension) 371 self.linkerObj = linker.getTarget(self.linkerSource, 0) 372 return linker.getProcessor() 373 374 def getLinkerFlags(self): 375 return self.framework.getLinkerObject(self.language[-1]).getFlags() 376 377 def getSharedLinker(self): 378 self.getHeaders() 379 linker = self.framework.getSharedLinkerObject(self.language[-1]) 380 linker.checkSetup() 381 self.linkerSource = os.path.join(self.tmpDir, 'conftest'+linker.sourceExtension) 382 self.linkerObj = linker.getTarget(self.linkerSource, 1) 383 return linker.getProcessor() 384 385 def getSharedLinkerFlags(self): 386 return self.framework.getSharedLinkerObject(self.language[-1]).getFlags() 387 388 def getDynamicLinker(self): 389 self.getHeaders() 390 linker = self.framework.getDynamicLinkerObject(self.language[-1]) 391 linker.checkSetup() 392 self.linkerSource = os.path.join(self.tmpDir, 'conftest'+linker.sourceExtension) 393 self.linkerObj = linker.getTarget(self.linkerSource, 1) 394 return linker.getProcessor() 395 396 def getDynamicLinkerFlags(self): 397 return self.framework.getDynamicLinkerObject(self.language[-1]).getFlags() 398 399 def getPreprocessorCmd(self): 400 self.getCompiler() 401 preprocessor = self.framework.getPreprocessorObject(self.language[-1]) 402 preprocessor.checkSetup() 403 preprocessor.includeDirectories.add(self.tmpDir) 404 return preprocessor.getCommand(self.compilerSource) 405 406 def getCompilerCmd(self): 407 self.getCompiler() 408 compiler = self.framework.getCompilerObject(self.language[-1]) 409 compiler.checkSetup() 410 compiler.includeDirectories.add(self.tmpDir) 411 return compiler.getCommand(self.compilerSource, self.compilerObj) 412 413 def getLinkerCmd(self): 414 self.getLinker() 415 linker = self.framework.getLinkerObject(self.language[-1]) 416 linker.checkSetup() 417 return linker.getCommand(self.linkerSource, self.linkerObj) 418 419 def getFullLinkerCmd(self, objects, executable): 420 self.getLinker() 421 linker = self.framework.getLinkerObject(self.language[-1]) 422 linker.checkSetup() 423 return linker.getCommand(objects, executable) 424 425 def getSharedLinkerCmd(self): 426 self.getSharedLinker() 427 linker = self.framework.getSharedLinkerObject(self.language[-1]) 428 linker.checkSetup() 429 return linker.getCommand(self.linkerSource, self.linkerObj) 430 431 def getDynamicLinkerCmd(self): 432 self.getDynamicLinker() 433 linker = self.framework.getDynamicLinkerObject(self.language[-1]) 434 linker.checkSetup() 435 return linker.getCommand(self.linkerSource, self.linkerObj) 436 437 def getCode(self, includes, body = None, codeBegin = None, codeEnd = None): 438 language = self.language[-1] 439 if includes and not includes[-1] == '\n': 440 includes += '\n' 441 if language in ['C', 'CUDA', 'Cxx', 'HIP', 'SYCL']: 442 codeStr = '' 443 if self.compilerDefines: codeStr = '#include "'+os.path.basename(self.compilerDefines)+'"\n' 444 codeStr += '#include "conffix.h"\n'+includes 445 if not body is None: 446 if codeBegin is None: 447 codeBegin = '\nint main(void) {\n' 448 if codeEnd is None: 449 if len(body) == 0: 450 codeEnd = ' return 0;\n}\n' 451 elif body.strip().endswith(';') or body.strip().endswith('}') or body.strip().endswith('\n#endif'): 452 codeEnd = '\n return 0;\n}\n' 453 else: 454 codeEnd = ';\n return 0;\n}\n' 455 codeStr += codeBegin+body+codeEnd 456 elif language == 'FC': 457 if not includes is None and body is None: 458 codeStr = includes 459 else: 460 codeStr = '' 461 if not body is None: 462 if codeBegin is None: 463 codeBegin = ' program main\n' 464 if not includes is None: 465 codeBegin = codeBegin+includes 466 if codeEnd is None: 467 codeEnd = '\n end\n' 468 codeStr += codeBegin+body+codeEnd 469 else: 470 raise RuntimeError('Cannot determine code body for language: '+language) 471 codeStr += '\n' 472 return codeStr 473 474 def preprocess(self, codeStr, timeout = 600.0): 475 def report(command, status, output, error): 476 if error or status: 477 self.logError('preprocessor', status, output, error) 478 self.logWrite('Source:\n'+self.getCode(codeStr)) 479 480 command = self.getPreprocessorCmd() 481 if self.compilerDefines: self.framework.outputHeader(self.compilerDefines) 482 self.framework.outputCHeader(self.compilerFixes) 483 self.logWrite('Preprocessing source:\n'+self.getCode(codeStr)) 484 f = open(self.compilerSource, 'w') 485 f.write(self.getCode(codeStr)) 486 f.close() 487 (out, err, ret) = Configure.executeShellCommand(command, checkCommand = report, timeout = timeout, log = self.log, logOutputflg = False, lineLimit = 100000) 488 if self.cleanup: 489 for filename in [self.compilerDefines, self.compilerFixes, self.compilerSource]: 490 if os.path.isfile(filename): os.remove(filename) 491 return (out, err, ret) 492 493 def outputPreprocess(self, codeStr): 494 '''Return the contents of stdout when preprocessing "codeStr"''' 495 return self.preprocess(codeStr)[0] 496 497 def checkPreprocess(self, codeStr, timeout = 600.0): 498 '''Return True if no error occurred 499 - An error is signaled by a nonzero return code, or output on stderr''' 500 (out, err, ret) = self.preprocess(codeStr, timeout = timeout) 501 err = self.framework.filterPreprocessOutput(err, self.log) 502 return not ret and not len(err) 503 504 # Should be static 505 def getPreprocessorFlagsName(self, language): 506 if language == 'C': 507 flagsArg = 'CPPFLAGS' 508 elif language == 'CUDA': 509 flagsArg = 'CUDAPPFLAGS' 510 elif language == 'Cxx': 511 flagsArg = 'CXXPPFLAGS' 512 elif language == 'FC': 513 flagsArg = 'FPPFLAGS' 514 elif language == 'HIP': 515 flagsArg = 'HIPPPFLAGS' 516 elif language == 'SYCL': 517 flagsArg = 'SYCLPPFLAGS' 518 else: 519 raise RuntimeError('Unknown language: '+language) 520 return flagsArg 521 522 def getPreprocessorFlagsArg(self): 523 '''Return the name of the argument which holds the preprocessor flags for the current language''' 524 return self.getPreprocessorFlagsName(self.language[-1]) 525 526 def filterCompileOutput(self, output, flag = '', filterAlways = 0): 527 return self.framework.filterCompileOutput(output, flag = flag, filterAlways = filterAlways) 528 529 def outputCompile(self, includes = '', body = '', cleanup = 1, codeBegin = None, codeEnd = None): 530 '''Return the error output from this compile and the return code''' 531 def report(command, status, output, error): 532 if error or status: 533 self.logError('compiler', status, output, error) 534 else: 535 self.logWrite('Successful compile:\n') 536 self.logWrite('Source:\n'+self.getCode(includes, body, codeBegin, codeEnd)) 537 538 cleanup = cleanup and self.framework.doCleanup 539 command = self.getCompilerCmd() 540 if self.compilerDefines: self.framework.outputHeader(self.compilerDefines) 541 self.framework.outputCHeader(self.compilerFixes) 542 with open(self.compilerSource, 'w') as f: 543 f.write(self.getCode(includes, body, codeBegin, codeEnd)) 544 (out, err, ret) = Configure.executeShellCommand(command, checkCommand = report, log = self.log) 545 if not os.path.isfile(self.compilerObj): 546 err += '\nPETSc Error: No output file produced' 547 if cleanup: 548 for filename in [self.compilerDefines, self.compilerFixes, self.compilerSource, self.compilerObj]: 549 if os.path.isfile(filename): os.remove(filename) 550 return (out, err, ret) 551 552 def checkCompile(self, includes = '', body = '', cleanup = 1, codeBegin = None, codeEnd = None, flag = ''): 553 '''Returns True if the compile was successful''' 554 (output, error, returnCode) = self.outputCompile(includes, body, cleanup, codeBegin, codeEnd) 555 output = self.filterCompileOutput(output+'\n'+error,flag=flag) 556 return not (returnCode or len(output)) 557 558 def getCompilerFlagsName(language, compilerOnly = 0): 559 if language == 'C': 560 flagsArg = 'CFLAGS' 561 elif language == 'CUDA': 562 flagsArg = 'CUDAFLAGS' 563 elif language == 'Cxx': 564 if compilerOnly: 565 flagsArg = 'CXX_CXXFLAGS' 566 else: 567 flagsArg = 'CXXFLAGS' 568 elif language == 'HIP': 569 flagsArg = 'HIPFLAGS' 570 elif language == 'SYCL': 571 flagsArg = 'SYCLFLAGS' 572 elif language == 'FC': 573 flagsArg = 'FFLAGS' 574 else: 575 raise RuntimeError('Unknown language: '+language) 576 return flagsArg 577 getCompilerFlagsName = staticmethod(getCompilerFlagsName) 578 579 def getCompilerFlagsArg(self, compilerOnly = 0): 580 '''Return the name of the argument which holds the compiler flags for the current language''' 581 return self.getCompilerFlagsName(self.language[-1], compilerOnly) 582 583 def filterLinkOutput(self, output, filterAlways = 0): 584 return self.framework.filterLinkOutput(output, filterAlways = filterAlways) 585 586 def outputLink(self, includes, body, cleanup = 1, codeBegin = None, codeEnd = None, shared = 0, linkLanguage=None, examineOutput=lambda ret,out,err:None,flag=''): 587 import sys 588 589 (out, err, ret) = self.outputCompile(includes, body, cleanup = 0, codeBegin = codeBegin, codeEnd = codeEnd) 590 examineOutput(ret, out, err) 591 out = self.filterCompileOutput(out+'\n'+err,flag=flag) 592 if ret or len(out): 593 self.logPrint('Compile failed inside link\n'+out) 594 self.linkerObj = '' 595 return (out, ret) 596 597 cleanup = cleanup and self.framework.doCleanup 598 599 langPushed = 0 600 if linkLanguage is not None and linkLanguage != self.language[-1]: 601 self.pushLanguage(linkLanguage) 602 langPushed = 1 603 if shared == 'dynamic': 604 cmd = self.getDynamicLinkerCmd() 605 elif shared: 606 cmd = self.getSharedLinkerCmd() 607 else: 608 cmd = self.getLinkerCmd() 609 if langPushed: 610 self.popLanguage() 611 612 linkerObj = self.linkerObj 613 def report(command, status, output, error): 614 if error or status: 615 self.logError('linker', status, output, error) 616 examineOutput(status, output, error) 617 return 618 (out, err, ret) = Configure.executeShellCommand(cmd, checkCommand = report, log = self.log) 619 self.linkerObj = linkerObj 620 if os.path.isfile(self.compilerObj): os.remove(self.compilerObj) 621 if cleanup: 622 if os.path.isfile(self.linkerObj):os.remove(self.linkerObj) 623 pdbfile = os.path.splitext(self.linkerObj)[0]+'.pdb' 624 if os.path.isfile(pdbfile): os.remove(pdbfile) 625 return (out+'\n'+err, ret) 626 627 def checkLink(self, includes = '', body = '', cleanup = 1, codeBegin = None, codeEnd = None, shared = 0, linkLanguage=None, examineOutput=lambda ret,out,err:None): 628 (output, returnCode) = self.outputLink(includes, body, cleanup, codeBegin, codeEnd, shared, linkLanguage, examineOutput) 629 output = self.filterLinkOutput(output) 630 return not (returnCode or len(output)) 631 632 def getLinkerFlagsName(language): 633 if language in ['C', 'CUDA', 'Cxx', 'FC', 'HIP']: 634 flagsArg = 'LDFLAGS' 635 elif language == 'SYCL': 636 flagsArg = 'SYCLC_LINKER_FLAGS' # refer to SYCL.py. I need standalone sycl linker flags in make macros, so I don't use LDFLAGS 637 else: 638 raise RuntimeError('Unknown language: '+language) 639 return flagsArg 640 getLinkerFlagsName = staticmethod(getLinkerFlagsName) 641 642 def getLinkerFlagsArg(self): 643 '''Return the name of the argument which holds the linker flags for the current language''' 644 return self.getLinkerFlagsName(self.language[-1]) 645 646 def outputRun(self, includes, body, cleanup = 1, defaultOutputArg = '', executor = None,linkLanguage=None, timeout = 60, threads = 1): 647 if not self.checkLink(includes, body, cleanup = 0, linkLanguage=linkLanguage): return ('', 1) 648 self.logWrite('Testing executable '+self.linkerObj+' to see if it can be run\n') 649 if not os.path.isfile(self.linkerObj): 650 self.logWrite('ERROR executable '+self.linkerObj+' does not exist\n') 651 return ('', 1) 652 if not os.access(self.linkerObj, os.X_OK): 653 self.logWrite('ERROR while running executable: '+self.linkerObj+' is not executable\n') 654 return ('', 1) 655 if self.argDB['with-batch']: 656 if defaultOutputArg: 657 if defaultOutputArg in self.argDB: 658 return (self.argDB[defaultOutputArg], 0) 659 else: 660 raise ConfigureSetupError('Must give a default value for '+defaultOutputArg+' since generated executables cannot be run with the --with-batch option') 661 else: 662 raise ConfigureSetupError('Generated executables cannot be run with the --with-batch option') 663 cleanup = cleanup and self.framework.doCleanup 664 if executor: 665 command = executor+' '+self.linkerObj 666 else: 667 command = self.linkerObj 668 output = '' 669 error = '' 670 status = 1 671 self.logWrite('Executing: '+command+'\n') 672 try: 673 (output, error, status) = Configure.executeShellCommand(command, log = self.log, timeout = timeout, threads = threads) 674 except RuntimeError as e: 675 self.logWrite('ERROR while running executable: '+str(e)+'\n') 676 if str(e).find('Runaway process exceeded time limit') > -1: 677 raise RuntimeError('Runaway process exceeded time limit') 678 if os.path.isfile(self.compilerObj): 679 try: 680 os.remove(self.compilerObj) 681 except RuntimeError as e: 682 self.logWrite('ERROR while removing object file: '+str(e)+'\n') 683 if cleanup and os.path.isfile(self.linkerObj): 684 try: 685 if os.path.exists('/usr/bin/cygcheck.exe'): time.sleep(1) 686 os.remove(self.linkerObj) 687 except RuntimeError as e: 688 self.logWrite('ERROR while removing executable file: '+str(e)+'\n') 689 return (output+error, status) 690 691 def checkRun(self, includes = '', body = '', cleanup = 1, defaultArg = '', executor = None, linkLanguage=None, timeout = 60, threads = 1): 692 self.logWrite('======== Checking running linked program\n') 693 (output, returnCode) = self.outputRun(includes, body, cleanup, defaultArg, executor,linkLanguage=linkLanguage, timeout = timeout, threads = threads) 694 return not returnCode 695 696 def splitLibs(self,libArgs): 697 '''Takes a string containing a list of libraries (including potentially -L, -l, -w etc) and generates a list of libraries''' 698 dirs = [] 699 libs = [] 700 for arg in libArgs.split(' '): 701 if not arg: continue 702 if arg.startswith('-L'): 703 dirs.append(arg[2:]) 704 elif arg.startswith('-l'): 705 libs.append(arg[2:]) 706 elif not arg.startswith('-'): 707 libs.append(arg) 708 libArgs = [] 709 for lib in libs: 710 if not os.path.isabs(lib): 711 added = 0 712 for dir in dirs: 713 if added: 714 break 715 for ext in ['a', 'so','dylib']: 716 filename = os.path.join(dir, 'lib'+lib+'.'+ext) 717 if os.path.isfile(filename): 718 libArgs.append(filename) 719 added = 1 720 break 721 else: 722 libArgs.append(lib) 723 return libArgs 724 725 def splitIncludes(self,incArgs): 726 '''Takes a string containing a list of include directories with -I and generates a list of includes''' 727 includes = [] 728 for inc in incArgs.split(' '): 729 if inc.startswith('-I'): 730 # check if directory exists? 731 includes.append(inc[2:]) 732 return includes 733 734 def setupPackageDependencies(self, framework): 735 '''All calls to the framework addPackageDependency() should be made here''' 736 pass 737 738 def setupDependencies(self, framework): 739 '''All calls to the framework require() should be made here''' 740 self.framework = framework 741 742 def configure(self): 743 pass 744 745 def no_configure(self): 746 pass 747