third_party:waf: update to upstream 2.0.4 release
[samba.git] / third_party / waf / waflib / extras / batched_cc.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #!/usr/bin/env python
6 # encoding: utf-8
7 # Thomas Nagy, 2006-2015 (ita)
8
9 """
10 Instead of compiling object files one by one, c/c++ compilers are often able to compile at once:
11 cc -c ../file1.c ../file2.c ../file3.c
12
13 Files are output on the directory where the compiler is called, and dependencies are more difficult
14 to track (do not run the command on all source files if only one file changes)
15 As such, we do as if the files were compiled one by one, but no command is actually run:
16 replace each cc/cpp Task by a TaskSlave. A new task called TaskMaster collects the
17 signatures from each slave and finds out the command-line to run.
18
19 Just import this module to start using it:
20 def build(bld):
21         bld.load('batched_cc')
22
23 Note that this is provided as an example, unity builds are recommended
24 for best performance results (fewer tasks and fewer jobs to execute).
25 See waflib/extras/unity.py.
26 """
27
28 from waflib import Task, Utils
29 from waflib.TaskGen import extension, feature, after_method
30 from waflib.Tools import c, cxx
31
32 MAX_BATCH = 50
33
34 c_str = '${CC} ${ARCH_ST:ARCH} ${CFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${tsk.batch_incpaths()} ${DEFINES_ST:DEFINES} -c ${SRCLST} ${CXX_TGT_F_BATCHED} ${CPPFLAGS}'
35 c_fun, _ = Task.compile_fun_noshell(c_str)
36
37 cxx_str = '${CXX} ${ARCH_ST:ARCH} ${CXXFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${tsk.batch_incpaths()} ${DEFINES_ST:DEFINES} -c ${SRCLST} ${CXX_TGT_F_BATCHED} ${CPPFLAGS}'
38 cxx_fun, _ = Task.compile_fun_noshell(cxx_str)
39
40 count = 70000
41 class batch(Task.Task):
42         color = 'PINK'
43
44         after = ['c', 'cxx']
45         before = ['cprogram', 'cshlib', 'cstlib', 'cxxprogram', 'cxxshlib', 'cxxstlib']
46
47         def uid(self):
48                 return Utils.h_list([Task.Task.uid(self), self.generator.idx, self.generator.path.abspath(), self.generator.target])
49
50         def __str__(self):
51                 return 'Batch compilation for %d slaves' % len(self.slaves)
52
53         def __init__(self, *k, **kw):
54                 Task.Task.__init__(self, *k, **kw)
55                 self.slaves = []
56                 self.inputs = []
57                 self.hasrun = 0
58
59                 global count
60                 count += 1
61                 self.idx = count
62
63         def add_slave(self, slave):
64                 self.slaves.append(slave)
65                 self.set_run_after(slave)
66
67         def runnable_status(self):
68                 for t in self.run_after:
69                         if not t.hasrun:
70                                 return Task.ASK_LATER
71
72                 for t in self.slaves:
73                         #if t.executed:
74                         if t.hasrun != Task.SKIPPED:
75                                 return Task.RUN_ME
76
77                 return Task.SKIP_ME
78
79         def get_cwd(self):
80                 return self.slaves[0].outputs[0].parent
81
82         def batch_incpaths(self):
83                 st = self.env.CPPPATH_ST
84                 return [st % node.abspath() for node in self.generator.includes_nodes]
85
86         def run(self):
87                 self.outputs = []
88
89                 srclst = []
90                 slaves = []
91                 for t in self.slaves:
92                         if t.hasrun != Task.SKIPPED:
93                                 slaves.append(t)
94                                 srclst.append(t.inputs[0].abspath())
95
96                 self.env.SRCLST = srclst
97
98                 if self.slaves[0].__class__.__name__ == 'c':
99                         ret = c_fun(self)
100                 else:
101                         ret = cxx_fun(self)
102
103                 if ret:
104                         return ret
105
106                 for t in slaves:
107                         t.old_post_run()
108
109 def hook(cls_type):
110         def n_hook(self, node):
111
112                 ext = '.obj' if self.env.CC_NAME == 'msvc' else '.o'
113                 name = node.name
114                 k = name.rfind('.')
115                 if k >= 0:
116                         basename = name[:k] + ext
117                 else:
118                         basename = name + ext
119
120                 outdir = node.parent.get_bld().make_node('%d' % self.idx)
121                 outdir.mkdir()
122                 out = outdir.find_or_declare(basename)
123
124                 task = self.create_task(cls_type, node, out)
125
126                 try:
127                         self.compiled_tasks.append(task)
128                 except AttributeError:
129                         self.compiled_tasks = [task]
130
131                 if not getattr(self, 'masters', None):
132                         self.masters = {}
133                         self.allmasters = []
134
135                 def fix_path(tsk):
136                         if self.env.CC_NAME == 'msvc':
137                                 tsk.env.append_unique('CXX_TGT_F_BATCHED', '/Fo%s\\' % outdir.abspath())
138
139                 if not node.parent in self.masters:
140                         m = self.masters[node.parent] = self.master = self.create_task('batch')
141                         fix_path(m)
142                         self.allmasters.append(m)
143                 else:
144                         m = self.masters[node.parent]
145                         if len(m.slaves) > MAX_BATCH:
146                                 m = self.masters[node.parent] = self.master = self.create_task('batch')
147                                 fix_path(m)
148                                 self.allmasters.append(m)
149                 m.add_slave(task)
150                 return task
151         return n_hook
152
153 extension('.c')(hook('c'))
154 extension('.cpp','.cc','.cxx','.C','.c++')(hook('cxx'))
155
156 @feature('cprogram', 'cshlib', 'cstaticlib', 'cxxprogram', 'cxxshlib', 'cxxstlib')
157 @after_method('apply_link')
158 def link_after_masters(self):
159         if getattr(self, 'allmasters', None):
160                 for m in self.allmasters:
161                         self.link_task.set_run_after(m)
162
163 # Modify the c and cxx task classes - in theory it would be best to
164 # create subclasses and to re-map the c/c++ extensions
165 for x in ('c', 'cxx'):
166         t = Task.classes[x]
167         def run(self):
168                 pass
169
170         def post_run(self):
171                 pass
172
173         setattr(t, 'oldrun', getattr(t, 'run', None))
174         setattr(t, 'run', run)
175         setattr(t, 'old_post_run', t.post_run)
176         setattr(t, 'post_run', post_run)
177