aboutsummaryrefslogtreecommitdiffstats
path: root/waflib/extras/build_logs.py
blob: cdf8ed097e90a21bc5fad699d8846daff25cac4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2013 (ita)

"""
A system for recording all outputs to a log file. Just add the following to your wscript file::

  def init(ctx):
    ctx.load('build_logs')
"""

import atexit, sys, time, os, shutil, threading
from waflib import ansiterm, Logs, Context

# adding the logs under the build/ directory will clash with the clean/ command
try:
	up = os.path.dirname(Context.g_module.__file__)
except AttributeError:
	up = '.'
LOGFILE = os.path.join(up, 'logs', time.strftime('%Y_%m_%d_%H_%M.log'))

wlock = threading.Lock()
class log_to_file(object):
	def __init__(self, stream, fileobj, filename):
		self.stream = stream
		self.encoding = self.stream.encoding
		self.fileobj = fileobj
		self.filename = filename
		self.is_valid = True
	def replace_colors(self, data):
		for x in Logs.colors_lst.values():
			if isinstance(x, str):
				data = data.replace(x, '')
		return data
	def write(self, data):
		try:
			wlock.acquire()
			self.stream.write(data)
			self.stream.flush()
			if self.is_valid:
				self.fileobj.write(self.replace_colors(data))
		finally:
			wlock.release()
	def fileno(self):
		return self.stream.fileno()
	def flush(self):
		self.stream.flush()
		if self.is_valid:
			self.fileobj.flush()
	def isatty(self):
		return self.stream.isatty()

def init(ctx):
	global LOGFILE
	filename = os.path.abspath(LOGFILE)
	try:
		os.makedirs(os.path.dirname(os.path.abspath(filename)))
	except OSError:
		pass

	if hasattr(os, 'O_NOINHERIT'):
		fd = os.open(LOGFILE, os.O_CREAT | os.O_TRUNC | os.O_WRONLY | os.O_NOINHERIT)
		fileobj = os.fdopen(fd, 'w')
	else:
		fileobj = open(LOGFILE, 'w')
	old_stderr = sys.stderr

	# sys.stdout has already been replaced, so __stdout__ will be faster
	#sys.stdout = log_to_file(sys.stdout, fileobj, filename)
	#sys.stderr = log_to_file(sys.stderr, fileobj, filename)
	def wrap(stream):
		if stream.isatty():
			return ansiterm.AnsiTerm(stream)
		return stream
	sys.stdout = log_to_file(wrap(sys.__stdout__), fileobj, filename)
	sys.stderr = log_to_file(wrap(sys.__stderr__), fileobj, filename)

	# now mess with the logging module...
	for x in Logs.log.handlers:
		try:
			stream = x.stream
		except AttributeError:
			pass
		else:
			if id(stream) == id(old_stderr):
				x.stream = sys.stderr

def exit_cleanup():
	try:
		fileobj = sys.stdout.fileobj
	except AttributeError:
		pass
	else:
		sys.stdout.is_valid = False
		sys.stderr.is_valid = False
		fileobj.close()
		filename = sys.stdout.filename

		Logs.info('Output logged to %r', filename)

		# then copy the log file to "latest.log" if possible
		up = os.path.dirname(os.path.abspath(filename))
		try:
			shutil.copy(filename, os.path.join(up, 'latest.log'))
		except OSError:
			# this may fail on windows due to processes spawned
			pass

atexit.register(exit_cleanup)