Skip to content

Commit 5f79f22

Browse files
committed
Merge pull request pypa/distutils#274 from msys2-contrib/ci-msvc-python-mingw-variant2
mingw: make get_msvcr() a noop + add a CI job testing MSVC Python with GCC
2 parents 6748224 + 1f999b9 commit 5f79f22

File tree

6 files changed

+49
-225
lines changed

6 files changed

+49
-225
lines changed

.github/workflows/main.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,28 @@ jobs:
163163
source /tmp/venv/bin/activate
164164
pytest
165165
166+
test_msvc_python_mingw:
167+
runs-on: windows-latest
168+
steps:
169+
- uses: actions/checkout@v4
170+
- name: Setup Python
171+
uses: actions/setup-python@v4
172+
with:
173+
python-version: 3.12
174+
- name: Install tox
175+
run: python -m pip install tox
176+
- name: Install GCC
177+
uses: msys2/setup-msys2@v2
178+
with:
179+
msystem: ucrt64
180+
install: mingw-w64-ucrt-x86_64-cc
181+
- name: Run
182+
run: |
183+
$env:MSYS2_ROOT = msys2 -c 'cygpath -m /'
184+
$env:PATH = "$env:MSYS2_ROOT/ucrt64/bin;$env:PATH"
185+
$env:DISTUTILS_TEST_DEFAULT_COMPILER = "mingw32"
186+
tox
187+
166188
ci_setuptools:
167189
# Integration testing with setuptools
168190
strategy:

conftest.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,26 @@ def disable_macos_customization(monkeypatch):
162162
from distutils import sysconfig
163163

164164
monkeypatch.setattr(sysconfig, '_customize_macos', lambda: None)
165+
166+
167+
@pytest.fixture(autouse=True, scope="session")
168+
def monkey_patch_get_default_compiler():
169+
"""
170+
Monkey patch distutils get_default_compiler to allow overriding the
171+
default compiler. Mainly to test mingw32 with a MSVC Python.
172+
"""
173+
from distutils import ccompiler
174+
175+
default_compiler = os.environ.get("DISTUTILS_TEST_DEFAULT_COMPILER")
176+
177+
if default_compiler is not None:
178+
179+
def patched_get_default_compiler(*args, **kwargs):
180+
return default_compiler
181+
182+
original = ccompiler.get_default_compiler
183+
ccompiler.get_default_compiler = patched_get_default_compiler
184+
yield
185+
ccompiler.get_default_compiler = original
186+
else:
187+
yield

distutils/_collections.py

Lines changed: 0 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
from __future__ import annotations
22

33
import collections
4-
import functools
54
import itertools
6-
import operator
7-
from collections.abc import Mapping
8-
from typing import Any
95

106

117
# from jaraco.collections 3.5.1
@@ -60,144 +56,3 @@ def __contains__(self, other):
6056

6157
def __len__(self):
6258
return len(list(iter(self)))
63-
64-
65-
# from jaraco.collections 5.0.1
66-
class RangeMap(dict):
67-
"""
68-
A dictionary-like object that uses the keys as bounds for a range.
69-
Inclusion of the value for that range is determined by the
70-
key_match_comparator, which defaults to less-than-or-equal.
71-
A value is returned for a key if it is the first key that matches in
72-
the sorted list of keys.
73-
74-
One may supply keyword parameters to be passed to the sort function used
75-
to sort keys (i.e. key, reverse) as sort_params.
76-
77-
Create a map that maps 1-3 -> 'a', 4-6 -> 'b'
78-
79-
>>> r = RangeMap({3: 'a', 6: 'b'}) # boy, that was easy
80-
>>> r[1], r[2], r[3], r[4], r[5], r[6]
81-
('a', 'a', 'a', 'b', 'b', 'b')
82-
83-
Even float values should work so long as the comparison operator
84-
supports it.
85-
86-
>>> r[4.5]
87-
'b'
88-
89-
Notice that the way rangemap is defined, it must be open-ended
90-
on one side.
91-
92-
>>> r[0]
93-
'a'
94-
>>> r[-1]
95-
'a'
96-
97-
One can close the open-end of the RangeMap by using undefined_value
98-
99-
>>> r = RangeMap({0: RangeMap.undefined_value, 3: 'a', 6: 'b'})
100-
>>> r[0]
101-
Traceback (most recent call last):
102-
...
103-
KeyError: 0
104-
105-
One can get the first or last elements in the range by using RangeMap.Item
106-
107-
>>> last_item = RangeMap.Item(-1)
108-
>>> r[last_item]
109-
'b'
110-
111-
.last_item is a shortcut for Item(-1)
112-
113-
>>> r[RangeMap.last_item]
114-
'b'
115-
116-
Sometimes it's useful to find the bounds for a RangeMap
117-
118-
>>> r.bounds()
119-
(0, 6)
120-
121-
RangeMap supports .get(key, default)
122-
123-
>>> r.get(0, 'not found')
124-
'not found'
125-
126-
>>> r.get(7, 'not found')
127-
'not found'
128-
129-
One often wishes to define the ranges by their left-most values,
130-
which requires use of sort params and a key_match_comparator.
131-
132-
>>> r = RangeMap({1: 'a', 4: 'b'},
133-
... sort_params=dict(reverse=True),
134-
... key_match_comparator=operator.ge)
135-
>>> r[1], r[2], r[3], r[4], r[5], r[6]
136-
('a', 'a', 'a', 'b', 'b', 'b')
137-
138-
That wasn't nearly as easy as before, so an alternate constructor
139-
is provided:
140-
141-
>>> r = RangeMap.left({1: 'a', 4: 'b', 7: RangeMap.undefined_value})
142-
>>> r[1], r[2], r[3], r[4], r[5], r[6]
143-
('a', 'a', 'a', 'b', 'b', 'b')
144-
145-
"""
146-
147-
def __init__(
148-
self,
149-
source,
150-
sort_params: Mapping[str, Any] = {},
151-
key_match_comparator=operator.le,
152-
):
153-
dict.__init__(self, source)
154-
self.sort_params = sort_params
155-
self.match = key_match_comparator
156-
157-
@classmethod
158-
def left(cls, source):
159-
return cls(
160-
source, sort_params=dict(reverse=True), key_match_comparator=operator.ge
161-
)
162-
163-
def __getitem__(self, item):
164-
sorted_keys = sorted(self.keys(), **self.sort_params)
165-
if isinstance(item, RangeMap.Item):
166-
result = self.__getitem__(sorted_keys[item])
167-
else:
168-
key = self._find_first_match_(sorted_keys, item)
169-
result = dict.__getitem__(self, key)
170-
if result is RangeMap.undefined_value:
171-
raise KeyError(key)
172-
return result
173-
174-
def get(self, key, default=None):
175-
"""
176-
Return the value for key if key is in the dictionary, else default.
177-
If default is not given, it defaults to None, so that this method
178-
never raises a KeyError.
179-
"""
180-
try:
181-
return self[key]
182-
except KeyError:
183-
return default
184-
185-
def _find_first_match_(self, keys, item):
186-
is_match = functools.partial(self.match, item)
187-
matches = list(filter(is_match, keys))
188-
if matches:
189-
return matches[0]
190-
raise KeyError(item)
191-
192-
def bounds(self):
193-
sorted_keys = sorted(self.keys(), **self.sort_params)
194-
return (sorted_keys[RangeMap.first_item], sorted_keys[RangeMap.last_item])
195-
196-
# some special values for the RangeMap
197-
undefined_value = type('RangeValueUndefined', (), {})()
198-
199-
class Item(int):
200-
"RangeMap Item"
201-
202-
first_item = Item(0)
203-
last_item = Item(-1)

distutils/cygwinccompiler.py

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@
99
import copy
1010
import os
1111
import pathlib
12-
import re
1312
import shlex
1413
import sys
1514
import warnings
1615
from subprocess import check_output
1716

18-
from ._collections import RangeMap
1917
from .errors import (
2018
CCompilerError,
2119
CompileError,
@@ -26,42 +24,10 @@
2624
from .unixccompiler import UnixCCompiler
2725
from .version import LooseVersion, suppress_known_deprecation
2826

29-
_msvcr_lookup = RangeMap.left(
30-
{
31-
# MSVC 7.0
32-
1300: ['msvcr70'],
33-
# MSVC 7.1
34-
1310: ['msvcr71'],
35-
# VS2005 / MSVC 8.0
36-
1400: ['msvcr80'],
37-
# VS2008 / MSVC 9.0
38-
1500: ['msvcr90'],
39-
# VS2010 / MSVC 10.0
40-
1600: ['msvcr100'],
41-
# VS2012 / MSVC 11.0
42-
1700: ['msvcr110'],
43-
# VS2013 / MSVC 12.0
44-
1800: ['msvcr120'],
45-
# VS2015 / MSVC 14.0
46-
1900: ['vcruntime140'],
47-
2000: RangeMap.undefined_value,
48-
},
49-
)
50-
5127

5228
def get_msvcr():
53-
"""Include the appropriate MSVC runtime library if Python was built
54-
with MSVC 7.0 or later.
55-
"""
56-
match = re.search(r'MSC v\.(\d{4})', sys.version)
57-
try:
58-
msc_ver = int(match.group(1))
59-
except AttributeError:
60-
return []
61-
try:
62-
return _msvcr_lookup[msc_ver]
63-
except KeyError:
64-
raise ValueError(f"Unknown MS Compiler version {msc_ver} ")
29+
"""No longer needed, but kept for backward compatibility."""
30+
return []
6531

6632

6733
_runtime_library_dirs_msg = (
@@ -113,8 +79,6 @@ def __init__(self, verbose=False, dry_run=False, force=False):
11379
linker_so_cxx=f'{self.linker_dll_cxx} -mcygwin {shared_option}',
11480
)
11581

116-
# Include the appropriate MSVC runtime library if Python was built
117-
# with MSVC 7.0 or later.
11882
self.dll_libraries = get_msvcr()
11983

12084
@property

distutils/tests/test_cygwinccompiler.py

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -71,50 +71,8 @@ def test_check_config_h(self):
7171
assert check_config_h()[0] == CONFIG_H_OK
7272

7373
def test_get_msvcr(self):
74-
# []
75-
sys.version = (
76-
'2.6.1 (r261:67515, Dec 6 2008, 16:42:21) '
77-
'\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]'
78-
)
7974
assert get_msvcr() == []
8075

81-
# MSVC 7.0
82-
sys.version = (
83-
'2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1300 32 bits (Intel)]'
84-
)
85-
assert get_msvcr() == ['msvcr70']
86-
87-
# MSVC 7.1
88-
sys.version = (
89-
'2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1310 32 bits (Intel)]'
90-
)
91-
assert get_msvcr() == ['msvcr71']
92-
93-
# VS2005 / MSVC 8.0
94-
sys.version = (
95-
'2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1400 32 bits (Intel)]'
96-
)
97-
assert get_msvcr() == ['msvcr80']
98-
99-
# VS2008 / MSVC 9.0
100-
sys.version = (
101-
'2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1500 32 bits (Intel)]'
102-
)
103-
assert get_msvcr() == ['msvcr90']
104-
105-
sys.version = (
106-
'3.10.0 (tags/v3.10.0:b494f59, Oct 4 2021, 18:46:30) '
107-
'[MSC v.1929 32 bit (Intel)]'
108-
)
109-
assert get_msvcr() == ['vcruntime140']
110-
111-
# unknown
112-
sys.version = (
113-
'2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.2000 32 bits (Intel)]'
114-
)
115-
with pytest.raises(ValueError):
116-
get_msvcr()
117-
11876
@pytest.mark.skipif('sys.platform != "cygwin"')
11977
def test_dll_libraries_not_none(self):
12078
from distutils.cygwinccompiler import CygwinCCompiler

tox.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ setenv =
55
PYTHONWARNDEFAULTENCODING = 1
66
# pypa/distutils#99
77
VIRTUALENV_NO_SETUPTOOLS = 1
8+
pass_env =
9+
DISTUTILS_TEST_DEFAULT_COMPILER
810
commands =
911
pytest {posargs}
1012
usedevelop = True

0 commit comments

Comments
 (0)