1#!/usr/bin/env python3 2# 3# Processes PETSc's (or SLEPc's) header and source files to determine 4# the PETSc enums, structs, functions, and classes 5# 6# Calling sequence: 7# getAPI(directory, pkgname = 'petsc', verbose = False) 8# 9# Notes: 10# const char *fill_array_of_strings[] + fills up an array of strings; the array already exists in the calling routine 11# const char * const set_with_array_of_strings + passes in an array of strings to be used by subroutine 12# const char * const * returns_an_array_of_strings + returns to the user an array of strings 13## 14import os 15import re 16import sys 17import pickle 18import pathlib 19import subprocess 20from subprocess import check_output 21 22def mansecpath(mansec): 23 '''Given a manual section, returns the path where it is located (it differs in some SLEPc classes)''' 24 return os.path.join('sys','classes',mansec) if mansec in ['bv','ds','fn','rg','st'] else mansec 25 26def verbosePrint(verbose, text): 27 '''Prints the text if run with verbose option''' 28 if verbose: print(text) 29 30classes = {} 31funcs = {} # standalone functions like PetscInitialize() 32allfuncs = set() # both class and standalone functions, used to prevent duplicates 33enums = {} 34senums = {} # like enums except strings instead of integer values for enumvalue 35typedefs = {} 36aliases = {} 37structs = {} 38includefiles = {} 39mansecs = {} # mansec[mansecname] = set(all submansecnames in mansecname) 40submansecs = set() 41 42regcomment = re.compile(r'/\* [-A-Za-z _(),<>|^\*/0-9.:=\[\]\.;]* \*/') 43regcomment2 = re.compile(r'// [-A-Za-z _(),<>|^\*/0-9.:=\[\]\.;]*') 44regblank = re.compile(r' [ ]*') 45 46def displayIncludeMansec(obj): 47 return ' ' + str(obj.includefile)+' (' + str(obj.mansec) + ')\n' 48 49def displayFile(obj): 50 return ' ' + str(obj.dir) + '/' + str(obj.file) + '\n' 51 52class Typedef: 53 '''Represents typedef oldtype newtype''' 54 def __init__(self, name, mansec, includefile, value, *args, **kwargs): 55 self.name = name 56 self.mansec = mansec 57 self.includefile = includefile 58 self.value = value 59 60 def __str__(self): 61 mstr = str(self.name) + ' ' + str(self.value)+'\n' 62 mstr += displayIncludeMansec(self) 63 return mstr 64 65class Function: 66 '''Represents a function in a class or standalone''' 67 def __init__(self, name, *args, **kwargs): 68 self.name = name 69 self.mansec = None 70 self.file = None 71 self.includefile = None 72 self.dir = None 73 self.opaque = False 74 self.opaquestub = False # only Fortran module interface is automatic, C stub is custom 75 self.penss = False # Function is labeled with PeNS or PeNSS 76 self.arguments = [] 77 78 def __str__(self): 79 mstr = ' ' + str(self.name) + '()\n' 80 mstr += ' ' + displayIncludeMansec(self) 81 mstr += ' ' + displayFile(self) 82 if self.opaque: mstr += ' opaque binding\n' 83 elif self.opaque: mstr += ' opaque stub\n' 84 if self.arguments: 85 mstr += ' Arguments\n' 86 for i in self.arguments: 87 mstr += ' ' + str(i) 88 return mstr 89 90class Argument: 91 '''Represents an argument in a Function''' 92 def __init__(self, name = None, typename = None, stars = 0, array = False, const = False, *args, **kwargs): 93 self.name = name 94 self.typename = typename 95 self.stars = stars 96 self.array = array 97 self.optional = False 98 self.const = const 99 self.isfunction = False 100 self.fnctnptr = None # contains the signature if argument is a function pointer 101 # PETSc returns strings in two ways either 102 # with a pointer to an array: char *[] 103 # or by copying the string into a given array with a given length: char [], size_t len 104 self.stringlen = False # if true the argument is the length of the previous argument which is a character string 105 self.char_type = None # 'string' if it is a string, 'single' if it is a single character 106 107 def __str__(self): 108 mstr = ' ' + str(self.typename) + ' ' 109 stars = self.stars 110 while stars: 111 mstr += '*' 112 stars = stars - 1 113 mstr += str(self.name) 114 if self.array: mstr += '[]' 115 if self.optional: mstr += ' optional' 116 mstr += '\n' 117 return mstr 118 119class Struct: 120 '''Represents a C struct''' 121 def __init__(self, name, mansec, includefile, opaque, records, *args, **kwargs): 122 self.name = name 123 self.mansec = mansec 124 self.includefile = includefile 125 self.opaque = opaque 126 self.records = records 127 128 def __str__(self): 129 mstr = str(self.name) + '\n' 130 mstr += displayIncludeMansec(self) 131 if self.opaque: mstr += ' opaque\n' 132 mstr += ' Records:\n' 133 for i in self.records: 134 mstr += str(i) 135 return mstr 136 137class Record: 138 '''Represents an entry in a struct''' 139 def __init__(self, rawrecord, *args, **kwargs): 140 self.name = None 141 # name is currently unused and type contains the type followed by all the names with that type: e.g. PetscInt i,j,k 142 self.type = rawrecord 143 144 def __str__(self): 145 mstr = ' ' + str(self.type)+'\n' 146 return mstr 147 148class Enum: 149 '''Represents a C enum''' 150 def __init__(self, name, mansec, includefile, values, *args, **kwargs): 151 self.name = name 152 self.mansec = mansec 153 self.includefile = includefile 154 self.values = values 155 156 def __str__(self): 157 mstr = str(self.name) + '\n' 158 mstr += displayIncludeMansec(self) 159 for i in self.values: 160 mstr += ' ' + str(i) + '\n' 161 return mstr 162 163class Senum: 164 '''Represents a PETSc string enum; a name and a set of string values''' 165 def __init__(self, name, mansec, includefile, values, *args, **kwargs): 166 self.name = name 167 self.mansec = mansec 168 self.includefile = includefile 169 self.values = values 170 171 def __str__(self): 172 mstr = str(self.name) + '\n' 173 mstr += displayIncludeMansec(self) 174 for i in self.values.keys(): 175 mstr += ' ' + i + ' ' + self.values[i] + '\n' 176 return mstr 177 178class IncludeFile: 179 '''Represents an include (interesting) file found and what interesting files it includes''' 180 def __init__(self, mansec, includefile, included, *args, **kwargs): 181 self.mansec = mansec 182 self.includefile = includefile 183 self.included = included # include files it includes 184 185 def __str__(self): 186 mstr = str(self.mansec) + ' ' + str(self.includefile) + '\n' 187 for i in self.included: 188 mstr += ' ' + str(i) + '\n' 189 return mstr 190 191class Class: 192 '''Represents a class (PetscObject and other _n_ opaque objects)''' 193 def __init__(self, name, *args, **kwargs): 194 self.name = name 195 self.mansec = None 196 self.includefile = None 197 self.petscobject = True 198 self.functions = {} 199 200 def __str__(self): 201 mstr = str(self.name) + '\n' 202 mstr += displayIncludeMansec(self) 203 mstr += ' PetscObject <' + str(self.petscobject) + '>\n\n' 204 for i in self.functions.keys(): 205 mstr += ' ' + str(self.functions[i]) + '\n' 206 return mstr 207 208def findmansec(line,mansec,submansec): 209 '''Finds mansec and submansec in line from include/petsc*.h''' 210 if line.find(' MANSEC') > -1: 211 mansec = re.sub(r'[ ]*/\* [ ]*MANSEC[ ]*=[ ]*','',line).strip('\n').strip('*/').strip() 212 if mansec == line[0].strip('\n'): 213 mansec = re.sub('MANSEC[ ]*=[ ]*','',line.strip('\n').strip()) 214 mansec = mansec.lower() 215 if line.find('SUBMANSEC') > -1: 216 submansec = re.sub(r'[ ]*/\* [ ]*SUBMANSEC[ ]*=[ ]*','',line).strip('\n').strip('*/').strip() 217 if submansec == line[0].strip('\n'): 218 submansec = re.sub('SUBMANSEC[ ]*=[ ]*','',line.strip('\n').strip()) 219 submansec = submansec.lower() 220 if not mansec: mansec = submansec 221 submansecs.add(submansec) 222 if not mansec in mansecs: mansecs[mansec] = set() 223 mansecs[mansec].add(submansec) 224 return mansec,submansec 225 226def getIncludeFiles(filename,pkgname): 227 import re 228 229 file = os.path.basename(filename) 230 mansec = None 231 reginclude = re.compile(r'^#include <[A-Za-z_0-9]*.h') 232 f = open(filename) 233 line = f.readline() 234 included = [] 235 while line: 236 mansec,submansec = findmansec(line,mansec,None) 237 fl = reginclude.search(line) 238 if fl and not line.find('deprecated') > -1: 239 line = regcomment.sub("",line) 240 line = regcomment2.sub("",line) 241 line = line.replace('#include <','').replace('>','').strip() 242 if not line == file and os.path.isfile(os.path.join('include',line)) or (pkgname == 'slepc' and line.startswith('petsc')): 243 included.append(line) 244 line = f.readline() 245 includefiles[file] = IncludeFile(mansec,file,included) 246 f.close() 247 248def getEnums(filename): 249 import re 250 regtypedef = re.compile(r'typedef [ ]*enum') 251 reg = re.compile(r'}') 252 regname = re.compile(r'}[ A-Za-z0-9]*') 253 254 file = os.path.basename(filename).replace('types.h','.h') 255 f = open(filename) 256 line = f.readline() 257 submansec = None 258 mansec = None 259 while line: 260 mansec,submansec = findmansec(line,mansec,submansec) 261 fl = regtypedef.search(line) 262 if fl: 263 struct = line 264 while line: 265 fl = reg.search(line) 266 if fl: 267 struct = regcomment.sub("",struct) 268 struct = struct.replace("\\","") 269 struct = struct.replace("\n","") 270 struct = struct.replace(";","") 271 struct = struct.replace("typedef enum","") 272 struct = regblank.sub(" ",struct) 273 274 name = regname.search(struct) 275 name = name.group(0) 276 name = name.replace("} ","") 277 278 values = struct[struct.find("{") + 1:struct.find("}")] 279 values = values.split(',') 280 281 ivalues = [] 282 for i in values: 283 if i: 284 if i[0] == " ": i = i[1:] 285 ivalues.append(i) 286 287 enums[name] = Enum(name,mansec,file,ivalues) 288 break 289 line = f.readline() 290 struct = struct + line 291 line = f.readline() 292 f.close() 293 294def getSenums(filename): 295 import re 296 regdefine = re.compile(r'typedef const char \*[A-Za-z]*;') 297 file = os.path.basename(filename).replace('types.h','.h') 298 mansec = None 299 f = open(filename) 300 line = f.readline() 301 while line: 302 mansec,submansec = findmansec(line,mansec,None) 303 fl = regdefine.search(line) 304 if fl: 305 senum = fl.group(0)[20:-1] 306 line = regblank.sub(" ",f.readline().strip()) 307 d = {} 308 while line: 309 values = line.split(" ") 310 d[values[1]] = values[2] 311 line = regblank.sub(" ",f.readline().strip()) 312 senums[senum] = Senum(senum,mansec,file,d) 313 line = f.readline() 314 f.close() 315 316def getTypedefs(filename): 317 import re 318 file = os.path.basename(filename).replace('types.h','.h') 319 regdefine = re.compile(r'typedef [A-Za-z0-9_]* [ ]*[A-Za-z0-9_]*;') 320 submansec = None 321 mansec = None 322 f = open(filename) 323 line = f.readline() 324 while line: 325 mansec,submansec = findmansec(line,mansec,submansec) 326 fl = regdefine.search(line) 327 if fl: 328 typedef = fl.group(0).split()[2][0:-1]; 329 if typedef in typedefs: 330 typedefs[typedef].name = None # mark to be deleted since it appears multiple times (with presumably different values) 331 else: 332 typedefs[typedef] = Typedef(typedef,mansec,file,fl.group(0).split()[1]) 333 line = f.readline() 334 f.close() 335 336def getStructs(filename): 337 import re 338 file = os.path.basename(filename).replace('types.h','.h') 339 regtypedef = re.compile(r'^typedef [ ]*struct {') 340 reg = re.compile(r'}') 341 regname = re.compile(r'}[ A-Za-z]*') 342 submansec = None 343 mansec = None 344 f = open(filename) 345 line = f.readline() 346 while line: 347 mansec,submansec = findmansec(line,mansec,submansec) 348 fl = regtypedef.search(line) 349 opaque = False 350 if fl: 351 struct = line 352 while line: 353 fl = reg.search(line) 354 if fl: 355 struct = regcomment.sub("",struct) 356 struct = regcomment2.sub("",struct) 357 struct = struct.replace("\\","") 358 struct = struct.replace("\n","") 359 struct = struct.replace("typedef struct {","") 360 struct = regblank.sub(" ",struct) 361 struct = struct.replace("; ",";") 362 363 name = regname.search(struct) 364 name = name.group(0) 365 name = name.replace("} ","") 366 367 values = struct[struct.find("{") + 1:struct.find(";}")] 368 if values.find('#') > -1 or values.find('*') > -1 or values.find('][') > -1: opaque = True 369 if not values.find('#') == -1: opaque = True 370 values = values.split(";") 371 ivalues = [] 372 for i in values: 373 ivalues.append(Record(i.strip())) 374 structs[name] = Struct(name,mansec,file,opaque,ivalues) 375 break 376 line = f.readline() 377 struct = struct + line 378 line = f.readline() 379 f.close() 380 381def getClasses(filename): 382 import re 383 regclass = re.compile(r'typedef struct _[np]_[A-Za-z_]*[ ]*\*') 384 regnclass = re.compile(r'typedef struct _n_[A-Za-z_]*[ ]*\*') 385 regsemi = re.compile(r';') 386 submansec = None 387 mansec = None 388 file = os.path.basename(filename).replace('types.h','.h') 389 f = open(filename) 390 line = f.readline() 391 while line: 392 mansec,submansec = findmansec(line,mansec,submansec) 393 fl = regclass.search(line) 394 gl = regnclass.search(line) 395 if fl: 396 struct = line 397 struct = regclass.sub("",struct) 398 struct = regcomment.sub("",struct) 399 struct = regblank.sub("",struct) 400 struct = regsemi.sub("",struct) 401 struct = struct.replace("\n","") 402 classes[struct] = Class(struct) 403 if not submansec: raise RuntimeError('No SUBMANSEC in file ' + filename) 404 classes[struct].mansec = mansec 405 classes[struct].includefile = file 406 if gl: classes[struct].petscobject = False 407 line = f.readline() 408 f.close() 409 410def findlmansec(dir): # could use dir to determine mansec 411 '''Finds mansec and submansec from a makefile''' 412 file = os.path.join(dir,'makefile') 413 mansec = None 414 submansec = None 415 with open(file) as mklines: 416 submansecl = [line for line in mklines if line.find('BFORTSUBMANSEC') > -1] 417 if submansecl: 418 submansec = re.sub('BFORTSUBMANSEC[ ]*=[ ]*','',submansecl[0]).strip('\n').strip().lower() 419 if not submansec: 420 with open(file) as mklines: 421 submansecl = [line for line in mklines if (line.find('SUBMANSEC') > -1 and line.find('BFORT') == -1)] 422 if submansecl: 423 submansec = re.sub('SUBMANSEC[ ]*=[ ]*','',submansecl[0]).strip('\n').strip().lower() 424 with open(file) as mklines: 425 mansecl = [line for line in mklines if line.startswith('MANSEC')] 426 if mansecl: 427 mansec = re.sub('MANSEC[ ]*=[ ]*','',mansecl[0]).strip('\n').strip().lower() 428 if not submansec: submansec = mansec 429 return mansec,submansec 430 431def getpossiblefunctions(pkgname): 432 '''Gets a list of all the functions in the include/ directory that may be used in the binding for other languages''' 433 try: 434 output = check_output('grep -F -e "' + pkgname.upper() + '_EXTERN PetscErrorCode" -e "static inline PetscErrorCode" include/*.h', shell=True).decode('utf-8') 435 except subprocess.CalledProcessError as e: 436 raise RuntimeError('Unable to find possible functions in the include files') 437 funs = output.replace('' + pkgname.upper() + '_EXTERN','').replace('PetscErrorCode','').replace('static inline','') 438 functiontoinclude = {} 439 for i in funs.split('\n'): 440 file = i[i.find('/') + 1:i.find('.') + 2] 441 f = i[i.find(': ') + 2:i.find('(')].strip() 442 functiontoinclude[f] = file.replace('types','') 443 444 try: 445 output = check_output('grep "' + pkgname.upper() + '_EXTERN [a-zA-Z]* *[a-zA-Z]*;" include/*.h', shell=True).decode('utf-8') 446 except subprocess.CalledProcessError as e: 447 raise RuntimeError('Unable to find possible functions in the include files') 448 funs = output.replace('' + pkgname.upper() + '_EXTERN','') 449 for i in funs.split('\n'): 450 if not i: continue 451 i = i.replace(';','').split() 452 file = i[0][i[0].find('/') + 1:i[0].find('.') + 2] 453 functiontoinclude[i[2]] = file.replace('types','') 454 return functiontoinclude 455 456def getFunctions(mansec, functiontoinclude, filename): 457 '''Appends the functions found in filename to their associated class classes[i], or funcs[] if they are classless''' 458 import re 459 regfun = re.compile(r'^[static inline]*PetscErrorCode ') 460 regarg = re.compile(r'\([A-Za-z0-9*_\[\]]*[,\) ]') 461 regerror = re.compile(r'PetscErrorCode') 462 reg = re.compile(r' ([*])*[a-zA-Z0-9_]*([\[\]]*)') 463 regname = re.compile(r' [*]*([a-zA-Z0-9_]*)[\[\]]*') 464 465 # for finding xxx (*yyy)([const] zzz, ...) 466 regfncntnptrname = re.compile(r'[A-Za-z0-9]* \(\*([A-Za-z0-9]*)\)\([_a-zA-Z0-9, *\[\]]*\)') 467 regfncntnptr = re.compile(r'[A-Za-z0-9]* \(\*[A-Za-z0-9]*\)\([_a-zA-Z0-9, *\[\]]*\)') 468 regfncntnptrtype = re.compile(r'([A-Za-z0-9]*) \(\*[A-Za-z0-9]*\)\([_a-zA-Z0-9, *\[\]]*\)') 469 470 # for rejecting (**func), (*indices)[3], (*monitor[X]), and xxx (*)(yyy) 471 regfncntnptrptr = re.compile(r'\([*]*\*\*[ A-Za-z0-9]*\)') 472 regfncntnptrarray = re.compile(r'\(\*[A-Za-z0-9]*\)\[[A-Za-z0-9_]*\]') 473 regfncntnptrarrays = re.compile(r'\(\*[A-Za-z0-9]*\[[A-Za-z0-9]*\]\)') 474 regfncntnptrnoname = re.compile(r'\(\*\)') 475 476 rejects = ['PetscErrorCode','...','<','(*)','(**)','off_t','MPI_Datatype','va_list','PetscStack','Ceed'] 477 # 478 # search through list BACKWARDS to get the longest match 479 # 480 classlist = classes.keys() 481 classlist = sorted(classlist) 482 classlist.reverse() 483 f = open(filename) 484 line = f.readline() 485 while line: 486 fl = regfun.search(line) 487 if fl: 488 opaque = False 489 opaquestub = False 490 penss = False 491 if line[0:line.find('(')].find('_') > -1: 492 line = f.readline() 493 continue 494 line = line.replace('PETSC_UNUSED ','') 495 line = line.replace('PETSC_RESTRICT ','') 496 line = line.strip() 497 if line.endswith(' PeNS'): 498 opaque = True 499 penss = True 500 line = line[0:-5] 501 if line.endswith(' PeNSS'): 502 opaquestub = True 503 penss = True 504 line = line[0:-6] 505 if line.endswith(';'): 506 line = f.readline() 507 continue 508 if not line.endswith(')'): 509 line = f.readline() 510 continue 511 line = regfun.sub("",line) 512 line = regcomment.sub("",line) 513 line = line.replace("\n","") 514 line = line.strip() 515 name = line[:line.find("(")] 516 if not name in functiontoinclude or name in allfuncs: 517 line = f.readline() 518 continue 519 520 # find arguments that return a function pointer (**xxx) 521 fnctnptrptrs = regfncntnptrptr.findall(line) 522 if fnctnptrptrs: 523 opaque = True 524 #print('Opaque due to (**xxx) argument ' + line) 525 526 # find arguments such as PetscInt (*indices)[3]) 527 fnctnptrarrays = regfncntnptrarray.findall(line) 528 if fnctnptrarrays: 529 opaque = True 530 #print('Opaque due to (*xxx][n] argument ' + line) 531 532 # find arguments such as PetscInt (*indices[XXX]) 533 fnctnptrarrays = regfncntnptrarrays.findall(line) 534 if fnctnptrarrays: 535 opaque = True 536 #print('Opaque due to (*xxx[yyy]) argument ' + line) 537 538 # find arguments that are unnamed function pointers (*) 539 fnctnptrnoname = regfncntnptrnoname.findall(line) 540 if fnctnptrnoname: 541 opaque = True 542 #print('Opaque due to (*) argument ' + line) 543 544 # find all function pointers in the arguments xxx (*yyy)(zzz) and convert them to external yyy 545 fnctnptrs = regfncntnptr.findall(line) 546 fnctnptrnames = regfncntnptrname.findall(line) 547 #if len(fnctnptrs): print(line) 548 for i in range(0,len(fnctnptrs)): 549 line = line.replace(fnctnptrs[i], 'external ' + fnctnptrnames[i]) 550 #if len(fnctnptrs): print(line) 551 552 fl = regarg.search(line) 553 if fl: 554 fun = Function(name) 555 fun.opaque = opaque 556 fun.opaquestub = opaquestub 557 fun.penss = penss 558 fun.file = os.path.basename(filename) 559 fun.mansec = mansec 560 fun.dir = os.path.dirname(filename) 561 562 arg = fl.group(0) 563 arg = arg[1:-1] 564 reject = 0 565 for i in rejects: 566 if line.find(i) > -1: 567 reject = 1 568 if not reject: 569 fun.includefile = functiontoinclude[name] 570 args = line[line.find("(") + 1:line.find(")")] 571 if args != 'void': 572 for i in ['FILE','hid_t','MPI_File','MPI_Offset','MPI_Info','PETSC_UINTPTR_T','LinkMode']: 573 if args.find(i) > -1: 574 fun.opaque = True 575 args = args.split(",") 576 argnames = [] 577 for i in args: 578 arg = Argument() 579 if i.find('**') > -1 and not i.strip().startswith('void'): fun.opaque = True 580 if i.find('unsigned ') > -1: fun.opaque = True 581 if i.count('const ') > 1: fun.opaque = True 582 if i.count("const "): arg.const = True 583 i = i.replace("const ","") 584 if i.find('PeOp ') > -1: arg.optional = True 585 i = i.replace('PeOp ','') 586 i = i.strip() 587 if re.match(r'[a-zA-Z 0-9_]*\[[0-9]*\]$',i.replace('*','')): 588 arg.array = True 589 i = i[:i.find('[')] 590 if i.find('*') > -1: arg.stars = 1 591 if i.find('**') > -1: arg.stars = 2 592 argname = re.findall(r' [*]*([a-zA-Z0-9_]*)[\[\]]*',i) 593 if argname and argname[0]: 594 arg.name = argname[0] 595 if arg.name.lower() in argnames: 596 arg.name = 'M_' + arg.name 597 argnames.append(arg.name.lower()) 598 else: 599 arg.name = 'noname' 600 fun.opaque = True 601 i = regblank.sub('',reg.sub(r'\1\2 ',i).strip()).replace('*','').replace('[]','') 602 arg.typename = i 603 # fix input character arrays that are written as *variable name 604 if arg.typename == 'char' and not arg.array and arg.stars == 1: 605 arg.array = 1 606 arg.stars = 0 607 if arg.typename == 'char' and not arg.array and arg.stars == 0: 608 arg.char_type = 'single' 609 #if arg.typename == 'char' and arg.array and arg.stars: 610 # arg.const = False 611 if arg.typename.endswith('Fn'): 612 arg.isfunction = True 613 if arg.typename == 'external': 614 arg.fnctnptr = fnctnptrs[fnctnptrnames.index(arg.name)] 615 fun.opaquestub = True 616 if arg.typename.count('_') and not arg.typename in ['MPI_Comm', 'size_t']: 617 fun.opaque = True 618 if fun.arguments and not fun.arguments[-1].const and fun.arguments[-1].typename == 'char' and arg.typename == 'size_t': 619 arg.stringlen = True 620 fun.arguments.append(arg) 621 622 #print('Opaqueness of function ' + fun.name + ' ' + str(fun.opaque) + ' ' + str(fun.opaquestub)) 623 # add function to appropriate class 624 allfuncs.add(name) 625 notfound = True 626 for i in classlist: 627 if name.lower().startswith(i.lower()): 628 classes[i].functions[name] = fun 629 notfound = False 630 break 631 if notfound: 632 funcs[name] = fun 633 634 line = f.readline() 635 f.close() 636 637ForbiddenDirectories = ['tests', 'tutorials', 'doc', 'output', 'ftn-custom', 'ftn-auto', 'ftn-mod', 'binding', 'binding', 'config', 'lib', '.git', 'share', 'systems'] 638 639def getAPI(directory,pkgname = 'petsc',verbose = False): 640 global typedefs 641 args = [os.path.join('include',i) for i in os.listdir(os.path.join(directory,'include')) if i.endswith('.h') and not i.endswith('deprecated.h')] 642 for i in args: 643 getIncludeFiles(i,pkgname) 644 verbosePrint(verbose, 'Include files -------------------------------------') 645 for i in includefiles.keys(): 646 verbosePrint(verbose, includefiles[i]) 647 648 for i in args: 649 getEnums(i) 650 verbosePrint(verbose, 'Enums ---------------------------------------------') 651 for i in enums.keys(): 652 verbosePrint(verbose, enums[i]) 653 654 for i in args: 655 getSenums(i) 656 verbosePrint(verbose, 'String enums ---------------------------------------------') 657 for i in senums.keys(): 658 verbosePrint(verbose, senums[i]) 659 660 for i in args: 661 getStructs(i) 662 verbosePrint(verbose, 'Structs ---------------------------------------------') 663 for i in structs.keys(): 664 verbosePrint(verbose, structs[i]) 665 666 for i in args: 667 getTypedefs(i) 668 cp = {} 669 for i in typedefs.keys(): 670 if typedefs[i].name: cp[i] = typedefs[i] # delete ones marked as having multiple definitions 671 typedefs = cp 672 verbosePrint(verbose, 'Typedefs ---------------------------------------------') 673 for i in typedefs.keys(): 674 verbosePrint(verbose, typedefs[i]) 675 676 for i in args: 677 getClasses(i) 678 679 functiontoinclude = getpossiblefunctions(pkgname) 680 for dirpath, dirnames, filenames in os.walk(os.path.join(directory,'src'),topdown=True): 681 dirpath = dirpath.replace(directory + '/','') 682 dirnames[:] = [d for d in dirnames if d not in ForbiddenDirectories] 683 if not os.path.isfile(os.path.join(dirpath,'makefile')): continue 684 mansec, submansec = findlmansec(dirpath) 685 for i in os.listdir(dirpath): 686 if i.endswith('.c') or i.endswith('.cxx'): getFunctions(mansec, functiontoinclude, os.path.join(dirpath,i)) 687 for i in args: 688 mansec = None 689 with open(i) as fd: 690 lines = fd.read().split('\n') 691 for line in lines: 692 if line.find(' MANSEC') > -1: 693 mansec = re.sub(r'[ ]*/\* [ ]*MANSEC[ ]*=[ ]*','',line).strip('\n').strip('*/').strip() 694 if not mansec: 695 with open(i) as fd: 696 lines = fd.read().split('\n') 697 for line in lines: 698 if line.find(' SUBMANSEC') > -1: 699 mansec = re.sub(r'[ ]*/\* [ ]*SUBMANSEC[ ]*=[ ]*','',line).strip('\n').strip('*/').strip() 700 if not mansec: raise RuntimeError(i + ' does not have a MANSEC or SUBMANSEC') 701 getFunctions(mansec.lower(), functiontoinclude, i) 702 703 if pkgname == 'petsc': 704 # a few special cases that must be handled manually 705 typedefs['PetscBool'] = Typedef('PetscBool','sys','petscsys.h','PetscBool') 706 classes['PetscNull'] = Class('PetscNull') 707 classes['PetscNull'].includefile = 'petscsys.h' 708 classes['PetscNull'].mansec = 'sys' 709 classes['PetscNull'].submansec = 'sys' 710 classes['PetscNull'].petscobject = False 711 classes['PetscObject'].petscobject = False 712 classes['PetscObject'].includefile = 'petscsys.h' 713 714 # these functions are funky macros in C and cannot be parsed directly 715 funcs['PetscOptionsBegin'] = Function('PetscOptionsBegin') 716 funcs['PetscOptionsBegin'].mansec = 'sys' 717 funcs['PetscOptionsBegin'].file = 'aoptions.c'; 718 funcs['PetscOptionsBegin'].includefile = 'petscoptions.h' 719 funcs['PetscOptionsBegin'].dir = 'src/sys/objects/' 720 funcs['PetscOptionsBegin'].opaquestub = True 721 funcs['PetscOptionsBegin'].arguments = [Argument('comm', 'MPI_Comm'), 722 Argument('prefix', 'char', stars = 0, array = True, const = True), 723 Argument('mess', 'char', stars = 0, array = True, const = True), 724 Argument('sec', 'char', stars = 0, array = True, const = True)] 725 funcs['PetscOptionsEnd'] = Function('PetscOptionsEnd') 726 funcs['PetscOptionsEnd'].mansec = 'sys' 727 funcs['PetscOptionsEnd'].file = 'aoptions.c'; 728 funcs['PetscOptionsEnd'].includefile = 'petscoptions.h' 729 funcs['PetscOptionsEnd'].dir = 'src/sys/objects/' 730 funcs['PetscOptionsEnd'].opaquestub = True 731 732 funcs['PetscOptionsBool'] = Function('PetscOptionsBool') 733 funcs['PetscOptionsBool'].mansec = 'sys' 734 funcs['PetscOptionsBool'].file = 'aoptions.c'; 735 funcs['PetscOptionsBool'].includefile = 'petscoptions.h' 736 funcs['PetscOptionsBool'].dir = 'src/sys/objects/' 737 funcs['PetscOptionsBool'].opaquestub = True 738 funcs['PetscOptionsBool'].arguments = [Argument('opt', 'char', stars = 0, array = True, const = True), 739 Argument('text', 'char', stars = 0, array = True, const = True), 740 Argument('man', 'char', stars = 0, array = True, const = True), 741 Argument('currentvalue', 'PetscBool'), 742 Argument('value', 'PetscBool', stars = 1), 743 Argument('set', 'PetscBool', stars = 1)] 744 funcs['PetscOptionsBool3'] = Function('PetscOptionsBool3') 745 funcs['PetscOptionsBool3'].mansec = 'sys' 746 funcs['PetscOptionsBool3'].file = 'aoptions.c'; 747 funcs['PetscOptionsBool3'].includefile = 'petscoptions.h' 748 funcs['PetscOptionsBool3'].dir = 'src/sys/objects/' 749 funcs['PetscOptionsBool3'].opaquestub = True 750 funcs['PetscOptionsBool3'].arguments = [Argument('opt', 'char', stars = 0, array = True, const = True), 751 Argument('text', 'char', stars = 0, array = True, const = True), 752 Argument('man', 'char', stars = 0, array = True, const = True), 753 Argument('currentvalue', 'PetscBool3'), 754 Argument('value', 'PetscBool3', stars = 1), 755 Argument('set', 'PetscBool3', stars = 1)] 756 funcs['PetscOptionsInt'] = Function('PetscOptionsInt') 757 funcs['PetscOptionsInt'].mansec = 'sys' 758 funcs['PetscOptionsInt'].file = 'aoptions.c'; 759 funcs['PetscOptionsInt'].includefile = 'petscoptions.h' 760 funcs['PetscOptionsInt'].dir = 'src/sys/objects/' 761 funcs['PetscOptionsInt'].opaquestub = True 762 funcs['PetscOptionsInt'].arguments = [Argument('opt', 'char', stars = 0, array = True, const = True), 763 Argument('text', 'char', stars = 0, array = True, const = True), 764 Argument('man', 'char', stars = 0, array = True, const = True), 765 Argument('currentvalue', 'PetscInt'), 766 Argument('value', 'PetscInt', stars = 1), 767 Argument('set', 'PetscBool', stars = 1)] 768 funcs['PetscOptionsReal'] = Function('PetscOptionsReal') 769 funcs['PetscOptionsReal'].mansec = 'sys' 770 funcs['PetscOptionsReal'].file = 'aoptions.c'; 771 funcs['PetscOptionsReal'].includefile = 'petscoptions.h' 772 funcs['PetscOptionsReal'].dir = 'src/sys/objects/' 773 funcs['PetscOptionsReal'].opaquestub = True 774 funcs['PetscOptionsReal'].arguments = [Argument('opt', 'char', stars = 0, array = True, const = True), 775 Argument('text', 'char', stars = 0, array = True, const = True), 776 Argument('man', 'char', stars = 0, array = True, const = True), 777 Argument('currentvalue', 'PetscReal'), 778 Argument('value', 'PetscReal', stars = 1), 779 Argument('set', 'PetscBool', stars = 1)] 780 funcs['PetscOptionsScalar'] = Function('PetscOptionsScalar') 781 funcs['PetscOptionsScalar'].mansec = 'sys' 782 funcs['PetscOptionsScalar'].file = 'aoptions.c'; 783 funcs['PetscOptionsScalar'].includefile = 'petscoptions.h' 784 funcs['PetscOptionsScalar'].dir = 'src/sys/objects/' 785 funcs['PetscOptionsScalar'].opaquestub = True 786 funcs['PetscOptionsScalar'].arguments = [Argument('opt', 'char', stars = 0, array = True, const = True), 787 Argument('text', 'char', stars = 0, array = True, const = True), 788 Argument('man', 'char', stars = 0, array = True, const = True), 789 Argument('currentvalue', 'PetscScalar'), 790 Argument('value', 'PetscScalar', stars = 1), 791 Argument('set', 'PetscBool', stars = 1)] 792 funcs['PetscOptionsScalarArray'] = Function('PetscOptionsScalarArray') 793 funcs['PetscOptionsScalarArray'].mansec = 'sys' 794 funcs['PetscOptionsScalarArray'].file = 'aoptions.c'; 795 funcs['PetscOptionsScalarArray'].includefile = 'petscoptions.h' 796 funcs['PetscOptionsScalarArray'].dir = 'src/sys/objects/' 797 funcs['PetscOptionsScalarArray'].opaquestub = True 798 funcs['PetscOptionsScalarArray'].arguments = [Argument('opt', 'char', stars = 0, array = True, const = True), 799 Argument('text', 'char', stars = 0, array = True, const = True), 800 Argument('man', 'char', stars = 0, array = True, const = True), 801 Argument('value', 'PetscScalar', array = 1), 802 Argument('n', 'PetscInt', stars = 1), 803 Argument('set', 'PetscBool', stars = 1)] 804 funcs['PetscOptionsIntArray'] = Function('PetscOptionsIntArray') 805 funcs['PetscOptionsIntArray'].mansec = 'sys' 806 funcs['PetscOptionsIntArray'].file = 'aoptions.c'; 807 funcs['PetscOptionsIntArray'].includefile = 'petscoptions.h' 808 funcs['PetscOptionsIntArray'].dir = 'src/sys/objects/' 809 funcs['PetscOptionsIntArray'].opaquestub = True 810 funcs['PetscOptionsIntArray'].arguments = [Argument('opt', 'char', stars = 0, array = True, const = True), 811 Argument('text', 'char', stars = 0, array = True, const = True), 812 Argument('man', 'char', stars = 0, array = True, const = True), 813 Argument('value', 'PetscInt', array = 1), 814 Argument('n', 'PetscInt', stars = 1), 815 Argument('set', 'PetscBool', stars = 1)] 816 funcs['PetscOptionsRealArray'] = Function('PetscOptionsRealArray') 817 funcs['PetscOptionsRealArray'].mansec = 'sys' 818 funcs['PetscOptionsRealArray'].file = 'aoptions.c'; 819 funcs['PetscOptionsRealArray'].includefile = 'petscoptions.h' 820 funcs['PetscOptionsRealArray'].dir = 'src/sys/objects/' 821 funcs['PetscOptionsRealArray'].opaquestub = True 822 funcs['PetscOptionsRealArray'].arguments = [Argument('opt', 'char', stars = 0, array = True, const = True), 823 Argument('text', 'char', stars = 0, array = True, const = True), 824 Argument('man', 'char', stars = 0, array = True, const = True), 825 Argument('value', 'PetscReal', array = 1), 826 Argument('n', 'PetscInt', stars = 1), 827 Argument('set', 'PetscBool', stars = 1)] 828 funcs['PetscOptionsBoolArray'] = Function('PetscOptionsBoolArray') 829 funcs['PetscOptionsBoolArray'].mansec = 'sys' 830 funcs['PetscOptionsBoolArray'].file = 'aoptions.c'; 831 funcs['PetscOptionsBoolArray'].includefile = 'petscoptions.h' 832 funcs['PetscOptionsBoolArray'].dir = 'src/sys/objects/' 833 funcs['PetscOptionsBoolArray'].opaquestub = True 834 funcs['PetscOptionsBoolArray'].arguments = [Argument('opt', 'char', stars = 0, array = True, const = True), 835 Argument('text', 'char', stars = 0, array = True, const = True), 836 Argument('man', 'char', stars = 0, array = True, const = True), 837 Argument('value', 'PetscBool', array = 1), 838 Argument('n', 'PetscInt', stars = 1), 839 Argument('set', 'PetscBool', stars = 1)] 840 841 verbosePrint(verbose, 'Classes ---------------------------------------------') 842 for i in classes.keys(): 843 verbosePrint(verbose, classes[i]) 844 845 verbosePrint(verbose, 'Standalone functions --------------------------------') 846 for i in funcs.keys(): 847 verbosePrint(verbose, funcs[i]) 848 849 #file = open('classes.data','wb') 850 #pickle.dump(enums,file) 851 #pickle.dump(senums,file) 852 #pickle.dump(structs,file) 853 #pickle.dump(aliases,file) 854 #pickle.dump(classes,file) 855 #pickle.dump(typedefs,file) 856 857 return classes, enums, senums, typedefs, structs, funcs, includefiles, mansecs, submansecs 858 859# 860if __name__ == '__main__': 861 import argparse 862 863 parser = argparse.ArgumentParser(description='Generate PETSc/SLEPc API', formatter_class=argparse.ArgumentDefaultsHelpFormatter) 864 parser.add_argument('--verbose', action='store_true', required=False, help='show generated API') 865 parser.add_argument('--package', metavar='petsc/slepc', required=False, help='package name', default='petsc') 866 parser.add_argument('directory', help='root directory, either PETSC_DIR or SLEPC_DIR') 867 args = parser.parse_args() 868 869 getAPI(args.directory, args.package, args.verbose) 870