test_system_info.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. from __future__ import division, print_function
  2. import os
  3. import shutil
  4. import pytest
  5. from tempfile import mkstemp, mkdtemp
  6. from subprocess import Popen, PIPE
  7. from distutils.errors import DistutilsError
  8. from numpy.testing import assert_, assert_equal, assert_raises
  9. from numpy.distutils import ccompiler, customized_ccompiler
  10. from numpy.distutils.system_info import system_info, ConfigParser
  11. from numpy.distutils.system_info import AliasedOptionError
  12. from numpy.distutils.system_info import default_lib_dirs, default_include_dirs
  13. from numpy.distutils import _shell_utils
  14. def get_class(name, notfound_action=1):
  15. """
  16. notfound_action:
  17. 0 - do nothing
  18. 1 - display warning message
  19. 2 - raise error
  20. """
  21. cl = {'temp1': Temp1Info,
  22. 'temp2': Temp2Info,
  23. 'duplicate_options': DuplicateOptionInfo,
  24. }.get(name.lower(), _system_info)
  25. return cl()
  26. simple_site = """
  27. [ALL]
  28. library_dirs = {dir1:s}{pathsep:s}{dir2:s}
  29. libraries = {lib1:s},{lib2:s}
  30. extra_compile_args = -I/fake/directory -I"/path with/spaces" -Os
  31. runtime_library_dirs = {dir1:s}
  32. [temp1]
  33. library_dirs = {dir1:s}
  34. libraries = {lib1:s}
  35. runtime_library_dirs = {dir1:s}
  36. [temp2]
  37. library_dirs = {dir2:s}
  38. libraries = {lib2:s}
  39. extra_link_args = -Wl,-rpath={lib2_escaped:s}
  40. rpath = {dir2:s}
  41. [duplicate_options]
  42. mylib_libs = {lib1:s}
  43. libraries = {lib2:s}
  44. """
  45. site_cfg = simple_site
  46. fakelib_c_text = """
  47. /* This file is generated from numpy/distutils/testing/test_system_info.py */
  48. #include<stdio.h>
  49. void foo(void) {
  50. printf("Hello foo");
  51. }
  52. void bar(void) {
  53. printf("Hello bar");
  54. }
  55. """
  56. def have_compiler():
  57. """ Return True if there appears to be an executable compiler
  58. """
  59. compiler = customized_ccompiler()
  60. try:
  61. cmd = compiler.compiler # Unix compilers
  62. except AttributeError:
  63. try:
  64. if not compiler.initialized:
  65. compiler.initialize() # MSVC is different
  66. except (DistutilsError, ValueError):
  67. return False
  68. cmd = [compiler.cc]
  69. try:
  70. p = Popen(cmd, stdout=PIPE, stderr=PIPE)
  71. p.stdout.close()
  72. p.stderr.close()
  73. p.wait()
  74. except OSError:
  75. return False
  76. return True
  77. HAVE_COMPILER = have_compiler()
  78. class _system_info(system_info):
  79. def __init__(self,
  80. default_lib_dirs=default_lib_dirs,
  81. default_include_dirs=default_include_dirs,
  82. verbosity=1,
  83. ):
  84. self.__class__.info = {}
  85. self.local_prefixes = []
  86. defaults = {'library_dirs': '',
  87. 'include_dirs': '',
  88. 'runtime_library_dirs': '',
  89. 'rpath': '',
  90. 'src_dirs': '',
  91. 'search_static_first': "0",
  92. 'extra_compile_args': '',
  93. 'extra_link_args': ''}
  94. self.cp = ConfigParser(defaults)
  95. # We have to parse the config files afterwards
  96. # to have a consistent temporary filepath
  97. def _check_libs(self, lib_dirs, libs, opt_libs, exts):
  98. """Override _check_libs to return with all dirs """
  99. info = {'libraries': libs, 'library_dirs': lib_dirs}
  100. return info
  101. class Temp1Info(_system_info):
  102. """For testing purposes"""
  103. section = 'temp1'
  104. class Temp2Info(_system_info):
  105. """For testing purposes"""
  106. section = 'temp2'
  107. class DuplicateOptionInfo(_system_info):
  108. """For testing purposes"""
  109. section = 'duplicate_options'
  110. class TestSystemInfoReading(object):
  111. def setup(self):
  112. """ Create the libraries """
  113. # Create 2 sources and 2 libraries
  114. self._dir1 = mkdtemp()
  115. self._src1 = os.path.join(self._dir1, 'foo.c')
  116. self._lib1 = os.path.join(self._dir1, 'libfoo.so')
  117. self._dir2 = mkdtemp()
  118. self._src2 = os.path.join(self._dir2, 'bar.c')
  119. self._lib2 = os.path.join(self._dir2, 'libbar.so')
  120. # Update local site.cfg
  121. global simple_site, site_cfg
  122. site_cfg = simple_site.format(**{
  123. 'dir1': self._dir1,
  124. 'lib1': self._lib1,
  125. 'dir2': self._dir2,
  126. 'lib2': self._lib2,
  127. 'pathsep': os.pathsep,
  128. 'lib2_escaped': _shell_utils.NativeParser.join([self._lib2])
  129. })
  130. # Write site.cfg
  131. fd, self._sitecfg = mkstemp()
  132. os.close(fd)
  133. with open(self._sitecfg, 'w') as fd:
  134. fd.write(site_cfg)
  135. # Write the sources
  136. with open(self._src1, 'w') as fd:
  137. fd.write(fakelib_c_text)
  138. with open(self._src2, 'w') as fd:
  139. fd.write(fakelib_c_text)
  140. # We create all class-instances
  141. def site_and_parse(c, site_cfg):
  142. c.files = [site_cfg]
  143. c.parse_config_files()
  144. return c
  145. self.c_default = site_and_parse(get_class('default'), self._sitecfg)
  146. self.c_temp1 = site_and_parse(get_class('temp1'), self._sitecfg)
  147. self.c_temp2 = site_and_parse(get_class('temp2'), self._sitecfg)
  148. self.c_dup_options = site_and_parse(get_class('duplicate_options'),
  149. self._sitecfg)
  150. def teardown(self):
  151. # Do each removal separately
  152. try:
  153. shutil.rmtree(self._dir1)
  154. except Exception:
  155. pass
  156. try:
  157. shutil.rmtree(self._dir2)
  158. except Exception:
  159. pass
  160. try:
  161. os.remove(self._sitecfg)
  162. except Exception:
  163. pass
  164. def test_all(self):
  165. # Read in all information in the ALL block
  166. tsi = self.c_default
  167. assert_equal(tsi.get_lib_dirs(), [self._dir1, self._dir2])
  168. assert_equal(tsi.get_libraries(), [self._lib1, self._lib2])
  169. assert_equal(tsi.get_runtime_lib_dirs(), [self._dir1])
  170. extra = tsi.calc_extra_info()
  171. assert_equal(extra['extra_compile_args'], ['-I/fake/directory', '-I/path with/spaces', '-Os'])
  172. def test_temp1(self):
  173. # Read in all information in the temp1 block
  174. tsi = self.c_temp1
  175. assert_equal(tsi.get_lib_dirs(), [self._dir1])
  176. assert_equal(tsi.get_libraries(), [self._lib1])
  177. assert_equal(tsi.get_runtime_lib_dirs(), [self._dir1])
  178. def test_temp2(self):
  179. # Read in all information in the temp2 block
  180. tsi = self.c_temp2
  181. assert_equal(tsi.get_lib_dirs(), [self._dir2])
  182. assert_equal(tsi.get_libraries(), [self._lib2])
  183. # Now from rpath and not runtime_library_dirs
  184. assert_equal(tsi.get_runtime_lib_dirs(key='rpath'), [self._dir2])
  185. extra = tsi.calc_extra_info()
  186. assert_equal(extra['extra_link_args'], ['-Wl,-rpath=' + self._lib2])
  187. def test_duplicate_options(self):
  188. # Ensure that duplicates are raising an AliasedOptionError
  189. tsi = self.c_dup_options
  190. assert_raises(AliasedOptionError, tsi.get_option_single, "mylib_libs", "libraries")
  191. assert_equal(tsi.get_libs("mylib_libs", [self._lib1]), [self._lib1])
  192. assert_equal(tsi.get_libs("libraries", [self._lib2]), [self._lib2])
  193. @pytest.mark.skipif(not HAVE_COMPILER, reason="Missing compiler")
  194. def test_compile1(self):
  195. # Compile source and link the first source
  196. c = customized_ccompiler()
  197. previousDir = os.getcwd()
  198. try:
  199. # Change directory to not screw up directories
  200. os.chdir(self._dir1)
  201. c.compile([os.path.basename(self._src1)], output_dir=self._dir1)
  202. # Ensure that the object exists
  203. assert_(os.path.isfile(self._src1.replace('.c', '.o')) or
  204. os.path.isfile(self._src1.replace('.c', '.obj')))
  205. finally:
  206. os.chdir(previousDir)
  207. @pytest.mark.skipif(not HAVE_COMPILER, reason="Missing compiler")
  208. @pytest.mark.skipif('msvc' in repr(ccompiler.new_compiler()),
  209. reason="Fails with MSVC compiler ")
  210. def test_compile2(self):
  211. # Compile source and link the second source
  212. tsi = self.c_temp2
  213. c = customized_ccompiler()
  214. extra_link_args = tsi.calc_extra_info()['extra_link_args']
  215. previousDir = os.getcwd()
  216. try:
  217. # Change directory to not screw up directories
  218. os.chdir(self._dir2)
  219. c.compile([os.path.basename(self._src2)], output_dir=self._dir2,
  220. extra_postargs=extra_link_args)
  221. # Ensure that the object exists
  222. assert_(os.path.isfile(self._src2.replace('.c', '.o')))
  223. finally:
  224. os.chdir(previousDir)