#!/usr/bin/env python #!/bin/env python """ 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'. Usage: wwwindex.py PETSC_DIR LOC """ import os import sys import re import glob import posixpath import subprocess 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): # 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') 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): 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): with open(filename, "r") as fd: buf = fd.read() re_name = re.compile('\*\*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) 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) # Reformat level and location tmpbuf = re_level.sub('',buf) re_loc = re.compile('(\*\*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): listdir = os.listdir(dirname) mdfiles = [os.path.join(dirname,f) for f in listdir if f.endswith('.md')] mdfiles.sort() if mdfiles == []: print('Cannot create table for empty directory:',dirname) 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) 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): 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) 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): 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. """ 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,LOC): HEADERDIR = 'doc/classic/manualpages-sec' dirs = glob.glob(LOC + '/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 subprocess.CalledProcessError: 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 + '/header_' + 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 = LOC + '/manualpages/singleindex.md' printsingleindex (outfilename,alphabet_dict) if __name__ == '__main__': main(os.path.abspath(os.environ['PETSC_DIR']),os.path.abspath(os.environ['LOC']))