1#!/usr/bin/env python3 2import os 3import re 4import sys 5import subprocess 6import time 7from pathlib import Path 8# 9# This script replaces the nagfor compiler wrapper that has design features 10# that make it impossible to work directly with PETSc (or any standard Unix compile system) 11# 12# It uses the nagfor option -dryrun to determine the programs nagfor would run and fixes the arguments to the calls 13# 14# It can be used with --download-mpich and --download-fblaslapack; PETSc configure automatically adds the option 15# -mismatch when building these packages (but not when building PETSc) 16# 17# -verbose and -v run the compile printing the commands used. -dryrun does NOT run the compiler just prints what nagfor would do 18# 19# -V or -version cause the version to be printed and then the compiler ends 20# 21# This script does multiple tries when the NAG license server does not respond 22# 23# nagfor puts temporary files in /tmp with a process id, if a different user runs and gets the same process id the compiler will fail 24# thus petscnagfor appends the /tmp file names with the USER id to generate unique file names. 25# nagfor does not handle -Wl,-rpath,path correctly (MPICH claims -Wl,-Wl,, works, but we can't change all of PETSc for this)) 26# nagfor links against two of its own .o files, if the link results in multiple definitions then it removes a set and tries again 27# this can cause problems when the linking is done by the C compiler which doesn't know about these libraries 28# nagfor cannot always handle -O0 so remove it 29# nagfor checks the length of all character strings; PETSc does not pass in the character string lengths to BLAS/LAPACK 30# hence this script removes the lines of generated C code that check the lengths. 31# nagfor does not handle -verbose or -v; so this script provides support 32# nagfor does not handle -shared; this script passes it to linker with -Wl,-shared 33# nagfor does not handle --whole-archive, --no-whole-archive that MPICH insists on putting in so they are removed 34# 35def runnagfor(args): 36 if not isinstance(args, list): args = args.strip().split(' ') 37 for i in range(1,4): 38 try: 39 sub = subprocess.Popen(args, stdout=subprocess.PIPE,stderr=subprocess.PIPE) 40 sub.wait() 41 output = sub.stdout.read().decode('UTF-8') 42 error = sub.stderr.read().decode('UTF-8') 43 status = sub.returncode 44 except: 45 print('petscnagfor: Unable to run process with job: '+' '.join(args)) 46 exit(1) 47 if (output+error).find('No licences currently available') == -1: 48 break; 49 time.sleep(30) 50 if (output+error).find('No licences currently available') > -1: 51 Path(os.path.join(os.path.dirname(__file__),'..','..','..','naglicenseproblem')).touch() 52 return (output,error,status) 53 54if __name__ == '__main__': 55 # Process options passed into compiler suite 56 verbose = 0 57 linkerargs = ['-rpath', '--whole-archive', '--no-whole-archive', '-soname' ] 58 args = sys.argv[1:] 59 argst = args 60 args = [] 61 i = 0 62 while i < len(argst): 63 flag = 1 64 if argst[i] == '-verbose': 65 verbose = 1 66 i = i + 1 67 continue 68 if argst[i] == '-v': 69 verbose = 1 70 i = i + 1 71 continue 72 if argst[i] == '-shared': 73 args.append('-Wl,-shared') 74 i = i + 1 75 continue 76 for j in linkerargs: 77 if argst[i].startswith(j): 78 args.append('-Wl,'+ argst[i] + ',' + argst[i+1]) 79 i = i + 1 80 flag = 0 81 break 82 if flag: args.append(argst[i]) 83 i = i + 1 84 if '-mismatch' in args or '-dusty' in args: 85 args = [x for x in args if not x.startswith('-C=')] 86 87 if '-version' in args or '-dryrun' in args or '-V' in args: 88 (output,error,status) = runnagfor(['nagfor']+args) 89 error = error.replace('-rpath ','-Wl,-rpath') 90 print(output) 91 print(error,file=sys.stderr) 92 exit(0) 93 94 if verbose: print(' '.join(['nagfor','-dryrun']+args)) 95 (output,error,status) = runnagfor(['nagfor','-dryrun']+args) 96 if status: 97 print(output) 98 print(error,file=sys.stderr) 99 exit(status) 100 101 # Run through the up to four commands that nagfor passes out from -dryrun 102 for i in (output+error).split('\n'): 103 import re 104 if not i or i.startswith('stdout') or i.startswith('stderr') or i.startswith('NAG') or i.startswith('Loading'): continue 105 if os.path.isfile(i[0:-1]): continue 106 i = i.replace(';',' ') 107 i = re.sub(r'/tmp/([a-zA-Z0-9_]*)\.([0-9]*)\.([fF90co]*)','/tmp/'+os.getenv('USER')+r"-\1.\2.\3",i) 108 for j in linkerargs: 109 i = i.replace(j+' ','-Wl,'+j+',').strip() 110 if i.find('forcomp') > -1: 111 i = i.replace(' -PIC','').strip() 112 # each option needs its own -options in front of it 113 i = i.replace(' -options','').strip() 114 i = i.replace(' -C=',' -options -C=') # note -C=xxx requires options but -C does not 115 i = i.replace(' -O',' -options -O') 116 i = i.replace(' -gline',' -options -gline') # note this is actually not needed 117 # add more compiler options here 118 js = [x for x in i.split(' ') if not x == '-Bstatic' and not x == '-Bdynamic'] 119 120 # Save all the .c files generated so they may be seen in the debugger 121 if (i.find('/fpp') == -1 and i.find('quickfit.o') == -1): 122 if i.find('forcomp') > -1: 123 last = js[-2][5:] 124 else: 125 last = js[-1][5:] 126 f1 = last.find('.') 127 f2 = last.rfind('.') 128 last = last[0:f1] + last[f2:] 129 if i.find('forcomp') > -1: 130 js[-2] = last 131 else: 132 js[-1] = last 133 134 if verbose: print(' '.join(js)) 135 (suboutput,error,status) = runnagfor(js) 136 if js[0].endswith('forcomp'): 137 lerror = '' 138 for k in error.split('\n'): 139 if k.find("Unused dummy variable") > -1: continue 140 if k.find("Fixed source form") > -1: continue 141 if k.find("[NAG Fortran Compiler normal termination") > -1: continue 142 if k.find("Line longer than") > -1: continue 143 lerror += k 144 if lerror: print("\n"+lerror,file=sys.stderr) 145 146 if (i.find('/fpp') == -1 and i.find('quickfit.o') == -1): 147 if i.find('forcomp') > -1: 148 if status: 149 print(suboutput) 150 print(error,file=sys.stderr) 151 exit(status) 152 fd = open(last) 153 f = fd.read() 154 fd.close() 155 fd = open(last,'w') 156 for k in f.split('\n'): 157 # comment out the next line if you want to see the generated C code in the debugger, not the Fortran 158 if k.find('# line') > -1: k = '/* removed hash line */' 159 if k.find('Len) __NAGf90_rterr') > -1: k = '/* removed length check code */' 160 fd.write(k+'\n') 161 fd.close() 162 163 if status and (suboutput+error).find('multiple') > -1: 164 js = i.strip().split(' ') 165 ks = [] 166 foundinit = 0 167 foundquickfit = 0 168 for x in js: 169 if not foundinit and x.endswith('init.o'): 170 foundinit = 1 171 continue 172 if not foundquickfit and x.endswith('quickfit.o'): 173 foundquickfit = 1 174 continue 175 ks.append(x) 176 (suboutput,error,status) = runnagfor(ks) 177 if status and (suboutput+error).find('multiple') > -1: 178 js = [x for x in js if not x.endswith('init.o') and not x.endswith('quickfit.o')] 179 (suboutput,error,status) = runnagfor(js) 180 if status: 181 print(suboutput) 182 print(error,file=sys.stderr) 183 exit(status) 184 185 exit(0) 186 187