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