xref: /petsc/config/BuildSystem/help.py (revision 116bdff65fed8fe543555730f89dc71bb7f8b1f2)
1'''This module is meant to provide support for information and help systems based upon RDict.'''
2from __future__ import print_function
3from __future__ import absolute_import
4import logger
5
6class Info(logger.Logger):
7  '''This basic class provides information independent of RDict'''
8  def __init__(self, argDB = None):
9    '''Creates a dictionary "sections" whose keys are section names, and values are a tuple of (ordinal, nameList)'''
10    logger.Logger.__init__(self, None, argDB)
11    self.sections = {}
12    return
13
14  def getTitle(self):
15    return self._title
16
17  def setTitle(self, title):
18    self._title = str(title)
19  title = property(getTitle, setTitle, None, 'Title of the Information Menu')
20
21  def getDescription(self, section, name):
22    return self._desc[(section, name)]
23
24  def setDescription(self, section, name, desc):
25    if not hasattr(self, '_desc'):
26      self._desc = {}
27    self._desc[(section, name)] = desc
28    return
29
30  def addArgument(self, section, name, desc):
31    '''Add an argument with given name and string to an information section'''
32    if not section in self.sections:
33      self.sections[section] = (len(self.sections), [])
34    if name in self.sections[section][1]:
35      name += '@'+str(len([n for n in self.sections[section][1] if name == n.split('@')[0]])+1)
36    self.sections[section][1].append(name)
37    self.setDescription(section, name, desc)
38    return
39
40  def printBanner(self, f):
41    '''Print a banner for the information screen'''
42    f.write(self.title+'\n')
43    for i in range(max(map(len, self.title.split('\n')))): f.write('-')
44    f.write('\n')
45    return
46
47  def getTextSizes(self):
48    '''Returns the maximum name and description lengths'''
49    nameLen = 1
50    descLen = 1
51    for section in self.sections:
52      nameLen = max([nameLen, max(map(lambda n: len(n.split('@')[0]), self.sections[section][1]))+1])
53      descLen = max([descLen, max(map(lambda name: len(self.getDescription(section, name)), self.sections[section][1]))+1])
54    return (nameLen, descLen)
55
56  def output(self, f = None):
57    '''Print a help screen with all the argument information.'''
58    if f is  None:
59      import sys
60      f = sys.stdout
61    self.printBanner(f)
62    (nameLen, descLen) = self.getTextSizes()
63    format = '  %-'+str(nameLen)+'s: %s\n'
64    items  = sorted(self.sections.items(), key=lambda a: a[1][0])
65    for section, names in items:
66      f.write(section+':\n')
67      for name in names[1]:
68        f.write(format % (name.split('@')[0], self.getDescription(section, name)))
69    return
70
71# I don't know how to not have this stupid global variable
72_outputDownloadDone = 0
73
74class Help(Info):
75  '''Help provides a simple help system for RDict'''
76  def __init__(self, argDB):
77    '''Creates a dictionary "sections" whose keys are section names, and values are a tuple of (ordinal, nameList). Also provide the RDict upon which this will be based.'''
78    Info.__init__(self, argDB)
79    self.title = 'Help'
80    return
81
82  def getDescription(self, section, name):
83    return self.argDB.getType(self.getArgName(name)).help
84
85  def setDescription(self, section, name, desc):
86    return
87
88  def getArgName(self, name):
89    '''Return the RDict key corresponding to a more verbose help name. Right now, this means discard everything after "=".'''
90    #return name.split('=')[0].strip('-')
91    argName = name.split('=')[0]
92    while argName[0] == '-': argName = argName[1:]
93    return argName
94
95  def addArgument(self, section, name, argType, ignoreDuplicates = 0):
96    '''Add an argument with given name and type to a help section. The type, which can also have an initializer and help string, will be put into RDict.'''
97##  super(Info, self).addArgument(section, name, None)
98    if section in self.sections:
99      if name in self.sections[section][1]:
100        if ignoreDuplicates:
101          return
102        raise RuntimeError('Duplicate configure option '+name+' in section '+section)
103    else:
104      self.sections[section] = (len(self.sections), [])
105    if not argType.deprecated:
106      self.sections[section][1].append(name)
107
108    self.argDB.setType(self.getArgName(name), argType, forceLocal = 1)
109    return
110
111  def addDownload(self,name,dlist):
112    if not hasattr(self.argDB,'dlist'):
113      self.argDB.dlist = {}
114    else:
115      self.argDB.dlist[name] = dlist
116
117  def output(self, f = None, sections = None):
118    '''Print a help screen with all the argument information.'''
119    if f is  None:
120      import sys
121      f = sys.stdout
122    if sections: sections = [s.lower() for s in sections]
123    self.printBanner(f)
124    (nameLen, descLen) = self.getTextSizes()
125#    format    = '  -%-'+str(nameLen)+'s: %s\n'
126#    formatDef = '  -%-'+str(nameLen)+'s: %-'+str(descLen)+'s  current: %s\n'
127    format    = '  -%s\n       %s\n'
128    formatDef = '  -%s\n       %s  current: %s\n'
129    items = self.sections.items()
130    items.sort(key=lambda a: a[1][0])
131    for section, names in items:
132      if sections and not section.lower() in sections: continue
133      f.write(section+':\n')
134      for name in names[1]:
135        argName = self.getArgName(name)
136        type    = self.argDB.getType(argName)
137        if argName in self.argDB:
138          f.write(formatDef % (name, type.help, str(self.argDB.getType(argName))))
139        else:
140          f.write(format % (name, type.help))
141    return
142
143
144  def outputDownload(self):
145    ''' Looks for downloaded packages in --with-packages-download-dir
146        For any it finds it updates the --download-xxx= argument to point to this local copy
147        If it does not find some needed packages then prints the packages that need to be downloaded and exits'''
148    import nargs
149    import os
150    import sys
151    global _outputDownloadDone
152    if _outputDownloadDone: return
153    _outputDownloadDone = 1
154    pkgdir = os.path.abspath(os.path.expanduser(nargs.Arg.findArgument('with-packages-download-dir', self.clArgs)))
155    missing = 0
156    for i in self.argDB.dlist.keys():
157      if not nargs.Arg.findArgument('download-'+i, self.clArgs) == None and not nargs.Arg.findArgument('download-'+i, self.clArgs) == '0':
158        dlist = self.argDB.dlist[i]
159        found = 0
160        for k in range(0,len(dlist)):
161          fd = os.path.join(pkgdir,os.path.basename(dlist[k]))
162          if os.path.isdir(fd) or os.path.isfile(fd):
163            found = 1
164            break
165        if not found:
166          missing = 1
167    if missing:
168      print('Download the following packages to '+pkgdir+' \n')
169    for i in self.argDB.dlist.keys():
170      if not nargs.Arg.findArgument('download-'+i, self.clArgs) == None and not nargs.Arg.findArgument('download-'+i, self.clArgs) == '0':
171        dlist = self.argDB.dlist[i]
172        found = 0
173        for k in range(0,len(dlist)):
174          fd = os.path.join(pkgdir,os.path.basename(dlist[k]))
175          if os.path.isdir(fd) or os.path.isfile(fd):
176            found = 1
177            for k in range(0,len(self.clArgs)):
178              if self.clArgs[k].startswith('--download-'+i):
179                self.clArgs[k] = 'download-'+i+'='+fd
180                self.argDB.insertArgs([self.clArgs[k]])
181            break
182        if not found:
183          print(i + ' ' + str(self.argDB.dlist[i]))
184    if missing:
185      print('\nThen run the script again\n')
186      sys.exit(10)
187
188