1#!/usr/bin/env python3 2# 3# Generates etag and ctag (use -noctags to skip generation of ctags) files for PETSc 4# Adds file names to list of tags in a TAGS file 5# Also removes the #define somefunction_ somefunction from the tags list 6# 7# 8# 9# Walks through the PETSc tree generating the TAGS file 10# 11import os 12import re 13import sys 14from string import * 15import subprocess 16try: 17 from subprocess import check_output 18except ImportError: 19 def check_output(*popenargs, **kwargs): 20 """Implementation from Python-2.7 subprocess.check_output for use with 21 Python-2.6 which does not provide check_output. 22 """ 23 if 'stdout' in kwargs: 24 raise ValueError('stdout argument not allowed, it will be overridden.') 25 process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) 26 output, unused_err = process.communicate() 27 retcode = process.poll() 28 if retcode: 29 cmd = kwargs.get("args") 30 if cmd is None: 31 cmd = popenargs[0] 32 raise subprocess.CalledProcessError(retcode, cmd, output=output) 33 return output 34 35DEVNULL = open(os.devnull, 'w') 36# 37# Copies structs from filename to filename.tmp 38 39def addFileNameTags(filename): 40 removedefines = 0 41 f = open(filename, 'rb') 42 g = open('TAGS', 'wb') 43 line = f.readline() 44 while line: 45 if not (removedefines and line.startswith(b'#define ')): g.write(line) 46 if line.startswith(b'\f'): 47 line = f.readline() 48 g.write(line) 49 line = line[0:line.index(b',')] 50 if os.path.dirname(line).endswith(b'custom') and not line.endswith(b'.h'): 51 removedefines = 1 52 else: removedefines = 0 53 line = os.path.basename(line) 54 g.write(line + b':^?' + line + b'^A,1\n') 55 line = f.readline() 56 f.close() 57 g.close() 58 return 59 60def createTags(flist,etagfile,ctagfile): 61 # split up the flist into blocks of 1000 - and call etags on each chunk 62 nfiles = len(flist) 63 niter = nfiles//1000 64 nrem = nfiles%1000 65 blocks = [i*1000 for i in range(niter+1)] 66 if nrem: blocks.append(nfiles) 67 for i in range(len(blocks)-1): 68 createTagsBlock(flist[blocks[i]:blocks[i+1]],etagfile,ctagfile) 69 return 70 71def createTagsBlock(flist,etagfile,ctagfile): 72 # error check for each parameter? 73 frlist = [os.path.relpath(path,os.getcwd()) for path in flist] 74 75 try: 76 response = subprocess.run('etags -a -o '+etagfile+' '+' '.join(frlist), capture_output = True, shell=True, check=True) 77 except subprocess.CalledProcessError as e: 78 # do not raise exception since it spews a huge file list to the screen obscuring the problem 79 print('Unable to run etags command. Likely it is not in your path. You may need to install etags or Emacs') 80 exit(1) 81 except e: 82 print('Unable to run etags command. Likely it is not in your path. You may need to install etags or Emacs') 83 exit(1) 84 85 # linux can use '--tag-relative=yes --langmap=c:+.cu'. For others [Mac,bsd] try running ctags in root directory - with relative path to file 86 if ctagfile: 87 status = subprocess.call('ctags --fields=+l --tag-relative=yes --langmap=c:+.cu -I PeNS,PeOP -a -f '+ctagfile+' '+' '.join(frlist), shell=True, stdout=DEVNULL, stderr=subprocess.STDOUT) 88 if status: 89 status = subprocess.call('/usr/local/bin/ctags -a -f '+ctagfile+' '+' '.join(frlist), shell=True, stdout=DEVNULL, stderr=subprocess.STDOUT) 90 if status: 91 status = subprocess.call('ctags -a -f '+ctagfile+' '+' '.join(frlist), shell=True, stdout=DEVNULL, stderr=subprocess.STDOUT) 92 if status: 93 raise RuntimeError("Error running ctags") 94 return 95 96def endsWithSuffix(file,suffixes): 97 # returns 1 if any of the suffixes match - else return 0 98 for suffix in suffixes: 99 if file.endswith(suffix): 100 return 1 101 return 0 102 103def startsWithPrefix(file,prefixes): 104 # returns 1 if any of the prefix match - else return 0 105 for prefix in prefixes: 106 if file.startswith(prefix): 107 return 1 108 return 0 109 110def badWebIndex(dirname,file): 111 # checks if the file is bad index.html document [i.e not generated] 112 if file != 'index.html': 113 return 0 114 elif file == 'index.html' and dirname.find('docs/website') >=0: 115 return 0 116 else: 117 return 1 118 119def processDir(flist, dirpath, dirnames, filenames): 120 newls = [] 121 gsfx = ['.py','.c','.cu','.F','.F90','.h','.h90','.tex','.cxx','.hh','makefile','.bib','.jl'] 122 bpfx = ['.#'] 123 hsfx = ['.html'] 124 bsfx = ['.py.html','.c.html','.F.html','.h.html','.tex.html','.cxx.html','.hh.html','makefile.html','.gcov.html','.cu.html','.cache.html'] 125 for l in filenames: 126 if endsWithSuffix(l,gsfx) and not startsWithPrefix(l,bpfx): 127 newls.append(l) 128 elif endsWithSuffix(l,hsfx) and not endsWithSuffix(l,bsfx) and not badWebIndex(dirpath,l): 129 # if html - and not bad suffix - and not badWebIndex - then add to etags-list 130 newls.append(l) 131 if newls: flist.extend([os.path.join(dirpath,name) for name in newls]) 132 133 # exclude 'petsc/docs/' only (and not docs/ in other locations) 134 for exname in ['docs']: 135 if exname in dirnames and os.path.realpath(dirpath) == os.path.realpath(os.getcwd()): 136 dirnames.remove(exname) 137 138 # One-level unique dirs 139 for exname in ['.git','.hg','SCCS', 'output', 'BitKeeper', 'externalpackages', 'bilinear', 'ftn-auto','lib','systems']: 140 if exname in dirnames: 141 dirnames.remove(exname) 142 # Multi-level unique dirs - specify from toplevel 143 for exname in ['src/python/PETSc','client/c++','client/c','client/python']: 144 for name in dirnames: 145 filename=os.path.join(dirpath,name) 146 if filename.find(exname) >=0: 147 dirnames.remove(name) 148 # check for configure generated PETSC_ARCHes 149 rmnames=[] 150 for name in dirnames: 151 if os.path.isdir(os.path.join(dirpath,name,'petsc','conf')): 152 rmnames.append(name) 153 for rmname in rmnames: 154 dirnames.remove(rmname) 155 return 156 157def processFiles(dirname,flist): 158 # list files that can't be done with global match [as above] with complete paths 159 import glob 160 files= [] 161 lists=['petsc/conf/*'] 162 163 for glist in lists: 164 gfiles = glob.glob(glist) 165 for file in gfiles: 166 if not (file.endswith('pyc') or file.endswith('/SCCS') or file.endswith('~')): 167 files.append(file) 168 if files: flist.extend([os.path.join(dirname,name) for name in files]) 169 return 170 171def main(ctags): 172 try: os.unlink('TAGS') 173 except: pass 174 etagfile = os.path.join(os.getcwd(),'ETAGS') 175 if ctags: 176 try: os.unlink('CTAGS') 177 except: pass 178 ctagfile = os.path.join(os.getcwd(),'CTAGS') 179 else: 180 ctagfile = None 181 flist = [] 182 if os.path.isdir('.git'): 183 output = check_output(r'git ls-files | grep -E -v \(^\(systems/\|share/petsc/datafiles/\)\|/output/\|\.\(png\|pdf\|ps\|ppt\|jpg\)$\)', shell=True) 184 flist = output.decode(sys.getfilesystemencoding()).splitlines() 185 else: 186 for dirpath, dirnames, filenames in os.walk(os.getcwd()): 187 processDir(flist, dirpath, dirnames, filenames) 188 processFiles(os.getcwd(),flist) 189 createTags(flist,etagfile,ctagfile) 190 addFileNameTags(etagfile) 191 try: os.unlink('ETAGS') 192 except: pass 193# 194# The classes in this file can also be used in other python-programs by using 'import' 195# 196if __name__ == '__main__': 197 if (len(sys.argv) > 1 and sys.argv[1] == "-noctags"): ctags = 0 198 else: ctags = 1 199 main(ctags) 200 201