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