| Viewing file:  test_unix.py (11.56 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
"""Tests for distutils.unixccompiler."""
 import os
 import sys
 import unittest.mock as mock
 from distutils import sysconfig
 from distutils.compat import consolidate_linker_args
 from distutils.errors import DistutilsPlatformError
 from distutils.tests import support
 from distutils.tests.compat.py39 import EnvironmentVarGuard
 from distutils.util import _clear_cached_macosx_ver
 
 import pytest
 
 from .. import unix
 
 
 @pytest.fixture(autouse=True)
 def save_values(monkeypatch):
 monkeypatch.setattr(sys, 'platform', sys.platform)
 monkeypatch.setattr(sysconfig, 'get_config_var', sysconfig.get_config_var)
 monkeypatch.setattr(sysconfig, 'get_config_vars', sysconfig.get_config_vars)
 
 
 @pytest.fixture(autouse=True)
 def compiler_wrapper(request):
 class CompilerWrapper(unix.Compiler):
 def rpath_foo(self):
 return self.runtime_library_dir_option('/foo')
 
 request.instance.cc = CompilerWrapper()
 
 
 class TestUnixCCompiler(support.TempdirManager):
 @pytest.mark.skipif('platform.system == "Windows"')
 def test_runtime_libdir_option(self):  # noqa: C901
 # Issue #5900; GitHub Issue #37
 #
 # Ensure RUNPATH is added to extension modules with RPATH if
 # GNU ld is used
 
 # darwin
 sys.platform = 'darwin'
 darwin_ver_var = 'MACOSX_DEPLOYMENT_TARGET'
 darwin_rpath_flag = '-Wl,-rpath,/foo'
 darwin_lib_flag = '-L/foo'
 
 # (macOS version from syscfg, macOS version from env var) -> flag
 # Version value of None generates two tests: as None and as empty string
 # Expected flag value of None means an mismatch exception is expected
 darwin_test_cases = [
 ((None, None), darwin_lib_flag),
 ((None, '11'), darwin_rpath_flag),
 (('10', None), darwin_lib_flag),
 (('10.3', None), darwin_lib_flag),
 (('10.3.1', None), darwin_lib_flag),
 (('10.5', None), darwin_rpath_flag),
 (('10.5.1', None), darwin_rpath_flag),
 (('10.3', '10.3'), darwin_lib_flag),
 (('10.3', '10.5'), darwin_rpath_flag),
 (('10.5', '10.3'), darwin_lib_flag),
 (('10.5', '11'), darwin_rpath_flag),
 (('10.4', '10'), None),
 ]
 
 def make_darwin_gcv(syscfg_macosx_ver):
 def gcv(var):
 if var == darwin_ver_var:
 return syscfg_macosx_ver
 return "xxx"
 
 return gcv
 
 def do_darwin_test(syscfg_macosx_ver, env_macosx_ver, expected_flag):
 env = os.environ
 msg = f"macOS version = (sysconfig={syscfg_macosx_ver!r}, env={env_macosx_ver!r})"
 
 # Save
 old_gcv = sysconfig.get_config_var
 old_env_macosx_ver = env.get(darwin_ver_var)
 
 # Setup environment
 _clear_cached_macosx_ver()
 sysconfig.get_config_var = make_darwin_gcv(syscfg_macosx_ver)
 if env_macosx_ver is not None:
 env[darwin_ver_var] = env_macosx_ver
 elif darwin_ver_var in env:
 env.pop(darwin_ver_var)
 
 # Run the test
 if expected_flag is not None:
 assert self.cc.rpath_foo() == expected_flag, msg
 else:
 with pytest.raises(
 DistutilsPlatformError, match=darwin_ver_var + r' mismatch'
 ):
 self.cc.rpath_foo()
 
 # Restore
 if old_env_macosx_ver is not None:
 env[darwin_ver_var] = old_env_macosx_ver
 elif darwin_ver_var in env:
 env.pop(darwin_ver_var)
 sysconfig.get_config_var = old_gcv
 _clear_cached_macosx_ver()
 
 for macosx_vers, expected_flag in darwin_test_cases:
 syscfg_macosx_ver, env_macosx_ver = macosx_vers
 do_darwin_test(syscfg_macosx_ver, env_macosx_ver, expected_flag)
 # Bonus test cases with None interpreted as empty string
 if syscfg_macosx_ver is None:
 do_darwin_test("", env_macosx_ver, expected_flag)
 if env_macosx_ver is None:
 do_darwin_test(syscfg_macosx_ver, "", expected_flag)
 if syscfg_macosx_ver is None and env_macosx_ver is None:
 do_darwin_test("", "", expected_flag)
 
 old_gcv = sysconfig.get_config_var
 
 # hp-ux
 sys.platform = 'hp-ux'
 
 def gcv(v):
 return 'xxx'
 
 sysconfig.get_config_var = gcv
 assert self.cc.rpath_foo() == ['+s', '-L/foo']
 
 def gcv(v):
 return 'gcc'
 
 sysconfig.get_config_var = gcv
 assert self.cc.rpath_foo() == ['-Wl,+s', '-L/foo']
 
 def gcv(v):
 return 'g++'
 
 sysconfig.get_config_var = gcv
 assert self.cc.rpath_foo() == ['-Wl,+s', '-L/foo']
 
 sysconfig.get_config_var = old_gcv
 
 # GCC GNULD
 sys.platform = 'bar'
 
 def gcv(v):
 if v == 'CC':
 return 'gcc'
 elif v == 'GNULD':
 return 'yes'
 
 sysconfig.get_config_var = gcv
 assert self.cc.rpath_foo() == consolidate_linker_args([
 '-Wl,--enable-new-dtags',
 '-Wl,-rpath,/foo',
 ])
 
 def gcv(v):
 if v == 'CC':
 return 'gcc -pthread -B /bar'
 elif v == 'GNULD':
 return 'yes'
 
 sysconfig.get_config_var = gcv
 assert self.cc.rpath_foo() == consolidate_linker_args([
 '-Wl,--enable-new-dtags',
 '-Wl,-rpath,/foo',
 ])
 
 # GCC non-GNULD
 sys.platform = 'bar'
 
 def gcv(v):
 if v == 'CC':
 return 'gcc'
 elif v == 'GNULD':
 return 'no'
 
 sysconfig.get_config_var = gcv
 assert self.cc.rpath_foo() == '-Wl,-R/foo'
 
 # GCC GNULD with fully qualified configuration prefix
 # see #7617
 sys.platform = 'bar'
 
 def gcv(v):
 if v == 'CC':
 return 'x86_64-pc-linux-gnu-gcc-4.4.2'
 elif v == 'GNULD':
 return 'yes'
 
 sysconfig.get_config_var = gcv
 assert self.cc.rpath_foo() == consolidate_linker_args([
 '-Wl,--enable-new-dtags',
 '-Wl,-rpath,/foo',
 ])
 
 # non-GCC GNULD
 sys.platform = 'bar'
 
 def gcv(v):
 if v == 'CC':
 return 'cc'
 elif v == 'GNULD':
 return 'yes'
 
 sysconfig.get_config_var = gcv
 assert self.cc.rpath_foo() == consolidate_linker_args([
 '-Wl,--enable-new-dtags',
 '-Wl,-rpath,/foo',
 ])
 
 # non-GCC non-GNULD
 sys.platform = 'bar'
 
 def gcv(v):
 if v == 'CC':
 return 'cc'
 elif v == 'GNULD':
 return 'no'
 
 sysconfig.get_config_var = gcv
 assert self.cc.rpath_foo() == '-Wl,-R/foo'
 
 @pytest.mark.skipif('platform.system == "Windows"')
 def test_cc_overrides_ldshared(self):
 # Issue #18080:
 # ensure that setting CC env variable also changes default linker
 def gcv(v):
 if v == 'LDSHARED':
 return 'gcc-4.2 -bundle -undefined dynamic_lookup '
 return 'gcc-4.2'
 
 def gcvs(*args, _orig=sysconfig.get_config_vars):
 if args:
 return list(map(sysconfig.get_config_var, args))
 return _orig()
 
 sysconfig.get_config_var = gcv
 sysconfig.get_config_vars = gcvs
 with EnvironmentVarGuard() as env:
 env['CC'] = 'my_cc'
 del env['LDSHARED']
 sysconfig.customize_compiler(self.cc)
 assert self.cc.linker_so[0] == 'my_cc'
 
 @pytest.mark.skipif('platform.system == "Windows"')
 @pytest.mark.usefixtures('disable_macos_customization')
 def test_cc_overrides_ldshared_for_cxx_correctly(self):
 """
 Ensure that setting CC env variable also changes default linker
 correctly when building C++ extensions.
 
 pypa/distutils#126
 """
 
 def gcv(v):
 if v == 'LDSHARED':
 return 'gcc-4.2 -bundle -undefined dynamic_lookup '
 elif v == 'LDCXXSHARED':
 return 'g++-4.2 -bundle -undefined dynamic_lookup '
 elif v == 'CXX':
 return 'g++-4.2'
 elif v == 'CC':
 return 'gcc-4.2'
 return ''
 
 def gcvs(*args, _orig=sysconfig.get_config_vars):
 if args:
 return list(map(sysconfig.get_config_var, args))
 return _orig()
 
 sysconfig.get_config_var = gcv
 sysconfig.get_config_vars = gcvs
 with (
 mock.patch.object(self.cc, 'spawn', return_value=None) as mock_spawn,
 mock.patch.object(self.cc, '_need_link', return_value=True),
 mock.patch.object(self.cc, 'mkpath', return_value=None),
 EnvironmentVarGuard() as env,
 ):
 env['CC'] = 'ccache my_cc'
 env['CXX'] = 'my_cxx'
 del env['LDSHARED']
 sysconfig.customize_compiler(self.cc)
 assert self.cc.linker_so[0:2] == ['ccache', 'my_cc']
 self.cc.link(None, [], 'a.out', target_lang='c++')
 call_args = mock_spawn.call_args[0][0]
 expected = ['my_cxx', '-bundle', '-undefined', 'dynamic_lookup']
 assert call_args[:4] == expected
 
 @pytest.mark.skipif('platform.system == "Windows"')
 def test_explicit_ldshared(self):
 # Issue #18080:
 # ensure that setting CC env variable does not change
 #   explicit LDSHARED setting for linker
 def gcv(v):
 if v == 'LDSHARED':
 return 'gcc-4.2 -bundle -undefined dynamic_lookup '
 return 'gcc-4.2'
 
 def gcvs(*args, _orig=sysconfig.get_config_vars):
 if args:
 return list(map(sysconfig.get_config_var, args))
 return _orig()
 
 sysconfig.get_config_var = gcv
 sysconfig.get_config_vars = gcvs
 with EnvironmentVarGuard() as env:
 env['CC'] = 'my_cc'
 env['LDSHARED'] = 'my_ld -bundle -dynamic'
 sysconfig.customize_compiler(self.cc)
 assert self.cc.linker_so[0] == 'my_ld'
 
 def test_has_function(self):
 # Issue https://github.com/pypa/distutils/issues/64:
 # ensure that setting output_dir does not raise
 # FileNotFoundError: [Errno 2] No such file or directory: 'a.out'
 self.cc.output_dir = 'scratch'
 os.chdir(self.mkdtemp())
 self.cc.has_function('abort')
 
 def test_find_library_file(self, monkeypatch):
 compiler = unix.Compiler()
 compiler._library_root = lambda dir: dir
 monkeypatch.setattr(os.path, 'exists', lambda d: 'existing' in d)
 
 libname = 'libabc.dylib' if sys.platform != 'cygwin' else 'cygabc.dll'
 dirs = ('/foo/bar/missing', '/foo/bar/existing')
 assert (
 compiler.find_library_file(dirs, 'abc').replace('\\', '/')
 == f'/foo/bar/existing/{libname}'
 )
 assert (
 compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/')
 == f'/foo/bar/existing/{libname}'
 )
 
 monkeypatch.setattr(
 os.path,
 'exists',
 lambda d: 'existing' in d and '.a' in d and '.dll.a' not in d,
 )
 assert (
 compiler.find_library_file(dirs, 'abc').replace('\\', '/')
 == '/foo/bar/existing/libabc.a'
 )
 assert (
 compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/')
 == '/foo/bar/existing/libabc.a'
 )
 
 |