jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
4.4KB

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # Thomas Nagy, 2006-2015 (ita)
  4. """
  5. Build as batches.
  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
  8. Files are output on the directory where the compiler is called, and dependencies are more difficult
  9. to track (do not run the command on all source files if only one file changes)
  10. As such, we do as if the files were compiled one by one, but no command is actually run:
  11. replace each cc/cpp Task by a TaskSlave. A new task called TaskMaster collects the
  12. signatures from each slave and finds out the command-line to run.
  13. Just import this module in the configuration (no other change required).
  14. This is provided as an example, for performance unity builds are recommended (fewer tasks and
  15. fewer jobs to execute). See waflib/extras/unity.py.
  16. """
  17. from waflib import Task, Utils
  18. from waflib.TaskGen import extension, feature, after_method
  19. from waflib.Tools import c, cxx
  20. MAX_BATCH = 50
  21. c_str = '${CC} ${CFLAGS} ${CPPFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} -c ${SRCLST} ${CXX_TGT_F_BATCHED}'
  22. c_fun, _ = Task.compile_fun_noshell(c_str)
  23. cxx_str = '${CXX} ${CXXFLAGS} ${CPPFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} -c ${SRCLST} ${CXX_TGT_F_BATCHED}'
  24. cxx_fun, _ = Task.compile_fun_noshell(cxx_str)
  25. count = 70000
  26. class batch_task(Task.Task):
  27. color = 'PINK'
  28. after = ['c', 'cxx']
  29. before = ['cprogram', 'cshlib', 'cstlib', 'cxxprogram', 'cxxshlib', 'cxxstlib']
  30. def uid(self):
  31. m = Utils.md5()
  32. m.update(Task.Task.uid(self))
  33. m.update(str(self.generator.idx).encode())
  34. return m.digest()
  35. def __str__(self):
  36. return 'Batch compilation for %d slaves' % len(self.slaves)
  37. def __init__(self, *k, **kw):
  38. Task.Task.__init__(self, *k, **kw)
  39. self.slaves = []
  40. self.inputs = []
  41. self.hasrun = 0
  42. global count
  43. count += 1
  44. self.idx = count
  45. def add_slave(self, slave):
  46. self.slaves.append(slave)
  47. self.set_run_after(slave)
  48. def runnable_status(self):
  49. for t in self.run_after:
  50. if not t.hasrun:
  51. return Task.ASK_LATER
  52. for t in self.slaves:
  53. #if t.executed:
  54. if t.hasrun != Task.SKIPPED:
  55. return Task.RUN_ME
  56. return Task.SKIP_ME
  57. def run(self):
  58. self.outputs = []
  59. srclst = []
  60. slaves = []
  61. for t in self.slaves:
  62. if t.hasrun != Task.SKIPPED:
  63. slaves.append(t)
  64. srclst.append(t.inputs[0].abspath())
  65. self.env.SRCLST = srclst
  66. self.cwd = slaves[0].outputs[0].parent.abspath()
  67. if self.slaves[0].__class__.__name__ == 'c':
  68. ret = c_fun(self)
  69. else:
  70. ret = cxx_fun(self)
  71. if ret:
  72. return ret
  73. for t in slaves:
  74. t.old_post_run()
  75. def hook(cls_type):
  76. def n_hook(self, node):
  77. ext = '.obj' if self.env.CC_NAME == 'msvc' else '.o'
  78. name = node.name
  79. k = name.rfind('.')
  80. if k >= 0:
  81. basename = name[:k] + ext
  82. else:
  83. basename = name + ext
  84. outdir = node.parent.get_bld().make_node('%d' % self.idx)
  85. outdir.mkdir()
  86. out = outdir.find_or_declare(basename)
  87. task = self.create_task(cls_type, node, out)
  88. try:
  89. self.compiled_tasks.append(task)
  90. except AttributeError:
  91. self.compiled_tasks = [task]
  92. if not getattr(self, 'masters', None):
  93. self.masters = {}
  94. self.allmasters = []
  95. def fix_path(tsk):
  96. if self.env.CC_NAME == 'msvc':
  97. tsk.env.append_unique('CXX_TGT_F_BATCHED', '/Fo%s\\' % outdir.abspath())
  98. if not node.parent in self.masters:
  99. m = self.masters[node.parent] = self.master = self.create_task('batch')
  100. fix_path(m)
  101. self.allmasters.append(m)
  102. else:
  103. m = self.masters[node.parent]
  104. if len(m.slaves) > MAX_BATCH:
  105. m = self.masters[node.parent] = self.master = self.create_task('batch')
  106. fix_path(m)
  107. self.allmasters.append(m)
  108. m.add_slave(task)
  109. return task
  110. return n_hook
  111. extension('.c')(hook('c'))
  112. extension('.cpp','.cc','.cxx','.C','.c++')(hook('cxx'))
  113. @feature('cprogram', 'cshlib', 'cstaticlib', 'cxxprogram', 'cxxshlib', 'cxxstlib')
  114. @after_method('apply_link')
  115. def link_after_masters(self):
  116. if getattr(self, 'allmasters', None):
  117. for m in self.allmasters:
  118. self.link_task.set_run_after(m)
  119. # Modify the c and cxx task classes - in theory it would be best to
  120. # create subclasses and to re-map the c/c++ extensions
  121. for x in ('c', 'cxx'):
  122. t = Task.classes[x]
  123. def run(self):
  124. pass
  125. def post_run(self):
  126. pass
  127. setattr(t, 'oldrun', getattr(t, 'run', None))
  128. setattr(t, 'run', run)
  129. setattr(t, 'old_post_run', t.post_run)
  130. setattr(t, 'post_run', post_run)