Stav 23.06.2026
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import platform
|
||||
import sysconfig
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
|
||||
from .. import base
|
||||
|
||||
pytestmark = pytest.mark.usefixtures('suppress_path_mangle')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def c_file(tmp_path):
|
||||
c_file = tmp_path / 'foo.c'
|
||||
gen_headers = ('Python.h',)
|
||||
is_windows = platform.system() == "Windows"
|
||||
plat_headers = ('windows.h',) * is_windows
|
||||
all_headers = gen_headers + plat_headers
|
||||
headers = '\n'.join(f'#include <{header}>\n' for header in all_headers)
|
||||
payload = (
|
||||
textwrap.dedent(
|
||||
"""
|
||||
#headers
|
||||
void PyInit_foo(void) {}
|
||||
"""
|
||||
)
|
||||
.lstrip()
|
||||
.replace('#headers', headers)
|
||||
)
|
||||
c_file.write_text(payload, encoding='utf-8')
|
||||
return c_file
|
||||
|
||||
|
||||
def test_set_include_dirs(c_file):
|
||||
"""
|
||||
Extensions should build even if set_include_dirs is invoked.
|
||||
In particular, compiler-specific paths should not be overridden.
|
||||
"""
|
||||
compiler = base.new_compiler()
|
||||
python = sysconfig.get_paths()['include']
|
||||
compiler.set_include_dirs([python])
|
||||
compiler.compile([c_file])
|
||||
|
||||
# do it again, setting include dirs after any initialization
|
||||
compiler.set_include_dirs([python])
|
||||
compiler.compile([c_file])
|
||||
|
||||
|
||||
def test_has_function_prototype():
|
||||
# Issue https://github.com/pypa/setuptools/issues/3648
|
||||
# Test prototype-generating behavior.
|
||||
|
||||
compiler = base.new_compiler()
|
||||
|
||||
# Every C implementation should have these.
|
||||
assert compiler.has_function('abort')
|
||||
assert compiler.has_function('exit')
|
||||
with pytest.deprecated_call(match='includes is deprecated'):
|
||||
# abort() is a valid expression with the <stdlib.h> prototype.
|
||||
assert compiler.has_function('abort', includes=['stdlib.h'])
|
||||
with pytest.deprecated_call(match='includes is deprecated'):
|
||||
# But exit() is not valid with the actual prototype in scope.
|
||||
assert not compiler.has_function('exit', includes=['stdlib.h'])
|
||||
# And setuptools_does_not_exist is not declared or defined at all.
|
||||
assert not compiler.has_function('setuptools_does_not_exist')
|
||||
with pytest.deprecated_call(match='includes is deprecated'):
|
||||
assert not compiler.has_function(
|
||||
'setuptools_does_not_exist', includes=['stdio.h']
|
||||
)
|
||||
|
||||
|
||||
def test_include_dirs_after_multiple_compile_calls(c_file):
|
||||
"""
|
||||
Calling compile multiple times should not change the include dirs
|
||||
(regression test for setuptools issue #3591).
|
||||
"""
|
||||
compiler = base.new_compiler()
|
||||
python = sysconfig.get_paths()['include']
|
||||
compiler.set_include_dirs([python])
|
||||
compiler.compile([c_file])
|
||||
assert compiler.include_dirs == [python]
|
||||
compiler.compile([c_file])
|
||||
assert compiler.include_dirs == [python]
|
||||
@@ -0,0 +1,76 @@
|
||||
"""Tests for distutils.cygwinccompiler."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from distutils import sysconfig
|
||||
from distutils.tests import support
|
||||
|
||||
import pytest
|
||||
|
||||
from .. import cygwin
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def stuff(request, monkeypatch, distutils_managed_tempdir):
|
||||
self = request.instance
|
||||
self.python_h = os.path.join(self.mkdtemp(), 'python.h')
|
||||
monkeypatch.setattr(sysconfig, 'get_config_h_filename', self._get_config_h_filename)
|
||||
monkeypatch.setattr(sys, 'version', sys.version)
|
||||
|
||||
|
||||
class TestCygwinCCompiler(support.TempdirManager):
|
||||
def _get_config_h_filename(self):
|
||||
return self.python_h
|
||||
|
||||
@pytest.mark.skipif('sys.platform != "cygwin"')
|
||||
@pytest.mark.skipif('not os.path.exists("/usr/lib/libbash.dll.a")')
|
||||
def test_find_library_file(self):
|
||||
from distutils.cygwinccompiler import CygwinCCompiler
|
||||
|
||||
compiler = CygwinCCompiler()
|
||||
link_name = "bash"
|
||||
linkable_file = compiler.find_library_file(["/usr/lib"], link_name)
|
||||
assert linkable_file is not None
|
||||
assert os.path.exists(linkable_file)
|
||||
assert linkable_file == f"/usr/lib/lib{link_name:s}.dll.a"
|
||||
|
||||
@pytest.mark.skipif('sys.platform != "cygwin"')
|
||||
def test_runtime_library_dir_option(self):
|
||||
from distutils.cygwinccompiler import CygwinCCompiler
|
||||
|
||||
compiler = CygwinCCompiler()
|
||||
assert compiler.runtime_library_dir_option('/foo') == []
|
||||
|
||||
def test_check_config_h(self):
|
||||
# check_config_h looks for "GCC" in sys.version first
|
||||
# returns CONFIG_H_OK if found
|
||||
sys.version = (
|
||||
'2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC '
|
||||
'4.0.1 (Apple Computer, Inc. build 5370)]'
|
||||
)
|
||||
|
||||
assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_OK
|
||||
|
||||
# then it tries to see if it can find "__GNUC__" in pyconfig.h
|
||||
sys.version = 'something without the *CC word'
|
||||
|
||||
# if the file doesn't exist it returns CONFIG_H_UNCERTAIN
|
||||
assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_UNCERTAIN
|
||||
|
||||
# if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK
|
||||
self.write_file(self.python_h, 'xxx')
|
||||
assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_NOTOK
|
||||
|
||||
# and CONFIG_H_OK if __GNUC__ is found
|
||||
self.write_file(self.python_h, 'xxx __GNUC__ xxx')
|
||||
assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_OK
|
||||
|
||||
def test_get_msvcr(self):
|
||||
assert cygwin.get_msvcr() == []
|
||||
|
||||
@pytest.mark.skipif('sys.platform != "cygwin"')
|
||||
def test_dll_libraries_not_none(self):
|
||||
from distutils.cygwinccompiler import CygwinCCompiler
|
||||
|
||||
compiler = CygwinCCompiler()
|
||||
assert compiler.dll_libraries is not None
|
||||
@@ -0,0 +1,48 @@
|
||||
from distutils import sysconfig
|
||||
from distutils.errors import DistutilsPlatformError
|
||||
from distutils.util import is_mingw, split_quoted
|
||||
|
||||
import pytest
|
||||
|
||||
from .. import cygwin, errors
|
||||
|
||||
|
||||
class TestMinGW32Compiler:
|
||||
@pytest.mark.skipif(not is_mingw(), reason='not on mingw')
|
||||
def test_compiler_type(self):
|
||||
compiler = cygwin.MinGW32Compiler()
|
||||
assert compiler.compiler_type == 'mingw32'
|
||||
|
||||
@pytest.mark.skipif(not is_mingw(), reason='not on mingw')
|
||||
def test_set_executables(self, monkeypatch):
|
||||
monkeypatch.setenv('CC', 'cc')
|
||||
monkeypatch.setenv('CXX', 'c++')
|
||||
|
||||
compiler = cygwin.MinGW32Compiler()
|
||||
|
||||
assert compiler.compiler == split_quoted('cc -O -Wall')
|
||||
assert compiler.compiler_so == split_quoted('cc -shared -O -Wall')
|
||||
assert compiler.compiler_cxx == split_quoted('c++ -O -Wall')
|
||||
assert compiler.linker_exe == split_quoted('cc')
|
||||
assert compiler.linker_so == split_quoted('cc -shared')
|
||||
|
||||
@pytest.mark.skipif(not is_mingw(), reason='not on mingw')
|
||||
def test_runtime_library_dir_option(self):
|
||||
compiler = cygwin.MinGW32Compiler()
|
||||
with pytest.raises(DistutilsPlatformError):
|
||||
compiler.runtime_library_dir_option('/usr/lib')
|
||||
|
||||
@pytest.mark.skipif(not is_mingw(), reason='not on mingw')
|
||||
def test_cygwincc_error(self, monkeypatch):
|
||||
monkeypatch.setattr(cygwin, 'is_cygwincc', lambda _: True)
|
||||
|
||||
with pytest.raises(errors.Error):
|
||||
cygwin.MinGW32Compiler()
|
||||
|
||||
@pytest.mark.skipif('sys.platform == "cygwin"')
|
||||
def test_customize_compiler_with_msvc_python(self):
|
||||
# In case we have an MSVC Python build, but still want to use
|
||||
# MinGW32Compiler, then customize_compiler() shouldn't fail at least.
|
||||
# https://github.com/pypa/setuptools/issues/4456
|
||||
compiler = cygwin.MinGW32Compiler()
|
||||
sysconfig.customize_compiler(compiler)
|
||||
@@ -0,0 +1,136 @@
|
||||
import os
|
||||
import sys
|
||||
import sysconfig
|
||||
import threading
|
||||
import unittest.mock as mock
|
||||
from distutils.errors import DistutilsPlatformError
|
||||
from distutils.tests import support
|
||||
from distutils.util import get_platform
|
||||
|
||||
import pytest
|
||||
|
||||
from .. import msvc
|
||||
|
||||
needs_winreg = pytest.mark.skipif('not hasattr(msvc, "winreg")')
|
||||
|
||||
|
||||
class Testmsvccompiler(support.TempdirManager):
|
||||
def test_no_compiler(self, monkeypatch):
|
||||
# makes sure query_vcvarsall raises
|
||||
# a DistutilsPlatformError if the compiler
|
||||
# is not found
|
||||
def _find_vcvarsall(plat_spec):
|
||||
return None, None
|
||||
|
||||
monkeypatch.setattr(msvc, '_find_vcvarsall', _find_vcvarsall)
|
||||
|
||||
with pytest.raises(DistutilsPlatformError):
|
||||
msvc._get_vc_env(
|
||||
'wont find this version',
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not sysconfig.get_platform().startswith("win"),
|
||||
reason="Only run test for non-mingw Windows platforms",
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"plat_name, expected",
|
||||
[
|
||||
("win-arm64", "win-arm64"),
|
||||
("win-amd64", "win-amd64"),
|
||||
(None, get_platform()),
|
||||
],
|
||||
)
|
||||
def test_cross_platform_compilation_paths(self, monkeypatch, plat_name, expected):
|
||||
"""
|
||||
Ensure a specified target platform is passed to _get_vcvars_spec.
|
||||
"""
|
||||
compiler = msvc.Compiler()
|
||||
|
||||
def _get_vcvars_spec(host_platform, platform):
|
||||
assert platform == expected
|
||||
|
||||
monkeypatch.setattr(msvc, '_get_vcvars_spec', _get_vcvars_spec)
|
||||
compiler.initialize(plat_name)
|
||||
|
||||
@needs_winreg
|
||||
def test_get_vc_env_unicode(self):
|
||||
test_var = 'ṰḖṤṪ┅ṼẨṜ'
|
||||
test_value = '₃⁴₅'
|
||||
|
||||
# Ensure we don't early exit from _get_vc_env
|
||||
old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None)
|
||||
os.environ[test_var] = test_value
|
||||
try:
|
||||
env = msvc._get_vc_env('x86')
|
||||
assert test_var.lower() in env
|
||||
assert test_value == env[test_var.lower()]
|
||||
finally:
|
||||
os.environ.pop(test_var)
|
||||
if old_distutils_use_sdk:
|
||||
os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk
|
||||
|
||||
@needs_winreg
|
||||
@pytest.mark.parametrize('ver', (2015, 2017))
|
||||
def test_get_vc(self, ver):
|
||||
# This function cannot be mocked, so pass if VC is found
|
||||
# and skip otherwise.
|
||||
lookup = getattr(msvc, f'_find_vc{ver}')
|
||||
expected_version = {2015: 14, 2017: 15}[ver]
|
||||
version, path = lookup()
|
||||
if not version:
|
||||
pytest.skip(f"VS {ver} is not installed")
|
||||
assert version >= expected_version
|
||||
assert os.path.isdir(path)
|
||||
|
||||
|
||||
class CheckThread(threading.Thread):
|
||||
exc_info = None
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
super().run()
|
||||
except Exception:
|
||||
self.exc_info = sys.exc_info()
|
||||
|
||||
def __bool__(self):
|
||||
return not self.exc_info
|
||||
|
||||
|
||||
class TestSpawn:
|
||||
def test_concurrent_safe(self):
|
||||
"""
|
||||
Concurrent calls to spawn should have consistent results.
|
||||
"""
|
||||
compiler = msvc.Compiler()
|
||||
compiler._paths = "expected"
|
||||
inner_cmd = 'import os; assert os.environ["PATH"] == "expected"'
|
||||
command = [sys.executable, '-c', inner_cmd]
|
||||
|
||||
threads = [
|
||||
CheckThread(target=compiler.spawn, args=[command]) for n in range(100)
|
||||
]
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
assert all(threads)
|
||||
|
||||
def test_concurrent_safe_fallback(self):
|
||||
"""
|
||||
If CCompiler.spawn has been monkey-patched without support
|
||||
for an env, it should still execute.
|
||||
"""
|
||||
from distutils import ccompiler
|
||||
|
||||
compiler = msvc.Compiler()
|
||||
compiler._paths = "expected"
|
||||
|
||||
def CCompiler_spawn(self, cmd):
|
||||
"A spawn without an env argument."
|
||||
assert os.environ["PATH"] == "expected"
|
||||
|
||||
with mock.patch.object(ccompiler.CCompiler, 'spawn', CCompiler_spawn):
|
||||
compiler.spawn(["n/a"])
|
||||
|
||||
assert os.environ.get("PATH") != "expected"
|
||||
@@ -0,0 +1,413 @@
|
||||
"""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"')
|
||||
def test_cxx_commands_used_are_correct(self):
|
||||
def gcv(v):
|
||||
if v == 'LDSHARED':
|
||||
return 'ccache gcc-4.2 -bundle -undefined dynamic_lookup'
|
||||
elif v == 'LDCXXSHARED':
|
||||
return 'ccache g++-4.2 -bundle -undefined dynamic_lookup'
|
||||
elif v == 'CXX':
|
||||
return 'ccache g++-4.2'
|
||||
elif v == 'CC':
|
||||
return 'ccache gcc-4.2'
|
||||
return ''
|
||||
|
||||
def gcvs(*args, _orig=sysconfig.get_config_vars):
|
||||
if args:
|
||||
return list(map(sysconfig.get_config_var, args))
|
||||
return _orig() # pragma: no cover
|
||||
|
||||
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,
|
||||
):
|
||||
# override environment overrides in case they're specified by CI
|
||||
del env['CXX']
|
||||
del env['LDCXXSHARED']
|
||||
|
||||
sysconfig.customize_compiler(self.cc)
|
||||
assert self.cc.linker_so_cxx[0:2] == ['ccache', 'g++-4.2']
|
||||
assert self.cc.linker_exe_cxx[0:2] == ['ccache', 'g++-4.2']
|
||||
self.cc.link(None, [], 'a.out', target_lang='c++')
|
||||
call_args = mock_spawn.call_args[0][0]
|
||||
expected = ['ccache', 'g++-4.2', '-bundle', '-undefined', 'dynamic_lookup']
|
||||
assert call_args[:5] == expected
|
||||
|
||||
self.cc.link_executable([], 'a.out', target_lang='c++')
|
||||
call_args = mock_spawn.call_args[0][0]
|
||||
expected = ['ccache', 'g++-4.2', '-o', self.cc.executable_filename('a.out')]
|
||||
assert call_args[:4] == expected
|
||||
|
||||
env['LDCXXSHARED'] = 'wrapper g++-4.2 -bundle -undefined dynamic_lookup'
|
||||
env['CXX'] = 'wrapper g++-4.2'
|
||||
sysconfig.customize_compiler(self.cc)
|
||||
assert self.cc.linker_so_cxx[0:2] == ['wrapper', 'g++-4.2']
|
||||
assert self.cc.linker_exe_cxx[0:2] == ['wrapper', 'g++-4.2']
|
||||
self.cc.link(None, [], 'a.out', target_lang='c++')
|
||||
call_args = mock_spawn.call_args[0][0]
|
||||
expected = ['wrapper', 'g++-4.2', '-bundle', '-undefined', 'dynamic_lookup']
|
||||
assert call_args[:5] == expected
|
||||
|
||||
self.cc.link_executable([], 'a.out', target_lang='c++')
|
||||
call_args = mock_spawn.call_args[0][0]
|
||||
expected = [
|
||||
'wrapper',
|
||||
'g++-4.2',
|
||||
'-o',
|
||||
self.cc.executable_filename('a.out'),
|
||||
]
|
||||
assert call_args[:4] == expected
|
||||
|
||||
@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'
|
||||
)
|
||||
Reference in New Issue
Block a user