xref: /petsc/config/BuildSystem/help.py (revision e44e973aa838678f50136dc10520b932d30ae891)
1179860b2SJed Brown'''This module is meant to provide support for information and help systems based upon RDict.'''
25b6bfdb9SJed Brownfrom __future__ import print_function
35b6bfdb9SJed Brownfrom __future__ import absolute_import
4179860b2SJed Brownimport logger
5*e44e973aSJacob Faibussowitschimport sys
6179860b2SJed Brown
7179860b2SJed Brownclass Info(logger.Logger):
8179860b2SJed Brown  '''This basic class provides information independent of RDict'''
9179860b2SJed Brown  def __init__(self, argDB = None):
10179860b2SJed Brown    '''Creates a dictionary "sections" whose keys are section names, and values are a tuple of (ordinal, nameList)'''
11179860b2SJed Brown    logger.Logger.__init__(self, None, argDB)
12179860b2SJed Brown    self.sections = {}
13179860b2SJed Brown    return
14179860b2SJed Brown
15179860b2SJed Brown  def getTitle(self):
16179860b2SJed Brown    return self._title
17179860b2SJed Brown
18179860b2SJed Brown  def setTitle(self, title):
19179860b2SJed Brown    self._title = str(title)
20179860b2SJed Brown  title = property(getTitle, setTitle, None, 'Title of the Information Menu')
21179860b2SJed Brown
22179860b2SJed Brown  def getDescription(self, section, name):
23179860b2SJed Brown    return self._desc[(section, name)]
24179860b2SJed Brown
25179860b2SJed Brown  def setDescription(self, section, name, desc):
26179860b2SJed Brown    if not hasattr(self, '_desc'):
27179860b2SJed Brown      self._desc = {}
28179860b2SJed Brown    self._desc[(section, name)] = desc
29179860b2SJed Brown    return
30179860b2SJed Brown
31179860b2SJed Brown  def addArgument(self, section, name, desc):
32179860b2SJed Brown    '''Add an argument with given name and string to an information section'''
33179860b2SJed Brown    if not section in self.sections:
34179860b2SJed Brown      self.sections[section] = (len(self.sections), [])
35179860b2SJed Brown    if name in self.sections[section][1]:
36bb3dd2f6SJed Brown      name += '@'+str(len([n for n in self.sections[section][1] if name == n.split('@')[0]])+1)
37179860b2SJed Brown    self.sections[section][1].append(name)
38179860b2SJed Brown    self.setDescription(section, name, desc)
39179860b2SJed Brown    return
40179860b2SJed Brown
41179860b2SJed Brown  def printBanner(self, f):
42179860b2SJed Brown    '''Print a banner for the information screen'''
43*e44e973aSJacob Faibussowitsch    title   = self.title
44*e44e973aSJacob Faibussowitsch    divider = '-' * logger.get_global_divider_length()
45*e44e973aSJacob Faibussowitsch    f.write('{}\n{}\n'.format(title, divider))
46179860b2SJed Brown    return
47179860b2SJed Brown
48179860b2SJed Brown  def getTextSizes(self):
49179860b2SJed Brown    '''Returns the maximum name and description lengths'''
50179860b2SJed Brown    nameLen = 1
51179860b2SJed Brown    descLen = 1
52179860b2SJed Brown    for section in self.sections:
53179860b2SJed Brown      nameLen = max([nameLen, max(map(lambda n: len(n.split('@')[0]), self.sections[section][1]))+1])
54179860b2SJed Brown      descLen = max([descLen, max(map(lambda name: len(self.getDescription(section, name)), self.sections[section][1]))+1])
55179860b2SJed Brown    return (nameLen, descLen)
56179860b2SJed Brown
57179860b2SJed Brown  def output(self, f = None):
58179860b2SJed Brown    '''Print a help screen with all the argument information.'''
59179860b2SJed Brown    if f is  None:
60179860b2SJed Brown      f = sys.stdout
61179860b2SJed Brown    self.printBanner(f)
62179860b2SJed Brown    (nameLen, descLen) = self.getTextSizes()
63179860b2SJed Brown    format = '  %-'+str(nameLen)+'s: %s\n'
64b8b3d021SJed Brown    items  = sorted(self.sections.items(), key=lambda a: a[1][0])
65179860b2SJed Brown    for section, names in items:
66179860b2SJed Brown      f.write(section+':\n')
67179860b2SJed Brown      for name in names[1]:
68179860b2SJed Brown        f.write(format % (name.split('@')[0], self.getDescription(section, name)))
69179860b2SJed Brown    return
70179860b2SJed Brown
71b93f8388SBarry Smith# I don't know how to not have this stupid global variable
72b93f8388SBarry Smith_outputDownloadDone = 0
73b93f8388SBarry Smith
74179860b2SJed Brownclass Help(Info):
75179860b2SJed Brown  '''Help provides a simple help system for RDict'''
76179860b2SJed Brown  def __init__(self, argDB):
77179860b2SJed Brown    '''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.'''
78179860b2SJed Brown    Info.__init__(self, argDB)
79179860b2SJed Brown    self.title = 'Help'
80179860b2SJed Brown    return
81179860b2SJed Brown
82179860b2SJed Brown  def getDescription(self, section, name):
83179860b2SJed Brown    return self.argDB.getType(self.getArgName(name)).help
84179860b2SJed Brown
85179860b2SJed Brown  def setDescription(self, section, name, desc):
86179860b2SJed Brown    return
87179860b2SJed Brown
88179860b2SJed Brown  def getArgName(self, name):
89179860b2SJed Brown    '''Return the RDict key corresponding to a more verbose help name. Right now, this means discard everything after "=".'''
90179860b2SJed Brown    #return name.split('=')[0].strip('-')
91179860b2SJed Brown    argName = name.split('=')[0]
92179860b2SJed Brown    while argName[0] == '-': argName = argName[1:]
93179860b2SJed Brown    return argName
94179860b2SJed Brown
95179860b2SJed Brown  def addArgument(self, section, name, argType, ignoreDuplicates = 0):
96179860b2SJed Brown    '''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.'''
97179860b2SJed Brown##  super(Info, self).addArgument(section, name, None)
98179860b2SJed Brown    if section in self.sections:
99179860b2SJed Brown      if name in self.sections[section][1]:
100179860b2SJed Brown        if ignoreDuplicates:
101179860b2SJed Brown          return
102179860b2SJed Brown        raise RuntimeError('Duplicate configure option '+name+' in section '+section)
103179860b2SJed Brown    else:
104179860b2SJed Brown      self.sections[section] = (len(self.sections), [])
105179860b2SJed Brown    if not argType.deprecated:
106179860b2SJed Brown      self.sections[section][1].append(name)
107179860b2SJed Brown
108179860b2SJed Brown    self.argDB.setType(self.getArgName(name), argType, forceLocal = 1)
109179860b2SJed Brown    return
110179860b2SJed Brown
11175f179b0SBarry Smith  def addDownload(self,name,dlist):
11275f179b0SBarry Smith    if not hasattr(self.argDB,'dlist'):
11375f179b0SBarry Smith      self.argDB.dlist = {}
11475f179b0SBarry Smith    else:
11575f179b0SBarry Smith      self.argDB.dlist[name] = dlist
11675f179b0SBarry Smith
117179860b2SJed Brown  def output(self, f = None, sections = None):
118179860b2SJed Brown    '''Print a help screen with all the argument information.'''
119*e44e973aSJacob Faibussowitsch    def output_items(section_title, items):
120*e44e973aSJacob Faibussowitsch      f.write(
121*e44e973aSJacob Faibussowitsch        logger.build_multiline_message(
122*e44e973aSJacob Faibussowitsch          '***** {} *****'.format(section_title), '', divider_char='-'
123*e44e973aSJacob Faibussowitsch        ) + '\n'
124*e44e973aSJacob Faibussowitsch      )
125*e44e973aSJacob Faibussowitsch
126179860b2SJed Brown      for section, names in items:
127*e44e973aSJacob Faibussowitsch        if sections and not section.casefold() in sections:
128*e44e973aSJacob Faibussowitsch          continue
129*e44e973aSJacob Faibussowitsch
130179860b2SJed Brown        f.write(section + ':\n')
131179860b2SJed Brown        for name in names[1]:
132*e44e973aSJacob Faibussowitsch          arg_name = self.getArgName(name)
133*e44e973aSJacob Faibussowitsch          arg_type = self.argDB.getType(arg_name)
134*e44e973aSJacob Faibussowitsch          arg_help = arg_type.help
135*e44e973aSJacob Faibussowitsch          if arg_name in self.argDB:
136*e44e973aSJacob Faibussowitsch            f.write('  -{}\n       {}  current: {}\n'.format(name, arg_help, arg_type))
137179860b2SJed Brown          else:
138*e44e973aSJacob Faibussowitsch            f.write('  -{}\n       {}\n'.format(name, arg_help))
139*e44e973aSJacob Faibussowitsch      return
140*e44e973aSJacob Faibussowitsch
141*e44e973aSJacob Faibussowitsch    if f is None:
142*e44e973aSJacob Faibussowitsch      f = sys.stdout
143*e44e973aSJacob Faibussowitsch    if sections:
144*e44e973aSJacob Faibussowitsch      sections = {s.casefold() for s in sections}
145*e44e973aSJacob Faibussowitsch
146*e44e973aSJacob Faibussowitsch    packages = []
147*e44e973aSJacob Faibussowitsch    modules  = []
148*e44e973aSJacob Faibussowitsch    for item in self.sections.items():
149*e44e973aSJacob Faibussowitsch      # Packages all have -- for whatever reason -- an uppercase section name, so use this
150*e44e973aSJacob Faibussowitsch      # to distinguish them. This is a vile hack.
151*e44e973aSJacob Faibussowitsch      if item[0].isupper():
152*e44e973aSJacob Faibussowitsch        packages.append(item)
153*e44e973aSJacob Faibussowitsch      else:
154*e44e973aSJacob Faibussowitsch        modules.append(item)
155*e44e973aSJacob Faibussowitsch
156*e44e973aSJacob Faibussowitsch    self.printBanner(f)
157*e44e973aSJacob Faibussowitsch    # sort the primary modules by their ordering, this happens to be nice and logical
158*e44e973aSJacob Faibussowitsch    output_items('CORE OPTIONS', sorted(modules, key=lambda a: a[1][0]))
159*e44e973aSJacob Faibussowitsch    # self.printBanner() will automatically append a '----' so we don't have to print a
160*e44e973aSJacob Faibussowitsch    # divider above, but we do have to here
161*e44e973aSJacob Faibussowitsch    f.write('-' * logger.get_global_divider_length() + '\n')
162*e44e973aSJacob Faibussowitsch    # sort packages by name
163*e44e973aSJacob Faibussowitsch    output_items('PACKAGE OPTIONS', sorted(packages, key=lambda a: a[0]))
164179860b2SJed Brown    return
16575f179b0SBarry Smith
166b93f8388SBarry Smith
16775f179b0SBarry Smith  def outputDownload(self):
1680aa1f76dSSatish Balay    ''' Looks for downloaded packages in --with-packages-download-dir
169b93f8388SBarry Smith        For any it finds it updates the --download-xxx= argument to point to this local copy
170b93f8388SBarry Smith        If it does not find some needed packages then prints the packages that need to be downloaded and exits'''
17175f179b0SBarry Smith    import nargs
17275f179b0SBarry Smith    import os
173b93f8388SBarry Smith    global _outputDownloadDone
174b93f8388SBarry Smith    if _outputDownloadDone: return
175b93f8388SBarry Smith    _outputDownloadDone = 1
1760aa1f76dSSatish Balay    pkgdir = os.path.abspath(os.path.expanduser(nargs.Arg.findArgument('with-packages-download-dir', self.clArgs)))
17775f179b0SBarry Smith    missing = 0
178b93f8388SBarry Smith    for i in self.argDB.dlist.keys():
179b93f8388SBarry Smith      if not nargs.Arg.findArgument('download-'+i, self.clArgs) == None and not nargs.Arg.findArgument('download-'+i, self.clArgs) == '0':
180b93f8388SBarry Smith        dlist = self.argDB.dlist[i]
181b93f8388SBarry Smith        found = 0
182b93f8388SBarry Smith        for k in range(0,len(dlist)):
183cadde5c7SSatish Balay          fd = os.path.join(pkgdir,(os.path.basename(dlist[k])))
184cadde5c7SSatish Balay          if fd.endswith('.git'):
185cadde5c7SSatish Balay            fd = fd[:-4]
186b93f8388SBarry Smith          if os.path.isdir(fd) or os.path.isfile(fd):
187b93f8388SBarry Smith            found = 1
188b93f8388SBarry Smith            break
189b93f8388SBarry Smith        if not found:
190b93f8388SBarry Smith          missing = 1
191b93f8388SBarry Smith    if missing:
1925b6bfdb9SJed Brown      print('Download the following packages to '+pkgdir+' \n')
19375f179b0SBarry Smith    for i in self.argDB.dlist.keys():
19475f179b0SBarry Smith      if not nargs.Arg.findArgument('download-'+i, self.clArgs) == None and not nargs.Arg.findArgument('download-'+i, self.clArgs) == '0':
19575f179b0SBarry Smith        dlist = self.argDB.dlist[i]
19675f179b0SBarry Smith        found = 0
19775f179b0SBarry Smith        for k in range(0,len(dlist)):
198cadde5c7SSatish Balay          fd = os.path.join(pkgdir,(os.path.basename(dlist[k])))
199cadde5c7SSatish Balay          if fd.endswith('.git'):
200cadde5c7SSatish Balay            fd = fd[:-4]
20175f179b0SBarry Smith          if os.path.isdir(fd) or os.path.isfile(fd):
20275f179b0SBarry Smith            found = 1
20375f179b0SBarry Smith            for k in range(0,len(self.clArgs)):
20475f179b0SBarry Smith              if self.clArgs[k].startswith('--download-'+i):
20575f179b0SBarry Smith                self.clArgs[k] = 'download-'+i+'='+fd
20675f179b0SBarry Smith                self.argDB.insertArgs([self.clArgs[k]])
20775f179b0SBarry Smith            break
20875f179b0SBarry Smith        if not found:
2095b6bfdb9SJed Brown          print(i + ' ' + str(self.argDB.dlist[i]))
2109e6d21dfSBarry Smith    if missing:
2115b6bfdb9SJed Brown      print('\nThen run the script again\n')
212c524ecbbSBarry Smith      sys.exit(10)
21375f179b0SBarry Smith
214