1from __future__ import generators 2import config.base 3 4import os 5import sys 6import re 7import itertools 8from hashlib import sha256 as checksum_algo 9 10def sliding_window(seq, n=2): 11 """ 12 Returns a sliding window (of width n) over data from the iterable 13 s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... 14 """ 15 it = iter(seq) 16 result = tuple(itertools.islice(it, n)) 17 if len(result) == n: 18 yield result 19 for elem in it: 20 result = result[1:] + (elem,) 21 yield result 22 23class FakePETScDir: 24 def __init__(self): 25 self.dir = 'UNKNOWN' 26 27class Package(config.base.Configure): 28 def __init__(self, framework): 29 config.base.Configure.__init__(self, framework) 30 self.headerPrefix = 'PETSC' 31 self.substPrefix = 'PETSC' 32 self.arch = None # The architecture identifier 33 self.externalPackagesDir = os.path.abspath('externalpackages') 34 35 # These are determined by the configure tests 36 self.found = 0 37 self.setNames() 38 self.include = [] 39 self.dinclude = [] # all includes in this package and all those it depends on 40 self.lib = [] 41 self.dlib = [] # all libraries in this package and all those it depends on 42 self.directory = None # path of the package installation point; for example /usr/local or /home/bsmith/mpich-2.0.1 43 44 self.version = '' # the version of the package that PETSc will build with the --download-package option 45 self.versionname = '' # string name that appears in package include file, for example HYPRE_RELEASE_VERSION 46 self.versioninclude = '' # include file that contains package version information; if not provided uses includes[0] 47 self.minversion = '' # minimum version of the package that is supported 48 self.maxversion = '' # maximum version of the package that is supported 49 self.foundversion = '' # version of the package actually found 50 self.version_tuple = '' # version of the package actually found (tuple) 51 self.requiresversion = 0 # error if the version information is not found 52 self.requirekandr = 0 # package requires KandR compiler flags to build 53 self.brokengnu23 = 0 # package requires a C standard lower than GNU23 54 55 # These are specified for the package 56 self.required = 0 # 1 means the package is required 57 self.devicePackage = 0 # 1 if PETSC_HAVE_DEVICE should be defined by 58 # inclusion of this package 59 self.lookforbydefault = 0 # 1 means the package is not required, but always look for and use if found 60 # cannot tell the difference between user requiring it with --with-PACKAGE=1 and 61 # this flag being one so hope user never requires it. Needs to be fixed in an overhaul of 62 # args database so it keeps track of what the user set vs what the program set 63 self.useddirectly = 1 # 1 indicates used by PETSc directly, 0 indicates used by a package used by PETSc 64 self.linkedbypetsc = 1 # 1 indicates PETSc shared libraries (and PETSc executables) need to link against this library 65 self.gitcommit = None # Git commit to use for downloads 66 self.gitcommitmain = None # Git commit to use for petsc/main or similar non-release branches 67 self.gcommfile = None # File within the git clone - that has the gitcommit for the current build - saved 68 self.gitsubmodules = [] # List of git submodues that should be cloned along with the repo 69 self.download = [] # list of URLs where repository or tarballs may be found (git is tested before tarballs) 70 self.deps = [] # other packages whose dlib or include we depend on, usually we also use self.framework.require() 71 self.odeps = [] # dependent packages that are optional 72 self.defaultLanguage = 'C' # The language in which to run tests 73 self.liblist = [[]] # list of libraries we wish to check for (packages can override with their own generateLibList() method) 74 self.extraLib = [] # additional libraries needed to link 75 self.includes = [] # headers to check for 76 self.macros = [] # optional macros we wish to check for in the headers 77 self.functions = [] # functions we wish to check for in the libraries 78 self.functionsDefine = [] # optional functions we wish to check for in the libraries that should generate a PETSC_HAVE_ define 79 self.functionsFortran = 0 # 1 means the symbols in self.functions are Fortran symbols, so name-mangling is done 80 self.functionsCxx = [0, '', ''] # 1 means the symbols in self.functions symbol are C++ symbol, so name-mangling with prototype/call is done 81 self.buildLanguages = ['C'] # Languages the package is written in, hence also the compilers needed to build it. Normally only contains one 82 # language, but can have multiple, such as ['FC', 'Cxx']. In PETSc's terminology, languages are C, Cxx, FC, CUDA, HIP, SYCL. 83 # We use the first language in the list to check include headers, library functions and versions. 84 self.noMPIUni = 0 # 1 means requires a real MPI 85 self.libDirs = ['lib', 'lib64'] # search locations of libraries in the package directory tree; self.libDir is self.installDir + self.libDirs[0] 86 self.includedir = 'include' # location of includes in the package directory tree 87 self.license = None # optional license text 88 self.excludedDirs = [] # list of directory names that could be false positives, SuperLU_DIST when looking for SuperLU 89 self.downloadonWindows = 0 # 1 means the --download-package works on Microsoft Windows 90 self.minCxxVersion = framework.compilers.setCompilers.cxxDialectRange['Cxx'][0] # minimum c++ standard version required by the package, e.g. 'c++11' 91 self.maxCxxVersion = framework.compilers.setCompilers.cxxDialectRange['Cxx'][1] # maximum c++ standard version allowed by the package, e.g. 'c++14', must be greater than self.minCxxVersion 92 self.publicInstall = 1 # Installs the package in the --prefix directory if it was given. Packages that are only used 93 # during the configuration/installation process such as sowing, make etc should be marked as 0 94 self.parallelMake = 1 # 1 indicates the package supports make -j np option 95 96 self.precisions = ['__fp16','single','double','__float128']; # Floating point precision package works with 97 self.complex = 1 # 0 means cannot use complex 98 self.requires32bitint = 0 # 1 means that the package will not work with 64-bit integers 99 self.requires32bitintblas = 1 # 1 means that the package will not work with 64-bit integer BLAS/LAPACK 100 self.skippackagewithoptions = 0 # packages like fblaslapack and MPICH do not support --with-package* options so do not print them in help 101 self.skippackagelibincludedirs = 0 # packages like make do not support --with-package-lib and --with-package-include so do not print them in help 102 self.alternativedownload = [] # Used by, for example mpi.py to print useful error messages, which does not support --download-mpi but one can use --download-mpich 103 self.usesopenmp = 'no' # yes, no, unknown package is built to use OpenMP 104 self.usespthreads = 'no' # yes, no, unknown package is built to use Pthreads 105 self.cmakelistsdir = '' # Location of CMakeLists.txt - if not located at the top level of the package dir 106 107 # Outside coupling 108 self.defaultInstallDir = '' 109 self.PrefixWriteCheck = 1 # check if specified prefix location is writable for 'make install' 110 111 self.skipMPIDependency = 0 # Does this package need to skip adding a dependency on MPI? Most packages work with MPI dependency - some libraries (ex. metis) don't care, while some build tools (ex. make, bison) and some MPI library dependencies (ex. hwloc, ucx) need to be built before it, so they can set/use this flag. 112 self.hastests = 0 # indicates that PETSc make alltests has tests for this package 113 self.hastestsdatafiles = 0 # indicates that PETSc make alltests has tests for this package that require DATAFILESPATH to be set 114 self.makerulename = '' # some packages do too many things with the make stage; this allows a package to limit to, for example, just building the libraries 115 self.installedpetsc = 0 # configure actually compiled and installed PETSc 116 self.installwithbatch = 1 # install the package even though configure in the batch mode; f2blaslapack and fblaslapack for example 117 self.builtafterpetsc = 0 # package is compiled/installed after PETSc is compiled 118 119 self.downloaded = 0 # 1 indicates that this package is being downloaded during this run (internal use only) 120 self.testoptions = '' # Any PETSc options that should be used when this package is installed and the test harness is run 121 self.executablename = '' # full path of executable, for example cmake 122 return 123 124 def __str__(self): 125 '''Prints the location of the packages includes and libraries''' 126 output = '' 127 if self.found: 128 output = self.name+':\n' 129 if self.foundversion: 130 if hasattr(self,'versiontitle'): 131 output += ' '+self.versiontitle+': '+self.foundversion+'\n' 132 else: 133 output += ' Version: '+self.foundversion+'\n' 134 else: 135 if self.version: output += ' Version: '+self.version+'\n' 136 if self.include: output += ' Includes: '+self.headers.toStringNoDupes(self.include)+'\n' 137 if self.lib: output += ' Libraries: '+self.libraries.toStringNoDupes(self.lib)+'\n' 138 if self.executablename: output += ' Executable: '+getattr(self,self.executablename)+'\n' 139 if self.usesopenmp == 'yes': output += ' uses OpenMP; use export OMP_NUM_THREADS=<p> or -omp_num_threads <p> to control the number of threads\n' 140 if self.usesopenmp == 'unknown': output += ' Unknown if this uses OpenMP (try export OMP_NUM_THREADS=<1-4> yourprogram -log_view) \n' 141 if self.usespthreads == 'yes': output += ' uses PTHREADS; please consult the documentation on how to control the number of threads\n' 142 return output 143 144 def setupDependencies(self, framework): 145 config.base.Configure.setupDependencies(self, framework) 146 self.setCompilers = framework.require('config.setCompilers', self) 147 self.compilers = framework.require('config.compilers', self) 148 self.fortran = framework.require('config.compilersFortran', self) 149 self.compilerFlags = framework.require('config.compilerFlags', self) 150 self.types = framework.require('config.types', self) 151 self.headers = framework.require('config.headers', self) 152 self.libraries = framework.require('config.libraries', self) 153 self.programs = framework.require('config.programs', self) 154 self.sourceControl = framework.require('config.sourceControl',self) 155 self.python = framework.require('config.packages.Python',self) 156 try: 157 import PETSc.options 158 self.sharedLibraries = framework.require('PETSc.options.sharedLibraries', self) 159 self.petscdir = framework.require('PETSc.options.petscdir', self.setCompilers) 160 self.petscclone = framework.require('PETSc.options.petscclone',self.setCompilers) 161 self.havePETSc = True 162 except ImportError: 163 self.havePETSc = False 164 self.petscdir = FakePETScDir() 165 # All packages depend on make 166 self.make = framework.require('config.packages.make',self) 167 if not self.skipMPIDependency: 168 self.mpi = framework.require('config.packages.MPI',self) 169 return 170 171 def setupHelp(self,help): 172 '''Prints help messages for the package''' 173 import nargs 174 if not self.skippackagewithoptions: 175 help.addArgument(self.PACKAGE,'-with-'+self.package+'=<bool>',nargs.ArgBool(None,self.required+self.lookforbydefault,'Indicate if you wish to test for '+self.name)) 176 help.addArgument(self.PACKAGE,'-with-'+self.package+'-dir=<dir>',nargs.ArgDir(None,None,'Indicate the root directory of the '+self.name+' installation',mustExist = 1)) 177 help.addArgument(self.PACKAGE,'-with-'+self.package+'-pkg-config=<dir>', nargs.ArgDir(None, None, 'Look for '+self.name+' using pkg-config utility optional directory to look in',mustExist = 1)) 178 if not self.skippackagelibincludedirs: 179 help.addArgument(self.PACKAGE,'-with-'+self.package+'-include=<dirs>',nargs.ArgDirList(None,None,'Indicate the directory of the '+self.name+' include files')) 180 help.addArgument(self.PACKAGE,'-with-'+self.package+'-lib=<libraries: e.g. [/Users/..../lib'+self.package+'.a,...]>',nargs.ArgLibrary(None,None,'Indicate the '+self.name+' libraries')) 181 if self.download: 182 help.addArgument(self.PACKAGE, '-download-'+self.package+'=<no,yes,filename,url>', nargs.ArgDownload(None, 0, 'Download and install '+self.name)) 183 if hasattr(self, 'download_git'): 184 help.addArgument(self.PACKAGE, '-download-'+self.package+'-commit=commitid', nargs.ArgString(None, 0, 'Switch from installing release tarballs to git repo - using the specified commit of '+self.name)) 185 else: 186 help.addArgument(self.PACKAGE, '-download-'+self.package+'-commit=commitid', nargs.ArgString(None, 0, 'The commit id from a git repository to use for the build of '+self.name)) 187 help.addDownload(self.package,self.download) 188 return 189 190 def setNames(self): 191 '''Setup various package names 192 name: The module name (usually the filename) 193 package: The lowercase name 194 PACKAGE: The uppercase name 195 pkgname: The name of pkg-config (.pc) file 196 downloadname: Name for download option (usually name) 197 downloaddirnames: names for downloaded directory (first part of string) (usually downloadname) 198 ''' 199 import sys 200 if hasattr(sys.modules.get(self.__module__), '__file__'): 201 self.name = os.path.splitext(os.path.basename(sys.modules.get(self.__module__).__file__))[0] 202 else: 203 self.name = 'DEBUGGING' 204 self.PACKAGE = self.name.upper() 205 self.package = self.name.lower() 206 self.pkgname = self.package 207 self.downloadname = self.name 208 self.downloaddirnames = [self.downloadname] 209 return 210 211 def getDefaultPrecision(self): 212 '''The precision of the library''' 213 if hasattr(self, 'precisionProvider'): 214 if hasattr(self.precisionProvider, 'precision'): 215 return self.precisionProvider.precision 216 if hasattr(self, '_defaultPrecision'): 217 return self._defaultPrecision 218 return 'double' 219 def setDefaultPrecision(self, defaultPrecision): 220 '''The precision of the library''' 221 self._defaultPrecision = defaultPrecision 222 return 223 defaultPrecision = property(getDefaultPrecision, setDefaultPrecision, doc = 'The precision of the library') 224 225 def getDefaultScalarType(self): 226 '''The scalar type for the library''' 227 if hasattr(self, 'precisionProvider'): 228 if hasattr(self.precisionProvider, 'scalartype'): 229 return self.precisionProvider.scalartype 230 return self._defaultScalarType 231 def setDefaultScalarType(self, defaultScalarType): 232 '''The scalar type for the library''' 233 self._defaultScalarType = defaultScalarType 234 return 235 defaultScalarType = property(getDefaultScalarType, setDefaultScalarType, doc = 'The scalar type for of the library') 236 237 def getDefaultIndexSize(self): 238 '''The index size for the library''' 239 if hasattr(self, 'indexProvider'): 240 if hasattr(self.indexProvider, 'integerSize'): 241 return self.indexProvider.integerSize 242 return self._defaultIndexSize 243 def setDefaultIndexSize(self, defaultIndexSize): 244 '''The index size for the library''' 245 self._defaultIndexSize = defaultIndexSize 246 return 247 defaultIndexSize = property(getDefaultIndexSize, setDefaultIndexSize, doc = 'The index size for of the library') 248 249 def checkNoOptFlag(self): 250 flags = '' 251 flag = '-O0 ' 252 if self.setCompilers.checkCompilerFlag(flag): flags = flags+flag 253 flag = '-mfp16-format=ieee' 254 if self.setCompilers.checkCompilerFlag(flag): flags = flags+flag 255 return flags 256 257 def getSharedFlag(self,cflags): 258 for flag in ['-PIC', '-fPIC', '-KPIC', '-qpic', '-fpic']: 259 if cflags.find(flag) >= 0: return flag 260 return '' 261 262 def getPointerSizeFlag(self,cflags): 263 for flag in ['-m32', '-m64', '-xarch=v9','-q64','-mmic']: 264 if cflags.find(flag) >=0: return flag 265 return '' 266 267 def getDebugFlags(self,cflags): 268 outflags = [] 269 for flag in cflags.split(): 270 if flag in ['-g','-g3','-Z7']: 271 outflags.append(flag) 272 return ' '.join(outflags) 273 274 def getWindowsNonOptFlags(self,cflags): 275 outflags = [] 276 for flag in cflags.split(): 277 if flag in ['-MT','-MTd','-MD','-MDd','-threads']: 278 outflags.append(flag) 279 return ' '.join(outflags) 280 281 def rmArgs(self,args,rejects): 282 self.logPrint('Removing configure arguments '+str(rejects)) 283 return [arg for arg in args if not arg in rejects] 284 285 def rmArgsPair(self,args,rejects,remove_ahead=True): 286 '''Remove an argument and the next argument from a list of arguments''' 287 '''For example: --ccbin compiler''' 288 '''If remove_ahead is true, arguments rejects specifies the first entry in the pair and the following argument is removed, otherwise rejects specifies the second entry in the pair and the previous argument is removed as well.''' 289 self.logPrint('Removing paired configure arguments '+str(rejects)) 290 rejects = set(rejects) 291 nargs = [] 292 skip = -1 293 for flag, next_flag in sliding_window(args): 294 if skip == 1: 295 skip = 0 296 continue 297 skip = 0 298 299 flag_to_check = flag if remove_ahead else next_flag 300 if flag_to_check in rejects: 301 skip = 1 302 else: 303 nargs.append(flag) 304 if remove_ahead is False and skip == 0: # append last flag 305 nargs.append(next_flag) 306 return nargs 307 308 def rmArgsStartsWith(self,args,rejectstarts): 309 '''Remove an argument that starts with given strings''' 310 rejects = [] 311 if not isinstance(rejectstarts, list): rejectstarts = [rejectstarts] 312 for i in rejectstarts: 313 rejects.extend([arg for arg in args if arg.startswith(i)]) 314 return self.rmArgs(args,rejects) 315 316 def addArgStartsWith(self,args,sw,value): 317 '''Adds another value with the argument that starts with sw, create sw if it does not exist''' 318 keep = [] 319 found = 0 320 for i in args: 321 if i.startswith(sw+'="'): 322 i = i[:-1] + ' ' + value + '"' 323 found = 1 324 keep.append(i) 325 if not found: 326 keep.append(sw+'="' + value + '"') 327 return keep 328 329 def rmValueArgStartsWith(self,args,sw,value): 330 '''Remove a value from arguments that start with sw''' 331 if not isinstance(sw, list): sw = [sw] 332 keep = [] 333 for i in args: 334 for j in sw: 335 if i.startswith(j+'="'): 336 i = i.replace(value,'') 337 keep.append(i) 338 return keep 339 340 def removeWarningFlags(self,flags): 341 flags = self.rmArgs( 342 flags, 343 { 344 '-Werror', '-Wall', '-Wwrite-strings', '-Wno-strict-aliasing', '-Wno-unknown-pragmas', 345 '-Wno-unused-variable', '-Wno-unused-dummy-argument', '-std=c89', '-pedantic','--coverage', 346 '-Mfree', '-fdefault-integer-8', '-fsanitize=address', '-fstack-protector', '-Wconversion' 347 } 348 ) 349 return ['-g' if f == '-g3' else f for f in flags] 350 351 def __remove_flag_pair(self, flags, flag_to_remove, pair_prefix): 352 """ 353 Remove FLAG_TO_REMOVE from FLAGS 354 355 Parameters 356 ---------- 357 - flags - iterable (or string) of flags to remove from 358 - flag_to_remove - the flag to remove 359 - pair_prefix - (Optional) if not None, indicates that FLAG_TO_REMOVE is in a pair, and 360 is prefixed by str(pair_prefix). For example, pair_prefix='-Xcompiler' indicates 361 that the flag is specified as <COMPILER_NAME> -Xcompiler FLAG_TO_REMOVE 362 363 Return 364 ------ 365 flags - list of post-processed flags 366 """ 367 if isinstance(flags, str): 368 flags = flags.split() 369 370 if pair_prefix is None: 371 return self.rmArgs(flags, {flag_to_remove}) 372 assert isinstance(pair_prefix, str) 373 # deals with bare PAIR_PREFIX FLAG_TO_REMOVE 374 flag_str = ' '.join(self.rmArgsPair(flags, {flag_to_remove}, remove_ahead=False)) 375 # handle PAIR_PREFIX -fsome_other_flag,FLAG_TO_REMOVE 376 flag_str = re.sub(r',{}\s'.format(flag_to_remove), ' ', flag_str) 377 # handle PAIR_PREFIX -fsome_other_flag,FLAG_TO_REMOVE,-fyet_another_flag 378 flag_str = re.sub(r',{},'.format(flag_to_remove), ',', flag_str) 379 # handle PAIR_PREFIX FLAG_TO_REMOVE,-fsome_another_flag 380 flag_str = re.sub(r'\s{},'.format(flag_to_remove), ' ', flag_str) 381 return flag_str.split() 382 383 def removeVisibilityFlag(self, flags, pair_prefix=None): 384 """Remove -fvisibility=hidden from flags.""" 385 return self.__remove_flag_pair(flags, '-fvisibility=hidden', pair_prefix) 386 387 def removeCoverageFlag(self, flags, pair_prefix=None): 388 """Remove --coverage from flags.""" 389 return self.__remove_flag_pair(flags, '--coverage', pair_prefix) 390 391 def removeOpenMPFlag(self, flags, pair_prefix=None): 392 """Remove -fopenmp from flags.""" 393 if hasattr(self,'openmp') and hasattr(self.openmp,'ompflag'): 394 return self.__remove_flag_pair(flags, self.openmp.ompflag, pair_prefix) 395 else: 396 return flags 397 398 def removeStdCxxFlag(self,flags): 399 '''Remove the -std=[CXX_VERSION] flag from the list of flags, but only for CMake packages''' 400 if issubclass(type(self),config.package.CMakePackage): 401 # only cmake packages get their std flags removed since they use 402 # -DCMAKE_CXX_STANDARD to set the std flag 403 cmakeLists = os.path.join(self.packageDir,self.cmakelistsdir,'CMakeLists.txt') 404 with open(cmakeLists,'r') as fd: 405 refcxxstd = re.compile(r'^\s*(?!#)(set\()(CMAKE_CXX_STANDARD\s[A-z0-9\s]*)') 406 for line in fd: 407 match = refcxxstd.search(line) 408 if match: 409 # from set(CMAKE_CXX_STANDARD <val> [CACHE <type> <docstring> [FORCE]]) extract 410 # <val> CACHE <type> <docstring> [FORCE] 411 cmakeSetCmd = match.groups()[1].split()[1:] 412 if (len(cmakeSetCmd) == 1) or 'CACHE' not in cmakeSetList: 413 # The worst behaved, we have a pure "set". we shouldn't rely on 414 # CMAKE_CXX_STANDARD, since the package overrides it unconditionally. Thus 415 # we leave the std flag in the compiler flags. 416 self.logPrint('removeStdCxxFlag: CMake Package {pkg} had an overriding \'set\' command in their CMakeLists.txt:\n\t{cmd}\nLeaving std flags in'.format(pkg=self.name,cmd=line.strip()),indent=1) 417 return flags 418 self.logPrint('removeStdCxxFlag: CMake Package {pkg} did NOT have an overriding \'set\' command in their CMakeLists.txt:\n\t{cmd}\nRemoving std flags'.format(pkg=self.name,cmd=line.strip()),indent=1) 419 # CACHE was found in the set command, meaning we can override it from the 420 # command line. So we continue on to remove the std flags. 421 break 422 stdFlags = ('-std=c++','-std=gnu++') 423 return [f for f in flags if not f.startswith(stdFlags)] 424 return flags 425 426 def updatePackageCFlags(self,flags): 427 '''To turn off various warnings or errors the compilers may produce with external packages, remove or add appropriate compiler flags''' 428 outflags = self.removeVisibilityFlag(flags.split()) 429 outflags = self.removeWarningFlags(outflags) 430 outflags = self.removeCoverageFlag(outflags) 431 if self.requirekandr: 432 outflags += self.setCompilers.KandRFlags 433 with self.Language('C'): 434 if self.brokengnu23 and config.setCompilers.Configure.isGcc150plus(self.getCompiler(), self.log): 435 outflags.append('-std=gnu17') 436 return ' '.join(outflags) 437 438 def updatePackageFFlags(self,flags): 439 outflags = self.removeVisibilityFlag(flags.split()) 440 outflags = self.removeWarningFlags(outflags) 441 outflags = self.removeCoverageFlag(outflags) 442 with self.Language('FC'): 443 if config.setCompilers.Configure.isNAG(self.getLinker(), self.log): 444 outflags.extend(['-mismatch','-dusty','-dcfuns']) 445 if config.setCompilers.Configure.isGfortran100plus(self.getCompiler(), self.log): 446 outflags.append('-fallow-argument-mismatch') 447 return ' '.join(outflags) 448 449 def updatePackageCxxFlags(self,flags): 450 outflags = self.removeVisibilityFlag(flags.split()) 451 outflags = self.removeWarningFlags(outflags) 452 outflags = self.removeCoverageFlag(outflags) 453 outflags = self.removeStdCxxFlag(outflags) 454 return ' '.join(outflags) 455 456 def updatePackageCUDAFlags(self, flags): 457 outflags = self.removeVisibilityFlag(flags, pair_prefix='-Xcompiler') 458 outflags = self.removeCoverageFlag(outflags, pair_prefix='-Xcompiler') 459 return ' '.join(outflags) 460 461 def getDefaultLanguage(self): 462 '''The language in which to run tests''' 463 if hasattr(self, 'forceLanguage'): 464 return self.forceLanguage 465 if hasattr(self, 'languageProvider'): 466 if hasattr(self.languageProvider, 'defaultLanguage'): 467 return self.languageProvider.defaultLanguage 468 elif hasattr(self.languageProvider, 'clanguage'): 469 return self.languageProvider.clanguage 470 return self._defaultLanguage 471 def setDefaultLanguage(self, defaultLanguage): 472 '''The language in which to run tests''' 473 if hasattr(self, 'languageProvider'): 474 del self.languageProvider 475 self._defaultLanguage = defaultLanguage 476 return 477 defaultLanguage = property(getDefaultLanguage, setDefaultLanguage, doc = 'The language in which to run tests') 478 479 def getArch(self): 480 '''The architecture identifier''' 481 if hasattr(self, 'archProvider'): 482 if hasattr(self.archProvider, 'arch'): 483 return self.archProvider.arch 484 return self._arch 485 def setArch(self, arch): 486 '''The architecture identifier''' 487 self._arch = arch 488 return 489 arch = property(getArch, setArch, doc = 'The architecture identifier') 490 491 # This construct should be removed and just have getInstallDir() handle the process 492 def getDefaultInstallDir(self): 493 '''The installation directory of the library''' 494 if hasattr(self, 'installDirProvider'): 495 if hasattr(self.installDirProvider, 'dir'): 496 return self.installDirProvider.dir 497 return self._defaultInstallDir 498 def setDefaultInstallDir(self, defaultInstallDir): 499 '''The installation directory of the library''' 500 self._defaultInstallDir = defaultInstallDir 501 return 502 defaultInstallDir = property(getDefaultInstallDir, setDefaultInstallDir, doc = 'The installation directory of the library') 503 504 def getExternalPackagesDir(self): 505 '''The directory for downloaded packages''' 506 if hasattr(self, 'externalPackagesDirProvider'): 507 if hasattr(self.externalPackagesDirProvider, 'dir'): 508 return self.externalPackagesDirProvider.dir 509 elif not self.framework.externalPackagesDir is None: 510 return os.path.abspath('externalpackages') 511 return self._externalPackagesDir 512 def setExternalPackagesDir(self, externalPackagesDir): 513 '''The directory for downloaded packages''' 514 self._externalPackagesDir = externalPackagesDir 515 return 516 externalPackagesDir = property(getExternalPackagesDir, setExternalPackagesDir, doc = 'The directory for downloaded packages') 517 518 def getSearchDirectories(self): 519 '''By default, do not search any particular directories, but try compiler default paths''' 520 return [''] 521 522 def getInstallDir(self): 523 '''Calls self.Install() to install the package''' 524 '''Returns --prefix (or the value computed from --package-prefix-hash) if provided otherwise $PETSC_DIR/$PETSC_ARCH''' 525 '''Special case for packages such as sowing that are have self.publicInstall == 0 it always locates them in $PETSC_DIR/$PETSC_ARCH''' 526 '''Special case if --package-prefix-hash then even self.publicInstall == 0 are installed in the prefix location''' 527 self.confDir = self.installDirProvider.confDir # private install location; $PETSC_DIR/$PETSC_ARCH for PETSc 528 self.packageDir = self.getDir() 529 self.setupDownload() 530 if not self.packageDir: self.packageDir = self.downLoad() 531 self.updateGitDir() 532 self.updatehgDir() 533 self.applyPatches() 534 if (self.publicInstall or 'package-prefix-hash' in self.argDB) and not ('package-prefix-hash' in self.argDB and (hasattr(self,'postProcess') or self.builtafterpetsc)): 535 self.installDir = self.defaultInstallDir 536 else: 537 self.installDir = self.confDir 538 if self.PrefixWriteCheck and self.publicInstall and not 'package-prefix-hash' in self.argDB and self.installDirProvider.installSudo: 539 if self.installDirProvider.dir in ['/usr','/usr/local']: prefixdir = os.path.join(self.installDirProvider.dir,'petsc') 540 else: prefixdir = self.installDirProvider.dir 541 msg='''\ 542Specified prefix-dir: %s is read-only! "%s" cannot install at this location! Suggest: 543 sudo mkdir %s 544 sudo chown $USER %s 545Now rerun configure''' % (self.installDirProvider.dir, '--download-'+self.package, prefixdir, prefixdir) 546 raise RuntimeError(msg) 547 self.includeDir = os.path.join(self.installDir, 'include') 548 self.libDir = os.path.join(self.installDir, self.libDirs[0]) 549 installDir = self.Install() 550 if not installDir: 551 raise RuntimeError(self.package+' forgot to return the install directory from the method Install()\n') 552 return os.path.abspath(installDir) 553 554 def getChecksum(self,source, chunkSize = 1024*1024): 555 '''Return the checksum for a given file, which may also be specified by its filename 556 - The chunkSize argument specifies the size of blocks read from the file''' 557 if hasattr(source, 'close'): 558 f = source 559 else: 560 f = open(source, 'rb') 561 m = checksum_algo() 562 size = chunkSize 563 buf = f.read(size) 564 while buf: 565 m.update(buf) 566 buf = f.read(size) 567 f.close() 568 return m.hexdigest() 569 570 def generateLibList(self, directory, liblist = None): 571 '''Generates full path list of libraries from self.liblist''' 572 if liblist == None: liblist = self.liblist 573 if [] in liblist: liblist.remove([]) # process null list later 574 if liblist == []: # most packages don't have a liblist - so return an empty list 575 return [[]] 576 alllibs = [] 577 if not directory: # compiler default path - so also check compiler default libs. 578 alllibs.insert(0,[]) 579 elif directory in self.libraries.sysDirs: 580 self.logPrint('generateLibList: systemDir detected! skipping: '+str(directory)) 581 directory = '' 582 for libSet in liblist: 583 libs = [] 584 use_L = 0 585 for library in libSet: 586 # if the library name starts with 'lib' or uses '-lfoo' then add in -Lpath. Otherwise - add the fullpath 587 if library.startswith('-l') or library.startswith('lib'): 588 libs.append(library) 589 use_L = 1 590 else: 591 libs.append(os.path.join(directory, library)) 592 if use_L and directory: 593 libs.insert(0,'-L'+directory) 594 libs.extend(self.extraLib) 595 alllibs.append(libs) 596 return alllibs 597 598 def getIncludeDirs(self, prefix, includeDirs): 599 if not isinstance(includeDirs, list): 600 includeDirs = [includeDirs] 601 iDirs = [inc for inc in includeDirs if os.path.isabs(inc)] + [os.path.join(prefix, inc) for inc in includeDirs if not os.path.isabs(inc)] 602 return [inc for inc in iDirs if os.path.exists(inc)] 603 604 def addToArgs(self,args,key,value): 605 found = 0 606 for i in range(0,len(args)): 607 if args[i].startswith(key+'='): 608 args[i] = args[i][0:-1] + ' '+ value +'"' 609 found = 1 610 if not found: args.append(key+'="'+value+'"') 611 612 def generateGuesses(self): 613 d = self.checkDownload() 614 if d: 615 if not self.liblist or not self.liblist[0] or self.builtafterpetsc : 616 yield('Download '+self.PACKAGE, d, [], self.getIncludeDirs(d, self.includedir)) 617 for libdir in self.libDirs: 618 libdirpath = os.path.join(d, libdir) 619 if not os.path.isdir(libdirpath): 620 self.logPrint(self.PACKAGE+': Downloaded DirPath not found.. skipping: '+libdirpath) 621 continue 622 for l in self.generateLibList(libdirpath): 623 yield('Download '+self.PACKAGE, d, l, self.getIncludeDirs(d, self.includedir)) 624 raise RuntimeError('Downloaded '+self.package+' could not be used. Please check install in '+d+'\n') 625 626 if 'with-'+self.package+'-pkg-config' in self.argDB: 627 if self.argDB['with-'+self.package+'-pkg-config']: 628 # user provided path to look for pkg info 629 if 'PKG_CONFIG_PATH' in os.environ: path = os.environ['PKG_CONFIG_PATH'] 630 else: path = None 631 os.environ['PKG_CONFIG_PATH'] = self.argDB['with-'+self.package+'-pkg-config'] 632 633 l,err,ret = config.base.Configure.executeShellCommand('pkg-config '+self.pkgname+' --libs', timeout=60, log = self.log) 634 l = l.strip() 635 i,err,ret = config.base.Configure.executeShellCommand('pkg-config '+self.pkgname+' --cflags', timeout=60, log = self.log) 636 i = i.strip() 637 if self.argDB['with-'+self.package+'-pkg-config']: 638 if path: os.environ['PKG_CONFIG_PATH'] = path 639 else: os.environ['PKG_CONFIG_PATH'] = '' 640 yield('pkg-config located libraries and includes '+self.PACKAGE, None, l.split(), i) 641 raise RuntimeError('pkg-config could not locate correct includes and libraries for '+self.package) 642 643 if 'with-'+self.package+'-dir' in self.argDB: 644 d = self.argDB['with-'+self.package+'-dir'] 645 # error if package-dir is in externalpackages 646 if os.path.realpath(d).find(os.path.realpath(self.externalPackagesDir)) >=0: 647 fakeExternalPackagesDir = d.replace(os.path.realpath(d).replace(os.path.realpath(self.externalPackagesDir),''),'') 648 raise RuntimeError('Bad option: '+'--with-'+self.package+'-dir='+self.argDB['with-'+self.package+'-dir']+'\n'+ 649 fakeExternalPackagesDir+' is reserved for --download-package scratch space. \n'+ 650 'Do not install software in this location nor use software in this directory.') 651 652 if not self.liblist or not self.liblist[0]: 653 yield('User specified root directory '+self.PACKAGE, d, [], self.getIncludeDirs(d, self.includedir)) 654 655 for libdir in self.libDirs: 656 libdirpath = os.path.join(d, libdir) 657 if not os.path.isdir(libdirpath): 658 self.logPrint(self.PACKAGE+': UserSpecified DirPath not found.. skipping: '+libdirpath) 659 continue 660 for l in self.generateLibList(libdirpath): 661 yield('User specified root directory '+self.PACKAGE, d, l, self.getIncludeDirs(d, self.includedir)) 662 663 if 'with-'+self.package+'-include' in self.argDB: 664 raise RuntimeError('Do not set --with-'+self.package+'-include if you set --with-'+self.package+'-dir') 665 if 'with-'+self.package+'-lib' in self.argDB: 666 raise RuntimeError('Do not set --with-'+self.package+'-lib if you set --with-'+self.package+'-dir') 667 raise RuntimeError('--with-'+self.package+'-dir='+self.argDB['with-'+self.package+'-dir']+' did not work') 668 669 if 'with-'+self.package+'-include' in self.argDB and not 'with-'+self.package+'-lib' in self.argDB: 670 if self.liblist and self.liblist[0]: 671 raise RuntimeError('If you provide --with-'+self.package+'-include you must also supply with-'+self.package+'-lib\n') 672 if 'with-'+self.package+'-lib' in self.argDB and not 'with-'+self.package+'-include' in self.argDB: 673 if self.includes: 674 raise RuntimeError('If you provide --with-'+self.package+'-lib you must also supply with-'+self.package+'-include\n') 675 if 'with-'+self.package+'-include-dir' in self.argDB: 676 raise RuntimeError('Use --with-'+self.package+'-include; not --with-'+self.package+'-include-dir') 677 678 if 'with-'+self.package+'-include' in self.argDB or 'with-'+self.package+'-lib' in self.argDB: 679 if self.liblist and self.liblist[0]: 680 libs = self.argDB['with-'+self.package+'-lib'] 681 slibs = str(self.argDB['with-'+self.package+'-lib']) 682 else: 683 libs = [] 684 slibs = 'NoneNeeded' 685 inc = [] 686 d = None 687 if self.includes: 688 inc = self.argDB['with-'+self.package+'-include'] 689 # hope that package root is one level above first include directory specified 690 if inc: 691 d = os.path.dirname(inc[0]) 692 693 if not isinstance(inc, list): inc = inc.split(' ') 694 if not isinstance(libs, list): libs = libs.split(' ') 695 inc = [os.path.abspath(i) for i in inc] 696 yield('User specified '+self.PACKAGE+' libraries', d, libs, inc) 697 msg = '--with-'+self.package+'-lib='+slibs 698 if self.includes: 699 msg += ' and \n'+'--with-'+self.package+'-include='+str(self.argDB['with-'+self.package+'-include']) 700 msg += ' did not work' 701 raise RuntimeError(msg) 702 703 for d in self.getSearchDirectories(): 704 if d: 705 if not os.path.isdir(d): 706 self.logPrint(self.PACKAGE+': SearchDir DirPath not found.. skipping: '+d) 707 continue 708 includedir = self.getIncludeDirs(d, self.includedir) 709 for libdir in self.libDirs: 710 libdirpath = os.path.join(d, libdir) 711 if not os.path.isdir(libdirpath): 712 self.logPrint(self.PACKAGE+': DirPath not found.. skipping: '+libdirpath) 713 continue 714 for l in self.generateLibList(libdirpath): 715 yield('Package specific search directory '+self.PACKAGE, d, l, includedir) 716 else: 717 includedir = '' 718 for l in self.generateLibList(d): # d = '' i.e search compiler libraries 719 yield('Compiler specific search '+self.PACKAGE, d, l, includedir) 720 721 if not self.lookforbydefault or ('with-'+self.package in self.framework.clArgDB and self.argDB['with-'+self.package]): 722 mesg = 'Unable to find '+self.package+' in default locations!\nPerhaps you can specify with --with-'+self.package+'-dir=<directory>\nIf you do not want '+self.name+', then give --with-'+self.package+'=0' 723 if self.download: mesg +='\nYou might also consider using --download-'+self.package+' instead' 724 if self.alternativedownload: mesg +='\nYou might also consider using --download-'+self.alternativedownload+' instead' 725 raise RuntimeError(mesg) 726 727 def checkDownload(self): 728 '''Check if we should download the package, returning the install directory or the empty string indicating installation''' 729 if not self.download: 730 return '' 731 if self.argDB['with-batch'] and self.argDB['download-'+self.package] and not (hasattr(self.setCompilers,'cross_cc') or self.installwithbatch): raise RuntimeError('--download-'+self.name+' cannot be used on batch systems. You must either\n\ 732 1) load the appropriate module on your system and use --with-'+self.name+' or \n\ 733 2) locate its installation on your machine or install it yourself and use --with-'+self.name+'-dir=path\n') 734 735 if self.argDB['download-'+self.package] and 'package-prefix-hash' in self.argDB and self.argDB['package-prefix-hash'] == 'reuse' and not hasattr(self,'postProcess') and not self.builtafterpetsc: # package already built in prefix hash location so reuse it 736 self.installDir = self.defaultInstallDir 737 return self.defaultInstallDir 738 if self.argDB['download-'+self.package]: 739 if self.license and not os.path.isfile('.'+self.package+'_license'): 740 self.logClear() 741 self.logPrint("**************************************************************************************************", debugSection='screen') 742 self.logPrint('Please register to use '+self.downloadname+' at '+self.license, debugSection='screen') 743 self.logPrint("**************************************************************************************************\n", debugSection='screen') 744 fd = open('.'+self.package+'_license','w') 745 fd.close() 746 return self.getInstallDir() 747 else: 748 # check if download option is set for MPI dependent packages - if so flag an error. 749 mesg='' 750 if hasattr(self,'mpi') and self.mpi in self.deps: 751 if 'download-mpich' in self.argDB and self.argDB['download-mpich'] or 'download-openmpi' in self.argDB and self.argDB['download-openmpi']: 752 mesg+='Cannot use --download-mpich or --download-openmpi when not using --download-%s. Perhaps you want --download-%s.\n' % (self.package,self.package) 753 if mesg: 754 raise RuntimeError(mesg) 755 return '' 756 757 def installNeeded(self, mkfile): 758 makefile = os.path.join(self.packageDir, mkfile) 759 makefileSaved = os.path.join(self.confDir, 'lib','petsc','conf','pkg.conf.'+self.package) 760 gcommfileSaved = os.path.join(self.confDir,'lib','petsc','conf', 'pkg.gitcommit.'+self.package) 761 if self.downloaded: 762 self.log.write(self.PACKAGE+' was just downloaded, forcing a rebuild because cannot determine if package has changed\n') 763 return 1 764 if not os.path.isfile(makefileSaved) or not (self.getChecksum(makefileSaved) == self.getChecksum(makefile)): 765 self.log.write('Have to rebuild '+self.PACKAGE+', '+makefile+' != '+makefileSaved+'\n') 766 return 1 767 else: 768 self.log.write('Makefile '+makefileSaved+' has correct checksum\n') 769 if self.gcommfile and os.path.isfile(self.gcommfile): 770 if not os.path.isfile(gcommfileSaved) or not (self.getChecksum(gcommfileSaved) == self.getChecksum(self.gcommfile)): 771 self.log.write('Have to rebuild '+self.PACKAGE+', '+self.gcommfile+' != '+gcommfileSaved+'\n') 772 return 1 773 else: 774 self.log.write('Commit file '+gcommfileSaved+' has correct checksum\n') 775 self.log.write('Do not need to rebuild '+self.PACKAGE+'\n') 776 return 0 777 778 def postInstall(self, output, mkfile): 779 '''Dump package build log into configure.log - also copy package config to prevent unnecessary rebuild''' 780 self.log.write('********Output of running make on '+self.PACKAGE+' follows *******\n') 781 self.log.write(output) 782 self.log.write('********End of Output of running make on '+self.PACKAGE+' *******\n') 783 subconfDir = os.path.join(self.confDir, 'lib', 'petsc', 'conf') 784 if not os.path.isdir(subconfDir): 785 os.makedirs(subconfDir) 786 makefile = os.path.join(self.packageDir, mkfile) 787 makefileSaved = os.path.join(subconfDir, 'pkg.conf.'+self.package) 788 gcommfileSaved = os.path.join(subconfDir, 'pkg.gitcommit.'+self.package) 789 import shutil 790 shutil.copyfile(makefile,makefileSaved) 791 if self.gcommfile and os.path.exists(self.gcommfile): 792 shutil.copyfile(self.gcommfile,gcommfileSaved) 793 self.framework.actions.addArgument(self.PACKAGE, 'Install', 'Installed '+self.PACKAGE+' into '+self.installDir) 794 795 def matchExcludeDir(self,dir): 796 '''Check is the dir matches something in the excluded directory list''' 797 for exdir in self.excludedDirs: 798 if dir.lower().startswith(exdir.lower()): 799 return 1 800 return 0 801 802 def gitPreReqCheck(self): 803 '''Some packages may need addition prerequisites if the package comes from a git repository''' 804 return 1 805 806 def applyPatches(self): 807 '''Patch the package's files with needed (likely portability) corrections''' 808 809 def updatehgDir(self): 810 '''Checkout the correct hash''' 811 if hasattr(self.sourceControl, 'hg') and (self.packageDir == os.path.join(self.externalPackagesDir,'hg.'+self.package)): 812 if hasattr(self,'hghash'): 813 config.base.Configure.executeShellCommand([self.sourceControl.hg, 'update', '-c', self.hghash], cwd=self.packageDir, log = self.log) 814 815 def updateGitDir(self): 816 '''Checkout the correct gitcommit for the gitdir - and update pkg.gitcommit''' 817 if hasattr(self.sourceControl, 'git') and (self.packageDir == os.path.join(self.externalPackagesDir,'git.'+self.package)): 818 if not (hasattr(self, 'gitcommit') and self.gitcommit): 819 if hasattr(self, 'download_git'): 820 self.gitcommit = 'HEAD' 821 else: 822 raise RuntimeError('Trying to update '+self.package+' package source directory '+self.packageDir+' which is supposed to be a git repository, but no gitcommit is set for this package.\n\ 823Try to delete '+self.packageDir+' and rerun configure.\n\ 824If the problem persists, please send your configure.log to petsc-maint@mcs.anl.gov') 825 # verify that packageDir is actually a git clone 826 if not os.path.isdir(os.path.join(self.packageDir,'.git')): 827 raise RuntimeError(self.packageDir +': is not a git repository! '+os.path.join(self.packageDir,'.git')+' not found!') 828 gitdir,err,ret = config.base.Configure.executeShellCommand([self.sourceControl.git, 'rev-parse','--git-dir'], cwd=self.packageDir, log = self.log) 829 if gitdir != '.git': 830 raise RuntimeError(self.packageDir +': is not a git repository! "git rev-parse --gitdir" gives: '+gitdir) 831 832 prefetch = 0 833 if self.gitcommit.startswith('origin/'): 834 prefetch = self.gitcommit.replace('origin/','') 835 else: 836 try: 837 config.base.Configure.executeShellCommand([self.sourceControl.git, 'cat-file', '-e', self.gitcommit+'^{commit}'], cwd=self.packageDir, log = self.log) 838 gitcommit_hash,err,ret = config.base.Configure.executeShellCommand([self.sourceControl.git, 'rev-parse', self.gitcommit], cwd=self.packageDir, log = self.log) 839 if self.gitcommit != 'HEAD': 840 # check if origin/branch exists - if so warn user that we are using the remote branch 841 try: 842 rbranch = 'origin/'+self.gitcommit 843 config.base.Configure.executeShellCommand([self.sourceControl.git, 'cat-file', '-e', rbranch+'^{commit}'], cwd=self.packageDir, log = self.log) 844 gitcommit_hash,err,ret = config.base.Configure.executeShellCommand([self.sourceControl.git, 'rev-parse', self.gitcommit], cwd=self.packageDir, log = self.log) 845 self.logPrintWarning('Branch "%s" is specified, however remote branch "%s" also exists! Proceeding with using the remote branch. \ 846To use the local branch (manually checkout local branch and) - rerun configure with option --download-%s-commit=HEAD)' % (self.gitcommit, rbranch, self.name)) 847 prefetch = self.gitcommit 848 except: 849 pass 850 except: 851 prefetch = self.gitcommit 852 if prefetch: 853 fetched = 0 854 self.logPrintBox('Attempting a "git fetch" commit/branch/tag: %s from Git repositor%s: %s' % (str(self.gitcommit), str('ies' if len(self.retriever.git_urls) > 1 else 'y'), str(self.retriever.git_urls))) 855 for git_url in self.retriever.git_urls: 856 try: 857 config.base.Configure.executeShellCommand([self.sourceControl.git, 'fetch', '--tags', git_url, prefetch], cwd=self.packageDir, log = self.log) 858 gitcommit_hash,err,ret = config.base.Configure.executeShellCommand([self.sourceControl.git, 'rev-parse', 'FETCH_HEAD'], cwd=self.packageDir, log = self.log) 859 fetched = 1 860 break 861 except: 862 continue 863 if not fetched: 864 raise RuntimeError('The above "git fetch" failed! Check if the specified "commit/branch/tag" is present in the remote git repo.\n\ 865To use currently downloaded (local) git snapshot - use: --download-'+self.package+'-commit=HEAD') 866 if self.gitcommit != 'HEAD': 867 try: 868 config.base.Configure.executeShellCommand([self.sourceControl.git, '-c', 'user.name=petsc-configure', '-c', 'user.email=petsc@configure', 'stash'], cwd=self.packageDir, log = self.log) 869 config.base.Configure.executeShellCommand([self.sourceControl.git, 'clean', '-f', '-d', '-x'], cwd=self.packageDir, log = self.log) 870 except RuntimeError as e: 871 if str(e).find("Unknown option: -c") >= 0: 872 self.logPrintWarning('Unable to "git stash". Likely due to antique Git version (<1.8). Proceeding without stashing!') 873 else: 874 raise RuntimeError('Unable to run git stash/clean in repository: '+self.packageDir+'.\nPerhaps its a git error!') 875 try: 876 if self.gitsubmodules: 877 config.base.Configure.executeShellCommand([self.sourceControl.git, 'checkout', '--recurse-submodules', '-f', gitcommit_hash], cwd=self.packageDir, log = self.log) 878 else: 879 config.base.Configure.executeShellCommand([self.sourceControl.git, 'checkout', '-f', gitcommit_hash], cwd=self.packageDir, log = self.log) 880 except: 881 raise RuntimeError('Unable to checkout commit: '+self.gitcommit+' in repository: '+self.packageDir+'.\nPerhaps its a git error!') 882 # write a commit-tag file 883 self.gcommfile = os.path.join(self.packageDir,'pkg.gitcommit') 884 with open(self.gcommfile,'w') as fd: 885 fd.write(gitcommit_hash) 886 return 887 888 def getDir(self): 889 '''Find the directory containing the package''' 890 packages = self.externalPackagesDir 891 if not os.path.isdir(packages): 892 os.makedirs(packages) 893 self.framework.actions.addArgument('Framework', 'Directory creation', 'Created the external packages directory: '+packages) 894 Dir = [] 895 pkgdirs = os.listdir(packages) 896 gitpkg = 'git.'+self.package 897 hgpkg = 'hg.'+self.package 898 self.logPrint('Looking for '+self.PACKAGE+' at '+gitpkg+ ', '+hgpkg+' or a directory starting with '+str(self.downloaddirnames)) 899 if hasattr(self.sourceControl, 'git') and gitpkg in pkgdirs: 900 Dir.append(gitpkg) 901 if hasattr(self.sourceControl, 'hg') and hgpkg in pkgdirs: 902 Dir.append(hgpkg) 903 for d in pkgdirs: 904 for j in self.downloaddirnames: 905 if d.lower().startswith(j.lower()) and os.path.isdir(os.path.join(packages, d)) and not self.matchExcludeDir(d): 906 Dir.append(d) 907 908 if len(Dir) > 1: 909 raise RuntimeError('Located multiple directories with package '+self.package+' '+str(Dir)+'\nDelete directory '+self.arch+' and rerun ./configure') 910 911 if Dir: 912 self.logPrint('Found a copy of '+self.PACKAGE+' in '+str(Dir[0])) 913 return os.path.join(packages, Dir[0]) 914 else: 915 self.logPrint('Could not locate an existing copy of '+self.PACKAGE+':') 916 self.logPrint(' '+str(pkgdirs)) 917 return 918 919 def setupDownload(self): 920 import retrieval 921 self.retriever = retrieval.Retriever(self.sourceControl, argDB = self.argDB) 922 self.retriever.setup() 923 self.retriever.ver = self.petscdir.version 924 self.retriever.setupURLs(self.package,self.download,self.gitsubmodules,self.gitPreReqCheck()) 925 926 def downLoad(self): 927 '''Downloads a package; using hg or ftp; opens it in the with-packages-build-dir directory''' 928 retriever = self.retriever 929 retriever.saveLog() 930 self.logPrint('Downloading '+self.name) 931 # now attempt to download each url until any one succeeds. 932 err ='' 933 for proto, url in retriever.generateURLs(): 934 self.logPrintBox('Trying to download '+url+' for '+self.PACKAGE) 935 try: 936 retriever.genericRetrieve(proto, url, self.externalPackagesDir) 937 self.logWrite(retriever.restoreLog()) 938 retriever.saveLog() 939 pkgdir = self.getDir() 940 if not pkgdir: 941 raise RuntimeError('Could not locate downloaded package ' +self.PACKAGE +' in '+self.externalPackagesDir) 942 self.framework.actions.addArgument(self.PACKAGE, 'Download', 'Downloaded '+self.PACKAGE+' into '+pkgdir) 943 retriever.restoreLog() 944 self.downloaded = 1 945 return pkgdir 946 except RuntimeError as e: 947 self.logPrint('ERROR: '+str(e)) 948 err += str(e) 949 self.logWrite(retriever.restoreLog()) 950 raise RuntimeError('Error during download/extract/detection of '+self.PACKAGE+':\n'+err) 951 952 def Install(self): 953 raise RuntimeError('No custom installation implemented for package '+self.package+'\n') 954 955 def checkInclude(self, incl, hfiles, otherIncludes = [], timeout = 600.0): 956 self.headers.pushLanguage(self.buildLanguages[0]) # default is to use the first language in checking 957 self.headers.saveLog() 958 ret = self.executeTest(self.headers.checkInclude, [incl, hfiles], {'otherIncludes' : otherIncludes, 'macro' : None, 'timeout': timeout}) 959 self.logWrite(self.headers.restoreLog()) 960 self.headers.popLanguage() 961 return ret 962 963 def checkMacros(self, timeout = 600.0): 964 if not len(self.macros): 965 return 966 self.headers.pushLanguage(self.buildLanguages[0]) # default is to use the first language in checking 967 self.logPrint('Checking for macros ' + str(self.macros) + ' in ' + str(self.includes)) 968 self.headers.saveLog() 969 for macro in self.macros: 970 self.executeTest(self.headers.checkInclude, [self.include, self.includes], {'macro' : macro, 'timeout' : timeout}) 971 self.logWrite(self.headers.restoreLog()) 972 self.headers.popLanguage() 973 return 974 975 def checkPackageLink(self, includes, body, cleanup = 1, codeBegin = None, codeEnd = None, shared = 0): 976 flagsArg = self.getPreprocessorFlagsArg() 977 oldFlags = getattr(self.compilers, flagsArg) 978 oldLibs = self.compilers.LIBS 979 setattr(self.compilers, flagsArg, oldFlags+' '+self.headers.toString(self.include)) 980 self.compilers.LIBS = self.libraries.toString(self.lib)+' '+self.compilers.LIBS 981 if 'FC' in self.buildLanguages: 982 self.compilers.LIBS = ' '.join([self.libraries.getLibArgument(lib) for lib in self.compilers.flibs])+' '+self.setCompilers.LIBS 983 if 'Cxx' in self.buildLanguages: 984 self.compilers.LIBS = ' '.join([self.libraries.getLibArgument(lib) for lib in self.compilers.cxxlibs])+' '+self.setCompilers.LIBS 985 result = self.checkLink(includes, body, cleanup, codeBegin, codeEnd, shared) 986 setattr(self.compilers, flagsArg,oldFlags) 987 self.compilers.LIBS = oldLibs 988 return result 989 990 @staticmethod 991 def sortPackageDependencies(startnode): 992 '''Create a dependency graph for all deps, and sort them''' 993 import graph 994 depGraph = graph.DirectedGraph() 995 996 def addGraph(Graph,node,nodesAdded=[]): 997 '''Recursively traverse the dependency graph - and add them as graph edges.''' 998 if not hasattr(node,'deps'): return 999 Graph.addVertex(node) 1000 nodesAdded.append(node) 1001 deps = list(node.deps) 1002 for odep in node.odeps: 1003 if odep.found: deps.append(odep) 1004 if deps: 1005 Graph.addEdges(node,outputs=deps) 1006 for dep in deps: 1007 if dep not in nodesAdded: 1008 addGraph(Graph,dep,nodesAdded) 1009 return 1010 1011 addGraph(depGraph,startnode) 1012 return [sortnode for sortnode in graph.DirectedGraph.topologicalSort(depGraph,start=startnode)] 1013 1014 def checkDependencies(self): 1015 '''Loop over declared dependencies of package and error if any are missing''' 1016 for package in self.deps: 1017 if not hasattr(package, 'found'): 1018 raise RuntimeError('Package '+package.name+' does not have found attribute!') 1019 if not package.found: 1020 if self.argDB['with-'+package.package] == 1: 1021 raise RuntimeError('Package '+package.PACKAGE+' needed by '+self.name+' failed to configure.\nMail configure.log to petsc-maint@mcs.anl.gov.') 1022 else: 1023 str = '' 1024 if package.download: str = ' or --download-'+package.package 1025 raise RuntimeError('Did not find package '+package.PACKAGE+' needed by '+self.name+'.\nEnable the package using --with-'+package.package+str) 1026 for package in self.odeps: 1027 if not hasattr(package, 'found'): 1028 raise RuntimeError('Package '+package.name+' does not have found attribute!') 1029 if not package.found: 1030 if 'with-'+package.package in self.framework.clArgDB and self.framework.clArgDB['with-'+package.package] == 1: 1031 raise RuntimeError('Package '+package.PACKAGE+' needed by '+self.name+' failed to configure.\nMail configure.log to petsc-maint@mcs.anl.gov.') 1032 1033 dpkgs = Package.sortPackageDependencies(self) 1034 dpkgs.remove(self) 1035 for package in dpkgs: 1036 if hasattr(package, 'lib'): self.dlib += package.lib 1037 if hasattr(package, 'include'): self.dinclude += package.include 1038 return 1039 1040 def addPost(self, dir, rules): 1041 '''Adds make rules that are run after PETSc is built 1042 1043 Without a prefix the rules are run at the end of make all, otherwise they are run at the end of make install 1044 ''' 1045 steps = ['@echo "=========================================="',\ 1046 '@echo "Building/installing ' + self.name + '. This may take several minutes"',\ 1047 '@${RM} ${PETSC_DIR}/${PETSC_ARCH}/lib/petsc/conf/' + self.package + '.build.log'] 1048 if not isinstance(rules, list): rules = [rules] 1049 for rule in rules: 1050 steps.append('$(shell [ "$(V)" != "1" ] && echo @)cd ' + dir + ' && ' + rule + ' >> ${PETSC_DIR}/${PETSC_ARCH}/lib/petsc/conf/' + self.package + '.build.log 2>&1 ||\ 1051 (echo "***** Error building/installing ' + self.name + '. Check ${PETSC_DIR}/${PETSC_ARCH}/lib/petsc/conf/' + self.package + '.build.log" && exit 1)') 1052 self.addMakeRule(self.package + 'build', '', steps) 1053 if self.argDB['prefix'] and not 'package-prefix-hash' in self.argDB: 1054 self.framework.postinstalls.append(self.package + 'build') 1055 else: 1056 self.framework.postbuilds.append(self.package + 'build') 1057 self.addDefine('HAVE_' + self.PACKAGE.replace('-','_'), 1) 1058 if self.useddirectly: 1059 if not hasattr(self.framework, 'packages'): 1060 self.framework.packages = [] 1061 self.framework.packages.append(self) 1062 1063 def addMakeCheck(self, dir, rule): 1064 '''Adds a small make check for the project''' 1065 self.addMakeRule(self.package + 'check','', \ 1066 ['@echo "*** Checking ' + self.name + ' ***"',\ 1067 '$(shell [ "$(V)" != "1" ] && echo @)cd ' + dir + ' && ' + rule + ' || (echo "***** Error checking ' + self.name + ' ******" && exit 1)']) 1068 self.framework.postchecks.append(self.package + 'check') 1069 1070 def addTest(self, dir, rule): 1071 '''Adds a large make test for the project''' 1072 self.addMakeRule(self.package + 'test','', \ 1073 ['@echo "*** Testing ' + self.name + ' ***"',\ 1074 '@${RM} ${PETSC_DIR}/${PETSC_ARCH}/lib/petsc/conf/' + self.package + '.errorflg',\ 1075 '$(shell [ "$(V)" != "1" ] && echo @)cd ' + dir + ' && ' + rule + ' || (echo "***** Error testing ' + self.name + ' ******" && exit 1)']) 1076 1077 def configureLibrary(self): 1078 '''Find an installation and check if it can work with PETSc''' 1079 self.log.write('==================================================================================\n') 1080 self.logPrint('Checking for a functional '+self.name) 1081 foundLibrary = 0 1082 foundHeader = 0 1083 1084 for location, directory, lib, incl in self.generateGuesses(): 1085 # directory is not used in the search, it is used only in logging messages about where the 1086 # searching is taking place. It has to already be embedded inside the lib argument 1087 if self.builtafterpetsc: 1088 self.found = 1 1089 return 1090 1091 if directory and not os.path.isdir(directory): 1092 self.logPrint('Directory does not exist: %s (while checking "%s" for "%r")' % (directory,location,lib)) 1093 continue 1094 if lib == '': lib = [] 1095 elif not isinstance(lib, list): lib = [lib] 1096 if incl == '': incl = [] 1097 elif not isinstance(incl, list): incl = [incl] 1098 testedincl = list(incl) 1099 # weed out duplicates when adding fincs 1100 for loc in self.compilers.fincs: 1101 if not loc in incl: 1102 incl.append(loc) 1103 if self.functions: 1104 self.logPrint('Checking for library in '+location+': '+str(lib)) 1105 if directory: 1106 self.logPrint('Contents of '+directory+': '+str(os.listdir(directory))) 1107 for libdir in self.libDirs: 1108 flibdir = os.path.join(directory, libdir) 1109 if os.path.isdir(flibdir): 1110 self.logPrint('Contents '+flibdir+': '+str(os.listdir(flibdir))) 1111 else: 1112 self.logPrint('Not checking for library in '+location+': '+str(lib)+' because no functions given to check for') 1113 1114 otherlibs = self.dlib 1115 if 'FC' in self.buildLanguages: 1116 otherlibs.extend(self.compilers.flibs) 1117 if 'Cxx' in self.buildLanguages: 1118 otherlibs.extend(self.compilers.cxxlibs) 1119 self.libraries.saveLog() 1120 if self.executeTest(self.libraries.check,[lib, self.functions],{'otherLibs' : self.dlib, 'fortranMangle' : self.functionsFortran, 'cxxMangle' : self.functionsCxx[0], 'prototype' : self.functionsCxx[1], 'call' : self.functionsCxx[2], 'cxxLink': 'Cxx' in self.buildLanguages}): 1121 self.lib = lib 1122 if self.functionsDefine: 1123 self.executeTest(self.libraries.check,[lib, self.functionsDefine],{'otherLibs' : self.dlib, 'fortranMangle' : self.functionsFortran, 'cxxMangle' : self.functionsCxx[0], 'prototype' : self.functionsCxx[1], 'call' : self.functionsCxx[2], 'cxxLink': 'Cxx' in self.buildLanguages, 'functionDefine': 1}) 1124 self.logWrite(self.libraries.restoreLog()) 1125 self.logPrint('Checking for headers '+str(self.includes)+' in '+location+': '+str(incl)) 1126 if (not self.includes) or self.checkInclude(incl, self.includes, self.dinclude, timeout = 60.0): 1127 if self.includes: 1128 self.include = testedincl 1129 self.found = 1 1130 self.dlib = self.lib+self.dlib 1131 dinc = [] 1132 [dinc.append(inc) for inc in incl+self.dinclude if inc not in dinc] 1133 self.dinclude = dinc 1134 self.checkMacros(timeout = 60.0) 1135 if not hasattr(self.framework, 'packages'): 1136 self.framework.packages = [] 1137 self.directory = directory 1138 self.framework.packages.append(self) 1139 return 1140 else: 1141 self.logWrite(self.libraries.restoreLog()) 1142 if not self.lookforbydefault or ('with-'+self.package in self.framework.clArgDB and self.argDB['with-'+self.package]): 1143 raise RuntimeError('Could not find a functional '+self.name+'\n') 1144 if self.lookforbydefault and 'with-'+self.package not in self.framework.clArgDB: 1145 self.argDB['with-'+self.package] = 0 1146 1147 def checkSharedLibrary(self): 1148 '''By default we don\'t care about checking if the library is shared''' 1149 return 1 1150 1151 def alternateConfigureLibrary(self): 1152 '''Called if --with-packagename=0; does nothing by default''' 1153 pass 1154 1155 def consistencyChecks(self): 1156 '''Checks run on the system and currently installed packages that need to be correct for the package now being configured''' 1157 def inVersionRange(myRange,reqRange): 1158 # my minimum needs to be less than the maximum and my maximum must be greater than 1159 # the minimum 1160 return (myRange[0].lower() <= reqRange[1].lower()) and (myRange[1].lower() >= reqRange[0].lower()) 1161 1162 self.printTest(self.consistencyChecks) 1163 if 'with-'+self.package+'-dir' in self.argDB and ('with-'+self.package+'-include' in self.argDB or 'with-'+self.package+'-lib' in self.argDB): 1164 raise RuntimeError('Specify either "--with-'+self.package+'-dir" or "--with-'+self.package+'-lib --with-'+self.package+'-include". But not both!') 1165 1166 blaslapackconflict = 0 1167 for pkg in self.deps: 1168 if pkg.package == 'blaslapack': 1169 if pkg.has64bitindices and self.requires32bitintblas: 1170 blaslapackconflict = 1 1171 1172 cxxVersionRange = (self.minCxxVersion,self.maxCxxVersion) 1173 cxxVersionConflict = not inVersionRange(cxxVersionRange,self.setCompilers.cxxDialectRange[self.getDefaultLanguage()]) 1174 # if user did not request option, then turn it off if conflicts with configuration 1175 if self.lookforbydefault and 'with-'+self.package not in self.framework.clArgDB: 1176 mess = None 1177 if 'Cxx' in self.buildLanguages and not hasattr(self.compilers, 'CXX'): 1178 mess = 'requires C++ but C++ compiler not set' 1179 if 'FC' in self.buildLanguages and not hasattr(self.compilers, 'FC'): 1180 mess = 'requires Fortran but Fortran compiler not set' 1181 if self.noMPIUni and self.mpi.usingMPIUni: 1182 mess = 'requires real MPI but MPIUNI is being used' 1183 if cxxVersionConflict: 1184 mess = 'cannot work with C++ version being used' 1185 if not self.defaultPrecision.lower() in self.precisions: 1186 mess = 'does not support the current precision '+ self.defaultPrecision.lower() 1187 if not self.complex and self.defaultScalarType.lower() == 'complex': 1188 mess = 'does not support complex numbers but PETSc being build with complex' 1189 if self.defaultIndexSize == 64 and self.requires32bitint: 1190 mess = 'does not support 64-bit indices which PETSc is configured for' 1191 if blaslapackconflict: 1192 mess = 'requires 32-bit BLAS/LAPACK indices but configure is building with 64-bit' 1193 1194 if mess: 1195 self.logPrint('Turning off default package '+ self.package + ' because package ' + mess) 1196 self.argDB['with-'+self.package] = 0 1197 1198 if self.argDB['with-'+self.package]: 1199 if blaslapackconflict: 1200 raise RuntimeError('Cannot use '+self.name+' with 64-bit BLAS/LAPACK indices') 1201 if 'Cxx' in self.buildLanguages and not hasattr(self.compilers, 'CXX'): 1202 raise RuntimeError('Cannot use '+self.name+' without C++, make sure you do NOT have --with-cxx=0') 1203 if 'FC' in self.buildLanguages and not hasattr(self.compilers, 'FC'): 1204 raise RuntimeError('Cannot use '+self.name+' without Fortran, make sure you do NOT have --with-fc=0') 1205 if self.noMPIUni and self.mpi.usingMPIUni: 1206 raise RuntimeError('Cannot use '+self.name+' with MPIUNI, you need a real MPI') 1207 if cxxVersionConflict: 1208 raise RuntimeError('Cannot use '+self.name+' as it requires -std=['+','.join(map(str,cxxVersionRange))+'], while your compiler seemingly only supports -std=['+','.join(map(str,self.setCompilers.cxxDialectRange[self.getDefaultLanguage()]))+']') 1209 if self.download and self.argDB.get('download-'+self.downloadname.lower()) and not self.downloadonWindows and (self.setCompilers.CC.find('win32fe') >= 0): 1210 raise RuntimeError('External package '+self.name+' does not support --download-'+self.downloadname.lower()+' with Microsoft compilers') 1211 if not self.defaultPrecision.lower() in self.precisions: 1212 raise RuntimeError('Cannot use '+self.name+' with '+self.defaultPrecision.lower()+', it is not available in this precision') 1213 if not self.complex and self.defaultScalarType.lower() == 'complex': 1214 raise RuntimeError('Cannot use '+self.name+' with complex numbers it is not coded for this capability') 1215 if self.defaultIndexSize == 64 and self.requires32bitint: 1216 raise RuntimeError('Cannot use '+self.name+' with 64-bit integers, it is not coded for this capability') 1217 if not self.download and 'download-'+self.downloadname.lower() in self.argDB and self.argDB['download-'+self.downloadname.lower()]: 1218 raise RuntimeError('External package '+self.name+' does not support --download-'+self.downloadname.lower()) 1219 return 1220 1221 def versionToStandardForm(self,version): 1222 '''Returns original string''' 1223 '''This can be overloaded by packages that have their own unique representation of versions; for example CUDA''' 1224 return version 1225 1226 def versionToTuple(self,version): 1227 '''Converts string of the form x.y to (x,y)''' 1228 if not version: return () 1229 vl = version.split('.') 1230 if len(vl) > 2: 1231 vl[-1] = re.compile(r'^[0-9]+').search(vl[-1]).group(0) 1232 return tuple(map(int,vl)) 1233 1234 def checkVersion(self): 1235 '''Uses self.version, self.minversion, self.maxversion, self.versionname, and self.versioninclude to determine if package has required version''' 1236 def dropPatch(str): 1237 '''Drops the patch version number in a version if it exists''' 1238 if str.find('.') == str.rfind('.'): return str 1239 return str[0:str.rfind('.')] 1240 def zeroPatch(str): 1241 '''Replaces the patch version number in a version if it exists with 0''' 1242 if str.find('.') == str.rfind('.'): return str 1243 return str[0:str.rfind('.')]+'.0' 1244 def infinitePatch(str): 1245 '''Replaces the patch version number in a version if it exists with a very large number''' 1246 if str.find('.') == str.rfind('.'): return str 1247 return str[0:str.rfind('.')]+'.100000' 1248 1249 if not self.version and not self.minversion and not self.maxversion and not self.versionname: return 1250 if not self.versioninclude: 1251 if not self.includes: 1252 self.log.write('For '+self.package+' unable to find version information since includes and version includes are missing skipping version check\n') 1253 self.version = '' 1254 return 1255 self.versioninclude = self.includes[0] 1256 self.pushLanguage(self.buildLanguages[0]) # default is to use the first language in checking 1257 flagsArg = self.getPreprocessorFlagsArg() 1258 oldFlags = getattr(self.compilers, flagsArg) 1259 if self.language[-1] == 'HIP': 1260 extraFlags = ' -o -' # Force 'hipcc -E' to output to stdout, instead of *.cui files (as of hip-4.0. hip-4.1+ does not need it, but does not get hurt either). 1261 else: 1262 extraFlags = '' 1263 setattr(self.compilers, flagsArg, oldFlags+extraFlags+' '+self.headers.toString(self.dinclude)) 1264 self.compilers.saveLog() 1265 1266 # Multiple headers are tried in order 1267 if not isinstance(self.versioninclude,list): 1268 headerList = [self.versioninclude] 1269 else: 1270 headerList = self.versioninclude 1271 1272 for header in headerList: 1273 try: 1274 # We once used '#include "'+self.versioninclude+'"\npetscpkgver('+self.versionname+');\n', 1275 # but some preprocessors are picky (ex. dpcpp -E), reporting errors on the code above even 1276 # it is just supposed to do preprocessing: 1277 # 1278 # error: C++ requires a type specifier for all declarations 1279 # petscpkgver(__SYCL_COMPILER_VERSION); 1280 # ^ 1281 # 1282 # So we instead use this compilable code. 1283 output = self.outputPreprocess( 1284''' 1285#include "{x}" 1286#define PetscXstr_(s) PetscStr_(s) 1287#define PetscStr_(s) #s 1288const char *ver = "petscpkgver(" PetscXstr_({y}) ")"; 1289'''.format(x=header, y=self.versionname)) 1290 # Ex. char *ver = "petscpkgver(" "20211206" ")"; 1291 # But after stripping spaces, quotes etc below, it becomes char*ver=petscpkgver(20211206); 1292 except: 1293 output = None 1294 self.logWrite(self.compilers.restoreLog()) 1295 if output: 1296 break 1297 self.popLanguage() 1298 setattr(self.compilers, flagsArg,oldFlags) 1299 if not output: 1300 self.log.write('For '+self.package+' unable to run preprocessor to obtain version information, skipping version check\n') 1301 self.version = '' 1302 return 1303 # the preprocessor output might be very long, but the petscpkgver line should be at the end. Therefore, we partition it backwards 1304 [mid, right] = output.rpartition('petscpkgver')[1:] 1305 version = '' 1306 if mid: # if mid is not empty, then it should be 'petscpkgver', meaning we found the version string 1307 verLine = right.split(';',1)[0] # get the string before the first ';'. Preprocessor might dump multiline result. 1308 self.log.write('Found the raw version string: ' + verLine +'\n') 1309 # strip backslashes, spaces, and quotes. Note MUMPS' version macro has "" around it, giving output: (" "\"5.4.1\"" ")"; 1310 for char in ['\\', ' ', '"']: 1311 verLine = verLine.replace(char, '') 1312 # get the string between the outer () 1313 version = verLine.split('(', 1)[-1].rsplit(')',1)[0] 1314 self.log.write('This is the processed version string: ' + version +'\n') 1315 if not version: 1316 self.log.write('For '+self.package+' unable to find version information: output below, skipping version check\n') 1317 self.log.write(output) 1318 if self.requiresversion: 1319 raise RuntimeError('Configure must be able to determined the version information for '+self.name+'. It was unable to, please send configure.log to petsc-maint@mcs.anl.gov') 1320 return 1321 try: 1322 # 'version' could be in many formats, like '10007201', '3.23.0', or '((((1)<<24)|((18)<<16)))'. As long as it doesn't contain '.', we eval it to simplify it. 1323 if '.' not in version: 1324 try: 1325 version = str(eval(version)) # eval a potentially complex version expression 1326 self.log.write('This is the evaluated version string: ' + version +'\n') 1327 except: 1328 self.log.write('For '+self.package+' failed to eval its version string ('+version+') to a number\n') 1329 self.foundversion = self.versionToStandardForm(version) 1330 except: 1331 self.log.write('For '+self.package+' unable to convert version information ('+version+') to standard form, skipping version check\n') 1332 if self.requiresversion: 1333 raise RuntimeError('Configure must be able to determined the version information for '+self.name+'. It was unable to, please send configure.log to petsc-maint@mcs.anl.gov') 1334 return 1335 1336 self.log.write('For '+self.package+' need '+self.minversion+' <= '+self.foundversion+' <= '+self.maxversion+'\n') 1337 1338 try: 1339 self.version_tuple = self.versionToTuple(self.foundversion) 1340 except: 1341 self.log.write('For '+self.package+' unable to convert version string to tuple, skipping version check\n') 1342 if self.requiresversion: 1343 raise RuntimeError('Configure must be able to determined the version information for '+self.name+'; it appears to be '+self.foundversion+'. It was unable to, please send configure.log to petsc-maint@mcs.anl.gov') 1344 self.foundversion = '' 1345 return 1346 1347 suggest = '' 1348 if self.download: 1349 suggest = '. Suggest using --download-'+self.package+' for a compatible '+self.name 1350 if self.argDB['download-'+self.package]: 1351 rmdir = None 1352 try: 1353 rmdir = self.getDir() 1354 except: 1355 pass 1356 if rmdir: 1357 # this means that --download-package was requested, the package was not rebuilt, but there are newer releases of the package so it should be rebuilt 1358 suggest += ' after running "rm -rf ' + self.getDir() +'"\n' 1359 suggest += 'DO NOT DO THIS if you rely on the exact version of the currently installed ' + self.name 1360 if self.minversion: 1361 if self.versionToTuple(self.minversion) > self.version_tuple: 1362 raise RuntimeError(self.PACKAGE+' version is '+self.foundversion+', this version of PETSc needs at least '+self.minversion+suggest+'\n') 1363 elif self.version: 1364 if self.versionToTuple(zeroPatch(self.version)) > self.version_tuple: 1365 self.logPrintWarning('Using version '+self.foundversion+' of package '+self.PACKAGE+', PETSc is tested with '+dropPatch(self.version)+suggest) 1366 if self.maxversion: 1367 if self.versionToTuple(self.maxversion) < self.version_tuple: 1368 raise RuntimeError(self.PACKAGE+' version is '+self.foundversion+', this version of PETSc needs at most '+self.maxversion+suggest+'\n') 1369 elif self.version: 1370 if self.versionToTuple(infinitePatch(self.version)) < self.version_tuple: 1371 self.logPrintWarning('Using version '+self.foundversion+' of package '+self.PACKAGE+', PETSc is tested with '+dropPatch(self.version)+suggest) 1372 return 1373 1374 def configure(self): 1375 if hasattr(self, 'download_solaris') and config.setCompilers.Configure.isSolaris(self.log): 1376 self.download = self.download_solaris 1377 if hasattr(self, 'download_darwin') and config.setCompilers.Configure.isDarwin(self.log): 1378 self.download = self.download_darwin 1379 if hasattr(self, 'download_mingw') and config.setCompilers.Configure.isMINGW(self.framework.getCompiler(), self.log): 1380 self.download = self.download_mingw 1381 if self.download and self.argDB['download-'+self.downloadname.lower()] and (not self.framework.batchBodies or self.installwithbatch): 1382 self.argDB['with-'+self.package] = 1 1383 downloadPackageVal = self.argDB['download-'+self.downloadname.lower()] 1384 if isinstance(downloadPackageVal, str): 1385 self.download = [downloadPackageVal] 1386 if self.download and self.argDB['download-'+self.downloadname.lower()+'-commit']: 1387 self.gitcommit = self.argDB['download-'+self.downloadname.lower()+'-commit'] 1388 if hasattr(self, 'download_git'): 1389 self.download = self.download_git 1390 elif self.gitcommitmain and not self.petscdir.versionRelease: 1391 self.gitcommit = self.gitcommitmain 1392 if not 'with-'+self.package in self.argDB: 1393 self.argDB['with-'+self.package] = 0 1394 if 'with-'+self.package+'-dir' in self.argDB or 'with-'+self.package+'-include' in self.argDB or 'with-'+self.package+'-lib' in self.argDB: 1395 self.argDB['with-'+self.package] = 1 1396 if 'with-'+self.package+'-pkg-config' in self.argDB: 1397 self.argDB['with-'+self.package] = 1 1398 1399 self.consistencyChecks() 1400 if self.argDB['with-'+self.package]: 1401 # If clanguage is c++, test external packages with the c++ compiler 1402 self.libraries.pushLanguage(self.defaultLanguage) 1403 self.executeTest(self.checkDependencies) 1404 self.executeTest(self.configureLibrary) 1405 if not self.builtafterpetsc: 1406 self.executeTest(self.checkVersion) 1407 self.executeTest(self.checkSharedLibrary) 1408 self.libraries.popLanguage() 1409 else: 1410 self.executeTest(self.alternateConfigureLibrary) 1411 return 1412 1413 def updateCompilers(self, installDir, mpiccName, mpicxxName, mpif77Name, mpif90Name): 1414 '''Check if mpicc, mpicxx etc binaries exist - and update setCompilers() database. 1415 The input arguments are the names of the binaries specified by the respective packages 1416 This should really be part of compilers.py but it also uses compilerFlags.configure() so 1417 I am putting it here and Matt can fix it''' 1418 1419 # Both MPICH and MVAPICH now depend on LD_LIBRARY_PATH for sharedlibraries. 1420 # So using LD_LIBRARY_PATH in configure - and -Wl,-rpath in makefiles 1421 if self.argDB['with-shared-libraries']: 1422 config.setCompilers.Configure.addLdPath(os.path.join(installDir,'lib')) 1423 # Initialize to empty 1424 mpicc='' 1425 mpicxx='' 1426 mpifc='' 1427 1428 mpicc = os.path.join(installDir,"bin",mpiccName) 1429 if not os.path.isfile(mpicc): raise RuntimeError('Could not locate installed MPI compiler: '+mpicc) 1430 try: 1431 self.logPrint('Showing compiler and options used by newly built MPI') 1432 self.executeShellCommand(mpicc + ' -show', log = self.log)[0] 1433 except: 1434 pass 1435 if hasattr(self.compilers, 'CXX'): 1436 mpicxx = os.path.join(installDir,"bin",mpicxxName) 1437 if not os.path.isfile(mpicxx): raise RuntimeError('Could not locate installed MPI compiler: '+mpicxx) 1438 try: 1439 self.executeShellCommand(mpicxx + ' -show', log = self.log)[0] 1440 except: 1441 pass 1442 if hasattr(self.compilers, 'FC'): 1443 if self.fortran.fortranIsF90: 1444 mpifc = os.path.join(installDir,"bin",mpif90Name) 1445 else: 1446 mpifc = os.path.join(installDir,"bin",mpif77Name) 1447 if not os.path.isfile(mpifc): raise RuntimeError('Could not locate installed MPI compiler: '+mpifc) 1448 try: 1449 self.executeShellCommand(mpifc + ' -show', log = self.log)[0] 1450 except: 1451 pass 1452 # redo compiler detection, copy the package cxx dialect restrictions though 1453 oldPackageRanges = self.setCompilers.cxxDialectPackageRanges 1454 self.setCompilers.updateMPICompilers(mpicc,mpicxx,mpifc) 1455 self.setCompilers.cxxDialectPackageRanges = oldPackageRanges 1456 self.compilers.__init__(self.framework) 1457 self.compilers.headerPrefix = self.headerPrefix 1458 self.compilers.setup() 1459 self.compilerFlags.saveLog() 1460 self.compilerFlags.configure() 1461 self.logWrite(self.compilerFlags.restoreLog()) 1462 self.compilers.saveLog() 1463 self.compilers.configure() 1464 self.logWrite(self.compilers.restoreLog()) 1465 if self.cuda.found: 1466 self.cuda.configureLibrary() 1467 return 1468 1469 def checkSharedLibrariesEnabled(self): 1470 if self.havePETSc: 1471 useShared = self.sharedLibraries.useShared 1472 else: 1473 useShared = True 1474 if 'download-'+self.package+'-shared' in self.framework.clArgDB and self.argDB['download-'+self.package+'-shared']: 1475 raise RuntimeError(self.package+' cannot use download-'+self.package+'-shared=1. This flag can only be used to disable '+self.package+' shared libraries') 1476 if not useShared or ('download-'+self.package+'-shared' in self.framework.clArgDB and not self.argDB['download-'+self.package+'-shared']): 1477 return False 1478 else: 1479 return True 1480 1481 def compilePETSc(self): 1482 try: 1483 self.logPrintBox('Compiling PETSc; this may take several minutes') 1484 output,err,ret = config.package.Package.executeShellCommand(self.make.make+' all PETSC_DIR='+self.petscdir.dir+' PETSC_ARCH='+self.arch, cwd=self.petscdir.dir, timeout=1000, log = self.log) 1485 self.log.write(output+err) 1486 except RuntimeError as e: 1487 raise RuntimeError('Error running make all on PETSc: '+str(e)) 1488 if self.framework.argDB['prefix']: 1489 try: 1490 self.logPrintBox('Installing PETSc; this may take several minutes') 1491 output,err,ret = config.package.Package.executeShellCommand(self.make.make+' install PETSC_DIR='+self.petscdir.dir+' PETSC_ARCH='+self.arch, cwd=self.petscdir.dir, timeout=60, log = self.log) 1492 self.log.write(output+err) 1493 except RuntimeError as e: 1494 raise RuntimeError('Error running make install on PETSc: '+str(e)) 1495 elif not self.argDB['with-batch']: 1496 try: 1497 self.logPrintBox('Testing PETSc; this may take several minutes') 1498 output,err,ret = config.package.Package.executeShellCommand(self.make.make+' test PETSC_DIR='+self.petscdir.dir+' PETSC_ARCH='+self.arch, cwd=self.petscdir.dir, timeout=60, log = self.log) 1499 output = output+err 1500 self.log.write(output) 1501 if output.find('error') > -1 or output.find('Error') > -1: 1502 raise RuntimeError('Error running make check on PETSc: '+output) 1503 except RuntimeError as e: 1504 raise RuntimeError('Error running make check on PETSc: '+str(e)) 1505 self.installedpetsc = 1 1506 1507''' 1508config.package.GNUPackage is a helper class whose intent is to simplify writing configure modules 1509for GNU-style packages that are installed using the "configure; make; make install" idiom. 1510 1511Brief overview of how BuildSystem\'s configuration of packages works. 1512--------------------------------------------------------------------- 1513 Configuration is carried out by "configure objects": instances of classes desendant from config.base.Configure. 1514 These configure objects implement the "configure()" method, and are inserted into a "framework" object, 1515 which makes the "configure()" calls according to the dependencies between the configure objects. 1516 config.package.Package extends config.base.Configure and adds instance variables and methods that facilitate 1517 writing classes that configure packages. Customized package configuration classes are written by subclassing 1518 config.package.Package -- the "parent class". 1519 1520 Packages essentially encapsulate libraries, that either 1521 (A) are already (prefix-)installed already somewhere on the system or 1522 (B) need to be downloaded, built and installed first 1523 If (A), the parent class provides a generic mechanism for locating the installation, by looking in user-specified and standard locations. 1524 If (B), the parent class provides a generic mechanism for determining whether a download is necessary, downloading and unpacking 1525 the source (if the download is, indeed, required), determining whether the package needs to be built, providing the build and 1526 installation directories, and a few other helper tasks. The package subclass is responsible for implementing the "Install" hook, 1527 which is called by the parent class when the actual installation (building the source code, etc.) is done. As an aside, BuildSystem- 1528 controlled build and install of a package at configuration time has a much better chance of guaranteeing language, compiler and library 1529 (shared or not) consistency among packages. 1530 No matter whether (A) or (B) is realized, the parent class control flow demands that the located or installed package 1531 be checked to ensure it is functional. Since a package is conceptualized as a library, the check consists in testing whether 1532 a specified set of libraries can be linked against, and ahat the specified headers can be located. The libraries and headers are specified 1533 by name, and the corresponding paths are supplied as a result of the process of locating or building the library. The verified paths and 1534 library names are then are stored by the configure object as instance variables. These can be used by other packages dependent on the package 1535 being configured; likewise, the package being configured will use the information from the packages it depends on by examining their instance 1536 variables. 1537 1538 Thus, the parent class provides for the overall control and data flow, which goes through several configuration stages: 1539 "init", "setup", "location/installation", "testing". At each stage, various "hooks" -- methods -- are called. 1540 Some hooks (e.g., Install) are left unimplemented by the parent class and must be implemented by the package subclass; 1541 other hooks are implemented by the parent class and provide generic functionality that is likely to suit most packages, 1542 but can be overridden for custom purposes. Each hook typically prepares the state -- instance variables -- of the configure object 1543 for the next phase of configuration. Below we describe the stages, some of the more typically-used hooks and instance variables in some 1544 detail. 1545 1546 init: 1547 ---- 1548 The init stage constructs the configure object; it is implemented by its __init__ method. 1549 Parent package class sets up the following useful state variables: 1550 self.name - derived from module name [string] 1551 self.package - lowercase name [string] 1552 self.PACKAGE - uppercase name [string] 1553 self.downloadname - same as self.name (usage a bit inconsistent) [string] 1554 self.downloaddirnames - same as self.name (usage a bit inconsistent) [string] 1555 Package subclass typically sets up the following state variables: 1556 self.download - url to download source from [string] 1557 self.includes - names of header files to locate [list of strings] 1558 self.liblist - names of library files to locate [list of lists of strings] 1559 self.functions - names of functions to locate in libraries [list of strings] 1560 self.cxx - whether C++ compiler, (this does not require that PETSc be built with C++, should it?) is required for this package [bool] 1561 self.functionsFortran - whether to mangle self.functions symbols [bool] 1562 Most of these instance variables determine the behavior of the location/installation and the testing stages. 1563 Ideally, a package subclass would extend only the __init__ method and parameterize the remainder of 1564 the configure process by the appropriate variables. This is not always possible, since some 1565 of the package-specific choices depend on 1566 1567 setup: 1568 ----- 1569 The setup stage follows init and is accomplished by the configure framework calling each configure objects 1570 setup hooks: 1571 1572 setupHelp: 1573 --------- 1574 This is used to define the command-line arguments expected by this configure object. 1575 The parent package class sets up generic arguments: 1576 --with-<package> [bool] 1577 --with-<package>-dir [string: directory] 1578 --download-<package> [string:"yes","no","filename"] 1579 --with-<package>-include [string: directory] 1580 --with-<package>-lib [string: directory] 1581 Here <package> is self.package defined in the init stage. 1582 The package subclass can add to these arguments. These arguments\' values are set 1583 from the defaults specified in setupHelp or from the user-supplied command-line arguments. 1584 Their values can be queried at any time during the configure process. 1585 1586 setupDependencies: 1587 ----------------- 1588 This is used to specify other configure objects that the package being configured depends on. 1589 This is done via the configure framework\'s "require" mechanism: 1590 self.framework.require(<dependentObject>, self) 1591 dependentObject is a string -- the name of the configure module this package depends on. 1592 1593 The parent package class by default sets up some of the common dependencies: 1594 config.compilers, config.types, config.headers, config.libraries, config.packages.MPI, 1595 among others. 1596 The package subclass should add package-specific dependencies via the "require" mechanism, 1597 as well as list them in self.deps [list]. This list is used during the location/installation 1598 stage to ensure that the package\'s dependencies have been configured correctly. 1599 1600 Location/installation: 1601 --------------------- 1602 These stages (somewhat mutually-exclusive), as well as the testing stage are carried out by the code in 1603 configureLibrary. These stages calls back to certain hooks that allow the user to control the 1604 location/installation process by overriding these hooks in the package subclass. 1605 1606 Location: 1607 -------- 1608 [Not much to say here, yet.] 1609 1610 Installation: 1611 ------------ 1612 This stage is carried out by configure and functions called from it, most notably, configureLibrary 1613 The essential difficulty here is that the function calls are deeply nested (A-->B-->C--> ...), 1614 as opposed to a single driver repeatedly calling small single-purpose callback hooks. This means that any 1615 customization would not be able to be self-contained by would need to know to call further down the chain. 1616 Moreover, the individual functions on the call stack combine generic code with the code that is naturally meant 1617 for customization by a package subclass. Thus, a customization would have to reproduce this generic code. 1618 Some of the potentially customizable functionality is split between different parts of the code below 1619 configure (see, e.g., the comment at the end of this paragraph). 1620 Because of this, there are few opportunities for customization in the installation stage, without a substantial 1621 restructuring of configure, configureLibrary and/or its callees. Here we mention the main customizable callback 1622 Install along with two generic services, installNeeded and postInstall, which are provided by the parent class and 1623 can be used in implementing a custom Install. 1624 Comment: Note that configure decides whether to configure the package, in part, based on whether 1625 self.download is a non-empty list at the beginning of configure. 1626 This means that resetting self.download cannot take place later than this. 1627 On the other hand, constructing the correct self.download here might be premature, as it might result 1628 in unnecessary prompts for user input, only to discover later that a download is not required. 1629 Because of this a package configure class must always have at least dummy string for self.download, if 1630 a download is possible. 1631 1632 Here is a schematic description of the main point on the call chain: 1633 1634 configure: 1635 check whether to configure the package: 1636 package is configured only if 1637 self.download is not an empty string list and the command-line download flag is on 1638 OR if 1639 the command-line flag "-with-"self.package is present, prompting a search for the package on the system 1640 OR if 1641 the command-line flag(s) pointing to a package installation "-with-"self.package+"-dir or ...-lib, ...-include are present 1642 ... 1643 configureLibrary: 1644 consistencyChecks: 1645 ... 1646 check that appropriate language support is on: 1647 self.cxx == 1 implies C++ compiler must be present 1648 self.fc == 1 implies Fortran compiler must be present 1649 self.noMPIUni == 1 implies real MPI must be present 1650 ... 1651 generateGuesses: 1652 ... 1653 checkDownload: 1654 ... 1655 check val = argDB[\'download-\'self.downloadname.tolower()\'] 1656 /* 1657 note the inconsistency with setupHelp: it declares \'download-\'self.package 1658 Thus, in order for the correct variable to be queried here, we have to have 1659 self.downloadname.tolower() == self.package 1660 */ 1661 if val is a string, set self.download = [val] 1662 check the package license 1663 getInstallDir: 1664 ... 1665 set the following instance variables, creating directories, if necessary: 1666 self.installDir /* This is where the package will be installed, after it is built. */ 1667 self.includeDir /* subdir of self.installDir */ 1668 self.libDir /* subdir of self.installDir, defined as self.installDir + self.libDirs[0] */ 1669 self.confDir /* where packages private to the configure/build process are built, such as --download-make */ 1670 /* The subdirectory of this 'conf' is where the configuration information will be stored for the package */ 1671 self.packageDir = /* this dir is where the source is unpacked and built */ 1672 self.getDir(): 1673 ... 1674 if a package dir starting with self.downloadname does not exist already 1675 create the package dir 1676 downLoad(): 1677 ... 1678 download and unpack the source to self.packageDir, 1679 Install(): 1680 /* This must be implemented by a package subclass */ 1681 1682 Install: 1683 ------ 1684 Note that it follows from the above pseudocode, that the package source is already in self.packageDir 1685 and the dir instance variables (e.g., installDir, confDir) already point to existing directories. 1686 The user can implement whatever actions are necessary to configure, build and install 1687 the package. Typically, the package is built using GNU\'s "configure; make; make install" 1688 idiom, so the customized Install forms GNU configure arguments using the compilers, 1689 system libraries and dependent packages (their locations, libs and includes) discovered 1690 by configure up to this point. 1691 1692 It is important to check whether the package source in self.packageDir needs rebuilding, since it might 1693 have been downloaded in a previous configure run, as is checked by getDir() above. 1694 However, the package might now need to be built with different options. For that reason, 1695 the parent class provides a helper method 1696 installNeeded(self, mkfile): 1697 This method compares two files: the file with name mkfile in self.packageDir and 1698 the file with name self.name in self.confDir (a subdir of the installation dir). 1699 If the former is absent or differs from the latter, this means the source has never 1700 been built or was built with different arguments, and needs to be rebuilt. 1701 This helper method should be run at the beginning of an Install implementation, 1702 to determine whether an install is actually needed. 1703 The other useful helper method provided by the parent class is 1704 postInstall(self, output,mkfile): 1705 This method will simply save string output in the file with name mkfile in self.confDir. 1706 Storing package configuration parameters there will enable installNeeded to do its job 1707 next time this package is being configured. 1708 1709 testing: 1710 ------- 1711 The testing is carried out by part of the code in config.package.configureLibrary, 1712 after the package library has been located or installed. 1713 The library is considered functional if two conditions are satisfied: 1714 (1) all of the symbols in self.functions have been resolved when linking against the libraries in self.liblist, 1715 either located on the system or newly installed; 1716 (2) the headers in self.includes have been located. 1717 If no symbols are supplied in self.functions, no link OR header testing is done. 1718 1719 Extending package class: 1720 ----------------------- 1721 Generally, extending the parent package configure class is done by overriding some 1722 or all of its methods (see config/BuildSystem/config/packages/hdf5.py, for example). 1723 Because convenient (i.e., localized) hooks are available onto to some parts of the 1724 configure process, frequently writing a custom configure class amounts to overriding 1725 configureLibrary so that pre- and post-code can be inserted before calling to 1726 config.package.Package.configureLibrary. 1727 1728 In any event, Install must be implemented anew for any package configure class extending 1729 config.package.Package. Naturally, instance variables have to be set appropriately 1730 in __init__ (or elsewhere), package-specific help options and dependencies must be defined. 1731 Therefore, the general pattern for package configure subclassing is this: 1732 - override __init__ and set package-specific instance variables 1733 - override setupHelp and setupDependencies hooks to set package-specific command-line 1734 arguments and dependencies on demand 1735 - override Install, making use of the parent class\'s installNeeded and postInstall 1736 - override configureLibrary, if necessary, to insert pre- and post-configure fixup code. 1737 1738 GNUPackage class: 1739 ---------------- 1740 This class is an attempt at making writing package configure classes easier for the packages 1741 that use the "configure; make; make install" idiom for the installation -- "GNU packages". 1742 The main contribution is in the implementation of a generic Install method, which attempts 1743 to automate the building of a package based on the mostly standard instance variables. 1744 1745 Besides running GNU configure, GNUPackage.Install runs installNeeded, make and postInstall 1746 at the appropriate times, automatically determining whether a rebuild is necessary, saving 1747 a GNU configure arguments stamp to perform the check in the future, etc. 1748 1749 setupHelp: 1750 --------- 1751 This method extends config.Package.setupHelp by adding two command-line arguments: 1752 "-download-"+self.package+"-version" with self.downloadversion as default or None, if it does not exist 1753 "-download-"+self.package+"-shared" with False as the default. 1754 1755 Summary: 1756 ------- 1757 In order to customize GNUPackage: 1758 - set up the usual instance variables in __init__, plus the following instance variables, if necessary/appropriate: 1759 self.downloadpath 1760 self.downloadext 1761 self.downloadversion 1762 - override setupHelp to declare command-line arguments that can be used anywhere below 1763 (GNUPackage takes care of some of the basic args, including the download version) 1764 - override setupDependencies to process self.odeps and enable this optional package feature in the current externalpackage. 1765 - override setupDownload to control the precise download URL and/or 1766 - override setupDownloadVersion to control the self.downloadversion string inserted into self.download between self.downloadpath and self.downloadext 1767''' 1768 1769class GNUPackage(Package): 1770 def __init__(self, framework): 1771 Package.__init__(self,framework) 1772 self.builddir = 'no' # requires build be done in a subdirectory, not in the directory tree 1773 self.configureName = 'configure' 1774 return 1775 1776 def setupHelp(self, help): 1777 config.package.Package.setupHelp(self,help) 1778 import nargs 1779 help.addArgument(self.PACKAGE, '-download-'+self.package+'-shared=<bool>', nargs.ArgBool(None, 0, 'Install '+self.PACKAGE+' with shared libraries')) 1780 help.addArgument(self.PACKAGE, '-download-'+self.package+'-configure-arguments=string', nargs.ArgString(None, 0, 'Additional GNU autoconf configure arguments for the build of '+self.name)) 1781 1782 def formGNUConfigureArgs(self): 1783 '''This sets up the prefix, compiler flags, shared flags, and other generic arguments 1784 that are fed into the configure script supplied with the package. 1785 Override this to set options needed by a particular package''' 1786 args=[] 1787 ## prefix 1788 args.append('--prefix='+self.installDir) 1789 args.append('MAKE='+self.make.make) 1790 args.append('--libdir='+self.libDir) 1791 ## compiler args 1792 self.pushLanguage('C') 1793 if not self.installwithbatch and hasattr(self.setCompilers,'cross_cc'): 1794 args.append('CC="'+self.setCompilers.cross_cc+'"') 1795 else: 1796 args.append('CC="'+self.getCompiler()+'"') 1797 args.append('CFLAGS="'+self.updatePackageCFlags(self.getCompilerFlags())+'"') 1798 args.append('AR="'+self.setCompilers.AR+'"') 1799 args.append('ARFLAGS="'+self.setCompilers.AR_FLAGS+'"') 1800 if not self.installwithbatch and hasattr(self.setCompilers,'cross_LIBS'): 1801 args.append('LIBS="'+self.setCompilers.cross_LIBS+'"') 1802 if self.setCompilers.LDFLAGS: 1803 args.append('LDFLAGS="'+self.setCompilers.LDFLAGS+'"') 1804 self.popLanguage() 1805 if hasattr(self.compilers, 'CXX'): 1806 self.pushLanguage('Cxx') 1807 if not self.installwithbatch and hasattr(self.setCompilers,'cross_CC'): 1808 args.append('CXX="'+self.setCompilers.cross_CC+'"') 1809 else: 1810 args.append('CXX="'+self.getCompiler()+'"') 1811 args.append('CXXFLAGS="'+self.updatePackageCxxFlags(self.getCompilerFlags())+'"') 1812 self.popLanguage() 1813 else: 1814 args.append('--disable-cxx') 1815 if hasattr(self.compilers, 'FC'): 1816 self.pushLanguage('FC') 1817 fc = self.getCompiler() 1818 if self.fortran.fortranIsF90: 1819 try: 1820 output, error, status = self.executeShellCommand(fc+' -v', log = self.log) 1821 output += error 1822 except: 1823 output = '' 1824 if output.find('IBM') >= 0: 1825 fc = os.path.join(os.path.dirname(fc), 'xlf') 1826 self.log.write('Using IBM f90 compiler, switching to xlf for compiling ' + self.PACKAGE + '\n') 1827 # now set F90 1828 if not self.installwithbatch and hasattr(self.setCompilers,'cross_fc'): 1829 args.append('F90="'+self.setCompilers.cross_fc+'"') 1830 else: 1831 args.append('F90="'+fc+'"') 1832 args.append('F90FLAGS="'+self.updatePackageFFlags(self.getCompilerFlags())+'"') 1833 else: 1834 args.append('--disable-f90') 1835 args.append('FFLAGS="'+self.updatePackageFFlags(self.getCompilerFlags())+'"') 1836 if not self.installwithbatch and hasattr(self.setCompilers,'cross_fc'): 1837 args.append('FC="'+self.setCompilers.cross_fc+'"') 1838 args.append('F77="'+self.setCompilers.cross_fc+'"') 1839 else: 1840 args.append('FC="'+fc+'"') 1841 args.append('F77="'+fc+'"') 1842 args.append('FCFLAGS="'+self.updatePackageFFlags(self.getCompilerFlags())+'"') 1843 self.popLanguage() 1844 else: 1845 args.append('--disable-fortran') 1846 args.append('--disable-fc') 1847 args.append('--disable-f77') 1848 args.append('--disable-f90') 1849 if self.checkSharedLibrariesEnabled(): 1850 args.append('--enable-shared') 1851 else: 1852 args.append('--disable-shared') 1853 1854 cuda_module = self.framework.findModule(self, config.packages.CUDA) 1855 if cuda_module and cuda_module.found: 1856 with self.Language('CUDA'): 1857 args.append('CUDAC='+self.getCompiler()) 1858 args.append('CUDAFLAGS="'+self.updatePackageCUDAFlags(self.getCompilerFlags())+'"') 1859 return args 1860 1861 def preInstall(self): 1862 '''Run pre-install steps like generate configure script''' 1863 if not os.path.isfile(os.path.join(self.packageDir,self.configureName)): 1864 if not self.programs.autoreconf: 1865 raise RuntimeError('autoreconf required for ' + self.PACKAGE+' not found (or broken)! Use your package manager to install autoconf') 1866 if not self.programs.libtoolize: 1867 raise RuntimeError('libtoolize required for ' + self.PACKAGE+' not found! Use your package manager to install libtool') 1868 try: 1869 self.logPrintBox('Running libtoolize on ' +self.PACKAGE+'; this may take several minutes') 1870 output,err,ret = config.base.Configure.executeShellCommand([self.programs.libtoolize, '--install'], cwd=self.packageDir, timeout=100, log=self.log) 1871 if ret: 1872 raise RuntimeError('Error in libtoolize: ' + output+err) 1873 except RuntimeError as e: 1874 raise RuntimeError('Error running libtoolize on ' + self.PACKAGE+': '+str(e)) 1875 try: 1876 self.logPrintBox('Running autoreconf on ' +self.PACKAGE+'; this may take several minutes') 1877 output,err,ret = config.base.Configure.executeShellCommand([self.programs.autoreconf, '--force', '--install'], cwd=self.packageDir, timeout=200, log = self.log) 1878 if ret: 1879 raise RuntimeError('Error in autoreconf: ' + output+err) 1880 except RuntimeError as e: 1881 raise RuntimeError('Error running autoreconf on ' + self.PACKAGE+': '+str(e)) 1882 1883 def Install(self): 1884 ##### getInstallDir calls this, and it sets up self.packageDir (source download), self.confDir and self.installDir 1885 args = self.formGNUConfigureArgs() # allow package to change self.packageDir 1886 if self.download and self.argDB['download-'+self.downloadname.lower()+'-configure-arguments']: 1887 args.append(self.argDB['download-'+self.downloadname.lower()+'-configure-arguments']) 1888 args = ' '.join(args) 1889 conffile = os.path.join(self.packageDir,self.package+'.petscconf') 1890 fd = open(conffile, 'w') 1891 fd.write(args) 1892 fd.close() 1893 ### Use conffile to check whether a reconfigure/rebuild is required 1894 if not self.installNeeded(conffile): 1895 return self.installDir 1896 1897 self.preInstall() 1898 1899 if self.builddir == 'yes': 1900 folder = os.path.join(self.packageDir, 'petsc-build') 1901 if os.path.isdir(folder): 1902 import shutil 1903 shutil.rmtree(folder) 1904 os.mkdir(folder) 1905 self.packageDir = folder 1906 dot = '..' 1907 else: 1908 dot = '.' 1909 1910 ### Configure and Build package 1911 try: 1912 self.logPrintBox('Running configure on ' +self.PACKAGE+'; this may take several minutes') 1913 output1,err1,ret1 = config.base.Configure.executeShellCommand(os.path.join(dot, self.configureName)+' '+args, cwd=self.packageDir, timeout=2000, log = self.log) 1914 except RuntimeError as e: 1915 self.logPrint('Error running configure on ' + self.PACKAGE+': '+str(e)) 1916 try: 1917 with open(os.path.join(self.packageDir,'config.log')) as fd: 1918 conf = fd.read() 1919 fd.close() 1920 self.logPrint('Output in config.log for ' + self.PACKAGE+': '+conf) 1921 except: 1922 pass 1923 raise RuntimeError('Error running configure on ' + self.PACKAGE) 1924 try: 1925 self.logPrintBox('Running make on '+self.PACKAGE+'; this may take several minutes') 1926 if self.parallelMake: pmake = self.make.make_jnp+' '+self.makerulename+' ' 1927 else: pmake = self.make.make+' '+self.makerulename+' ' 1928 1929 output2,err2,ret2 = config.base.Configure.executeShellCommand(self.make.make+' clean', cwd=self.packageDir, timeout=200, log = self.log) 1930 output3,err3,ret3 = config.base.Configure.executeShellCommand(pmake, cwd=self.packageDir, timeout=6000, log = self.log) 1931 self.logPrintBox('Running make install on '+self.PACKAGE+'; this may take several minutes') 1932 output4,err4,ret4 = config.base.Configure.executeShellCommand(self.make.make+' install', cwd=self.packageDir, timeout=1000, log = self.log) 1933 except RuntimeError as e: 1934 self.logPrint('Error running make; make install on '+self.PACKAGE+': '+str(e)) 1935 raise RuntimeError('Error running make; make install on '+self.PACKAGE) 1936 self.postInstall(output1+err1+output2+err2+output3+err3+output4+err4, conffile) 1937 return self.installDir 1938 1939 def Bootstrap(self,command): 1940 '''check for configure script - and run bootstrap - if needed''' 1941 import os 1942 if not os.path.isfile(os.path.join(self.packageDir,self.configureName)): 1943 if not self.programs.libtoolize: 1944 raise RuntimeError('Could not bootstrap '+self.PACKAGE+' using autotools: libtoolize not found') 1945 if not self.programs.autoreconf: 1946 raise RuntimeError('Could not bootstrap '+self.PACKAGE+' using autotools: autoreconf not found') 1947 self.logPrintBox('Bootstrapping '+self.PACKAGE+' using autotools; this may take several minutes') 1948 try: 1949 self.executeShellCommand(command,cwd=self.packageDir,log=self.log) 1950 except RuntimeError as e: 1951 raise RuntimeError('Could not bootstrap '+self.PACKAGE+': maybe autotools (or recent enough autotools) could not be found?\nError: '+str(e)) 1952 1953class CMakePackage(Package): 1954 def __init__(self, framework): 1955 Package.__init__(self, framework) 1956 self.minCmakeVersion = (2,0,0) 1957 self.need35policy = False 1958 return 1959 1960 def setupHelp(self, help): 1961 config.package.Package.setupHelp(self,help) 1962 import nargs 1963 help.addArgument(self.PACKAGE, '-download-'+self.package+'-shared=<bool>', nargs.ArgBool(None, 0, 'Install '+self.PACKAGE+' with shared libraries')) 1964 help.addArgument(self.PACKAGE, '-download-'+self.package+'-cmake-arguments=string', nargs.ArgString(None, 0, 'Additional CMake arguments for the build of '+self.name)) 1965 1966 def setupDependencies(self, framework): 1967 Package.setupDependencies(self, framework) 1968 self.cmake = framework.require('config.packages.CMake',self) 1969 if self.argDB['download-'+self.downloadname.lower()]: 1970 self.cmake.maxminCmakeVersion = max(self.minCmakeVersion,self.cmake.maxminCmakeVersion) 1971 return 1972 1973 def formCMakeConfigureArgs(self): 1974 import os 1975 import shlex 1976 # If user has set, for example, CMAKE_GENERATOR Ninja then CMake calls from configure will generate the wrong external package tools for building 1977 try: 1978 del os.environ['CMAKE_GENERATOR'] 1979 except: 1980 pass 1981 1982 args = ['-DCMAKE_INSTALL_PREFIX='+self.installDir] 1983 args.append('-DCMAKE_INSTALL_NAME_DIR:STRING="'+self.libDir+'"') 1984 args.append('-DCMAKE_INSTALL_LIBDIR:STRING="lib"') 1985 args.append('-DCMAKE_VERBOSE_MAKEFILE=1') 1986 if self.compilerFlags.debugging: 1987 args.append('-DCMAKE_BUILD_TYPE=Debug') 1988 else: 1989 args.append('-DCMAKE_BUILD_TYPE=Release') 1990 args.append('-DCMAKE_AR="'+self.setCompilers.AR+'"') 1991 self.framework.pushLanguage('C') 1992 args.append('-DCMAKE_C_COMPILER="'+self.framework.getCompiler()+'"') 1993 # bypass CMake findMPI() bug that can find compilers later in the PATH before the first one in the PATH. 1994 # relevant lines of findMPI() begins with if(_MPI_BASE_DIR) 1995 self.getExecutable(self.framework.getCompiler(), getFullPath=1, resultName='mpi_C',setMakeMacro=0) 1996 args.append('-DMPI_C_COMPILER="'+self.mpi_C+'"') 1997 ranlib = shlex.split(self.setCompilers.RANLIB)[0] 1998 args.append('-DCMAKE_RANLIB='+ranlib) 1999 cflags = self.updatePackageCFlags(self.setCompilers.getCompilerFlags()) 2000 args.append('-DCMAKE_C_FLAGS:STRING="'+cflags+'"') 2001 args.append('-DCMAKE_C_FLAGS_DEBUG:STRING="'+cflags+'"') 2002 args.append('-DCMAKE_C_FLAGS_RELEASE:STRING="'+cflags+'"') 2003 self.framework.popLanguage() 2004 if hasattr(self.compilers, 'CXX'): 2005 lang = self.framework.pushLanguage('Cxx').lower() 2006 args.append('-DCMAKE_CXX_COMPILER="'+self.framework.getCompiler()+'"') 2007 # bypass CMake findMPI() bug that can find compilers later in the PATH before the first one in the PATH. 2008 # relevant lines of findMPI() begins with if(_MPI_BASE_DIR) 2009 self.getExecutable(self.framework.getCompiler(), getFullPath=1, resultName='mpi_CC',setMakeMacro=0) 2010 args.append('-DMPI_CXX_COMPILER="'+self.mpi_CC+'"') 2011 cxxFlags = self.updatePackageCxxFlags(self.framework.getCompilerFlags()) 2012 2013 cxxFlags = cxxFlags.split(' ') 2014 # next line is needed because CMAKE passes CXX flags even when linking an object file! 2015 cxxFlags = self.rmArgs(cxxFlags,['-TP']) 2016 cxxFlags = ' '.join(cxxFlags) 2017 2018 args.append('-DCMAKE_CXX_FLAGS:STRING="{cxxFlags}"'.format(cxxFlags=cxxFlags)) 2019 args.append('-DCMAKE_CXX_FLAGS_DEBUG:STRING="{cxxFlags}"'.format(cxxFlags=cxxFlags)) 2020 args.append('-DCMAKE_CXX_FLAGS_RELEASE:STRING="{cxxFlags}"'.format(cxxFlags=cxxFlags)) 2021 langdialect = getattr(self.setCompilers,lang+'dialect',None) 2022 if langdialect: 2023 # langdialect is only set as an attribute if the user specifically chose a dialect 2024 # (see config/setCompilers.py::checkCxxDialect()) 2025 args.append('-DCMAKE_CXX_STANDARD={stdver}'.format(stdver=langdialect[-2:])) # extract '17' from c++17 2026 self.framework.popLanguage() 2027 2028 if hasattr(self.compilers, 'FC'): 2029 self.framework.pushLanguage('FC') 2030 args.append('-DCMAKE_Fortran_COMPILER="'+self.framework.getCompiler()+'"') 2031 # bypass CMake findMPI() bug that can find compilers later in the PATH before the first one in the PATH. 2032 # relevant lines of findMPI() begins with if(_MPI_BASE_DIR) 2033 self.getExecutable(self.framework.getCompiler(), getFullPath=1, resultName='mpi_FC',setMakeMacro=0) 2034 args.append('-DMPI_Fortran_COMPILER="'+self.mpi_FC+'"') 2035 args.append('-DCMAKE_Fortran_FLAGS:STRING="'+self.updatePackageFFlags(self.framework.getCompilerFlags())+'"') 2036 args.append('-DCMAKE_Fortran_FLAGS_DEBUG:STRING="'+self.updatePackageFFlags(self.framework.getCompilerFlags())+'"') 2037 args.append('-DCMAKE_Fortran_FLAGS_RELEASE:STRING="'+self.updatePackageFFlags(self.framework.getCompilerFlags())+'"') 2038 self.framework.popLanguage() 2039 2040 if self.setCompilers.LDFLAGS: 2041 ldflags = self.setCompilers.LDFLAGS.replace('"','\\"') # escape double quotes (") in LDFLAGS 2042 args.append('-DCMAKE_EXE_LINKER_FLAGS:STRING="'+ldflags+'"') 2043 if self.checkSharedLibrariesEnabled(): 2044 args.append('-DCMAKE_SHARED_LINKER_FLAGS:STRING="'+ldflags+'"') 2045 2046 if not config.setCompilers.Configure.isWindows(self.setCompilers.CC, self.log) and self.checkSharedLibrariesEnabled(): 2047 args.append('-DBUILD_SHARED_LIBS:BOOL=ON') 2048 args.append('-DBUILD_STATIC_LIBS:BOOL=OFF') 2049 else: 2050 args.append('-DBUILD_SHARED_LIBS:BOOL=OFF') 2051 args.append('-DBUILD_STATIC_LIBS:BOOL=ON') 2052 2053 if self.checkSharedLibrariesEnabled(): 2054 args.append('-DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=ON') 2055 args.append('-DCMAKE_BUILD_WITH_INSTALL_RPATH:BOOL=ON') 2056 2057 if 'MSYSTEM' in os.environ: 2058 args.append('-G "MSYS Makefiles"') 2059 for package in self.deps + self.odeps: 2060 if package.found and package.name == 'CUDA': 2061 with self.Language('CUDA'): 2062 args.append('-DCMAKE_CUDA_COMPILER='+self.getCompiler()) 2063 cuda_flags = self.updatePackageCUDAFlags(self.getCompilerFlags()) 2064 args.append('-DCMAKE_CUDA_FLAGS:STRING="{}"'.format(cuda_flags)) 2065 args.append('-DCMAKE_CUDA_FLAGS_DEBUG:STRING="{}"'.format(cuda_flags)) 2066 args.append('-DCMAKE_CUDA_FLAGS_RELEASE:STRING="{}"'.format(cuda_flags)) 2067 if hasattr(self.setCompilers,'CUDA_CXX'): # CUDA_CXX is set in cuda.py and might be mpicxx. 2068 args.append('-DCMAKE_CUDA_HOST_COMPILER="{}"'.format(self.setCompilers.CUDA_CXX)) 2069 else: 2070 with self.Language('C++'): 2071 args.append('-DCMAKE_CUDA_HOST_COMPILER="{}"'.format(self.getCompiler())) 2072 break 2073 if self.need35policy: 2074 args.append('-DCMAKE_POLICY_VERSION_MINIMUM=3.5') 2075 return args 2076 2077 def updateControlFiles(self): 2078 # Override to change build control files 2079 return 2080 2081 def Install(self): 2082 import os 2083 args = self.formCMakeConfigureArgs() 2084 if self.download and 'download-'+self.downloadname.lower()+'-cmake-arguments' in self.framework.clArgDB: 2085 args.append(self.argDB['download-'+self.downloadname.lower()+'-cmake-arguments']) 2086 args = ' '.join(args) 2087 conffile = os.path.join(self.packageDir,self.package+'.petscconf') 2088 fd = open(conffile, 'w') 2089 fd.write(args) 2090 fd.close() 2091 2092 if self.installNeeded(conffile): 2093 2094 if not self.cmake.found: 2095 raise RuntimeError('CMake not found, needed to build '+self.PACKAGE+'. Rerun configure with --download-cmake.') 2096 2097 self.updateControlFiles() 2098 2099 # effectively, this is 'make clean' 2100 folder = os.path.join(self.packageDir, self.cmakelistsdir, 'petsc-build') 2101 if os.path.isdir(folder): 2102 import shutil 2103 shutil.rmtree(folder) 2104 os.mkdir(folder) 2105 2106 try: 2107 self.logPrintBox('Configuring '+self.PACKAGE+' with CMake; this may take several minutes') 2108 output1,err1,ret1 = config.package.Package.executeShellCommand(self.cmake.cmake+' .. '+args, cwd=folder, timeout=900, log = self.log) 2109 except RuntimeError as e: 2110 self.logPrint('Error configuring '+self.PACKAGE+' with CMake '+str(e)) 2111 try: 2112 with open(os.path.join(folder, 'CMakeFiles', 'CMakeOutput.log')) as fd: 2113 conf = fd.read() 2114 fd.close() 2115 self.logPrint('Output in CMakeOutput.log for ' + self.PACKAGE+':\n'+conf) 2116 except: 2117 pass 2118 raise RuntimeError('Error configuring '+self.PACKAGE+' with CMake') 2119 try: 2120 self.logPrintBox('Compiling and installing '+self.PACKAGE+'; this may take several minutes') 2121 if self.parallelMake: pmake = self.make.make_jnp+' '+self.makerulename+' ' 2122 else: pmake = self.make.make+' '+self.makerulename+' ' 2123 output2,err2,ret2 = config.package.Package.executeShellCommand(pmake, cwd=folder, timeout=3000, log = self.log) 2124 output3,err3,ret3 = config.package.Package.executeShellCommand(self.make.make+' install', cwd=folder, timeout=3000, log = self.log) 2125 except RuntimeError as e: 2126 self.logPrint('Error running make on '+self.PACKAGE+': '+str(e)) 2127 raise RuntimeError('Error running make on '+self.PACKAGE) 2128 self.postInstall(output1+err1+output2+err2+output3+err3,conffile) 2129 # CMake has no option to set the library name to .lib instead of .a so rename libraries 2130 if config.setCompilers.Configure.isWindows(self.setCompilers.AR, self.log): 2131 import pathlib 2132 path = pathlib.Path(os.path.join(self.installDir,'lib')) 2133 self.logPrint('Changing .a files to .lib files in'+str(path)) 2134 for f in path.iterdir(): 2135 if f.is_file() and f.suffix in ['.a']: 2136 self.logPrint('Changing '+str(f)+' to '+str(f.with_suffix('.lib'))) 2137 f.rename(f.with_suffix('.lib')) 2138 2139 if os.path.isfile(os.path.join(self.packageDir,'pyproject.toml')): 2140 # this code is duplicated below for PythonPackage 2141 env = os.environ.copy() 2142 env["CMAKE_MODULE_PATH"] = folder 2143 env["CC"] = self.compilers.CC 2144 if 'Cxx' in self.buildLanguages: 2145 self.pushLanguage('C++') 2146 env["CXX"] = self.compilers.CXX 2147 env["CXXFLAGS"] = self.updatePackageCxxFlags(self.getCompilerFlags()) 2148 self.popLanguage() 2149 try: 2150 # Uses --no-deps so does not install any listed dependencies of the package that Python pip would normally install 2151 output,err,ret = config.package.Package.executeShellCommandSeq([[self.python.pyexe, '-m', 'pip', 'install', '--no-build-isolation', '--no-deps', '--upgrade-strategy', 'only-if-needed', '--upgrade', '--target='+os.path.join(self.installDir,'lib'), '.']],cwd=self.packageDir, env=env, timeout=30, log = self.log) 2152 except RuntimeError as e: 2153 raise RuntimeError('Error running pip install on '+self.pkgname) 2154 return self.installDir 2155 2156class PythonPackage(Package): 2157 def __init__(self, framework): 2158 Package.__init__(self, framework) 2159 self.download = 'PyPi' 2160 2161 def setupDependencies(self, framework): 2162 config.package.Package.setupDependencies(self, framework) 2163 self.python = framework.require('config.packages.Python', self) 2164 2165 def __str__(self): 2166 if self.found: 2167 s = self.name + ':\n' 2168 if hasattr(self,'pythonpath'): 2169 s += ' PYTHONPATH: '+self.pythonpath+'\n' 2170 return s 2171 return '' 2172 2173 def configureLibrary(self): 2174 import importlib 2175 2176 self.checkDownload() 2177 if self.builtafterpetsc: return 2178 2179 if self.argDB.get('with-' + self.name + '-dir'): 2180 dir = self.argDB['with-' + self.name + '-dir'] 2181 sys.path.insert(0, dir) 2182 try: 2183 self.logPrint('Trying to import ' + self.pkgname + ' which was indicated with the --with-' + self.name + '-dir option') 2184 importlib.import_module(self.pkgname) 2185 self.python.path.add(dir) 2186 self.pythonpath = dir 2187 except: 2188 raise RuntimeError('--with-' + self.name + '-dir=' + dir + ' was not successful, check the directory or use --download-' + self.name) 2189 elif self.argDB.get('download-' + self.name): 2190 dir = os.path.join(self.installDir,'lib') 2191 sys.path.insert(0, dir) 2192 try: 2193 self.logPrint('Trying to import ' + self.pkgname + ' which was just installed with the --download-' + self.name + ' option') 2194 # importlib.import_module fails python 3.11 a newer 2195 #importlib.import_module(self.pkgname) 2196 self.python.path.add(dir) 2197 self.pythonpath = dir 2198 except: 2199 raise RuntimeError('--download-' + self.name + ' was not successful, send configure.log to petsc-maint@mcs.anl.gov') 2200 elif self.argDB.get('with-' + self.name): 2201 try: 2202 self.logPrint('Trying to import ' + self.pkgname + ' which was just included with the --with-' + self.name + ' option') 2203 importlib.import_module(self.pkgname) 2204 except: 2205 raise RuntimeError(self.name + ' not found in default Python PATH! Suggest --download-' + self.name + ' or --with-' + self.name + '-dir') 2206 self.found = 1 2207 2208 def downLoad(self): 2209 pass 2210 2211 def Install(self): 2212 pkgname = self.pkgname 2213 if hasattr(self,'pkgbuild'): pkgname = self.pkgbuild 2214 if hasattr(self,'version') and self.version: pkgname = pkgname + '==' + self.version 2215 2216 if not self.builtafterpetsc: 2217 env = os.environ.copy() 2218 env["CC"] = self.compilers.CC 2219 if 'Cxx' in self.buildLanguages: 2220 self.pushLanguage('C++') 2221 env["CXX"] = self.compilers.CXX 2222 env["CXXFLAGS"] = self.updatePackageCxxFlags(self.getCompilerFlags()) 2223 self.popLanguage() 2224 2225 if 'PYTHONPATH' in env: 2226 env['PYTHONPATH'] = env['PYTHONPATH'] + ':' + os.path.join(self.installDir,'lib') 2227 else: 2228 env['PYTHONPATH'] = os.path.join(self.installDir,'lib') 2229 2230 try: 2231 output,err,ret = config.package.Package.executeShellCommandSeq([[self.python.pyexe, '-m', 'pip', 'install', '--no-build-isolation', '--no-deps', '--upgrade-strategy', 'only-if-needed', '--upgrade', '--target='+os.path.join(self.installDir,'lib'), pkgname]],env=env, timeout=30, log = self.log) 2232 except RuntimeError as e: 2233 raise RuntimeError('Error running pip install on '+self.pkgname) 2234 else: 2235 # provide access to previously built pip packages 2236 ppath = 'PYTHONPATH=' + os.path.join(self.installDir,'lib') 2237 2238 ccarg = 'CC=' + self.compilers.CC 2239 if 'Cxx' in self.buildLanguages: 2240 self.pushLanguage('C++') 2241 ccarg += ' CXX=' + self.compilers.CXX 2242 ccarg += ' CXXFLAGS="' + self.updatePackageCxxFlags(self.getCompilerFlags()) + '"' 2243 self.popLanguage() 2244 2245 if hasattr(self,'env'): 2246 for i in self.env: 2247 ccarg += ' ' + i + '=' + self.env[i] 2248 2249 self.addPost('', [ccarg + ' ' + ppath + ' ' + ' ' + self.python.pyexe + ' -m pip install --no-build-isolation --no-deps --upgrade-strategy only-if-needed --upgrade --target=' + os.path.join(self.installDir,'lib') + ' ' + pkgname]) 2250 return self.installDir 2251