3 # Thomas Nagy, 2006 (ita)
6 Batched builds - compile faster
7 instead of compiling object files one by one, c/c++ compilers are often able to compile at once:
8 cc -c ../file1.c ../file2.c ../file3.c
10 Files are output on the directory where the compiler is called, and dependencies are more difficult
11 to track (do not run the command on all source files if only one file changes)
13 As such, we do as if the files were compiled one by one, but no command is actually run:
14 replace each cc/cpp Task by a TaskSlave
15 A new task called TaskMaster collects the signatures from each slave and finds out the command-line
18 To set this up, the method ccroot::create_task is replaced by a new version, to enable batched builds
19 it is only necessary to import this module in the configuration (no other change required)
25 EXT_C = ['.c', '.cc', '.cpp', '.cxx']
28 import TaskGen, Task, ccroot, Build, Logs
29 from TaskGen import extension, feature, before
30 from Constants import *
32 cc_str = '${CC} ${CCFLAGS} ${CPPFLAGS} ${_CCINCFLAGS} ${_CCDEFFLAGS} -c ${SRCLST}'
33 cc_fun = Task.compile_fun_noshell('batched_cc', cc_str)[0]
35 cxx_str = '${CXX} ${CXXFLAGS} ${CPPFLAGS} ${_CXXINCFLAGS} ${_CXXDEFFLAGS} -c ${SRCLST}'
36 cxx_fun = Task.compile_fun_noshell('batched_cxx', cxx_str)[0]
39 class batch_task(Task.Task):
43 before = 'cc_link cxx_link static_link'
46 return '(batch compilation for %d slaves)\n' % len(self.slaves)
48 def __init__(self, *k, **kw):
49 Task.Task.__init__(self, *k, **kw)
58 def add_slave(self, slave):
59 self.slaves.append(slave)
60 self.set_run_after(slave)
62 def runnable_status(self):
63 for t in self.run_after:
69 if t.hasrun != SKIPPED:
81 if t.hasrun != SKIPPED:
83 srclst.append(t.inputs[0].abspath(self.env))
85 self.env.SRCLST = srclst
86 self.cwd = slaves[0].inputs[0].parent.abspath(self.env)
89 app = env.append_unique
90 cpppath_st = env['CPPPATH_ST']
91 env._CCINCFLAGS = env.CXXINCFLAGS = []
93 # local flags come first
94 # set the user-defined includes paths
95 for i in env['INC_PATHS']:
96 app('_CCINCFLAGS', cpppath_st % i.abspath())
97 app('_CXXINCFLAGS', cpppath_st % i.abspath())
98 app('_CCINCFLAGS', cpppath_st % i.abspath(env))
99 app('_CXXINCFLAGS', cpppath_st % i.abspath(env))
101 # set the library include paths
102 for i in env['CPPPATH']:
103 app('_CCINCFLAGS', cpppath_st % i)
104 app('_CXXINCFLAGS', cpppath_st % i)
106 if self.slaves[0].__class__.__name__ == 'cc':
117 from TaskGen import extension, feature, after
122 # we cannot control the extension, this sucks
125 task = fun(self, node)
126 if not getattr(self, 'masters', None):
130 if not node.parent.id in self.masters:
131 m = self.masters[node.parent.id] = self.master = self.create_task('batch')
132 self.allmasters.append(m)
134 m = self.masters[node.parent.id]
135 if len(m.slaves) > MAX_BATCH:
136 m = self.masters[node.parent.id] = self.master = self.create_task('batch')
137 self.allmasters.append(m)
143 c_hook = wrap(cc.c_hook)
144 extension(cc.EXT_CC)(c_hook)
146 cxx_hook = wrap(cxx.cxx_hook)
147 extension(cxx.EXT_CXX)(cxx_hook)
150 @feature('cprogram', 'cshlib', 'cstaticlib')
152 def link_after_masters(self):
153 if getattr(self, 'allmasters', None):
154 for m in self.allmasters:
155 self.link_task.set_run_after(m)
157 for c in ['cc', 'cxx']:
158 t = Task.TaskBase.classes[c]
166 def can_retrieve_cache(self):
167 if self.old_can_retrieve_cache():
168 for m in self.generator.allmasters:
170 m.slaves.remove(self)
172 pass #this task wasn't included in that master
177 setattr(t, 'oldrun', t.__dict__['run'])
178 setattr(t, 'run', run)
179 setattr(t, 'old_post_run', t.post_run)
180 setattr(t, 'post_run', post_run)
181 setattr(t, 'old_can_retrieve_cache', t.can_retrieve_cache)
182 setattr(t, 'can_retrieve_cache', can_retrieve_cache)