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