1*fb2aa080SBarry Smith#!/usr/bin/env python 2*fb2aa080SBarry Smith#!/bin/env python 3*fb2aa080SBarry Smith 4*fb2aa080SBarry Smith""" Reads in all the generated manual pages, and creates the index 5*fb2aa080SBarry Smithfor the manualpages, ordering the indices into sections based 6*fb2aa080SBarry Smithon the 'Level of Difficulty'. 7*fb2aa080SBarry Smith 8*fb2aa080SBarry Smith Usage: 9*fb2aa080SBarry Smith wwwindex.py PETSC_DIR LOC 10*fb2aa080SBarry Smith""" 11*fb2aa080SBarry Smith 12*fb2aa080SBarry Smithimport os 13*fb2aa080SBarry Smithimport sys 14*fb2aa080SBarry Smithimport re 15*fb2aa080SBarry Smithimport glob 16*fb2aa080SBarry Smithimport posixpath 17*fb2aa080SBarry Smithimport subprocess 18*fb2aa080SBarry Smith 19*fb2aa080SBarry SmithHLIST_COLUMNS = 3 20*fb2aa080SBarry Smith 21*fb2aa080SBarry Smith# Read an optional header file, whose contents are first copied over 22*fb2aa080SBarry Smith# Use the level info, and print a formatted index table of all the manual pages 23*fb2aa080SBarry Smith# 24*fb2aa080SBarry Smithdef printindex(outfilename, headfilename, levels, titles, tables): 25*fb2aa080SBarry Smith # Read in the header file 26*fb2aa080SBarry Smith headbuf = '' 27*fb2aa080SBarry Smith if posixpath.exists(headfilename) : 28*fb2aa080SBarry Smith with open(headfilename, "r") as fd: 29*fb2aa080SBarry Smith headbuf = fd.read() 30*fb2aa080SBarry Smith headbuf = headbuf.replace('PETSC_DIR', '../../../') 31*fb2aa080SBarry Smith else: 32*fb2aa080SBarry Smith print('Error! SUBMANSEC header file "%s" does not exist' % headfilename) 33*fb2aa080SBarry Smith print('Likley you introduced a new set of manual pages but did not add the header file that describes them') 34*fb2aa080SBarry Smith 35*fb2aa080SBarry Smith with open(outfilename, "w") as fd: 36*fb2aa080SBarry Smith # Since it uses three columns we must remove right sidebar so all columns are displayed completely 37*fb2aa080SBarry Smith # https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/page-toc.html 38*fb2aa080SBarry Smith fd.write(':html_theme.sidebar_secondary.remove: true\n') 39*fb2aa080SBarry Smith fd.write(headbuf) 40*fb2aa080SBarry Smith fd.write('\n') 41*fb2aa080SBarry Smith all_names = [] 42*fb2aa080SBarry Smith for i, level in enumerate(levels): 43*fb2aa080SBarry Smith title = titles[i] 44*fb2aa080SBarry Smith if not tables[i]: 45*fb2aa080SBarry Smith if level != 'none' and level != 'deprecated': 46*fb2aa080SBarry Smith fd.write('\n## No %s routines\n' % level) 47*fb2aa080SBarry Smith continue 48*fb2aa080SBarry Smith 49*fb2aa080SBarry Smith fd.write('\n## %s\n' % title) 50*fb2aa080SBarry Smith fd.write('```{hlist}\n') 51*fb2aa080SBarry Smith fd.write("---\n") 52*fb2aa080SBarry Smith fd.write("columns: %d\n" % HLIST_COLUMNS) 53*fb2aa080SBarry Smith fd.write("---\n") 54*fb2aa080SBarry Smith 55*fb2aa080SBarry Smith for filename in tables[i]: 56*fb2aa080SBarry Smith path,name = posixpath.split(filename) 57*fb2aa080SBarry Smith func_name,ext = posixpath.splitext(name) 58*fb2aa080SBarry Smith fd.write('- [](%s)\n' % name) 59*fb2aa080SBarry Smith all_names.append(name) 60*fb2aa080SBarry Smith fd.write('```\n\n\n') 61*fb2aa080SBarry Smith 62*fb2aa080SBarry Smith fd.write('\n## Single list of manual pages\n') 63*fb2aa080SBarry Smith fd.write('```{hlist}\n') 64*fb2aa080SBarry Smith fd.write("---\n") 65*fb2aa080SBarry Smith fd.write("columns: %d\n" % HLIST_COLUMNS) 66*fb2aa080SBarry Smith fd.write("---\n") 67*fb2aa080SBarry Smith for name in sorted(all_names): 68*fb2aa080SBarry Smith fd.write('- [](%s)\n' % name) 69*fb2aa080SBarry Smith fd.write('```\n\n\n') 70*fb2aa080SBarry Smith 71*fb2aa080SBarry Smith 72*fb2aa080SBarry Smith# This routine takes in as input a dictionary, which contains the 73*fb2aa080SBarry Smith# alhabetical index to all the man page functions, and prints them all in 74*fb2aa080SBarry Smith# a single index page 75*fb2aa080SBarry Smithdef printsingleindex(outfilename, alphabet_dict): 76*fb2aa080SBarry Smith with open(outfilename, "w") as fd: 77*fb2aa080SBarry Smith fd.write("# Single Index of all PETSc Manual Pages\n\n") 78*fb2aa080SBarry Smith fd.write(" Also see the [Manual page table of contents, by section](/manualpages/index.md).\n\n") 79*fb2aa080SBarry Smith for key in sorted(alphabet_dict.keys()): 80*fb2aa080SBarry Smith fd.write("## %s\n\n" % key.upper()) 81*fb2aa080SBarry Smith fd.write("```{hlist}\n") 82*fb2aa080SBarry Smith fd.write("---\n") 83*fb2aa080SBarry Smith fd.write("columns: %d\n" % HLIST_COLUMNS) 84*fb2aa080SBarry Smith fd.write("---\n") 85*fb2aa080SBarry Smith function_dict = alphabet_dict[key] 86*fb2aa080SBarry Smith for name in sorted(function_dict.keys()): 87*fb2aa080SBarry Smith if name: 88*fb2aa080SBarry Smith path_name = function_dict[name] 89*fb2aa080SBarry Smith else: 90*fb2aa080SBarry Smith path_name = '' 91*fb2aa080SBarry Smith fd.write("- [%s](%s)\n" % (name, path_name)) 92*fb2aa080SBarry Smith fd.write("```\n") 93*fb2aa080SBarry Smith 94*fb2aa080SBarry Smith 95*fb2aa080SBarry Smith# Read in the filename contents, and search for the formatted 96*fb2aa080SBarry Smith# String 'Level:' and return the level info. 97*fb2aa080SBarry Smith# Also adds the BOLD HTML format to Level field 98*fb2aa080SBarry Smithdef modifylevel(filename,secname,edit_branch): 99*fb2aa080SBarry Smith with open(filename, "r") as fd: 100*fb2aa080SBarry Smith buf = fd.read() 101*fb2aa080SBarry Smith 102*fb2aa080SBarry Smith re_name = re.compile('\*\*Location:\*\*(.*)') # As defined in myst.def 103*fb2aa080SBarry Smith m = re_name.search(buf) 104*fb2aa080SBarry Smith if m: 105*fb2aa080SBarry Smith loc_html = m.group(1) 106*fb2aa080SBarry Smith if loc_html: 107*fb2aa080SBarry Smith pattern = re.compile(r"<A.*>(.*)</A>") 108*fb2aa080SBarry Smith loc = re.match(pattern, loc_html) 109*fb2aa080SBarry Smith if loc: 110*fb2aa080SBarry Smith source_path = loc.group(1) 111*fb2aa080SBarry Smith buf += "\n\n---\n[Edit on GitLab](https://gitlab.com/petsc/petsc/-/edit/%s/%s)\n\n" % (edit_branch, source_path) 112*fb2aa080SBarry Smith else: 113*fb2aa080SBarry Smith print("Warning. Could not find source path in %s" % filename) 114*fb2aa080SBarry Smith else: 115*fb2aa080SBarry Smith print('Error! No location in file:', filename) 116*fb2aa080SBarry Smith 117*fb2aa080SBarry Smith re_level = re.compile(r'(Level:)\s+(\w+)') 118*fb2aa080SBarry Smith m = re_level.search(buf) 119*fb2aa080SBarry Smith level = 'none' 120*fb2aa080SBarry Smith if m: 121*fb2aa080SBarry Smith level = m.group(2) 122*fb2aa080SBarry Smith else: 123*fb2aa080SBarry Smith print('Error! No level info in file:', filename) 124*fb2aa080SBarry Smith 125*fb2aa080SBarry Smith # Reformat level and location 126*fb2aa080SBarry Smith tmpbuf = re_level.sub('',buf) 127*fb2aa080SBarry Smith re_loc = re.compile('(\*\*Location:\*\*)') 128*fb2aa080SBarry Smith tmpbuf = re_loc.sub('\n## Level\n' + level + '\n\n## Location\n',tmpbuf) 129*fb2aa080SBarry Smith 130*fb2aa080SBarry Smith # Modify .c#,.h#,.cu#,.cxx# to .c.html#,.h.html#,.cu.html#,.cxx.html# 131*fb2aa080SBarry Smith tmpbuf = re.sub('.c#', '.c.html#', tmpbuf) 132*fb2aa080SBarry Smith tmpbuf = re.sub('.h#', '.h.html#', tmpbuf) 133*fb2aa080SBarry Smith tmpbuf = re.sub('.cu#', '.cu.html#', tmpbuf) 134*fb2aa080SBarry Smith tmpbuf = re.sub('.cxx#', '.cxx.html#', tmpbuf) 135*fb2aa080SBarry Smith 136*fb2aa080SBarry Smith # Add footer links 137*fb2aa080SBarry Smith outbuf = tmpbuf + '\n[Index of all %s routines](index.md) \n' % secname + '[Table of Contents for all manual pages](/manualpages/index.md) \n' + '[Index of all manual pages](/manualpages/singleindex.md) \n' 138*fb2aa080SBarry Smith 139*fb2aa080SBarry Smith # write the modified manpage 140*fb2aa080SBarry Smith with open(filename, "w") as fd: 141*fb2aa080SBarry Smith fd.write(':orphan:\n'+outbuf) 142*fb2aa080SBarry Smith 143*fb2aa080SBarry Smith return level 144*fb2aa080SBarry Smith 145*fb2aa080SBarry Smith# Go through each manpage file, present in dirname, 146*fb2aa080SBarry Smith# and create and return a table for it, wrt levels specified. 147*fb2aa080SBarry Smithdef createtable(dirname,levels,secname,editbranch): 148*fb2aa080SBarry Smith listdir = os.listdir(dirname) 149*fb2aa080SBarry Smith mdfiles = [os.path.join(dirname,f) for f in listdir if f.endswith('.md')] 150*fb2aa080SBarry Smith mdfiles.sort() 151*fb2aa080SBarry Smith if mdfiles == []: 152*fb2aa080SBarry Smith print('Cannot create table for empty directory:',dirname) 153*fb2aa080SBarry Smith return None 154*fb2aa080SBarry Smith 155*fb2aa080SBarry Smith table = [] 156*fb2aa080SBarry Smith for level in levels: table.append([]) 157*fb2aa080SBarry Smith 158*fb2aa080SBarry Smith for filename in mdfiles: 159*fb2aa080SBarry Smith level = modifylevel(filename,secname,editbranch) 160*fb2aa080SBarry Smith if level.lower() in levels: 161*fb2aa080SBarry Smith table[levels.index(level.lower())].append(filename) 162*fb2aa080SBarry Smith else: 163*fb2aa080SBarry Smith print('Error! Unknown level \''+ level + '\' in', filename) 164*fb2aa080SBarry Smith return table 165*fb2aa080SBarry Smith 166*fb2aa080SBarry Smith# This routine is called for each man dir. Each time, it 167*fb2aa080SBarry Smith# adds the list of manpages, to the given list, and returns 168*fb2aa080SBarry Smith# the union list. 169*fb2aa080SBarry Smith 170*fb2aa080SBarry Smithdef addtolist(dirname,singlelist): 171*fb2aa080SBarry Smith mdfiles = [os.path.join(dirname,f) for f in os.listdir(dirname) if f.endswith('.md')] 172*fb2aa080SBarry Smith mdfiles.sort() 173*fb2aa080SBarry Smith if mdfiles == []: 174*fb2aa080SBarry Smith print('Error! Empty directory:',dirname) 175*fb2aa080SBarry Smith return None 176*fb2aa080SBarry Smith 177*fb2aa080SBarry Smith singlelist.extend(mdfiles) 178*fb2aa080SBarry Smith 179*fb2aa080SBarry Smith return singlelist 180*fb2aa080SBarry Smith 181*fb2aa080SBarry Smith# This routine creates a dictionary, with entries such that each 182*fb2aa080SBarry Smith# key is the alphabet, and the vaue corresponds to this key is a dictionary 183*fb2aa080SBarry Smith# of FunctionName/PathToFile Pair. 184*fb2aa080SBarry Smithdef createdict(singlelist): 185*fb2aa080SBarry Smith newdict = {} 186*fb2aa080SBarry Smith for filename in singlelist: 187*fb2aa080SBarry Smith path,name = posixpath.split(filename) 188*fb2aa080SBarry Smith # grab the short path Mat from /wired/path/Mat 189*fb2aa080SBarry Smith junk,path = posixpath.split(path) 190*fb2aa080SBarry Smith index_char = name[0:1].lower() 191*fb2aa080SBarry Smith # remove the .name suffix from name 192*fb2aa080SBarry Smith func_name,ext = posixpath.splitext(name) 193*fb2aa080SBarry Smith if index_char not in newdict: 194*fb2aa080SBarry Smith newdict[index_char] = {} 195*fb2aa080SBarry Smith newdict[index_char][func_name] = path + '/' + name 196*fb2aa080SBarry Smith 197*fb2aa080SBarry Smith return newdict 198*fb2aa080SBarry Smith 199*fb2aa080SBarry Smith 200*fb2aa080SBarry Smithdef getallmandirs(dirs): 201*fb2aa080SBarry Smith """ Gets the list of man* dirs present in the doc dir. Each dir will have an index created for it. """ 202*fb2aa080SBarry Smith mandirs = [] 203*fb2aa080SBarry Smith for filename in dirs: 204*fb2aa080SBarry Smith path,name = posixpath.split(filename) 205*fb2aa080SBarry Smith if name == 'RCS' or name == 'sec' or name == "concepts" or name == "SCCS" : continue 206*fb2aa080SBarry Smith if posixpath.isdir(filename): 207*fb2aa080SBarry Smith mandirs.append(filename) 208*fb2aa080SBarry Smith return mandirs 209*fb2aa080SBarry Smith 210*fb2aa080SBarry Smith 211*fb2aa080SBarry Smithdef main(PETSC_DIR,LOC): 212*fb2aa080SBarry Smith import time 213*fb2aa080SBarry Smith x = time.clock_gettime(time.CLOCK_REALTIME) 214*fb2aa080SBarry Smith print('Building manual page indices\n') 215*fb2aa080SBarry Smith HEADERDIR = 'doc/classic/manualpages-sec' 216*fb2aa080SBarry Smith dirs = glob.glob(LOC + '/manualpages/*') 217*fb2aa080SBarry Smith mandirs = getallmandirs(dirs) 218*fb2aa080SBarry Smith 219*fb2aa080SBarry Smith levels = ['beginner','intermediate','advanced','developer','deprecated','none'] 220*fb2aa080SBarry Smith titles = ['Beginner - Basic usage', 221*fb2aa080SBarry Smith 'Intermediate - Setting options for algorithms and data structures', 222*fb2aa080SBarry Smith 'Advanced - Setting more advanced options and customization', 223*fb2aa080SBarry Smith 'Developer - Interfaces rarely needed by applications programmers', 224*fb2aa080SBarry Smith 'Deprecated - Functionality scheduled for removal in the future', 225*fb2aa080SBarry Smith 'None: Not yet cataloged'] 226*fb2aa080SBarry Smith 227*fb2aa080SBarry Smith singlelist = [] 228*fb2aa080SBarry Smith git_ref = subprocess.check_output(['git', 'rev-parse', 'HEAD']).rstrip() 229*fb2aa080SBarry Smith try: 230*fb2aa080SBarry Smith git_ref_release = subprocess.check_output(['git', 'rev-parse', 'origin/release']).rstrip() 231*fb2aa080SBarry Smith edit_branch = 'release' if git_ref == git_ref_release else 'main' 232*fb2aa080SBarry Smith except subprocess.CalledProcessError: 233*fb2aa080SBarry Smith print("WARNING: checking branch for man page edit links failed") 234*fb2aa080SBarry Smith edit_branch = 'main' 235*fb2aa080SBarry Smith 236*fb2aa080SBarry Smith for dirname in mandirs: 237*fb2aa080SBarry Smith outfilename = dirname + '/index.md' 238*fb2aa080SBarry Smith dname,secname = posixpath.split(dirname) 239*fb2aa080SBarry Smith headfilename = PETSC_DIR + '/' + HEADERDIR + '/header_' + secname 240*fb2aa080SBarry Smith table = createtable(dirname,levels,secname,edit_branch) 241*fb2aa080SBarry Smith if not table: continue 242*fb2aa080SBarry Smith singlelist = addtolist(dirname,singlelist) 243*fb2aa080SBarry Smith printindex(outfilename,headfilename,levels,titles,table) 244*fb2aa080SBarry Smith 245*fb2aa080SBarry Smith alphabet_dict = createdict(singlelist) 246*fb2aa080SBarry Smith outfilename = LOC + '/manualpages/singleindex.md' 247*fb2aa080SBarry Smith printsingleindex (outfilename,alphabet_dict) 248*fb2aa080SBarry Smith print("Time "+str(time.clock_gettime(time.CLOCK_REALTIME) - x)) 249*fb2aa080SBarry Smith 250*fb2aa080SBarry Smithif __name__ == '__main__': 251*fb2aa080SBarry Smith main(os.path.abspath(os.environ['PETSC_DIR']),os.path.abspath(os.environ['LOC'])) 252