1#!/usr/bin/env python3 2""" Loops through all the source using doctext to generate the manual pages""" 3 4import os 5import re 6import subprocess 7import pathlib 8 9def findlmansec(file): 10 mansec = None 11 submansec = None 12 with open(file) as mklines: 13 #print(file) 14 submansecl = [line for line in mklines if (line.find('SUBMANSEC') > -1 and line.find('BFORT') == -1)] 15 if submansecl: 16 submansec = re.sub(r'[ ]*/\* [ ]*SUBMANSEC[ ]*=[ ]*','',submansecl[0]).strip('\n').strip('*/').strip() 17 if submansec == submansecl[0].strip('\n'): 18 submansec = re.sub('SUBMANSEC[ ]*=[ ]*','',submansecl[0]).strip('\n').strip() 19 #print(':SUBMANSEC:'+submansec) 20 return submansec 21 with open(file) as mklines: 22 mansecl = [line for line in mklines if line.startswith('MANSEC')] 23 if mansecl: 24 mansec = re.sub('MANSEC[ ]*=[ ]*','',mansecl[0]).strip('\n').strip() 25 #print(':MANSEC:'+mansec) 26 return mansec 27 return None 28 29def processdir(petsc_dir, dir, doctext): 30 '''Runs doctext on each source file in the directory''' 31 #print('Processing '+dir) 32 #print('build_man_pages: Using doctext '+doctext) 33 loc = os.path.join(petsc_dir,'doc') 34 doctext_path = os.path.join(petsc_dir,'doc','manualpages','doctext') 35 lmansec = None 36 if os.path.isfile(os.path.join(dir,'makefile')): 37 lmansec = findlmansec(os.path.join(dir,'makefile')) 38 39 numberErrors = 0 40 for file in os.listdir(dir): 41 llmansec = lmansec 42 if os.path.isfile(os.path.join(dir,file)) and pathlib.Path(file).suffix in ['.c', '.cxx', '.h', '.cu', '.cpp', '.hpp']: 43 #print('Processing '+file) 44 if not llmansec: 45 llmansec = findlmansec(os.path.join(dir,file)) 46 if not llmansec: continue 47 if not os.path.isdir(os.path.join(loc,'manualpages',llmansec)): os.mkdir(os.path.join(loc,'manualpages',llmansec)) 48 49 command = [doctext, 50 '-myst', 51 '-mpath', os.path.join(loc,'manualpages',llmansec), 52 '-heading', 'PETSc', 53 '-defn', os.path.join(loc,'manualpages','doctext','myst.def'), 54 '-indexdir', '../'+llmansec, 55 '-index', os.path.join(loc,'manualpages','manualpages.cit'), 56 '-locdir', dir[len(petsc_dir)+1:]+'/', 57 '-Wargdesc', os.path.join(loc,'manualpages','doctext','doctextcommon.txt'), 58 file] 59 #print(command) 60 sp = subprocess.run(command, cwd=dir, capture_output=True, encoding='UTF-8', check=True) 61 if sp.stdout and sp.stdout.find('WARNING') > -1: 62 print(sp.stdout) 63 numberErrors = numberErrors + 1 64 if sp.stderr and sp.stderr.find('WARNING') > -1: 65 print(sp.stderr) 66 numberErrors = numberErrors + 1 67 return numberErrors 68 69 70def processkhash(T, t, KeyType, ValType, text): 71 '''Replaces T, t, KeyType, and ValType in text (from include/petsc/private/hashset.txt) with a set of supported values''' 72 import re 73 return re.sub('<ValType>',ValType,re.sub('<KeyType>',KeyType,re.sub('<t>',t,re.sub('<T>',T,text)))) 74 75def main(petsc_dir, doctext): 76 # generate source code for manual pages for PETSc khash functions 77 text = '' 78 for f in ['hashset.txt', 'hashmap.txt']: 79 with open(os.path.join(petsc_dir,'include','petsc','private',f)) as mklines: 80 text = mklines.read() 81 with open(os.path.join(petsc_dir,'include','petsc','private',f+'.h'),mode='w') as khash: 82 khash.write(processkhash('I','i','PetscInt','',text)) 83 khash.write(processkhash('IJ','ij','struct {PetscInt i, j;}','',text)) 84 khash.write(processkhash('I','i','PetscInt','PetscInt',text)) 85 khash.write(processkhash('IJ','ij','struct {PetscInt i, j;}','PetscInt',text)) 86 khash.write(processkhash('IJ','ij','struct {PetscInt i, j;}','PetscScalar',text)) 87 khash.write(processkhash('IV','iv','PetscInt','PetscScalar',text)) 88 khash.write(processkhash('Obj','obj','PetscInt64','PetscObject',text)) 89 90 # generate the .md files for the manual pages from all the PETSc source code 91 try: 92 os.unlink(os.path.join(petsc_dir,'doc','manualpages','manualpages.cit')) 93 except: 94 pass 95 numberErrors = 0 96 for dirpath, dirnames, filenames in os.walk(os.path.join(petsc_dir),topdown=True): 97 dirnames[:] = [d for d in dirnames if d not in ['tests', 'tutorials', 'doc', 'output', 'ftn-custom', 'ftn-auto', 'ftn-mod', 'binding', 'binding', 'config', 'lib', '.git', 'share', 'systems'] and not d.startswith('arch')] 98 numberErrors = numberErrors + processdir(petsc_dir,dirpath,doctext) 99 if numberErrors: 100 raise RuntimeError('Stopping document build since errors were detected in generating manual pages') 101 102 # generate list of all manual pages 103 with open(os.path.join(petsc_dir,'doc','manualpages','htmlmap'),mode='w') as map: 104 with open(os.path.join(petsc_dir,'doc','manualpages','manualpages.cit')) as cit: 105 map.write(re.sub(r'man\+../','man+manualpages/',cit.read())) 106 with open(os.path.join(petsc_dir,'doc','manualpages','mpi.www.index')) as mpi: 107 map.write(mpi.read()) 108 109if __name__ == "__main__": 110 # TODO Accept doctext from command line 111 main(os.path.abspath(os.environ['PETSC_DIR'])) 112