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