c_tests.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # Thomas Nagy, 2016-2018 (ita)
  4. """
  5. Various configuration tests.
  6. """
  7. from waflib import Task
  8. from waflib.Configure import conf
  9. from waflib.TaskGen import feature, before_method, after_method
  10. LIB_CODE = '''
  11. #ifdef _MSC_VER
  12. #define testEXPORT __declspec(dllexport)
  13. #else
  14. #define testEXPORT
  15. #endif
  16. testEXPORT int lib_func(void) { return 9; }
  17. '''
  18. MAIN_CODE = '''
  19. #ifdef _MSC_VER
  20. #define testEXPORT __declspec(dllimport)
  21. #else
  22. #define testEXPORT
  23. #endif
  24. testEXPORT int lib_func(void);
  25. int main(int argc, char **argv) {
  26. (void)argc; (void)argv;
  27. return !(lib_func() == 9);
  28. }
  29. '''
  30. @feature('link_lib_test')
  31. @before_method('process_source')
  32. def link_lib_test_fun(self):
  33. """
  34. The configuration test :py:func:`waflib.Configure.run_build` declares a unique task generator,
  35. so we need to create other task generators from here to check if the linker is able to link libraries.
  36. """
  37. def write_test_file(task):
  38. task.outputs[0].write(task.generator.code)
  39. rpath = []
  40. if getattr(self, 'add_rpath', False):
  41. rpath = [self.bld.path.get_bld().abspath()]
  42. mode = self.mode
  43. m = '%s %s' % (mode, mode)
  44. ex = self.test_exec and 'test_exec' or ''
  45. bld = self.bld
  46. bld(rule=write_test_file, target='test.' + mode, code=LIB_CODE)
  47. bld(rule=write_test_file, target='main.' + mode, code=MAIN_CODE)
  48. bld(features='%sshlib' % m, source='test.' + mode, target='test')
  49. bld(features='%sprogram %s' % (m, ex), source='main.' + mode, target='app', use='test', rpath=rpath)
  50. @conf
  51. def check_library(self, mode=None, test_exec=True):
  52. """
  53. Checks if libraries can be linked with the current linker. Uses :py:func:`waflib.Tools.c_tests.link_lib_test_fun`.
  54. :param mode: c or cxx or d
  55. :type mode: string
  56. """
  57. if not mode:
  58. mode = 'c'
  59. if self.env.CXX:
  60. mode = 'cxx'
  61. self.check(
  62. compile_filename = [],
  63. features = 'link_lib_test',
  64. msg = 'Checking for libraries',
  65. mode = mode,
  66. test_exec = test_exec)
  67. ########################################################################################
  68. INLINE_CODE = '''
  69. typedef int foo_t;
  70. static %s foo_t static_foo () {return 0; }
  71. %s foo_t foo () {
  72. return 0;
  73. }
  74. '''
  75. INLINE_VALUES = ['inline', '__inline__', '__inline']
  76. @conf
  77. def check_inline(self, **kw):
  78. """
  79. Checks for the right value for inline macro.
  80. Define INLINE_MACRO to 1 if the define is found.
  81. If the inline macro is not 'inline', add a define to the ``config.h`` (#define inline __inline__)
  82. :param define_name: define INLINE_MACRO by default to 1 if the macro is defined
  83. :type define_name: string
  84. :param features: by default *c* or *cxx* depending on the compiler present
  85. :type features: list of string
  86. """
  87. self.start_msg('Checking for inline')
  88. if not 'define_name' in kw:
  89. kw['define_name'] = 'INLINE_MACRO'
  90. if not 'features' in kw:
  91. if self.env.CXX:
  92. kw['features'] = ['cxx']
  93. else:
  94. kw['features'] = ['c']
  95. for x in INLINE_VALUES:
  96. kw['fragment'] = INLINE_CODE % (x, x)
  97. try:
  98. self.check(**kw)
  99. except self.errors.ConfigurationError:
  100. continue
  101. else:
  102. self.end_msg(x)
  103. if x != 'inline':
  104. self.define('inline', x, quote=False)
  105. return x
  106. self.fatal('could not use inline functions')
  107. ########################################################################################
  108. LARGE_FRAGMENT = '''#include <unistd.h>
  109. int main(int argc, char **argv) {
  110. (void)argc; (void)argv;
  111. return !(sizeof(off_t) >= 8);
  112. }
  113. '''
  114. @conf
  115. def check_large_file(self, **kw):
  116. """
  117. Checks for large file support and define the macro HAVE_LARGEFILE
  118. The test is skipped on win32 systems (DEST_BINFMT == pe).
  119. :param define_name: define to set, by default *HAVE_LARGEFILE*
  120. :type define_name: string
  121. :param execute: execute the test (yes by default)
  122. :type execute: bool
  123. """
  124. if not 'define_name' in kw:
  125. kw['define_name'] = 'HAVE_LARGEFILE'
  126. if not 'execute' in kw:
  127. kw['execute'] = True
  128. if not 'features' in kw:
  129. if self.env.CXX:
  130. kw['features'] = ['cxx', 'cxxprogram']
  131. else:
  132. kw['features'] = ['c', 'cprogram']
  133. kw['fragment'] = LARGE_FRAGMENT
  134. kw['msg'] = 'Checking for large file support'
  135. ret = True
  136. try:
  137. if self.env.DEST_BINFMT != 'pe':
  138. ret = self.check(**kw)
  139. except self.errors.ConfigurationError:
  140. pass
  141. else:
  142. if ret:
  143. return True
  144. kw['msg'] = 'Checking for -D_FILE_OFFSET_BITS=64'
  145. kw['defines'] = ['_FILE_OFFSET_BITS=64']
  146. try:
  147. ret = self.check(**kw)
  148. except self.errors.ConfigurationError:
  149. pass
  150. else:
  151. self.define('_FILE_OFFSET_BITS', 64)
  152. return ret
  153. self.fatal('There is no support for large files')
  154. ########################################################################################
  155. ENDIAN_FRAGMENT = '''
  156. short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
  157. short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
  158. int use_ascii (int i) {
  159. return ascii_mm[i] + ascii_ii[i];
  160. }
  161. short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
  162. short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
  163. int use_ebcdic (int i) {
  164. return ebcdic_mm[i] + ebcdic_ii[i];
  165. }
  166. extern int foo;
  167. '''
  168. class grep_for_endianness(Task.Task):
  169. """
  170. Task that reads a binary and tries to determine the endianness
  171. """
  172. color = 'PINK'
  173. def run(self):
  174. txt = self.inputs[0].read(flags='rb').decode('latin-1')
  175. if txt.find('LiTTleEnDian') > -1:
  176. self.generator.tmp.append('little')
  177. elif txt.find('BIGenDianSyS') > -1:
  178. self.generator.tmp.append('big')
  179. else:
  180. return -1
  181. @feature('grep_for_endianness')
  182. @after_method('process_source')
  183. def grep_for_endianness_fun(self):
  184. """
  185. Used by the endianness configuration test
  186. """
  187. self.create_task('grep_for_endianness', self.compiled_tasks[0].outputs[0])
  188. @conf
  189. def check_endianness(self):
  190. """
  191. Executes a configuration test to determine the endianness
  192. """
  193. tmp = []
  194. def check_msg(self):
  195. return tmp[0]
  196. self.check(fragment=ENDIAN_FRAGMENT, features='c grep_for_endianness',
  197. msg='Checking for endianness', define='ENDIANNESS', tmp=tmp, okmsg=check_msg)
  198. return tmp[0]