3 # Thomas Nagy, 2006-2015 (ita)
6 Instead of compiling object files one by one, c/c++ compilers are often able to compile at once:
7 cc -c ../file1.c ../file2.c ../file3.c
9 Files are output on the directory where the compiler is called, and dependencies are more difficult
10 to track (do not run the command on all source files if only one file changes)
11 As such, we do as if the files were compiled one by one, but no command is actually run:
12 replace each cc/cpp Task by a TaskSlave. A new task called TaskMaster collects the
13 signatures from each slave and finds out the command-line to run.
15 Just import this module to start using it:
17 bld.load('batched_cc')
19 Note that this is provided as an example, unity builds are recommended
20 for best performance results (fewer tasks and fewer jobs to execute).
21 See waflib/extras/unity.py.
24 from waflib
import Task
, Utils
25 from waflib
.TaskGen
import extension
, feature
, after_method
26 from waflib
.Tools
import c
, cxx
30 c_str
= '${CC} ${ARCH_ST:ARCH} ${CFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${tsk.batch_incpaths()} ${DEFINES_ST:DEFINES} -c ${SRCLST} ${CXX_TGT_F_BATCHED} ${CPPFLAGS}'
31 c_fun
, _
= Task
.compile_fun_noshell(c_str
)
33 cxx_str
= '${CXX} ${ARCH_ST:ARCH} ${CXXFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${tsk.batch_incpaths()} ${DEFINES_ST:DEFINES} -c ${SRCLST} ${CXX_TGT_F_BATCHED} ${CPPFLAGS}'
34 cxx_fun
, _
= Task
.compile_fun_noshell(cxx_str
)
37 class batch(Task
.Task
):
41 before
= ['cprogram', 'cshlib', 'cstlib', 'cxxprogram', 'cxxshlib', 'cxxstlib']
44 return Utils
.h_list([Task
.Task
.uid(self
), self
.generator
.idx
, self
.generator
.path
.abspath(), self
.generator
.target
])
47 return 'Batch compilation for %d slaves' % len(self
.slaves
)
49 def __init__(self
, *k
, **kw
):
50 Task
.Task
.__init
__(self
, *k
, **kw
)
59 def add_slave(self
, slave
):
60 self
.slaves
.append(slave
)
61 self
.set_run_after(slave
)
63 def runnable_status(self
):
64 for t
in self
.run_after
:
70 if t
.hasrun
!= Task
.SKIPPED
:
76 return self
.slaves
[0].outputs
[0].parent
78 def batch_incpaths(self
):
79 st
= self
.env
.CPPPATH_ST
80 return [st
% node
.abspath() for node
in self
.generator
.includes_nodes
]
88 if t
.hasrun
!= Task
.SKIPPED
:
90 srclst
.append(t
.inputs
[0].abspath())
92 self
.env
.SRCLST
= srclst
94 if self
.slaves
[0].__class
__.__name
__ == 'c':
106 def n_hook(self
, node
):
108 ext
= '.obj' if self
.env
.CC_NAME
== 'msvc' else '.o'
112 basename
= name
[:k
] + ext
114 basename
= name
+ ext
116 outdir
= node
.parent
.get_bld().make_node('%d' % self
.idx
)
118 out
= outdir
.find_or_declare(basename
)
120 task
= self
.create_task(cls_type
, node
, out
)
123 self
.compiled_tasks
.append(task
)
124 except AttributeError:
125 self
.compiled_tasks
= [task
]
127 if not getattr(self
, 'masters', None):
132 if self
.env
.CC_NAME
== 'msvc':
133 tsk
.env
.append_unique('CXX_TGT_F_BATCHED', '/Fo%s\\' % outdir
.abspath())
135 if not node
.parent
in self
.masters
:
136 m
= self
.masters
[node
.parent
] = self
.master
= self
.create_task('batch')
138 self
.allmasters
.append(m
)
140 m
= self
.masters
[node
.parent
]
141 if len(m
.slaves
) > MAX_BATCH
:
142 m
= self
.masters
[node
.parent
] = self
.master
= self
.create_task('batch')
144 self
.allmasters
.append(m
)
149 extension('.c')(hook('c'))
150 extension('.cpp','.cc','.cxx','.C','.c++')(hook('cxx'))
152 @feature('cprogram', 'cshlib', 'cstaticlib', 'cxxprogram', 'cxxshlib', 'cxxstlib')
153 @after_method('apply_link')
154 def link_after_masters(self
):
155 if getattr(self
, 'allmasters', None):
156 for m
in self
.allmasters
:
157 self
.link_task
.set_run_after(m
)
159 # Modify the c and cxx task classes - in theory it would be best to
160 # create subclasses and to re-map the c/c++ extensions
161 for x
in ('c', 'cxx'):
169 setattr(t
, 'oldrun', getattr(t
, 'run', None))
170 setattr(t
, 'run', run
)
171 setattr(t
, 'old_post_run', t
.post_run
)
172 setattr(t
, 'post_run', post_run
)