build_logs.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # Thomas Nagy, 2013 (ita)
  4. """
  5. A system for recording all outputs to a log file. Just add the following to your wscript file::
  6. def init(ctx):
  7. ctx.load('build_logs')
  8. """
  9. import atexit, sys, time, os, shutil, threading
  10. from waflib import ansiterm, Logs, Context
  11. # adding the logs under the build/ directory will clash with the clean/ command
  12. try:
  13. up = os.path.dirname(Context.g_module.__file__)
  14. except AttributeError:
  15. up = '.'
  16. LOGFILE = os.path.join(up, 'logs', time.strftime('%Y_%m_%d_%H_%M.log'))
  17. wlock = threading.Lock()
  18. class log_to_file(object):
  19. def __init__(self, stream, fileobj, filename):
  20. self.stream = stream
  21. self.encoding = self.stream.encoding
  22. self.fileobj = fileobj
  23. self.filename = filename
  24. self.is_valid = True
  25. def replace_colors(self, data):
  26. for x in Logs.colors_lst.values():
  27. if isinstance(x, str):
  28. data = data.replace(x, '')
  29. return data
  30. def write(self, data):
  31. try:
  32. wlock.acquire()
  33. self.stream.write(data)
  34. self.stream.flush()
  35. if self.is_valid:
  36. self.fileobj.write(self.replace_colors(data))
  37. finally:
  38. wlock.release()
  39. def fileno(self):
  40. return self.stream.fileno()
  41. def flush(self):
  42. self.stream.flush()
  43. if self.is_valid:
  44. self.fileobj.flush()
  45. def isatty(self):
  46. return self.stream.isatty()
  47. def init(ctx):
  48. global LOGFILE
  49. filename = os.path.abspath(LOGFILE)
  50. try:
  51. os.makedirs(os.path.dirname(os.path.abspath(filename)))
  52. except OSError:
  53. pass
  54. if hasattr(os, 'O_NOINHERIT'):
  55. fd = os.open(LOGFILE, os.O_CREAT | os.O_TRUNC | os.O_WRONLY | os.O_NOINHERIT)
  56. fileobj = os.fdopen(fd, 'w')
  57. else:
  58. fileobj = open(LOGFILE, 'w')
  59. old_stderr = sys.stderr
  60. # sys.stdout has already been replaced, so __stdout__ will be faster
  61. #sys.stdout = log_to_file(sys.stdout, fileobj, filename)
  62. #sys.stderr = log_to_file(sys.stderr, fileobj, filename)
  63. def wrap(stream):
  64. if stream.isatty():
  65. return ansiterm.AnsiTerm(stream)
  66. return stream
  67. sys.stdout = log_to_file(wrap(sys.__stdout__), fileobj, filename)
  68. sys.stderr = log_to_file(wrap(sys.__stderr__), fileobj, filename)
  69. # now mess with the logging module...
  70. for x in Logs.log.handlers:
  71. try:
  72. stream = x.stream
  73. except AttributeError:
  74. pass
  75. else:
  76. if id(stream) == id(old_stderr):
  77. x.stream = sys.stderr
  78. def exit_cleanup():
  79. try:
  80. fileobj = sys.stdout.fileobj
  81. except AttributeError:
  82. pass
  83. else:
  84. sys.stdout.is_valid = False
  85. sys.stderr.is_valid = False
  86. fileobj.close()
  87. filename = sys.stdout.filename
  88. Logs.info('Output logged to %r', filename)
  89. # then copy the log file to "latest.log" if possible
  90. up = os.path.dirname(os.path.abspath(filename))
  91. try:
  92. shutil.copy(filename, os.path.join(up, 'latest.log'))
  93. except OSError:
  94. # this may fail on windows due to processes spawned
  95. pass
  96. atexit.register(exit_cleanup)