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") 75*1d27aa22SBarry Smith fd.write(" Also see the [Manual page table of contents, by section](/manualpages/index.rst).\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 134*1d27aa22SBarry Smith outbuf = tmpbuf + '\n[Index of all %s routines](index.md) \n' % secname + '[Table of Contents for all manual pages](/manualpages/index.rst) \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 print('Cannot create table for empty directory:',dirname) 150fb2aa080SBarry Smith return None 151fb2aa080SBarry Smith 152fb2aa080SBarry Smith table = [] 153fb2aa080SBarry Smith for level in levels: table.append([]) 154fb2aa080SBarry Smith 155fb2aa080SBarry Smith for filename in mdfiles: 156fb2aa080SBarry Smith level = modifylevel(filename,secname,editbranch) 157fb2aa080SBarry Smith if level.lower() in levels: 158fb2aa080SBarry Smith table[levels.index(level.lower())].append(filename) 159fb2aa080SBarry Smith else: 160fb2aa080SBarry Smith print('Error! Unknown level \''+ level + '\' in', filename) 161fb2aa080SBarry Smith return table 162fb2aa080SBarry Smith 163fb2aa080SBarry Smith# This routine is called for each man dir. Each time, it 164fb2aa080SBarry Smith# adds the list of manpages, to the given list, and returns 165fb2aa080SBarry Smith# the union list. 166fb2aa080SBarry Smith 167fb2aa080SBarry Smithdef addtolist(dirname,singlelist): 168fb2aa080SBarry Smith mdfiles = [os.path.join(dirname,f) for f in os.listdir(dirname) if f.endswith('.md')] 169fb2aa080SBarry Smith mdfiles.sort() 170fb2aa080SBarry Smith if mdfiles == []: 171fb2aa080SBarry Smith print('Error! Empty directory:',dirname) 172fb2aa080SBarry Smith return None 173fb2aa080SBarry Smith 174fb2aa080SBarry Smith singlelist.extend(mdfiles) 175fb2aa080SBarry Smith 176fb2aa080SBarry Smith return singlelist 177fb2aa080SBarry Smith 178fb2aa080SBarry Smith# This routine creates a dictionary, with entries such that each 179fb2aa080SBarry Smith# key is the alphabet, and the vaue corresponds to this key is a dictionary 180fb2aa080SBarry Smith# of FunctionName/PathToFile Pair. 181fb2aa080SBarry Smithdef createdict(singlelist): 182fb2aa080SBarry Smith newdict = {} 183fb2aa080SBarry Smith for filename in singlelist: 184fb2aa080SBarry Smith path,name = posixpath.split(filename) 185fb2aa080SBarry Smith # grab the short path Mat from /wired/path/Mat 186fb2aa080SBarry Smith junk,path = posixpath.split(path) 187fb2aa080SBarry Smith index_char = name[0:1].lower() 188fb2aa080SBarry Smith # remove the .name suffix from name 189fb2aa080SBarry Smith func_name,ext = posixpath.splitext(name) 190fb2aa080SBarry Smith if index_char not in newdict: 191fb2aa080SBarry Smith newdict[index_char] = {} 192fb2aa080SBarry Smith newdict[index_char][func_name] = path + '/' + name 193fb2aa080SBarry Smith 194fb2aa080SBarry Smith return newdict 195fb2aa080SBarry Smith 196fb2aa080SBarry Smith 197fb2aa080SBarry Smithdef getallmandirs(dirs): 198fb2aa080SBarry Smith """ Gets the list of man* dirs present in the doc dir. Each dir will have an index created for it. """ 199fb2aa080SBarry Smith mandirs = [] 200fb2aa080SBarry Smith for filename in dirs: 201fb2aa080SBarry Smith path,name = posixpath.split(filename) 202fb2aa080SBarry Smith if name == 'RCS' or name == 'sec' or name == "concepts" or name == "SCCS" : continue 203fb2aa080SBarry Smith if posixpath.isdir(filename): 204fb2aa080SBarry Smith mandirs.append(filename) 205fb2aa080SBarry Smith return mandirs 206fb2aa080SBarry Smith 207fb2aa080SBarry Smith 208fb2aa080SBarry Smithdef main(PETSC_DIR,LOC): 209fb2aa080SBarry Smith HEADERDIR = 'doc/classic/manualpages-sec' 210fb2aa080SBarry Smith dirs = glob.glob(LOC + '/manualpages/*') 211fb2aa080SBarry Smith mandirs = getallmandirs(dirs) 212fb2aa080SBarry Smith 213fb2aa080SBarry Smith levels = ['beginner','intermediate','advanced','developer','deprecated','none'] 214fb2aa080SBarry Smith titles = ['Beginner - Basic usage', 215fb2aa080SBarry Smith 'Intermediate - Setting options for algorithms and data structures', 216fb2aa080SBarry Smith 'Advanced - Setting more advanced options and customization', 217fb2aa080SBarry Smith 'Developer - Interfaces rarely needed by applications programmers', 218fb2aa080SBarry Smith 'Deprecated - Functionality scheduled for removal in the future', 219fb2aa080SBarry Smith 'None: Not yet cataloged'] 220fb2aa080SBarry Smith 221fb2aa080SBarry Smith singlelist = [] 222fb2aa080SBarry Smith git_ref = subprocess.check_output(['git', 'rev-parse', 'HEAD']).rstrip() 223fb2aa080SBarry Smith try: 224fb2aa080SBarry Smith git_ref_release = subprocess.check_output(['git', 'rev-parse', 'origin/release']).rstrip() 225fb2aa080SBarry Smith edit_branch = 'release' if git_ref == git_ref_release else 'main' 226fb2aa080SBarry Smith except subprocess.CalledProcessError: 227fb2aa080SBarry Smith print("WARNING: checking branch for man page edit links failed") 228fb2aa080SBarry Smith edit_branch = 'main' 229fb2aa080SBarry Smith 230fb2aa080SBarry Smith for dirname in mandirs: 231fb2aa080SBarry Smith outfilename = dirname + '/index.md' 232fb2aa080SBarry Smith dname,secname = posixpath.split(dirname) 233fb2aa080SBarry Smith headfilename = PETSC_DIR + '/' + HEADERDIR + '/header_' + secname 234fb2aa080SBarry Smith table = createtable(dirname,levels,secname,edit_branch) 235fb2aa080SBarry Smith if not table: continue 236fb2aa080SBarry Smith singlelist = addtolist(dirname,singlelist) 237fb2aa080SBarry Smith printindex(outfilename,headfilename,levels,titles,table) 238fb2aa080SBarry Smith 239fb2aa080SBarry Smith alphabet_dict = createdict(singlelist) 240fb2aa080SBarry Smith outfilename = LOC + '/manualpages/singleindex.md' 241fb2aa080SBarry Smith printsingleindex (outfilename,alphabet_dict) 242fb2aa080SBarry Smith 243fb2aa080SBarry Smithif __name__ == '__main__': 244fb2aa080SBarry Smith main(os.path.abspath(os.environ['PETSC_DIR']),os.path.abspath(os.environ['LOC'])) 245