unity.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. """
  4. Compile whole groups of C/C++ files at once
  5. (C and C++ files are processed independently though).
  6. To enable globally::
  7. def options(opt):
  8. opt.load('compiler_cxx')
  9. def build(bld):
  10. bld.load('compiler_cxx unity')
  11. To enable for specific task generators only::
  12. def build(bld):
  13. bld(features='c cprogram unity', source='main.c', ...)
  14. The file order is often significant in such builds, so it can be
  15. necessary to adjust the order of source files and the batch sizes.
  16. To control the amount of files processed in a batch per target
  17. (the default is 50)::
  18. def build(bld):
  19. bld(features='c cprogram', unity_size=20)
  20. """
  21. from waflib import Task, Options
  22. from waflib.Tools import c_preproc
  23. from waflib import TaskGen
  24. MAX_BATCH = 50
  25. EXTS_C = ('.c',)
  26. EXTS_CXX = ('.cpp','.cc','.cxx','.C','.c++')
  27. def options(opt):
  28. global MAX_BATCH
  29. opt.add_option('--batchsize', action='store', dest='batchsize', type='int', default=MAX_BATCH,
  30. help='default unity batch size (0 disables unity builds)')
  31. @TaskGen.taskgen_method
  32. def batch_size(self):
  33. default = getattr(Options.options, 'batchsize', MAX_BATCH)
  34. if default < 1:
  35. return 0
  36. return getattr(self, 'unity_size', default)
  37. class unity(Task.Task):
  38. color = 'BLUE'
  39. scan = c_preproc.scan
  40. def to_include(self, node):
  41. ret = node.path_from(self.outputs[0].parent)
  42. ret = ret.replace('\\', '\\\\').replace('"', '\\"')
  43. return ret
  44. def run(self):
  45. lst = ['#include "%s"\n' % self.to_include(node) for node in self.inputs]
  46. txt = ''.join(lst)
  47. self.outputs[0].write(txt)
  48. def __str__(self):
  49. node = self.outputs[0]
  50. return node.path_from(node.ctx.launch_node())
  51. def bind_unity(obj, cls_name, exts):
  52. if not 'mappings' in obj.__dict__:
  53. obj.mappings = dict(obj.mappings)
  54. for j in exts:
  55. fun = obj.mappings[j]
  56. if fun.__name__ == 'unity_fun':
  57. raise ValueError('Attempt to bind unity mappings multiple times %r' % j)
  58. def unity_fun(self, node):
  59. cnt = self.batch_size()
  60. if cnt <= 1:
  61. return fun(self, node)
  62. x = getattr(self, 'master_%s' % cls_name, None)
  63. if not x or len(x.inputs) >= cnt:
  64. x = self.create_task('unity')
  65. setattr(self, 'master_%s' % cls_name, x)
  66. cnt_cur = getattr(self, 'cnt_%s' % cls_name, 0)
  67. c_node = node.parent.find_or_declare('unity_%s_%d_%d.%s' % (self.idx, cnt_cur, cnt, cls_name))
  68. x.outputs = [c_node]
  69. setattr(self, 'cnt_%s' % cls_name, cnt_cur + 1)
  70. fun(self, c_node)
  71. x.inputs.append(node)
  72. obj.mappings[j] = unity_fun
  73. @TaskGen.feature('unity')
  74. @TaskGen.before('process_source')
  75. def single_unity(self):
  76. lst = self.to_list(self.features)
  77. if 'c' in lst:
  78. bind_unity(self, 'c', EXTS_C)
  79. if 'cxx' in lst:
  80. bind_unity(self, 'cxx', EXTS_CXX)
  81. def build(bld):
  82. if bld.env.CC_NAME:
  83. bind_unity(TaskGen.task_gen, 'c', EXTS_C)
  84. if bld.env.CXX_NAME:
  85. bind_unity(TaskGen.task_gen, 'cxx', EXTS_CXX)