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