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,'lib','petsc','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.options.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('config.packages.BlasLapack', self) 98 self.mpi = framework.require('config.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);') and 238 self.libraries.check(libraries, 'VecDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_Vec *Vec;int VecDestroy(Vec*);', call = 'VecDestroy((Vec*) 0)') and 239 self.libraries.check(libraries, 'MatDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_Mat *Mat;int MatDestroy(Mat*);', call = 'MatDestroy((Mat*) 0)') and 240 self.libraries.check(libraries, 'DMDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_DM *DA;int DMDestroy(DA*);', call = 'DMDestroy((DA*) 0)') and 241 self.libraries.check(libraries, 'KSPDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_KSP *KSP;int KSPDestroy(KSP*);', call = 'KSPDestroy((KSP*) 0)') and 242 self.libraries.check(libraries, 'SNESDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_SNES *SNES;int SNESDestroy(SNES*);', call = 'SNESDestroy((SNES*) 0)') and 243 self.libraries.check(libraries, 'TSDestroy', otherLibs = self.otherLibs, prototype = 'typedef struct _p_TS *TS;int TSDestroy(TS*);', call = 'TSDestroy((TS*) 0)')) 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 <petsctime.h>\n', 'PetscLogDouble time;\nPetscErrorCode ierr;\n\nierr = PetscTime(&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('#include <petsctime.h>\n', 'PetscLogDouble time;\nPetscErrorCode ierr;\n\nierr = PetscTime(&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 PetscTime(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(self.log): 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 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]*$'); 322 input = file(os.path.join(self.dir, 'include', 'petscversion.h')) 323 lines = [] 324 majorNum = 'Unknown' 325 minorNum = 'Unknown' 326 subminorNum = 'Unknown' 327 self.date = 'Unknown' 328 for line in input.readlines(): 329 m1 = majorRE.match(line) 330 m2 = minorRE.match(line) 331 m3 = subminorRE.match(line) 332 m5 = dateRE.match(line) 333 if m1: 334 majorNum = int(m1.group('versionNum')) 335 elif m2: 336 minorNum = int(m2.group('versionNum')) 337 elif m3: 338 subminorNum = int(m3.group('versionNum')) 339 340 if m5: 341 self.date = time.strftime('%b %d, %Y', time.localtime(time.time())) 342 lines.append('#define PETSC_VERSION_DATE'+m5.group(1)+'"'+self.date+'"\n') 343 else: 344 lines.append(line) 345 input.close() 346 self.logPrint('Found PETSc version (%s,%s,%s) on %s' % (majorNum, minorNum, subminorNum, self.date)) 347 return '%d.%d.%d' % (majorNum, minorNum, subminorNum) 348 349 def includeGuesses(self, path = None): 350 '''Return all include directories present in path or its ancestors''' 351 if not path: 352 yield [] 353 while path: 354 dir = os.path.join(path, 'include') 355 if os.path.isdir(dir): 356 yield [dir, os.path.join(path, self.arch,'include')] 357 if path == '/': 358 return 359 path = os.path.dirname(path) 360 return 361 362 def libraryGuesses(self, root = None): 363 '''Return standard library name guesses for a given installation root''' 364 libs = ['ts', 'snes', 'ksp', 'dm', 'mat', 'vec', ''] 365 if root: 366 d = os.path.join(root, 'lib', self.arch) 367 if not os.path.isdir(d): 368 self.logPrint('', 3, 'petsc') 369 return 370 yield [os.path.join(d, 'libpetsc'+lib+'.a') for lib in libs] 371 else: 372 yield ['libpetsc'+lib+'.a' for lib in libs] 373 return 374 375 def configureLibrary(self): 376 '''Find a working PETSc''' 377 for location, name in self.trial.items(): 378 self.framework.logPrintDivider() 379 self.framework.logPrint('Checking for a functional PETSc in '+name+', location/origin '+str(location)) 380 lib = None 381 include = None 382 found = 0 383 for libraries in self.libraryGuesses(location[0]): 384 if self.checkLib(libraries): 385 lib = libraries 386 for includeDir in self.includeGuesses(location[0]): 387 if self.checkInclude(includeDir): 388 include = includeDir 389 self.trial[location] = (name, include, lib, 'Unknown') 390 if self.executeTest(self.checkWorkingLink): 391 found = 1 392 break 393 else: 394 self.framework.logPrintDivider(single = 1) 395 self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkWorkingLink test') 396 else: 397 self.framework.logPrintDivider(single = 1) 398 self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkInclude test with includeDir: '+str(includeDir)) 399 if not found: 400 self.framework.logPrintDivider(single = 1) 401 self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkIncludes test') 402 continue 403 else: 404 self.framework.logPrintDivider(single = 1) 405 self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkLib test with libraries: '+str(libraries)) 406 continue 407 if self.framework.argDB['with-petsc-shared']: 408 if not self.executeTest(self.checkSharedLibrary, [libraries]): 409 self.framework.logPrintDivider(single = 1) 410 self.framework.logPrint('PETSc in '+name+', location/origin '+str(location)+' failed checkSharedLibrary test with libraries: '+str(libraries)) 411 found = 0 412 if found: 413 break 414 if found: 415 version = self.executeTest(self.configureVersion) 416 self.working[location] = (name, include, lib, version) 417 break 418 if found: 419 self.logPrint('Choose PETSc '+self.version+' in '+self.name) 420 else: 421 raise RuntimeError('Could not locate any functional PETSc') 422 return 423 424 def setOutput(self): 425 '''Add defines and substitutions 426 - HAVE_PETSC is defined if a working PETSc is found 427 - PETSC_INCLUDE and PETSC_LIB are command line arguments for the compile and link''' 428 if self.found: 429 self.addDefine('HAVE_PETSC', 1) 430 self.addSubstitution('PETSC_INCLUDE', ' '.join([self.headers.getIncludeArgument(inc) for inc in self.include])) 431 self.addSubstitution('PETSC_LIB', ' '.join(map(self.libraries.getLibArgument, self.lib))) 432 return 433 434 def configure(self): 435 self.executeTest(self.configureLibrary) 436 self.setOutput() 437 return 438 439if __name__ == '__main__': 440 import config.framework 441 import sys 442 framework = config.framework.Framework(sys.argv[1:]) 443 framework.setup() 444 framework.addChild(Configure(framework)) 445 framework.configure() 446 framework.dumpSubstitutions() 447