xref: /petsc/config/BuildSystem/help.py (revision 7b5fd022a6ba26727040df7457b27566b4c6742d)
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
5e44e973aSJacob 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'''
43e44e973aSJacob Faibussowitsch    title   = self.title
44e44e973aSJacob Faibussowitsch    divider = '-' * logger.get_global_divider_length()
45e44e973aSJacob 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.'''
119e44e973aSJacob Faibussowitsch    def output_items(section_title, items):
120e44e973aSJacob Faibussowitsch      f.write(
121e44e973aSJacob Faibussowitsch        logger.build_multiline_message(
122e44e973aSJacob Faibussowitsch          '***** {} *****'.format(section_title), '', divider_char='-'
123e44e973aSJacob Faibussowitsch        ) + '\n'
124e44e973aSJacob Faibussowitsch      )
125e44e973aSJacob Faibussowitsch
126179860b2SJed Brown      for section, names in items:
127e44e973aSJacob Faibussowitsch        if sections and not section.casefold() in sections:
128e44e973aSJacob Faibussowitsch          continue
129e44e973aSJacob Faibussowitsch
130179860b2SJed Brown        f.write(section + ':\n')
131179860b2SJed Brown        for name in names[1]:
132e44e973aSJacob Faibussowitsch          arg_name = self.getArgName(name)
133e44e973aSJacob Faibussowitsch          arg_type = self.argDB.getType(arg_name)
134e44e973aSJacob Faibussowitsch          arg_help = arg_type.help
135e44e973aSJacob Faibussowitsch          if arg_name in self.argDB:
136e44e973aSJacob Faibussowitsch            f.write('  -{}\n       {}  current: {}\n'.format(name, arg_help, arg_type))
137179860b2SJed Brown          else:
138e44e973aSJacob Faibussowitsch            f.write('  -{}\n       {}\n'.format(name, arg_help))
139e44e973aSJacob Faibussowitsch      return
140e44e973aSJacob Faibussowitsch
141e44e973aSJacob Faibussowitsch    if f is None:
142e44e973aSJacob Faibussowitsch      f = sys.stdout
143e44e973aSJacob Faibussowitsch    if sections:
144e44e973aSJacob Faibussowitsch      sections = {s.casefold() for s in sections}
145e44e973aSJacob Faibussowitsch
146e44e973aSJacob Faibussowitsch    packages = []
147e44e973aSJacob Faibussowitsch    modules  = []
148e44e973aSJacob Faibussowitsch    for item in self.sections.items():
149e44e973aSJacob Faibussowitsch      # Packages all have -- for whatever reason -- an uppercase section name, so use this
150e44e973aSJacob Faibussowitsch      # to distinguish them. This is a vile hack.
151e44e973aSJacob Faibussowitsch      if item[0].isupper():
152e44e973aSJacob Faibussowitsch        packages.append(item)
153e44e973aSJacob Faibussowitsch      else:
154e44e973aSJacob Faibussowitsch        modules.append(item)
155e44e973aSJacob Faibussowitsch
156e44e973aSJacob Faibussowitsch    self.printBanner(f)
157e44e973aSJacob Faibussowitsch    # sort the primary modules by their ordering, this happens to be nice and logical
158e44e973aSJacob Faibussowitsch    output_items('CORE OPTIONS', sorted(modules, key=lambda a: a[1][0]))
159e44e973aSJacob Faibussowitsch    # self.printBanner() will automatically append a '----' so we don't have to print a
160e44e973aSJacob Faibussowitsch    # divider above, but we do have to here
161e44e973aSJacob Faibussowitsch    f.write('-' * logger.get_global_divider_length() + '\n')
162e44e973aSJacob Faibussowitsch    # sort packages by name
163e44e973aSJacob Faibussowitsch    output_items('PACKAGE OPTIONS', sorted(packages, key=lambda a: a[0]))
164179860b2SJed Brown    return
16575f179b0SBarry Smith
16675f179b0SBarry Smith  def outputDownload(self):
1670aa1f76dSSatish Balay    ''' Looks for downloaded packages in --with-packages-download-dir
168b93f8388SBarry Smith        For any it finds it updates the --download-xxx= argument to point to this local copy
169b93f8388SBarry Smith        If it does not find some needed packages then prints the packages that need to be downloaded and exits'''
17075f179b0SBarry Smith    import nargs
17175f179b0SBarry Smith    import os
172b93f8388SBarry Smith    global _outputDownloadDone
173b93f8388SBarry Smith    if _outputDownloadDone: return
174b93f8388SBarry Smith    _outputDownloadDone = 1
1750aa1f76dSSatish Balay    pkgdir = os.path.abspath(os.path.expanduser(nargs.Arg.findArgument('with-packages-download-dir', self.clArgs)))
1765fd7084cSSatish Balay    mesg = ''
177b93f8388SBarry Smith    for i in self.argDB.dlist.keys():
178b93f8388SBarry Smith      if not nargs.Arg.findArgument('download-'+i, self.clArgs) == None and not nargs.Arg.findArgument('download-'+i, self.clArgs) == '0':
179b93f8388SBarry Smith        dlist = self.argDB.dlist[i]
180b93f8388SBarry Smith        found = 0
181b93f8388SBarry Smith        for k in range(0,len(dlist)):
182*a2cf3c57SSatish Balay          # github tarballs do not have pkg names, can cause namespace conflict, so also check for pkgname-FILENAME
183*a2cf3c57SSatish Balay          # note: cannot check for pkgname/FILENAME - as there can be conflicts with git-URL [that gets checked before the tarball check]
184*a2cf3c57SSatish Balay          fd = os.path.join(pkgdir,i + '-' + (os.path.basename(dlist[k])))
185*a2cf3c57SSatish Balay          if os.path.isfile(fd):
186*a2cf3c57SSatish Balay            found = 1
187*a2cf3c57SSatish Balay            break
188*a2cf3c57SSatish Balay          # Now check for the unmodified FILENAME (tarball, git repo, or extracted folder)
189cadde5c7SSatish Balay          fd = os.path.join(pkgdir,(os.path.basename(dlist[k])))
190cadde5c7SSatish Balay          if fd.endswith('.git'):
191cadde5c7SSatish Balay            fd = fd[:-4]
192b93f8388SBarry Smith          if os.path.isdir(fd) or os.path.isfile(fd):
193b93f8388SBarry Smith            found = 1
194b93f8388SBarry Smith            break
1955fd7084cSSatish Balay        if found:
19675f179b0SBarry Smith          for k in range(0,len(self.clArgs)):
19775f179b0SBarry Smith            if self.clArgs[k].startswith('--download-'+i):
19875f179b0SBarry Smith              self.clArgs[k] = 'download-'+i+'='+fd
19975f179b0SBarry Smith              self.argDB.insertArgs([self.clArgs[k]])
2005fd7084cSSatish Balay        else:
2015fd7084cSSatish Balay          mesg += i + ' ' + str(self.argDB.dlist[i]).replace("git://","git clone ")+'\n'
2025fd7084cSSatish Balay    if mesg:
2035fd7084cSSatish Balay      print('Download the following packages to '+pkgdir+' \n')
2045fd7084cSSatish Balay      print(mesg)
2055fd7084cSSatish Balay      print('Then run the script again\n')
206c524ecbbSBarry Smith      sys.exit(10)
207