xref: /petsc/lib/petsc/bin/getAPI.py (revision 9fc2013d4d62c156699d7098aeeebb1b97746c9a)
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