xref: /petsc/doc/build_man_index.py (revision e4094ef18e7e53fda86cf35f3a47fda48a8e77d8)
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