1import config.package 2import os 3 4def getMakeUserPath(arch): 5 import re 6 file = os.path.join(arch, 'lib', 'petsc', 'conf', 'petscvariables') 7 try: 8 with open(file, 'r') as f: 9 return next(line for line in f if re.match(r'\AMAKE_USER\s*=',line)).split('=')[1].strip() 10 except: 11 return 'make' 12 13class Configure(config.package.GNUPackage): 14 def __init__(self, framework): 15 config.package.GNUPackage.__init__(self, framework) 16 self.minversion = '3.81' 17 self.download = ['https://ftp.gnu.org/gnu/make/make-4.4.1.tar.gz', 18 'https://web.cels.anl.gov/projects/petsc/download/externalpackages/make-4.4.1.tar.gz'] 19 self.downloadonWindows = 1 20 self.useddirectly = 0 21 self.linkedbypetsc = 0 22 self.printdirflag = '' 23 self.noprintdirflag = '' 24 self.paroutflg = '' 25 self.shuffleflg = '' 26 self.publicInstall = 0 # always install in PETSC_DIR/PETSC_ARCH (not --prefix) since this is not used by users 27 self.parallelMake = 0 28 self.skippackagelibincludedirs = 1 29 self.executablename = 'make' 30 self.skipMPIDependency = 1 31 return 32 33 def setupHelp(self, help): 34 import nargs 35 config.package.GNUPackage.setupHelp(self, help) 36 help.addArgument('MAKE', '-with-make-np=<np>', nargs.ArgInt(None, None, min=1, help='Default number of processes to use for parallel builds')) 37 help.addArgument('MAKE', '-with-make-test-np=<np>', nargs.ArgInt(None, None, min=1, help='Default number of processes to use for parallel tests')) 38 help.addArgument('MAKE', '-with-make-load=<load>', nargs.ArgReal(None, None, min=1.0, help='max load to use for parallel builds')) 39 help.addArgument('MAKE', '-download-make-cc=<prog>', nargs.Arg(None, None, 'C compiler for GNU make configure')) 40 help.addArgument('MAKE', '-with-make-exec=<executable>', nargs.Arg(None, None, 'Make executable to look for')) 41 return 42 43 def formGNUConfigureArgs(self): 44 '''Does not use the standard arguments at all since this does not use the MPI compilers etc 45 Sowing will chose its own compilers if they are not provided explicitly here''' 46 args = ['--prefix='+self.installDir] 47 args.append('--without-guile') 48 if 'download-make-cc' in self.argDB and self.argDB['download-make-cc']: 49 args.append('CC="'+self.argDB['download-make-cc']+'"') 50 return args 51 52 def Install(self): 53 ''' Cannot use GNUPackage Install because that one uses make which does not yet exist 54 This is almost a copy of GNUPackage Install just avoiding the use of make''' 55 args = self.formGNUConfigureArgs() 56 args = ' '.join(args) 57 conffile = os.path.join(self.packageDir,self.package+'.petscconf') 58 fd = open(conffile, 'w') 59 fd.write(args) 60 fd.close() 61 ### Use conffile to check whether a reconfigure/rebuild is required 62 if not self.installNeeded(conffile): 63 return self.installDir 64 ### Configure and Build package 65 try: 66 self.logPrintBox('Running configure on ' +self.PACKAGE+'; this may take several minutes') 67 output1,err1,ret1 = config.base.Configure.executeShellCommand('cd '+self.packageDir+' && ./configure '+args, timeout=2000, log = self.log) 68 except RuntimeError as e: 69 raise RuntimeError('Error running configure on ' + self.PACKAGE+': '+str(e)) 70 try: 71 self.logPrintBox('Running make on '+self.PACKAGE+'; this may take several minutes') 72 output1,err1,ret = config.package.Package.executeShellCommand('cd '+self.packageDir+' && ./build.sh && ./make install && ./make clean', timeout=2500, log = self.log) 73 except RuntimeError as e: 74 raise RuntimeError('Error building or installing make '+self.PACKAGE+': '+str(e)) 75 self.postInstall(output1+err1, conffile) 76 return self.installDir 77 78 def generateGMakeGuesses(self): 79 if self.argDB['download-make']: 80 self.log.write('Checking downloaded make\n') 81 yield os.path.join(self.installDir,'bin','make') 82 raise RuntimeError('Error! --download-make does not work on this system') 83 84 if 'with-make-exec' in self.argDB: 85 self.log.write('Looking for user provided Make executable '+self.argDB['with-make-exec']+'\n') 86 yield self.argDB['with-make-exec'] 87 raise RuntimeError('Error! User provided with-make-exec is not GNU make: '+self.argDB['with-make-exec']) 88 89 if 'with-make-dir' in self.argDB: 90 d = self.argDB['with-make-dir'] 91 self.log.write('Looking in user provided directory '+d+'\n') 92 yield os.path.join(d,'bin','make') 93 yield os.path.join(d,'bin','gmake') 94 raise RuntimeError('Error! User provided --with-make-dir=%s but %s/bin does not contain GNU make' % (d, d)) 95 96 yield 'make' 97 yield 'gmake' 98 99 def configureMake(self): 100 '''Check Guesses for GNU make''' 101 102 # Check internal make (found in PATH or specified with --download-make, --with-make-exec, --with-make-dir) 103 # Store in self.make 104 for gmake in self.generateGMakeGuesses(): 105 self.foundversion, self.haveGNUMake, self.haveGNUMake4, self.haveGNUMake44 = self.checkGNUMake(gmake) 106 if self.haveGNUMake: 107 self.getExecutable(gmake,getFullPath = 1,resultName = 'make') 108 break 109 110 if self.haveGNUMake: 111 # Set user-facing make (self.make_user) to 'make' if found in PATH, otherwise use the internal make (self.make) 112 found = self.getExecutable('make',getFullPath = 0,resultName = 'make_user') 113 if not found: 114 self.getExecutable(self.make,getFullPath = 0,resultName = 'make_user') 115 116 if not self.haveGNUMake4: 117 self.logPrintWarning('You have a version of GNU make older than 4.0. It will work, \ 118but may not support all the parallel testing options. You can install the \ 119latest GNU make with your package manager, such as Brew or MacPorts, or use \ 120the --download-make option to get the latest GNU make') 121 return 122 123 if os.path.exists('/usr/bin/cygcheck.exe'): 124 raise RuntimeError('''\ 125Incomplete cygwin install detected: the make utility is missing. 126Please rerun cygwin-setup and select module "make" for install, or try --download-make''') 127 else: 128 raise RuntimeError('''\ 129Could not locate the GNU make utility (version greater than or equal to %s) on your system. 130If it is already installed, specify --with-make-exec=<executable> or --with-make-dir=<directory>, or add it to PATH. 131Otherwise try --download-make or install "make" with a package manager.''' % self.minversion) 132 133 def checkGNUMake(self,make): 134 '''Check for GNU make''' 135 foundVersion = None 136 haveGNUMake = False 137 haveGNUMake4 = False 138 haveGNUMake44 = False 139 try: 140 import re 141 # accept gnumake version >= self.minversion only [as older version break with gmakefile] 142 (output, error, status) = config.base.Configure.executeShellCommand(make+' --version', log = self.log) 143 gver = re.compile('GNU Make ([0-9]+).([0-9]+)').match(output) 144 if not status and gver: 145 major = int(gver.group(1)) 146 minor = int(gver.group(2)) 147 if (major,minor) >= self.versionToTuple(self.minversion): haveGNUMake = True 148 if (major > 3): haveGNUMake4 = True 149 foundVersion = ".".join([str(major),str(minor)]) 150 if (major,minor) >= (4,4): haveGNUMake44 = True 151 except RuntimeError as e: 152 self.log.write('GNUMake check failed: '+str(e)+'\n') 153 return foundVersion, haveGNUMake, haveGNUMake4, haveGNUMake44 154 155 def setupGNUMake(self): 156 '''Setup other GNU make stuff''' 157 if self.haveGNUMake4 and not self.setCompilers.isDarwin(self.log) and not self.setCompilers.isFreeBSD(self.log): 158 self.paroutflg = "--output-sync=recurse" 159 if self.haveGNUMake44: 160 self.shuffleflg = "--shuffle" 161 162 # Setup make flags 163 self.printdirflag = ' --print-directory' 164 self.noprintdirflag = ' --no-print-directory' 165 # Use rules which look inside archives 166 self.addMakeRule('libc','${LIBNAME}(${OBJSC})') 167 self.addMakeRule('libcxx','${LIBNAME}(${OBJSCXX})') 168 self.addMakeRule('libcu','${LIBNAME}(${OBJSCU})') 169 self.addMakeRule('libf','${OBJSF}','-${AR} ${AR_FLAGS} ${LIBNAME} ${OBJSF}') 170 self.addMakeMacro('OMAKE_PRINTDIR', self.make+self.printdirflag) 171 self.addMakeMacro('OMAKE', self.make+self.noprintdirflag) 172 self.addDefine('OMAKE','"'+self.make+self.noprintdirflag+'"') 173 self.addMakeMacro('MAKE_PAR_OUT_FLG', self.paroutflg) 174 self.addMakeMacro('MAKE_SHUFFLE_FLG', self.shuffleflg) 175 return 176 177 def compute_make_np(self,i): 178 f16 = .80 179 f32 = .65 180 f64 = .50 181 f99 = .30 182 if (i<=2): return 2 183 elif (i<=4): return i 184 elif (i<=16): return int(4+(i-4)*f16) 185 elif (i<=32): return int(4+12*f16+(i-16)*f32) 186 elif (i<=64): return int(4+12*f16+16*f32+(i-32)*f64) 187 else: return int(4+12*f16+16*f32+32*f64+(i-64)*f99) 188 return 189 190 def compute_make_test_np(self,i): 191 f32 = 0.50 192 f99 = 0.35 193 if (i<=2): return 1 194 elif (i<=4): return 2 195 elif (i<=32): return int(i*f32) 196 else: return int(32*f32+(i-32)*f99) 197 return 198 199 def compute_make_load(self,i): 200 f64 = 1.0 201 f99 = .8 202 if (i<=64): return i*f64 203 else: return 64*f64+(i-64)*f99 204 return 205 206 def configureMakeNP(self): 207 '''check no of cores on the build machine [perhaps to do make '-j ncores']''' 208 try: 209 import multiprocessing # python-2.6 feature 210 cores = multiprocessing.cpu_count() 211 make_np = self.compute_make_np(cores) 212 make_test_np = self.compute_make_test_np(cores) 213 make_load = self.compute_make_load(cores) 214 self.logPrint('module multiprocessing found %d cores: using make_np = %d' % (cores,make_np)) 215 except (ImportError) as e: 216 cores = 2 217 make_np = 2 218 make_test_np = 1 219 make_load = 3 220 self.logPrint('module multiprocessing *not* found: using default make_np = %d' % make_np) 221 222 if 'with-make-np' in self.argDB and self.argDB['with-make-np']: 223 make_np = self.argDB['with-make-np'] 224 self.logPrint('using user-provided make_np = %d' % make_np) 225 226 if not self.argDB.get('with-mpi'): 227 make_test_np = make_np 228 229 if 'with-make-test-np' in self.argDB and self.argDB['with-make-test-np']: 230 make_test_np = self.argDB['with-make-test-np'] 231 self.logPrint('using user-provided make_test_np = %d' % make_test_np) 232 233 if 'with-make-load' in self.argDB and self.argDB['with-make-load']: 234 make_load = self.argDB['with-make-load'] 235 self.logPrint('using user-provided make_load = %f' % make_load) 236 237 self.make_np = make_np 238 self.make_test_np = make_test_np 239 self.make_load = make_load 240 self.addMakeMacro('MAKE_NP',str(make_np)) 241 self.addMakeMacro('MAKE_TEST_NP',str(make_test_np)) 242 self.addMakeMacro('MAKE_LOAD',str(make_load)) 243 self.addMakeMacro('NPMAX',str(cores)) 244 self.make_jnp_list = [self.make, '-j'+str(self.make_np), '-l'+str(self.make_load)] 245 self.make_jnp = ' '.join(self.make_jnp_list) 246 return 247 248 def configure(self): 249 config.package.GNUPackage.configure(self) 250 self.executeTest(self.configureMake) 251 self.executeTest(self.setupGNUMake) 252 self.executeTest(self.configureMakeNP) 253 return 254