xref: /petsc/config/BuildSystem/logger.py (revision 179860b23afbef20daed3359c1645679d1efa988)
1*179860b2SJed Brownimport args
2*179860b2SJed Brownimport sys
3*179860b2SJed Brownimport os
4*179860b2SJed Brown
5*179860b2SJed Brown# Ugly stuff to have curses called ONLY once, instead of for each
6*179860b2SJed Brown# new Configure object created (and flashing the screen)
7*179860b2SJed Brownglobal LineWidth
8*179860b2SJed Brownglobal RemoveDirectory
9*179860b2SJed Brownglobal backupRemoveDirectory
10*179860b2SJed BrownLineWidth = -1
11*179860b2SJed BrownRemoveDirectory = os.path.join(os.getcwd(),'')
12*179860b2SJed BrownbackupRemoveDirectory = ''
13*179860b2SJed Brown
14*179860b2SJed Brown# Compatibility fixes
15*179860b2SJed Browntry:
16*179860b2SJed Brown  enumerate([0, 1])
17*179860b2SJed Brownexcept NameError:
18*179860b2SJed Brown  def enumerate(l):
19*179860b2SJed Brown    return zip(range(len(l)), l)
20*179860b2SJed Browntry:
21*179860b2SJed Brown  True, False
22*179860b2SJed Brownexcept NameError:
23*179860b2SJed Brown  True, False = (0==0, 0!=0)
24*179860b2SJed Brown
25*179860b2SJed Brownclass Logger(args.ArgumentProcessor):
26*179860b2SJed Brown  '''This class creates a shared log and provides methods for writing to it'''
27*179860b2SJed Brown  defaultLog = None
28*179860b2SJed Brown  defaultOut = sys.stdout
29*179860b2SJed Brown
30*179860b2SJed Brown  def __init__(self, clArgs = None, argDB = None, log = None, out = defaultOut, debugLevel = None, debugSections = None, debugIndent = None):
31*179860b2SJed Brown    args.ArgumentProcessor.__init__(self, clArgs, argDB)
32*179860b2SJed Brown    self.logName       = None
33*179860b2SJed Brown    self.log           = log
34*179860b2SJed Brown    self.out           = out
35*179860b2SJed Brown    self.debugLevel    = debugLevel
36*179860b2SJed Brown    self.debugSections = debugSections
37*179860b2SJed Brown    self.debugIndent   = debugIndent
38*179860b2SJed Brown    self.getRoot()
39*179860b2SJed Brown    return
40*179860b2SJed Brown
41*179860b2SJed Brown  def __getstate__(self):
42*179860b2SJed Brown    '''We do not want to pickle the default log stream'''
43*179860b2SJed Brown    d = args.ArgumentProcessor.__getstate__(self)
44*179860b2SJed Brown    if 'log' in d:
45*179860b2SJed Brown      if d['log'] is Logger.defaultLog:
46*179860b2SJed Brown        del d['log']
47*179860b2SJed Brown      else:
48*179860b2SJed Brown        d['log'] = None
49*179860b2SJed Brown    if 'out' in d:
50*179860b2SJed Brown      if d['out'] is Logger.defaultOut:
51*179860b2SJed Brown        del d['out']
52*179860b2SJed Brown      else:
53*179860b2SJed Brown        d['out'] = None
54*179860b2SJed Brown    return d
55*179860b2SJed Brown
56*179860b2SJed Brown  def __setstate__(self, d):
57*179860b2SJed Brown    '''We must create the default log stream'''
58*179860b2SJed Brown    args.ArgumentProcessor.__setstate__(self, d)
59*179860b2SJed Brown    if not 'log' in d:
60*179860b2SJed Brown      self.log = self.createLog(None)
61*179860b2SJed Brown    if not 'out' in d:
62*179860b2SJed Brown      self.out = Logger.defaultOut
63*179860b2SJed Brown    self.__dict__.update(d)
64*179860b2SJed Brown    return
65*179860b2SJed Brown
66*179860b2SJed Brown  def setupArguments(self, argDB):
67*179860b2SJed Brown    '''Setup types in the argument database'''
68*179860b2SJed Brown    import nargs
69*179860b2SJed Brown
70*179860b2SJed Brown    argDB = args.ArgumentProcessor.setupArguments(self, argDB)
71*179860b2SJed Brown    argDB.setType('log',           nargs.Arg(None, 'build.log', 'The filename for the log'))
72*179860b2SJed Brown    argDB.setType('logAppend',     nargs.ArgBool(None, 0, 'The flag determining whether we backup or append to the current log', isTemporary = 1))
73*179860b2SJed Brown    argDB.setType('debugLevel',    nargs.ArgInt(None, 3, 'Integer 0 to 4, where a higher level means more detail', 0, 5))
74*179860b2SJed Brown    argDB.setType('debugSections', nargs.Arg(None, [], 'Message types to print, e.g. [compile,link,hg,install]'))
75*179860b2SJed Brown    argDB.setType('debugIndent',   nargs.Arg(None, '  ', 'The string used for log indentation'))
76*179860b2SJed Brown    argDB.setType('scrollOutput',  nargs.ArgBool(None, 0, 'Flag to allow output to scroll rather than overwriting a single line'))
77*179860b2SJed Brown    argDB.setType('noOutput',      nargs.ArgBool(None, 0, 'Flag to suppress output to the terminal'))
78*179860b2SJed Brown    return argDB
79*179860b2SJed Brown
80*179860b2SJed Brown  def setup(self):
81*179860b2SJed Brown    '''Setup the terminal output and filtering flags'''
82*179860b2SJed Brown    self.log = self.createLog(self.logName, self.log)
83*179860b2SJed Brown    args.ArgumentProcessor.setup(self)
84*179860b2SJed Brown
85*179860b2SJed Brown    if self.argDB['noOutput']:
86*179860b2SJed Brown      self.out           = None
87*179860b2SJed Brown    if self.debugLevel is None:
88*179860b2SJed Brown      self.debugLevel    = self.argDB['debugLevel']
89*179860b2SJed Brown    if self.debugSections is None:
90*179860b2SJed Brown      self.debugSections = self.argDB['debugSections']
91*179860b2SJed Brown    if self.debugIndent is None:
92*179860b2SJed Brown      self.debugIndent   = self.argDB['debugIndent']
93*179860b2SJed Brown    return
94*179860b2SJed Brown
95*179860b2SJed Brown  def checkLog(self, logName):
96*179860b2SJed Brown    import nargs
97*179860b2SJed Brown    import os
98*179860b2SJed Brown
99*179860b2SJed Brown    if logName is None:
100*179860b2SJed Brown      logName = nargs.Arg.findArgument('log', self.clArgs)
101*179860b2SJed Brown    if logName is None:
102*179860b2SJed Brown      if not self.argDB is None and 'log' in self.argDB:
103*179860b2SJed Brown        logName    = self.argDB['log']
104*179860b2SJed Brown      else:
105*179860b2SJed Brown        logName    = 'default.log'
106*179860b2SJed Brown    self.logName   = logName
107*179860b2SJed Brown    self.logExists = os.path.exists(self.logName)
108*179860b2SJed Brown    return self.logExists
109*179860b2SJed Brown
110*179860b2SJed Brown  def createLog(self, logName, initLog = None):
111*179860b2SJed Brown    '''Create a default log stream, unless initLog is given'''
112*179860b2SJed Brown    import nargs
113*179860b2SJed Brown
114*179860b2SJed Brown    if not initLog is None:
115*179860b2SJed Brown      log = initLog
116*179860b2SJed Brown    else:
117*179860b2SJed Brown      if Logger.defaultLog is None:
118*179860b2SJed Brown        appendArg = nargs.Arg.findArgument('logAppend', self.clArgs)
119*179860b2SJed Brown        if self.checkLog(logName):
120*179860b2SJed Brown          if not self.argDB is None and ('logAppend' in self.argDB and self.argDB['logAppend']) or (not appendArg is None and bool(appendArg)):
121*179860b2SJed Brown            Logger.defaultLog = file(self.logName, 'a')
122*179860b2SJed Brown          else:
123*179860b2SJed Brown            try:
124*179860b2SJed Brown              import os
125*179860b2SJed Brown
126*179860b2SJed Brown              os.rename(self.logName, self.logName+'.bkp')
127*179860b2SJed Brown              Logger.defaultLog = file(self.logName, 'w')
128*179860b2SJed Brown            except OSError:
129*179860b2SJed Brown              print 'WARNING: Cannot backup log file, appending instead.'
130*179860b2SJed Brown              Logger.defaultLog = file(self.logName, 'a')
131*179860b2SJed Brown        else:
132*179860b2SJed Brown          Logger.defaultLog = file(self.logName, 'w')
133*179860b2SJed Brown      log = Logger.defaultLog
134*179860b2SJed Brown    return log
135*179860b2SJed Brown
136*179860b2SJed Brown  def closeLog(self):
137*179860b2SJed Brown    '''Closes the log file'''
138*179860b2SJed Brown    self.log.close()
139*179860b2SJed Brown
140*179860b2SJed Brown  def getLinewidth(self):
141*179860b2SJed Brown    global LineWidth
142*179860b2SJed Brown    if not hasattr(self, '_linewidth'):
143*179860b2SJed Brown      if self.out is None or not self.out.isatty() or self.argDB['scrollOutput']:
144*179860b2SJed Brown        self._linewidth = -1
145*179860b2SJed Brown      else:
146*179860b2SJed Brown        if LineWidth == -1:
147*179860b2SJed Brown          try:
148*179860b2SJed Brown            import curses
149*179860b2SJed Brown
150*179860b2SJed Brown            try:
151*179860b2SJed Brown              curses.setupterm()
152*179860b2SJed Brown              (y, self._linewidth) = curses.initscr().getmaxyx()
153*179860b2SJed Brown              curses.endwin()
154*179860b2SJed Brown            except curses.error:
155*179860b2SJed Brown              self._linewidth = -1
156*179860b2SJed Brown          except:
157*179860b2SJed Brown            self._linewidth = -1
158*179860b2SJed Brown          LineWidth = self._linewidth
159*179860b2SJed Brown        else:
160*179860b2SJed Brown          self._linewidth = LineWidth
161*179860b2SJed Brown    return self._linewidth
162*179860b2SJed Brown  def setLinewidth(self, linewidth):
163*179860b2SJed Brown    self._linewidth = linewidth
164*179860b2SJed Brown    return
165*179860b2SJed Brown  linewidth = property(getLinewidth, setLinewidth, doc = 'The maximum number of characters per log line')
166*179860b2SJed Brown
167*179860b2SJed Brown  def checkWrite(self, f, debugLevel, debugSection, writeAll = 0):
168*179860b2SJed Brown    '''Check whether the log line should be written
169*179860b2SJed Brown       - If writeAll is true, return true
170*179860b2SJed Brown       - If debugLevel >= current level, and debugSection in current section or sections is empty, return true'''
171*179860b2SJed Brown    if not isinstance(debugLevel, int):
172*179860b2SJed Brown      raise RuntimeError('Debug level must be an integer: '+str(debugLevel))
173*179860b2SJed Brown    if f is None:
174*179860b2SJed Brown      return False
175*179860b2SJed Brown    if writeAll:
176*179860b2SJed Brown      return True
177*179860b2SJed Brown    if self.debugLevel >= debugLevel and (not len(self.debugSections) or debugSection in self.debugSections):
178*179860b2SJed Brown      return True
179*179860b2SJed Brown    return False
180*179860b2SJed Brown
181*179860b2SJed Brown  def logIndent(self, debugLevel = -1, debugSection = None, comm = None):
182*179860b2SJed Brown    '''Write the proper indentation to the log streams'''
183*179860b2SJed Brown    import traceback
184*179860b2SJed Brown
185*179860b2SJed Brown    indentLevel = len(traceback.extract_stack())-5
186*179860b2SJed Brown    for writeAll, f in enumerate([self.out, self.log]):
187*179860b2SJed Brown      if self.checkWrite(f, debugLevel, debugSection, writeAll):
188*179860b2SJed Brown        if not comm is None:
189*179860b2SJed Brown          f.write('[')
190*179860b2SJed Brown          f.write(str(comm.rank()))
191*179860b2SJed Brown          f.write(']')
192*179860b2SJed Brown        for i in range(indentLevel):
193*179860b2SJed Brown          f.write(self.debugIndent)
194*179860b2SJed Brown    return
195*179860b2SJed Brown
196*179860b2SJed Brown  def logBack(self):
197*179860b2SJed Brown    '''Backup the current line if we are not scrolling output'''
198*179860b2SJed Brown    if not self.out is None and self.linewidth > 0:
199*179860b2SJed Brown      self.out.write('\r')
200*179860b2SJed Brown    return
201*179860b2SJed Brown
202*179860b2SJed Brown  def logClear(self):
203*179860b2SJed Brown    '''Clear the current line if we are not scrolling output'''
204*179860b2SJed Brown    if not self.out is None and self.linewidth > 0:
205*179860b2SJed Brown      self.out.write('\r')
206*179860b2SJed Brown      self.out.write(''.join([' '] * self.linewidth))
207*179860b2SJed Brown      self.out.write('\r')
208*179860b2SJed Brown    return
209*179860b2SJed Brown
210*179860b2SJed Brown  def logPrintDivider(self, debugLevel = -1, debugSection = None, single = 0):
211*179860b2SJed Brown    if single:
212*179860b2SJed Brown      self.logPrint('-------------------------------------------------------------------------------', debugLevel = debugLevel, debugSection = debugSection)
213*179860b2SJed Brown    else:
214*179860b2SJed Brown      self.logPrint('===============================================================================', debugLevel = debugLevel, debugSection = debugSection)
215*179860b2SJed Brown    return
216*179860b2SJed Brown
217*179860b2SJed Brown  def logPrintBox(self,msg, debugLevel = -1, debugSection = 'screen', indent = 1, comm = None):
218*179860b2SJed Brown    self.logClear()
219*179860b2SJed Brown    self.logPrintDivider(debugLevel = debugLevel, debugSection = debugSection)
220*179860b2SJed Brown    [self.logPrint('      '+line, debugLevel = debugLevel, debugSection = debugSection) for line in msg.split('\n')]
221*179860b2SJed Brown    self.logPrintDivider(debugLevel = debugLevel, debugSection = debugSection)
222*179860b2SJed Brown    self.logPrint('', debugLevel = debugLevel, debugSection = debugSection)
223*179860b2SJed Brown    return
224*179860b2SJed Brown
225*179860b2SJed Brown  def logClearRemoveDirectory(self):
226*179860b2SJed Brown    global RemoveDirectory
227*179860b2SJed Brown    global backupRemoveDirectory
228*179860b2SJed Brown    backupRemoveDirectory = RemoveDirectory
229*179860b2SJed Brown    RemoveDirectory = ''
230*179860b2SJed Brown
231*179860b2SJed Brown  def logResetRemoveDirectory(self):
232*179860b2SJed Brown    global RemoveDirectory
233*179860b2SJed Brown    global backupRemoveDirectory
234*179860b2SJed Brown    RemoveDirectory = backupRemoveDirectory
235*179860b2SJed Brown
236*179860b2SJed Brown
237*179860b2SJed Brown  def logWrite(self, msg, debugLevel = -1, debugSection = None, forceScroll = 0):
238*179860b2SJed Brown    '''Write the message to the log streams'''
239*179860b2SJed Brown    for writeAll, f in enumerate([self.out, self.log]):
240*179860b2SJed Brown      if self.checkWrite(f, debugLevel, debugSection, writeAll):
241*179860b2SJed Brown        if not forceScroll and not writeAll and self.linewidth > 0:
242*179860b2SJed Brown          global RemoveDirectory
243*179860b2SJed Brown          self.logBack()
244*179860b2SJed Brown          msg = msg.replace(RemoveDirectory,'')
245*179860b2SJed Brown          for ms in msg.split('\n'):
246*179860b2SJed Brown            f.write(ms[0:self.linewidth])
247*179860b2SJed Brown            f.write(''.join([' '] * (self.linewidth - len(ms))))
248*179860b2SJed Brown        else:
249*179860b2SJed Brown          if not debugSection is None and not debugSection == 'screen' and len(msg):
250*179860b2SJed Brown            f.write(str(debugSection))
251*179860b2SJed Brown            f.write(': ')
252*179860b2SJed Brown          f.write(msg)
253*179860b2SJed Brown        if hasattr(f, 'flush'):
254*179860b2SJed Brown          f.flush()
255*179860b2SJed Brown    return
256*179860b2SJed Brown
257*179860b2SJed Brown  def logPrint(self, msg, debugLevel = -1, debugSection = None, indent = 1, comm = None, forceScroll = 0):
258*179860b2SJed Brown    '''Write the message to the log streams with proper indentation and a newline'''
259*179860b2SJed Brown    if indent:
260*179860b2SJed Brown      self.logIndent(debugLevel, debugSection, comm)
261*179860b2SJed Brown    self.logWrite(msg, debugLevel, debugSection, forceScroll = forceScroll)
262*179860b2SJed Brown    for writeAll, f in enumerate([self.out, self.log]):
263*179860b2SJed Brown      if self.checkWrite(f, debugLevel, debugSection, writeAll):
264*179860b2SJed Brown        if writeAll or self.linewidth < 0:
265*179860b2SJed Brown          f.write('\n')
266*179860b2SJed Brown    return
267*179860b2SJed Brown
268*179860b2SJed Brown
269*179860b2SJed Brown  def getRoot(self):
270*179860b2SJed Brown    '''Return the directory containing this module
271*179860b2SJed Brown       - This has the problem that when we reload a module of the same name, this gets screwed up
272*179860b2SJed Brown         Therefore, we call it in the initializer, and stash it'''
273*179860b2SJed Brown    #print '      In getRoot'
274*179860b2SJed Brown    #print hasattr(self, '__root')
275*179860b2SJed Brown    #print '      done checking'
276*179860b2SJed Brown    if not hasattr(self, '__root'):
277*179860b2SJed Brown      import os
278*179860b2SJed Brown      import sys
279*179860b2SJed Brown
280*179860b2SJed Brown      # Work around a bug with pdb in 2.3
281*179860b2SJed Brown      if hasattr(sys.modules[self.__module__], '__file__') and not os.path.basename(sys.modules[self.__module__].__file__) == 'pdb.py':
282*179860b2SJed Brown        self.__root = os.path.abspath(os.path.dirname(sys.modules[self.__module__].__file__))
283*179860b2SJed Brown      else:
284*179860b2SJed Brown        self.__root = os.getcwd()
285*179860b2SJed Brown    #print '      Exiting getRoot'
286*179860b2SJed Brown    return self.__root
287*179860b2SJed Brown  def setRoot(self, root):
288*179860b2SJed Brown    self.__root = root
289*179860b2SJed Brown    return
290*179860b2SJed Brown  root = property(getRoot, setRoot, doc = 'The directory containing this module')
291