summaryrefslogtreecommitdiffstats
path: root/third_party/waf/waflib/extras/stale.py
blob: cac3f469c941bbaea2e63341268af8831a7e148b (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
#! /usr/bin/env python
# encoding: UTF-8
# Thomas Nagy, 2006-2015 (ita)

"""
Add a pre-build hook to remove build files (declared in the system)
that do not have a corresponding target

This can be used for example to remove the targets
that have changed name without performing
a full 'waf clean'

Of course, it will only work if there are no dynamically generated
nodes/tasks, in which case the method will have to be modified
to exclude some folders for example.

Make sure to set bld.post_mode = waflib.Build.POST_AT_ONCE
"""

from waflib import Logs, Build
from waflib.Runner import Parallel

DYNAMIC_EXT = [] # add your non-cleanable files/extensions here
MOC_H_EXTS = '.cpp .cxx .hpp .hxx .h'.split()

def can_delete(node):
	"""Imperfect moc cleanup which does not look for a Q_OBJECT macro in the files"""
	if not node.name.endswith('.moc'):
		return True
	base = node.name[:-4]
	p1 = node.parent.get_src()
	p2 = node.parent.get_bld()
	for k in MOC_H_EXTS:
		h_name = base + k
		n = p1.search_node(h_name)
		if n:
			return False
		n = p2.search_node(h_name)
		if n:
			return False

		# foo.cpp.moc, foo.h.moc, etc.
		if base.endswith(k):
			return False

	return True

# recursion over the nodes to find the stale files
def stale_rec(node, nodes):
	if node.abspath() in node.ctx.env[Build.CFG_FILES]:
		return

	if getattr(node, 'children', []):
		for x in node.children.values():
			if x.name != "c4che":
				stale_rec(x, nodes)
	else:
		for ext in DYNAMIC_EXT:
			if node.name.endswith(ext):
				break
		else:
			if not node in nodes:
				if can_delete(node):
					Logs.warn('Removing stale file -> %r', node)
					node.delete()

old = Parallel.refill_task_list
def refill_task_list(self):
	iit = old(self)
	bld = self.bld

	# execute this operation only once
	if getattr(self, 'stale_done', False):
		return iit
	self.stale_done = True

	# this does not work in partial builds
	if bld.targets != '*':
		return iit

	# this does not work in dynamic builds
	if getattr(bld, 'post_mode') == Build.POST_AT_ONCE:
		return iit

	# obtain the nodes to use during the build
	nodes = []
	for tasks in bld.groups:
		for x in tasks:
			try:
				nodes.extend(x.outputs)
			except AttributeError:
				pass

	stale_rec(bld.bldnode, nodes)
	return iit

Parallel.refill_task_list = refill_task_list