#!/usr/bin/env python # encoding: utf-8 # Thomas Nagy, 2006 (ita) """ Batched builds - compile faster instead of compiling object files one by one, c/c++ compilers are often able to compile at once: cc -c ../file1.c ../file2.c ../file3.c Files are output on the directory where the compiler is called, and dependencies are more difficult to track (do not run the command on all source files if only one file changes) As such, we do as if the files were compiled one by one, but no command is actually run: replace each cc/cpp Task by a TaskSlave A new task called TaskMaster collects the signatures from each slave and finds out the command-line to run. To set this up, the method ccroot::create_task is replaced by a new version, to enable batched builds it is only necessary to import this module in the configuration (no other change required) """ MAX_BATCH = 50 MAXPARALLEL = False EXT_C = ['.c', '.cc', '.cpp', '.cxx'] import os, threading import TaskGen, Task, ccroot, Build, Logs from TaskGen import extension, feature, before from Constants import * cc_str = '${CC} ${CCFLAGS} ${CPPFLAGS} ${_CCINCFLAGS} ${_CCDEFFLAGS} -c ${SRCLST}' cc_fun = Task.compile_fun_noshell('batched_cc', cc_str)[0] cxx_str = '${CXX} ${CXXFLAGS} ${CPPFLAGS} ${_CXXINCFLAGS} ${_CXXDEFFLAGS} -c ${SRCLST}' cxx_fun = Task.compile_fun_noshell('batched_cxx', cxx_str)[0] count = 70000 class batch_task(Task.Task): color = 'RED' after = 'cc cxx' before = 'cc_link cxx_link static_link' def __str__(self): return '(batch compilation for %d slaves)\n' % len(self.slaves) def __init__(self, *k, **kw): Task.Task.__init__(self, *k, **kw) self.slaves = [] self.inputs = [] self.hasrun = 0 global count count += 1 self.idx = count def add_slave(self, slave): self.slaves.append(slave) self.set_run_after(slave) def runnable_status(self): for t in self.run_after: if not t.hasrun: return ASK_LATER for t in self.slaves: #if t.executed: if t.hasrun != SKIPPED: return RUN_ME return SKIP_ME def run(self): outputs = [] self.outputs = [] srclst = [] slaves = [] for t in self.slaves: if t.hasrun != SKIPPED: slaves.append(t) srclst.append(t.inputs[0].abspath(self.env)) self.env.SRCLST = srclst self.cwd = slaves[0].inputs[0].parent.abspath(self.env) env = self.env app = env.append_unique cpppath_st = env['CPPPATH_ST'] env._CCINCFLAGS = env.CXXINCFLAGS = [] # local flags come first # set the user-defined includes paths for i in env['INC_PATHS']: app('_CCINCFLAGS', cpppath_st % i.abspath()) app('_CXXINCFLAGS', cpppath_st % i.abspath()) app('_CCINCFLAGS', cpppath_st % i.abspath(env)) app('_CXXINCFLAGS', cpppath_st % i.abspath(env)) # set the library include paths for i in env['CPPPATH']: app('_CCINCFLAGS', cpppath_st % i) app('_CXXINCFLAGS', cpppath_st % i) if self.slaves[0].__class__.__name__ == 'cc': ret = cc_fun(self) else: ret = cxx_fun(self) if ret: return ret for t in slaves: t.old_post_run() from TaskGen import extension, feature, after import cc, cxx def wrap(fun): def foo(self, node): # we cannot control the extension, this sucks self.obj_ext = '.o' task = fun(self, node) if not getattr(self, 'masters', None): self.masters = {} self.allmasters = [] if not node.parent.id in self.masters: m = self.masters[node.parent.id] = self.master = self.create_task('batch') self.allmasters.append(m) else: m = self.masters[node.parent.id] if len(m.slaves) > MAX_BATCH: m = self.masters[node.parent.id] = self.master = self.create_task('batch') self.allmasters.append(m) m.add_slave(task) return task return foo c_hook = wrap(cc.c_hook) extension(cc.EXT_CC)(c_hook) cxx_hook = wrap(cxx.cxx_hook) extension(cxx.EXT_CXX)(cxx_hook) @feature('cprogram', 'cshlib', 'cstaticlib') @after('apply_link') def link_after_masters(self): if getattr(self, 'allmasters', None): for m in self.allmasters: self.link_task.set_run_after(m) for c in ['cc', 'cxx']: t = Task.TaskBase.classes[c] def run(self): pass def post_run(self): #self.executed=1 pass def can_retrieve_cache(self): if self.old_can_retrieve_cache(): for m in self.generator.allmasters: try: m.slaves.remove(self) except ValueError: pass #this task wasn't included in that master return 1 else: return None setattr(t, 'oldrun', t.__dict__['run']) setattr(t, 'run', run) setattr(t, 'old_post_run', t.post_run) setattr(t, 'post_run', post_run) setattr(t, 'old_can_retrieve_cache', t.can_retrieve_cache) setattr(t, 'can_retrieve_cache', can_retrieve_cache)