#!/usr/bin/env python3 """ Reads in all the generated manual pages, and creates the index for the manualpages, ordering the indices into sections based on the 'Level of Difficulty'. """ import os import sys import re import glob import posixpath import subprocess numberErrors = 0 HLIST_COLUMNS = 3 # Read an optional header file, whose contents are first copied over # Use the level info, and print a formatted index table of all the manual pages # def printindex(outfilename, headfilename, levels, titles, tables): global numberErrors # Read in the header file headbuf = '' if posixpath.exists(headfilename) : with open(headfilename, "r") as fd: headbuf = fd.read() headbuf = headbuf.replace('PETSC_DIR', '../../../') else: print('Error! SUBMANSEC header file "%s" does not exist' % headfilename) print('Likley you introduced a new set of manual pages but did not add the header file that describes them') numberErrors = numberErrors + 1 with open(outfilename, "w") as fd: # Since it uses three columns we must remove right sidebar so all columns are displayed completely # https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/page-toc.html fd.write(':html_theme.sidebar_secondary.remove: true\n') fd.write(headbuf) fd.write('\n') all_names = [] for i, level in enumerate(levels): title = titles[i] if not tables[i]: if level != 'none' and level != 'deprecated': fd.write('\n## No %s routines\n' % level) continue fd.write('\n## %s\n' % title) fd.write('```{hlist}\n') fd.write("---\n") fd.write("columns: %d\n" % HLIST_COLUMNS) fd.write("---\n") for filename in tables[i]: path,name = posixpath.split(filename) func_name,ext = posixpath.splitext(name) fd.write('- [](%s)\n' % name) all_names.append(name) fd.write('```\n\n\n') fd.write('\n## Single list of manual pages\n') fd.write('```{hlist}\n') fd.write("---\n") fd.write("columns: %d\n" % HLIST_COLUMNS) fd.write("---\n") for name in sorted(all_names): fd.write('- [](%s)\n' % name) fd.write('```\n\n\n') # This routine takes in as input a dictionary, which contains the # alhabetical index to all the man page functions, and prints them all in # a single index page def printsingleindex(outfilename, alphabet_dict): global numberErrors with open(outfilename, "w") as fd: fd.write("# Single Index of all PETSc Manual Pages\n\n") fd.write(" Also see the [Manual page table of contents, by section](/manualpages/index.md).\n\n") for key in sorted(alphabet_dict.keys()): fd.write("## %s\n\n" % key.upper()) fd.write("```{hlist}\n") fd.write("---\n") fd.write("columns: %d\n" % HLIST_COLUMNS) fd.write("---\n") function_dict = alphabet_dict[key] for name in sorted(function_dict.keys()): if name: path_name = function_dict[name] else: path_name = '' fd.write("- [%s](%s)\n" % (name, path_name)) fd.write("```\n") # Read in the filename contents, and search for the formatted # String 'Level:' and return the level info. # Also adds the BOLD HTML format to Level field def modifylevel(filename,secname,edit_branch): global numberErrors with open(filename, "r") as fd: buf = fd.read() re_name = re.compile(r'\*\*Location:\*\*(.*)') # As defined in myst.def m = re_name.search(buf) if m: loc_html = m.group(1) if loc_html: pattern = re.compile(r"(.*)") loc = re.match(pattern, loc_html) if loc: source_path = loc.group(1) buf += "\n\n---\n[Edit on GitLab](https://gitlab.com/petsc/petsc/-/edit/%s/%s)\n\n" % (edit_branch, source_path) else: print("Warning. Could not find source path in %s" % filename) else: print('Error! No location in file:', filename) numberErrors = numberErrors + 1 re_level = re.compile(r'(Level:)\s+(\w+)') m = re_level.search(buf) level = 'none' if m: level = m.group(2) else: print('Error! No level info in file:', filename) numberErrors = numberErrors + 1 # Reformat level and location tmpbuf = re_level.sub('',buf) re_loc = re.compile(r'(\*\*Location:\*\*)') tmpbuf = re_loc.sub('\n## Level\n' + level + '\n\n## Location\n',tmpbuf) # Modify .c#,.h#,.cu#,.cxx# to .c.html#,.h.html#,.cu.html#,.cxx.html# tmpbuf = re.sub('.c#', '.c.html#', tmpbuf) tmpbuf = re.sub('.h#', '.h.html#', tmpbuf) tmpbuf = re.sub('.cu#', '.cu.html#', tmpbuf) tmpbuf = re.sub('.cxx#', '.cxx.html#', tmpbuf) # Add footer links 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' # write the modified manpage with open(filename, "w") as fd: fd.write(':orphan:\n'+outbuf) return level # Go through each manpage file, present in dirname, # and create and return a table for it, wrt levels specified. def createtable(dirname,levels,secname,editbranch): global numberErrors listdir = os.listdir(dirname) mdfiles = [os.path.join(dirname,f) for f in listdir if f.endswith('.md') and not f == 'index.md'] mdfiles.sort() if mdfiles == []: return None table = [] for level in levels: table.append([]) for filename in mdfiles: level = modifylevel(filename,secname,editbranch) if level.lower() in levels: table[levels.index(level.lower())].append(filename) else: print('Error! Unknown level \''+ level + '\' in', filename) numberErrors = numberErrors + 1 return table # This routine is called for each man dir. Each time, it # adds the list of manpages, to the given list, and returns # the union list. def addtolist(dirname,singlelist): global numberErrors mdfiles = [os.path.join(dirname,f) for f in os.listdir(dirname) if f.endswith('.md')] mdfiles.sort() if mdfiles == []: print('Error! Empty directory:',dirname) numberErrors = numberErrors + 1 return None singlelist.extend(mdfiles) return singlelist # This routine creates a dictionary, with entries such that each # key is the alphabet, and the vaue corresponds to this key is a dictionary # of FunctionName/PathToFile Pair. def createdict(singlelist): global numberErrors newdict = {} for filename in singlelist: path,name = posixpath.split(filename) # grab the short path Mat from /wired/path/Mat junk,path = posixpath.split(path) index_char = name[0:1].lower() # remove the .name suffix from name func_name,ext = posixpath.splitext(name) if index_char not in newdict: newdict[index_char] = {} newdict[index_char][func_name] = path + '/' + name return newdict def getallmandirs(dirs): """ Gets the list of man* dirs present in the doc dir. Each dir will have an index created for it. """ global numberErrors mandirs = [] for filename in dirs: path,name = posixpath.split(filename) if name == 'RCS' or name == 'sec' or name == "concepts" or name == "SCCS" : continue if posixpath.isdir(filename): mandirs.append(filename) return mandirs def main(PETSC_DIR, build_dir): global numberErrors HEADERDIR = 'doc/manualpages/MANSECHeaders' dirs = glob.glob(os.path.join(build_dir,'manualpages','*')) mandirs = getallmandirs(dirs) levels = ['beginner','intermediate','advanced','developer','deprecated','none'] titles = ['Beginner - Basic usage', 'Intermediate - Setting options for algorithms and data structures', 'Advanced - Setting more advanced options and customization', 'Developer - Interfaces rarely needed by applications programmers', 'Deprecated - Functionality scheduled for removal in the future', 'None: Not yet cataloged'] singlelist = [] git_ref = subprocess.check_output(['git', 'rev-parse', 'HEAD']).rstrip() try: git_ref_release = subprocess.check_output(['git', 'rev-parse', 'origin/release']).rstrip() edit_branch = 'release' if git_ref == git_ref_release else 'main' except: print("WARNING: checking branch for man page edit links failed") edit_branch = 'main' for dirname in mandirs: outfilename = dirname + '/index.md' dname,secname = posixpath.split(dirname) headfilename = PETSC_DIR + '/' + HEADERDIR + '/' + secname table = createtable(dirname,levels,secname,edit_branch) if not table: continue singlelist = addtolist(dirname,singlelist) printindex(outfilename,headfilename,levels,titles,table) alphabet_dict = createdict(singlelist) outfilename = os.path.join(build_dir,'manualpages','singleindex.md') printsingleindex (outfilename,alphabet_dict) if numberErrors: raise RuntimeError('Stopping document build since errors were detected in generating manual page indices')