1#!/usr/bin/env python 2''' 3 This is the first try for a hierarchically configured module. The idea is to 4add the configure objects from a previously executed framework into the current 5framework. However, this necessitates a reorganization of the activities in the 6module. 7 8 We must now have three distinct phases: location, construction, and testing. 9This is very similar to the current compiler checks. The construction phase is 10optional, and only necessary when the package has not been previously configured. 11The phases will necessarily interact, as an installtion must be located before 12testing, however anothe should be located if the testing fails. 13 14 We will give each installation a unique key, which is returned by the location 15method. This will allow us to identify working installations, as well as those 16that failed testing. 17 18 There is a wierd role reversal that can happen. If we look for PETSc, but 19cannot find it, it is reasonable to ask to have it automatically downloaded. 20However, in this case, rather than using the configure objects from the existing 21PETSc, we contribute objects to the PETSc which will be built. 22 23''' 24from __future__ import generators 25import user 26import config.base 27 28import re 29import os 30 31class InvalidPETScError(RuntimeError): 32 pass 33 34class Configure(config.base.Configure): 35 def __init__(self, framework): 36 config.base.Configure.__init__(self, framework) 37 self.headerPrefix = '' 38 self.substPrefix = '' 39 self.location = None 40 self.trial = {} 41 self.working = {} 42 return 43 44 def __str__(self): 45 if self.found: 46 desc = ['PETSc:'] 47 desc.append(' Type: '+self.name) 48 desc.append(' Version: '+self.version) 49 desc.append(' Includes: '+str(self.include)) 50 desc.append(' Library: '+str(self.lib)) 51 return '\n'.join(desc)+'\n' 52 else: 53 return '' 54 55 def setupHelp(self, help): 56 import nargs 57 help.addArgument('PETSc', '-with-petsc=<bool>', nargs.ArgBool(None, 1, 'Activate PETSc')) 58 # Location options 59 help.addArgument('PETSc', '-with-petsc-dir=<root dir>', nargs.ArgDir(None, None, 'Specify the root directory of the PETSc installation')) 60 help.addArgument('PETSc', '-with-petsc-arch=<arch>', nargs.Arg(None, None, 'Specify PETSC_ARCH')) 61 # Construction options 62 help.addArgument('PETSc', '-download-petsc=<bool>', nargs.ArgBool(None, 0, 'Install PETSc')) 63 # Testing options 64 help.addArgument('PETSc', '-with-petsc-shared=<bool>', nargs.ArgBool(None, 1, 'Require that the PETSc library be shared')) 65 return 66 67 def setupPackageDependencies(self, framework): 68 import sys 69 70 petscConf = None 71 for (name, (petscDir, petscArch)) in self.getLocations(): 72 petscPythonDir = os.path.join(petscDir, 'config') 73 sys.path.append(petscPythonDir) 74 confPath = os.path.join(petscDir, petscArch,'conf') 75 petscConf = framework.loadFramework(confPath) 76 if petscConf: 77 self.logPrint('Loaded PETSc-AS configuration ('+name+') from '+confPath) 78 self.location = (petscDir, petscArch) 79 self.trial[self.location] = name 80 break 81 else: 82 self.logPrint('PETSc-AS has no cached configuration in '+confPath) 83 sys.path.reverse() 84 sys.path.remove(petscPythonDir) 85 sys.path.reverse() 86 if not petscConf: 87 self.downloadPETSc() 88 framework.addPackageDependency(petscConf, confPath) 89 return 90 91 def setupDependencies(self, framework): 92 config.base.Configure.setupDependencies(self, framework) 93 self.languages = framework.require('PETSc.utilities.languages', self) 94 self.compilers = framework.require('config.compilers', self) 95 self.headers = framework.require('config.headers', self) 96 self.libraries = framework.require('config.libraries', self) 97 self.blaslapack = framework.require('PETSc.packages.BlasLapack', self) 98 self.mpi = framework.require('PETSc.packages.MPI', self) 99 return 100 101 def getPETScArch(self, petscDir): 102 '''Return the allowable PETSc architectures for a given root''' 103 if 'with-petsc-arch' in self.framework.argDB: 104 yield self.framework.argDB['with-petsc-arch'] 105 elif 'PETSC_ARCH' in os.environ: 106 yield os.environ['PETSC_ARCH'] 107 else: 108 raise InvalidPETScError('Must set PETSC_ARCH or use --with-petsc-arch') 109 return 110 111 def getLocations(self): 112 '''Return all allowable locations for PETSc''' 113 if hasattr(self, '_configured'): 114 key =(self.dir, self.arch) 115 yield (self.working[key], key) 116 raise InvalidPETScError('Configured PETSc is not usable') 117 if self.framework.argDB['download-petsc'] == 1: 118 yield self.downloadPETSc() 119 raise InvalidPETScError('Downloaded PETSc is not usable') 120 if 'with-petsc-dir' in self.framework.argDB: 121 petscDir = self.framework.argDB['with-petsc-dir'] 122 for petscArch in self.getPETScArch(petscDir): 123 yield ('User specified installation root', (petscDir, petscArch)) 124 raise InvalidPETScError('No working architecitures in '+str(petscDir)) 125 elif 'PETSC_DIR' in os.environ: 126 petscDir = os.environ['PETSC_DIR'] 127 for petscArch in self.getPETScArch(petscDir): 128 yield ('User specified installation root', (petscDir, petscArch)) 129 raise InvalidPETScError('No working architecitures in '+str(petscDir)) 130 else: 131 for petscArch in self.getPETScArch(petscDir): 132 yield ('Default compiler locations', ('', petscArch)) 133 petscDirRE = re.compile(r'(PETSC|pets)c(-.*)?') 134 trialDirs = [] 135 for packageDir in self.framework.argDB['package-dirs']: 136 if os.path.isdir(packageDir): 137 for d in os.listdir(packageDir): 138 if petscDirRE.match(d): 139 trialDirs.append(('Package directory installation root', os.path.join(packageDir, d))) 140 usrLocal = os.path.join('/usr', 'local') 141 if os.path.isdir(os.path.join('/usr', 'local')): 142 trialDirs.append(('Frequent user install location (/usr/local)', usrLocal)) 143 for d in os.listdir(usrLocal): 144 if petscDirRE.match(d): 145 trialDirs.append(('Frequent user install location (/usr/local/'+d+')', os.path.join(usrLocal, d))) 146 if 'HOME' in os.environ and os.path.isdir(os.environ['HOME']): 147 for d in os.listdir(os.environ['HOME']): 148 if petscDirRE.match(d): 149 trialDirs.append(('Frequent user install location (~/'+d+')', os.path.join(os.environ['HOME'], d))) 150 return 151 152 def downloadPETSc(self): 153 if self.framework.argDB['download-petsc'] == 0: 154 raise RuntimeError('No functioning PETSc located') 155 # Download and build PETSc 156 # Use only the already configured objects from this run 157 raise RuntimeError('Not implemented') 158 159 def getDir(self): 160 if self.location: 161 return self.location[0] 162 return None 163 dir = property(getDir, doc = 'The PETSc root directory') 164 165 def getArch(self): 166 if self.location: 167 return self.location[1] 168 return None 169 arch = property(getArch, doc = 'The PETSc architecture') 170 171 def getFound(self): 172 return self.location and self.location in self.working 173 found = property(getFound, doc = 'Did we find a valid PETSc installation') 174 175 def getName(self): 176 if self.location and self.location in self.working: 177 return self.working[self.location][0] 178 return None 179 name = property(getName, doc = 'The PETSc installation type') 180 181 def getInclude(self, useTrial = 0): 182 if self.location and self.location in self.working: 183 return self.working[self.location][1] 184 elif useTrial and self.location and self.location in self.trial: 185 return self.trial[self.location][1] 186 return None 187 include = property(getInclude, doc = 'The PETSc include directories') 188 189 def getLib(self, useTrial = 0): 190 if self.location and self.location in self.working: 191 return self.working[self.location][2] 192 elif useTrial and self.location and self.location in self.trial: 193 return self.trial[self.location][2] 194 return None 195 lib = property(getLib, doc = 'The PETSc libraries') 196 197 def getVersion(self): 198 if self.location and self.location in self.working: 199 return self.working[self.location][3] 200 return None 201 version = property(getVersion, doc = 'The PETSc version') 202 203 def getOtherIncludes(self): 204 if not hasattr(self, '_otherIncludes'): 205 includes = [] 206 includes.extend([self.headers.getIncludeArgument(inc) for inc in self.mpi.include]) 207 return ' '.join(includes) 208 return self._otherIncludes 209 def setOtherIncludes(self, otherIncludes): 210 self._otherIncludes = otherIncludes 211 otherIncludes = property(getOtherIncludes, setOtherIncludes, doc = 'Includes needed to compile PETSc') 212 213 def getOtherLibs(self): 214 if not hasattr(self, '_otherLibs'): 215 libs = self.compilers.flibs[:] 216 libs.extend(self.mpi.lib) 217 libs.extend(self.blaslapack.lib) 218 return libs 219 return self._otherLibs 220 def setOtherLibs(self, otherLibs): 221 self._otherLibs = otherLibs 222 otherLibs = property(getOtherLibs, setOtherLibs, doc = 'Libraries needed to link PETSc') 223 224 def checkLib(self, libraries): 225 '''Check for PETSc creation functions in libraries, which can be a list of libraries or a single library 226 - PetscInitialize from libpetsc 227 - VecCreate from libpetscvec 228 - MatCreate from libpetscmat 229 - DMDestroy from libpetscdm 230 - KSPCreate from libpetscksp 231 - SNESCreate from libpetscsnes 232 - TSCreate from libpetscts 233 ''' 234 if not isinstance(libraries, list): libraries = [libraries] 235 oldLibs = self.compilers.LIBS 236 self.libraries.pushLanguage(self.languages.clanguage) 237 found = (self.libraries.check(libraries, 'PetscInitializeNoArguments', otherLibs = self.otherLibs, prototype = 'int PetscInitializeNoArguments(void);', cxxMangle = not self.languages.cSupport) and 238 self.libraries.check(libraries, 'VecDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_Vec *Vec;int VecDestroy(Vec*);', call = 'VecDestroy((Vec*) 0)', cxxMangle = not self.languages.cSupport) and 239 self.libraries.check(libraries, 'MatDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_Mat *Mat;int MatDestroy(Mat*);', call = 'MatDestroy((Mat*) 0)', cxxMangle = not self.languages.cSupport) and 240 self.libraries.check(libraries, 'DMDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_DM *DA;int DMDestroy(DA*);', call = 'DMDestroy((DA*) 0)', cxxMangle = not self.languages.cSupport) and 241 self.libraries.check(libraries, 'KSPDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_KSP *KSP;int KSPDestroy(KSP*);', call = 'KSPDestroy((KSP*) 0)', cxxMangle = not self.languages.cSupport) and 242 self.libraries.check(libraries, 'SNESDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_SNES *SNES;int SNESDestroy(SNES*);', call = 'SNESDestroy((SNES*) 0)', cxxMangle = not self.languages.cSupport) and 243 self.libraries.check(libraries, 'TSDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_TS *TS;int TSDestroy(TS*);', call = 'TSDestroy((TS*) 0)', cxxMangle = not self.languages.cSupport)) 244 self.libraries.popLanguage() 245 self.compilers.LIBS = oldLibs 246 return found 247 248 def checkInclude(self, includeDir): 249 '''Check that petscsys.h is present''' 250 oldFlags = self.compilers.CPPFLAGS 251 self.compilers.CPPFLAGS += ' '.join([self.headers.getIncludeArgument(inc) for inc in includeDir]) 252 if self.otherIncludes: 253 self.compilers.CPPFLAGS += ' '+self.otherIncludes 254 self.pushLanguage(self.languages.clanguage) 255 found = self.checkPreprocess('#include <petscsys.h>\n') 256 self.popLanguage() 257 self.compilers.CPPFLAGS = oldFlags 258 return found 259 260 def checkPETScLink(self, includes, body, cleanup = 1, codeBegin = None, codeEnd = None, shared = None): 261 '''Analogous to checkLink(), but the PETSc includes and libraries are automatically provided''' 262 success = 0 263 oldFlags = self.compilers.CPPFLAGS 264 self.compilers.CPPFLAGS += ' '.join([self.headers.getIncludeArgument(inc) for inc in self.getInclude(useTrial = 1)]) 265 if self.otherIncludes: 266 self.compilers.CPPFLAGS += ' '+self.otherIncludes 267 oldLibs = self.compilers.LIBS 268 self.compilers.LIBS = ' '.join([self.libraries.getLibArgument(lib) for lib in self.getLib(useTrial = 1)+self.otherLibs])+' '+self.compilers.LIBS 269 if self.checkLink(includes, body, cleanup, codeBegin, codeEnd, shared): 270 success = 1 271 self.compilers.CPPFLAGS = oldFlags 272 self.compilers.LIBS = oldLibs 273 return success 274 275 def checkWorkingLink(self): 276 '''Checking that we can link a PETSc executable''' 277 self.pushLanguage(self.languages.clanguage) 278 if not self.checkPETScLink('#include <petsclog.h>\n', 'PetscLogDouble time;\nPetscErrorCode ierr;\n\nierr = PetscGetTime(&time);CHKERRQ(ierr);\n'): 279 self.logPrint('PETSc cannot link, which indicates a problem with the PETSc installation') 280 return 0 281 self.logPrint('PETSc can link with '+self.languages.clanguage) 282 self.popLanguage() 283 284 if hasattr(self.compilers, 'CXX') and self.languages.clanguage == 'C': 285 self.pushLanguage('C++') 286 self.sourceExtension = '.C' 287 if not self.checkPETScLink('#define PETSC_USE_EXTERN_CXX\n#include <petscsys.h>\n', 'PetscLogDouble time;\nPetscErrorCode ierr;\n\nierr = PetscGetTime(&time);CHKERRQ(ierr);\n'): 288 self.logPrint('PETSc cannot link C++ but can link C, which indicates a problem with the PETSc installation') 289 self.popLanguage() 290 return 0 291 self.popLanguage() 292 self.logPrint('PETSc can link with C++') 293 294 if hasattr(self.compilers, 'FC'): 295 self.pushLanguage('FC') 296 self.sourceExtension = '.F' 297 if not self.checkPETScLink('', ' integer ierr\n real time\n call PetscGetTime(time, ierr)\n'): 298 self.logPrint('PETSc cannot link Fortran, but can link C, which indicates a problem with the PETSc installation\nRun with -with-fc=0 if you do not wish to use Fortran') 299 self.popLanguage() 300 return 0 301 self.popLanguage() 302 self.logPrint('PETSc can link with Fortran') 303 return 1 304 305 def checkSharedLibrary(self, libraries): 306 '''Check that the libraries for PETSc are shared libraries''' 307 if config.setCompilers.Configure.isDarwin(): 308 # on Apple if you list the MPI libraries again you will generate multiply defined errors 309 # since they are already copied into the PETSc dynamic library. 310 self.setOtherLibs([]) 311 self.pushLanguage(self.languages.clanguage) 312 isShared = self.libraries.checkShared('#include <petscsys.h>\n', 'PetscInitialize', 'PetscInitialized', 'PetscFinalize', checkLink = self.checkPETScLink, libraries = libraries, initArgs = '&argc, &argv, 0, 0', boolType = 'PetscBool ', executor = self.mpi.mpiexec) 313 self.popLanguage() 314 return isShared 315 316 def configureVersion(self): 317 '''Determine the PETSc version''' 318 majorRE = re.compile(r'^#define PETSC_VERSION_MAJOR([\s]+)(?P<versionNum>\d+)[\s]*$'); 319 minorRE = re.compile(r'^#define PETSC_VERSION_MINOR([\s]+)(?P<versionNum>\d+)[\s]*$'); 320 subminorRE = re.compile(r'^#define PETSC_VERSION_SUBMINOR([\s]+)(?P<versionNum>\d+)[\s]*$'); 321 patchRE = re.compile(r'^#define PETSC_VERSION_PATCH([\s]+)(?P<patchNum>\d+)[\s]*$'); 322 dateRE = re.compile(r'^#define PETSC_VERSION_DATE([\s]+)"(?P<date>(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d\d?, \d\d\d\d)"[\s]*$'); 323 input = file(os.path.join(self.dir, 'include', 'petscversion.h')) 324 lines = [] 325 majorNum = 'Unknown' 326 minorNum = 'Unknown' 327 subminorNum = 'Unknown' 328 patchNum = 'Unknown' 329 self.date = 'Unknown' 330 for line in input.readlines(): 331 m1 = majorRE.match(line) 332 m2 = minorRE.match(line) 333 m3 = subminorRE.match(line) 334 m4 = patchRE.match(line) 335 m5 = dateRE.match(line) 336 if m1: 337 majorNum = int(m1.group('versionNum')) 338 elif m2: 339 minorNum = int(m2.group('versionNum')) 340 elif m3: 341 subminorNum = int(m3.group('versionNum')) 342 343 if m4: 344 patchNum = int(m4.group('patchNum'))+1 345 lines.append('#define PETSC_VERSION_PATCH'+m4.group(1)+str(patchNum)+'\n') 346 elif m5: 347 self.date = time.strftime('%b %d, %Y', time.localtime(time.time())) 348 lines.append('#define PETSC_VERSION_DATE'+m5.group(1)+'"'+self.date+'"\n') 349 else: 350 lines.append(line) 351 input.close() 352 self.logPrint('Found PETSc version (%s,%s,%s) patch %s on %s' % (majorNum, minorNum, subminorNum, patchNum, self.date)) 353 return '%d.%d.%d' % (majorNum, minorNum, subminorNum) 354 355 def includeGuesses(self, path = None): 356 '''Return all include directories present in path or its ancestors''' 357 if not path: 358 yield [] 359 while path: 360 dir = os.path.join(path, 'include') 361 if os.path.isdir(dir): 362 yield [dir, os.path.join(path, self.arch,'include')] 363 if path == '/': 364 return 365 path = os.path.dirname(path) 366 return 367 368 def libraryGuesses(self, root = None): 369 '''Return standard library name guesses for a given installation root''' 370 libs = ['ts', 'snes', 'ksp', 'dm', 'mat', 'vec', ''] 371 if root: 372 d = os.path.join(root, 'lib', self.arch) 373 if not os.path.isdir(d): 374 self.logPrint('', 3, 'petsc') 375 return 376 yield [os.path.join(d, 'libpetsc'+lib+'.a') for lib in libs] 377 else: 378 yield ['libpetsc'+lib+'.a' for lib in libs] 379 return 380 381 def configureLibrary(self): 382 '''Find a working PETSc 383 - Right now, C++ builds are required to use PETSC_USE_EXTERN_CXX''' 384 for location, name in self.trial.items(): 385 self.framework.logPrintDivider() 386 self.framework.logPrint('Checking for a functional PETSc in '+name+', location/origin '+str(location)) 387 lib = None 388 include = None 389 found = 0 390 for libraries in self.libraryGuesses(location[0]): 391 if self.checkLib(libraries): 392 lib = libraries 393 for includeDir in self.includeGuesses(location[0]): 394 if self.checkInclude(includeDir): 395 include = includeDir 396 self.trial[location] = (name, include, lib, 'Unknown') 397 if self.executeTest(self.checkWorkingLink): 398 found = 1 399 break 400 else: 401 self.framework.logPrintDivider(single = 1) 402 self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkWorkingLink test') 403 else: 404 self.framework.logPrintDivider(single = 1) 405 self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkInclude test with includeDir: '+str(includeDir)) 406 if not found: 407 self.framework.logPrintDivider(single = 1) 408 self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkIncludes test') 409 continue 410 else: 411 self.framework.logPrintDivider(single = 1) 412 self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkLib test with libraries: '+str(libraries)) 413 continue 414 if self.framework.argDB['with-petsc-shared']: 415 if not self.executeTest(self.checkSharedLibrary, [libraries]): 416 self.framework.logPrintDivider(single = 1) 417 self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkSharedLibrary test with libraries: '+str(libraries)) 418 found = 0 419 if found: 420 break 421 if found: 422 version = self.executeTest(self.configureVersion) 423 self.working[location] = (name, include, lib, version) 424 break 425 if found: 426 self.logPrint('Choose PETSc '+self.version+' in '+self.name) 427 else: 428 raise RuntimeError('Could not locate any functional PETSc') 429 return 430 431 def setOutput(self): 432 '''Add defines and substitutions 433 - HAVE_PETSC is defined if a working PETSc is found 434 - PETSC_INCLUDE and PETSC_LIB are command line arguments for the compile and link''' 435 if self.found: 436 self.addDefine('HAVE_PETSC', 1) 437 self.addSubstitution('PETSC_INCLUDE', ' '.join([self.headers.getIncludeArgument(inc) for inc in self.include])) 438 self.addSubstitution('PETSC_LIB', ' '.join(map(self.libraries.getLibArgument, self.lib))) 439 return 440 441 def configure(self): 442 self.executeTest(self.configureLibrary) 443 self.setOutput() 444 return 445 446if __name__ == '__main__': 447 import config.framework 448 import sys 449 framework = config.framework.Framework(sys.argv[1:]) 450 framework.setup() 451 framework.addChild(Configure(framework)) 452 framework.configure() 453 framework.dumpSubstitutions() 454