xref: /petsc/config/BuildSystem/help.py (revision feff33ee0b5b037fa8f9f294dede656a2f85cc47)
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(filter(lambda n: name == n.split('@')[0], self.sections[section][1]))+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  = self.sections.items()
65    items.sort(lambda a, b: a[1][0].__cmp__(b[1][0]))
66    for section, names in items:
67      f.write(section+':\n')
68      for name in names[1]:
69        f.write(format % (name.split('@')[0], self.getDescription(section, name)))
70    return
71
72# I don't know how to not have this stupid global variable
73_outputDownloadDone = 0
74
75class Help(Info):
76  '''Help provides a simple help system for RDict'''
77  def __init__(self, argDB):
78    '''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.'''
79    Info.__init__(self, argDB)
80    self.title = 'Help'
81    return
82
83  def getDescription(self, section, name):
84    return self.argDB.getType(self.getArgName(name)).help
85
86  def setDescription(self, section, name, desc):
87    return
88
89  def getArgName(self, name):
90    '''Return the RDict key corresponding to a more verbose help name. Right now, this means discard everything after "=".'''
91    #return name.split('=')[0].strip('-')
92    argName = name.split('=')[0]
93    while argName[0] == '-': argName = argName[1:]
94    return argName
95
96  def addArgument(self, section, name, argType, ignoreDuplicates = 0):
97    '''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.'''
98##  super(Info, self).addArgument(section, name, None)
99    if section in self.sections:
100      if name in self.sections[section][1]:
101        if ignoreDuplicates:
102          return
103        raise RuntimeError('Duplicate configure option '+name+' in section '+section)
104    else:
105      self.sections[section] = (len(self.sections), [])
106    if not argType.deprecated:
107      self.sections[section][1].append(name)
108
109    self.argDB.setType(self.getArgName(name), argType, forceLocal = 1)
110    return
111
112  def addDownload(self,name,dlist):
113    if not hasattr(self.argDB,'dlist'):
114      self.argDB.dlist = {}
115    else:
116      self.argDB.dlist[name] = dlist
117
118  def output(self, f = None, sections = None):
119    '''Print a help screen with all the argument information.'''
120    if f is  None:
121      import sys
122      f = sys.stdout
123    if sections: sections = [s.lower() for s in sections]
124    self.printBanner(f)
125    (nameLen, descLen) = self.getTextSizes()
126#    format    = '  -%-'+str(nameLen)+'s: %s\n'
127#    formatDef = '  -%-'+str(nameLen)+'s: %-'+str(descLen)+'s  current: %s\n'
128    format    = '  -%s\n       %s\n'
129    formatDef = '  -%s\n       %s  current: %s\n'
130    items = self.sections.items()
131    items.sort(lambda a, b: a[1][0].__cmp__(b[1][0]))
132    for section, names in items:
133      if sections and not section.lower() in sections: continue
134      f.write(section+':\n')
135      for name in names[1]:
136        argName = self.getArgName(name)
137        type    = self.argDB.getType(argName)
138        if argName in self.argDB:
139          f.write(formatDef % (name, type.help, str(self.argDB.getType(argName))))
140        else:
141          f.write(format % (name, type.help))
142    return
143
144
145  def outputDownload(self):
146    ''' Looks for downloaded packages in --with-packages-dir
147        For any it finds it updates the --download-xxx= argument to point to this local copy
148        If it does not find some needed packages then prints the packages that need to be downloaded and exits'''
149    import nargs
150    import os
151    import sys
152    global _outputDownloadDone
153    if _outputDownloadDone: return
154    _outputDownloadDone = 1
155    pkgdir = os.path.abspath(os.path.expanduser(nargs.Arg.findArgument('with-packages-dir', self.clArgs)))
156    missing = 0
157    for i in self.argDB.dlist.keys():
158      if not nargs.Arg.findArgument('download-'+i, self.clArgs) == None and not nargs.Arg.findArgument('download-'+i, self.clArgs) == '0':
159        dlist = self.argDB.dlist[i]
160        found = 0
161        for k in range(0,len(dlist)):
162          fd = os.path.join(pkgdir,os.path.basename(dlist[k]))
163          if os.path.isdir(fd) or os.path.isfile(fd):
164            found = 1
165            break
166        if not found:
167          missing = 1
168    if missing:
169      print('Download the following packages to '+pkgdir+' \n')
170    for i in self.argDB.dlist.keys():
171      if not nargs.Arg.findArgument('download-'+i, self.clArgs) == None and not nargs.Arg.findArgument('download-'+i, self.clArgs) == '0':
172        dlist = self.argDB.dlist[i]
173        found = 0
174        for k in range(0,len(dlist)):
175          fd = os.path.join(pkgdir,os.path.basename(dlist[k]))
176          if os.path.isdir(fd) or os.path.isfile(fd):
177            found = 1
178            for k in range(0,len(self.clArgs)):
179              if self.clArgs[k].startswith('--download-'+i):
180                self.clArgs[k] = 'download-'+i+'='+fd
181                self.argDB.insertArgs([self.clArgs[k]])
182            break
183        if not found:
184          print(i + ' ' + str(self.argDB.dlist[i]))
185    if missing:
186      print('\nThen run the script again\n')
187      sys.exit(10)
188
189