| import os |
| import shutil |
| import pytest |
| from tempfile import mkstemp, mkdtemp |
| from subprocess import Popen, PIPE |
| import importlib.metadata |
| 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 |
|
|
|
|
| try: |
| if importlib.metadata.version('setuptools') >= '60': |
| |
| |
| pytest.skip("setuptools is too new", allow_module_level=True) |
| except importlib.metadata.PackageNotFoundError: |
| |
| pass |
|
|
|
|
| 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 |
| except AttributeError: |
| try: |
| if not compiler.initialized: |
| compiler.initialize() |
| 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) |
| |
| |
|
|
| 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_method(self): |
| """ Create the 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') |
| |
| 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]) |
| ) |
| |
| fd, self._sitecfg = mkstemp() |
| os.close(fd) |
| with open(self._sitecfg, 'w') as fd: |
| fd.write(site_cfg) |
| |
| with open(self._src1, 'w') as fd: |
| fd.write(fakelib_c_text) |
| with open(self._src2, 'w') as fd: |
| fd.write(fakelib_c_text) |
| |
|
|
| 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_method(self): |
| |
| 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): |
| |
| 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): |
| |
| 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): |
| |
| tsi = self.c_temp2 |
| assert_equal(tsi.get_lib_dirs(), [self._dir2]) |
| assert_equal(tsi.get_libraries(), [self._lib2]) |
| |
| 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): |
| |
| 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): |
| |
| c = customized_ccompiler() |
| previousDir = os.getcwd() |
| try: |
| |
| os.chdir(self._dir1) |
| c.compile([os.path.basename(self._src1)], output_dir=self._dir1) |
| |
| 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): |
| |
| tsi = self.c_temp2 |
| c = customized_ccompiler() |
| extra_link_args = tsi.calc_extra_info()['extra_link_args'] |
| previousDir = os.getcwd() |
| try: |
| |
| os.chdir(self._dir2) |
| c.compile([os.path.basename(self._src2)], output_dir=self._dir2, |
| extra_postargs=extra_link_args) |
| |
| assert_(os.path.isfile(self._src2.replace('.c', '.o'))) |
| finally: |
| os.chdir(previousDir) |
|
|
| HAS_MKL = "mkl_rt" in mkl_info().calc_libraries_info().get("libraries", []) |
|
|
| @pytest.mark.xfail(HAS_MKL, reason=("`[DEFAULT]` override doesn't work if " |
| "numpy is built with MKL support")) |
| 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) |
| |
| |
| info = mkl_info() |
| lib_dirs = info.cp['ALL']['library_dirs'].split(os.pathsep) |
| assert info.get_lib_dirs() != lib_dirs |
|
|
| |
| |
| with open(cfg) 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 |
|
|
| |
| with open(cfg) 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 |
|
|
| |
| 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) |
|
|