2016-03-26 15:32:11 +03:00
#!/usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006-2015 (ita)
"""
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 .
2018-01-31 12:48:43 +03:00
Just import this module to start using it :
def build ( bld ) :
bld . load ( ' batched_cc ' )
Note that this is provided as an example , unity builds are recommended
for best performance results ( fewer tasks and fewer jobs to execute ) .
See waflib / extras / unity . py .
2016-03-26 15:32:11 +03:00
"""
from waflib import Task , Utils
from waflib . TaskGen import extension , feature , after_method
from waflib . Tools import c , cxx
MAX_BATCH = 50
2018-01-31 12:48:43 +03:00
c_str = ' $ {CC} $ { ARCH_ST:ARCH} $ {CFLAGS} $ { FRAMEWORKPATH_ST:FRAMEWORKPATH} $ { tsk.batch_incpaths()} $ { DEFINES_ST:DEFINES} -c $ {SRCLST} $ {CXX_TGT_F_BATCHED} $ {CPPFLAGS} '
2016-03-26 15:32:11 +03:00
c_fun , _ = Task . compile_fun_noshell ( c_str )
2018-01-31 12:48:43 +03:00
cxx_str = ' $ {CXX} $ { ARCH_ST:ARCH} $ {CXXFLAGS} $ { FRAMEWORKPATH_ST:FRAMEWORKPATH} $ { tsk.batch_incpaths()} $ { DEFINES_ST:DEFINES} -c $ {SRCLST} $ {CXX_TGT_F_BATCHED} $ {CPPFLAGS} '
2016-03-26 15:32:11 +03:00
cxx_fun , _ = Task . compile_fun_noshell ( cxx_str )
count = 70000
2018-01-31 12:48:43 +03:00
class batch ( Task . Task ) :
2016-03-26 15:32:11 +03:00
color = ' PINK '
after = [ ' c ' , ' cxx ' ]
before = [ ' cprogram ' , ' cshlib ' , ' cstlib ' , ' cxxprogram ' , ' cxxshlib ' , ' cxxstlib ' ]
def uid ( self ) :
2018-01-31 12:48:43 +03:00
return Utils . h_list ( [ Task . Task . uid ( self ) , self . generator . idx , self . generator . path . abspath ( ) , self . generator . target ] )
2016-03-26 15:32:11 +03:00
def __str__ ( self ) :
return ' Batch compilation for %d slaves ' % 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 Task . ASK_LATER
for t in self . slaves :
#if t.executed:
if t . hasrun != Task . SKIPPED :
return Task . RUN_ME
return Task . SKIP_ME
2018-01-31 12:48:43 +03:00
def get_cwd ( self ) :
return self . slaves [ 0 ] . outputs [ 0 ] . parent
def batch_incpaths ( self ) :
st = self . env . CPPPATH_ST
return [ st % node . abspath ( ) for node in self . generator . includes_nodes ]
2016-03-26 15:32:11 +03:00
def run ( self ) :
self . outputs = [ ]
srclst = [ ]
slaves = [ ]
for t in self . slaves :
if t . hasrun != Task . SKIPPED :
slaves . append ( t )
srclst . append ( t . inputs [ 0 ] . abspath ( ) )
self . env . SRCLST = srclst
if self . slaves [ 0 ] . __class__ . __name__ == ' c ' :
ret = c_fun ( self )
else :
ret = cxx_fun ( self )
if ret :
return ret
for t in slaves :
t . old_post_run ( )
def hook ( cls_type ) :
def n_hook ( self , node ) :
ext = ' .obj ' if self . env . CC_NAME == ' msvc ' else ' .o '
name = node . name
k = name . rfind ( ' . ' )
if k > = 0 :
basename = name [ : k ] + ext
else :
basename = name + ext
outdir = node . parent . get_bld ( ) . make_node ( ' %d ' % self . idx )
outdir . mkdir ( )
out = outdir . find_or_declare ( basename )
task = self . create_task ( cls_type , node , out )
try :
self . compiled_tasks . append ( task )
except AttributeError :
self . compiled_tasks = [ task ]
if not getattr ( self , ' masters ' , None ) :
self . masters = { }
self . allmasters = [ ]
def fix_path ( tsk ) :
if self . env . CC_NAME == ' msvc ' :
tsk . env . append_unique ( ' CXX_TGT_F_BATCHED ' , ' /Fo %s \\ ' % outdir . abspath ( ) )
if not node . parent in self . masters :
m = self . masters [ node . parent ] = self . master = self . create_task ( ' batch ' )
fix_path ( m )
self . allmasters . append ( m )
else :
m = self . masters [ node . parent ]
if len ( m . slaves ) > MAX_BATCH :
m = self . masters [ node . parent ] = self . master = self . create_task ( ' batch ' )
fix_path ( m )
self . allmasters . append ( m )
m . add_slave ( task )
return task
return n_hook
extension ( ' .c ' ) ( hook ( ' c ' ) )
extension ( ' .cpp ' , ' .cc ' , ' .cxx ' , ' .C ' , ' .c++ ' ) ( hook ( ' cxx ' ) )
@feature ( ' cprogram ' , ' cshlib ' , ' cstaticlib ' , ' cxxprogram ' , ' cxxshlib ' , ' cxxstlib ' )
@after_method ( ' apply_link ' )
def link_after_masters ( self ) :
if getattr ( self , ' allmasters ' , None ) :
for m in self . allmasters :
self . link_task . set_run_after ( m )
# Modify the c and cxx task classes - in theory it would be best to
# create subclasses and to re-map the c/c++ extensions
for x in ( ' c ' , ' cxx ' ) :
t = Task . classes [ x ]
def run ( self ) :
pass
def post_run ( self ) :
pass
setattr ( t , ' oldrun ' , getattr ( t , ' run ' , None ) )
setattr ( t , ' run ' , run )
setattr ( t , ' old_post_run ' , t . post_run )
setattr ( t , ' post_run ' , post_run )
2018-01-31 12:48:43 +03:00