1#! /usr/bin/env python
2# encoding: UTF-8
3# Thomas Nagy, 2006-2015 (ita)
4
5"""
6Add a pre-build hook to remove build files (declared in the system)
7that do not have a corresponding target
8
9This can be used for example to remove the targets
10that have changed name without performing
11a full 'waf clean'
12
13Of course, it will only work if there are no dynamically generated
14nodes/tasks, in which case the method will have to be modified
15to exclude some folders for example.
16
17Make sure to set bld.post_mode = waflib.Build.POST_AT_ONCE
18"""
19
20from waflib import Logs, Build
21from waflib.Runner import Parallel
22
23DYNAMIC_EXT = [] # add your non-cleanable files/extensions here
24MOC_H_EXTS = '.cpp .cxx .hpp .hxx .h'.split()
25
26def can_delete(node):
27	"""Imperfect moc cleanup which does not look for a Q_OBJECT macro in the files"""
28	if not node.name.endswith('.moc'):
29		return True
30	base = node.name[:-4]
31	p1 = node.parent.get_src()
32	p2 = node.parent.get_bld()
33	for k in MOC_H_EXTS:
34		h_name = base + k
35		n = p1.search_node(h_name)
36		if n:
37			return False
38		n = p2.search_node(h_name)
39		if n:
40			return False
41
42		# foo.cpp.moc, foo.h.moc, etc.
43		if base.endswith(k):
44			return False
45
46	return True
47
48# recursion over the nodes to find the stale files
49def stale_rec(node, nodes):
50	if node.abspath() in node.ctx.env[Build.CFG_FILES]:
51		return
52
53	if getattr(node, 'children', []):
54		for x in node.children.values():
55			if x.name != "c4che":
56				stale_rec(x, nodes)
57	else:
58		for ext in DYNAMIC_EXT:
59			if node.name.endswith(ext):
60				break
61		else:
62			if not node in nodes:
63				if can_delete(node):
64					Logs.warn('Removing stale file -> %r', node)
65					node.delete()
66
67old = Parallel.refill_task_list
68def refill_task_list(self):
69	iit = old(self)
70	bld = self.bld
71
72	# execute this operation only once
73	if getattr(self, 'stale_done', False):
74		return iit
75	self.stale_done = True
76
77	# this does not work in partial builds
78	if bld.targets != '*':
79		return iit
80
81	# this does not work in dynamic builds
82	if getattr(bld, 'post_mode') == Build.POST_AT_ONCE:
83		return iit
84
85	# obtain the nodes to use during the build
86	nodes = []
87	for tasks in bld.groups:
88		for x in tasks:
89			try:
90				nodes.extend(x.outputs)
91			except AttributeError:
92				pass
93
94	stale_rec(bld.bldnode, nodes)
95	return iit
96
97Parallel.refill_task_list = refill_task_list
98
99