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