File size: 11,079 Bytes
dc2106c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
import os
import shutil
import pytest
from tempfile import mkstemp, mkdtemp
from subprocess import Popen, PIPE
from distutils.errors import DistutilsError

from numpy.testing import assert_, assert_equal, assert_raises
from numpy.distutils import ccompiler, customized_ccompiler
from numpy.distutils.system_info import system_info, ConfigParser, mkl_info
from numpy.distutils.system_info import AliasedOptionError
from numpy.distutils.system_info import default_lib_dirs, default_include_dirs
from numpy.distutils import _shell_utils


def get_class(name, notfound_action=1):
    """

    notfound_action:

      0 - do nothing

      1 - display warning message

      2 - raise error

    """
    cl = {'temp1': Temp1Info,
          'temp2': Temp2Info,
          'duplicate_options': DuplicateOptionInfo,
          }.get(name.lower(), _system_info)
    return cl()

simple_site = """

[ALL]

library_dirs = {dir1:s}{pathsep:s}{dir2:s}

libraries = {lib1:s},{lib2:s}

extra_compile_args = -I/fake/directory -I"/path with/spaces" -Os

runtime_library_dirs = {dir1:s}



[temp1]

library_dirs = {dir1:s}

libraries = {lib1:s}

runtime_library_dirs = {dir1:s}



[temp2]

library_dirs = {dir2:s}

libraries = {lib2:s}

extra_link_args = -Wl,-rpath={lib2_escaped:s}

rpath = {dir2:s}



[duplicate_options]

mylib_libs = {lib1:s}

libraries = {lib2:s}

"""
site_cfg = simple_site

fakelib_c_text = """

/* This file is generated from numpy/distutils/testing/test_system_info.py */

#include<stdio.h>

void foo(void) {

   printf("Hello foo");

}

void bar(void) {

   printf("Hello bar");

}

"""

def have_compiler():
    """ Return True if there appears to be an executable compiler

    """
    compiler = customized_ccompiler()
    try:
        cmd = compiler.compiler  # Unix compilers
    except AttributeError:
        try:
            if not compiler.initialized:
                compiler.initialize()  # MSVC is different
        except (DistutilsError, ValueError):
            return False
        cmd = [compiler.cc]
    try:
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        p.stdout.close()
        p.stderr.close()
        p.wait()
    except OSError:
        return False
    return True


HAVE_COMPILER = have_compiler()


class _system_info(system_info):

    def __init__(self,

                 default_lib_dirs=default_lib_dirs,

                 default_include_dirs=default_include_dirs,

                 verbosity=1,

                 ):
        self.__class__.info = {}
        self.local_prefixes = []
        defaults = {'library_dirs': '',
                    'include_dirs': '',
                    'runtime_library_dirs': '',
                    'rpath': '',
                    'src_dirs': '',
                    'search_static_first': "0",
                    'extra_compile_args': '',
                    'extra_link_args': ''}
        self.cp = ConfigParser(defaults)
        # We have to parse the config files afterwards
        # to have a consistent temporary filepath

    def _check_libs(self, lib_dirs, libs, opt_libs, exts):
        """Override _check_libs to return with all dirs """
        info = {'libraries': libs, 'library_dirs': lib_dirs}
        return info


class Temp1Info(_system_info):
    """For testing purposes"""
    section = 'temp1'


class Temp2Info(_system_info):
    """For testing purposes"""
    section = 'temp2'

class DuplicateOptionInfo(_system_info):
    """For testing purposes"""
    section = 'duplicate_options'


class TestSystemInfoReading:

    def setup(self):
        """ Create the libraries """
        # Create 2 sources and 2 libraries
        self._dir1 = mkdtemp()
        self._src1 = os.path.join(self._dir1, 'foo.c')
        self._lib1 = os.path.join(self._dir1, 'libfoo.so')
        self._dir2 = mkdtemp()
        self._src2 = os.path.join(self._dir2, 'bar.c')
        self._lib2 = os.path.join(self._dir2, 'libbar.so')
        # Update local site.cfg
        global simple_site, site_cfg
        site_cfg = simple_site.format(**{
            'dir1': self._dir1,
            'lib1': self._lib1,
            'dir2': self._dir2,
            'lib2': self._lib2,
            'pathsep': os.pathsep,
            'lib2_escaped': _shell_utils.NativeParser.join([self._lib2])
        })
        # Write site.cfg
        fd, self._sitecfg = mkstemp()
        os.close(fd)
        with open(self._sitecfg, 'w') as fd:
            fd.write(site_cfg)
        # Write the sources
        with open(self._src1, 'w') as fd:
            fd.write(fakelib_c_text)
        with open(self._src2, 'w') as fd:
            fd.write(fakelib_c_text)
        # We create all class-instances

        def site_and_parse(c, site_cfg):
            c.files = [site_cfg]
            c.parse_config_files()
            return c
        self.c_default = site_and_parse(get_class('default'), self._sitecfg)
        self.c_temp1 = site_and_parse(get_class('temp1'), self._sitecfg)
        self.c_temp2 = site_and_parse(get_class('temp2'), self._sitecfg)
        self.c_dup_options = site_and_parse(get_class('duplicate_options'),
                                            self._sitecfg)


    def teardown(self):
        # Do each removal separately
        try:
            shutil.rmtree(self._dir1)
        except Exception:
            pass
        try:
            shutil.rmtree(self._dir2)
        except Exception:
            pass
        try:
            os.remove(self._sitecfg)
        except Exception:
            pass

    def test_all(self):
        # Read in all information in the ALL block
        tsi = self.c_default
        assert_equal(tsi.get_lib_dirs(), [self._dir1, self._dir2])
        assert_equal(tsi.get_libraries(), [self._lib1, self._lib2])
        assert_equal(tsi.get_runtime_lib_dirs(), [self._dir1])
        extra = tsi.calc_extra_info()
        assert_equal(extra['extra_compile_args'], ['-I/fake/directory', '-I/path with/spaces', '-Os'])

    def test_temp1(self):
        # Read in all information in the temp1 block
        tsi = self.c_temp1
        assert_equal(tsi.get_lib_dirs(), [self._dir1])
        assert_equal(tsi.get_libraries(), [self._lib1])
        assert_equal(tsi.get_runtime_lib_dirs(), [self._dir1])

    def test_temp2(self):
        # Read in all information in the temp2 block
        tsi = self.c_temp2
        assert_equal(tsi.get_lib_dirs(), [self._dir2])
        assert_equal(tsi.get_libraries(), [self._lib2])
        # Now from rpath and not runtime_library_dirs
        assert_equal(tsi.get_runtime_lib_dirs(key='rpath'), [self._dir2])
        extra = tsi.calc_extra_info()
        assert_equal(extra['extra_link_args'], ['-Wl,-rpath=' + self._lib2])

    def test_duplicate_options(self):
        # Ensure that duplicates are raising an AliasedOptionError
        tsi = self.c_dup_options
        assert_raises(AliasedOptionError, tsi.get_option_single, "mylib_libs", "libraries")
        assert_equal(tsi.get_libs("mylib_libs", [self._lib1]), [self._lib1])
        assert_equal(tsi.get_libs("libraries", [self._lib2]), [self._lib2])

    @pytest.mark.skipif(not HAVE_COMPILER, reason="Missing compiler")
    def test_compile1(self):
        # Compile source and link the first source
        c = customized_ccompiler()
        previousDir = os.getcwd()
        try:
            # Change directory to not screw up directories
            os.chdir(self._dir1)
            c.compile([os.path.basename(self._src1)], output_dir=self._dir1)
            # Ensure that the object exists
            assert_(os.path.isfile(self._src1.replace('.c', '.o')) or
                    os.path.isfile(self._src1.replace('.c', '.obj')))
        finally:
            os.chdir(previousDir)

    @pytest.mark.skipif(not HAVE_COMPILER, reason="Missing compiler")
    @pytest.mark.skipif('msvc' in repr(ccompiler.new_compiler()),

                         reason="Fails with MSVC compiler ")
    def test_compile2(self):
        # Compile source and link the second source
        tsi = self.c_temp2
        c = customized_ccompiler()
        extra_link_args = tsi.calc_extra_info()['extra_link_args']
        previousDir = os.getcwd()
        try:
            # Change directory to not screw up directories
            os.chdir(self._dir2)
            c.compile([os.path.basename(self._src2)], output_dir=self._dir2,
                      extra_postargs=extra_link_args)
            # Ensure that the object exists
            assert_(os.path.isfile(self._src2.replace('.c', '.o')))
        finally:
            os.chdir(previousDir)

    def test_overrides(self):
        previousDir = os.getcwd()
        cfg = os.path.join(self._dir1, 'site.cfg')
        shutil.copy(self._sitecfg, cfg)
        try:
            os.chdir(self._dir1)
            # Check that the '[ALL]' section does not override
            # missing values from other sections
            info = mkl_info()
            lib_dirs = info.cp['ALL']['library_dirs'].split(os.pathsep)
            assert info.get_lib_dirs() != lib_dirs

            # But if we copy the values to a '[mkl]' section the value
            # is correct
            with open(cfg, 'r') as fid:
                mkl = fid.read().replace('[ALL]', '[mkl]', 1)
            with open(cfg, 'w') as fid:
                fid.write(mkl)
            info = mkl_info()
            assert info.get_lib_dirs() == lib_dirs

            # Also, the values will be taken from a section named '[DEFAULT]'
            with open(cfg, 'r') as fid:
                dflt = fid.read().replace('[mkl]', '[DEFAULT]', 1)
            with open(cfg, 'w') as fid:
                fid.write(dflt)
            info = mkl_info()
            assert info.get_lib_dirs() == lib_dirs
        finally:
            os.chdir(previousDir)


def test_distutils_parse_env_order(monkeypatch):
    from numpy.distutils.system_info import _parse_env_order
    env = 'NPY_TESTS_DISTUTILS_PARSE_ENV_ORDER'

    base_order = list('abcdef')

    monkeypatch.setenv(env, 'b,i,e,f')
    order, unknown = _parse_env_order(base_order, env)
    assert len(order) == 3
    assert order == list('bef')
    assert len(unknown) == 1

    # For when LAPACK/BLAS optimization is disabled
    monkeypatch.setenv(env, '')
    order, unknown = _parse_env_order(base_order, env)
    assert len(order) == 0
    assert len(unknown) == 0

    for prefix in '^!':
        monkeypatch.setenv(env, f'{prefix}b,i,e')
        order, unknown = _parse_env_order(base_order, env)
        assert len(order) == 4
        assert order == list('acdf')
        assert len(unknown) == 1

    with pytest.raises(ValueError):
        monkeypatch.setenv(env, 'b,^e,i')
        _parse_env_order(base_order, env)

    with pytest.raises(ValueError):
        monkeypatch.setenv(env, '!b,^e,i')
        _parse_env_order(base_order, env)