Skip to content

Commit 28a2e2b

Browse files
authored
Support PEP 600 tags (#293)
1 parent 19fbc45 commit 28a2e2b

File tree

2 files changed

+318
-107
lines changed

2 files changed

+318
-107
lines changed

packaging/tags.py

Lines changed: 98 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()]
1515
del imp
16+
import collections
1617
import logging
1718
import os
1819
import platform
@@ -57,6 +58,24 @@
5758
_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32
5859

5960

61+
_LEGACY_MANYLINUX_MAP = {
62+
# CentOS 7 w/ glibc 2.17 (PEP 599)
63+
(2, 17): "manylinux2014",
64+
# CentOS 6 w/ glibc 2.12 (PEP 571)
65+
(2, 12): "manylinux2010",
66+
# CentOS 5 w/ glibc 2.5 (PEP 513)
67+
(2, 5): "manylinux1",
68+
}
69+
70+
# If glibc ever changes its major version, we need to know what the last
71+
# minor version was, so we can build the complete list of all versions.
72+
# For now, guess what the highest minor version might be, assume it will
73+
# be 50 for testing. Once this actually happens, update the dictionary
74+
# with the actual value.
75+
_LAST_GLIBC_MINOR = collections.defaultdict(lambda: 50) # type: Dict[int, int]
76+
glibcVersion = collections.namedtuple("Version", ["major", "minor"])
77+
78+
6079
class Tag(object):
6180
"""
6281
A representation of the tag triple for a wheel.
@@ -416,19 +435,35 @@ def mac_platforms(version=None, arch=None):
416435
)
417436

418437

419-
# From PEP 513.
420-
def _is_manylinux_compatible(name, glibc_version):
421-
# type: (str, GlibcVersion) -> bool
438+
# From PEP 513, PEP 600
439+
def _is_manylinux_compatible(name, arch, glibc_version):
440+
# type: (str, str, GlibcVersion) -> bool
441+
sys_glibc = _get_glibc_version()
442+
if sys_glibc < glibc_version:
443+
return False
422444
# Check for presence of _manylinux module.
423445
try:
424446
import _manylinux # noqa
425-
426-
return bool(getattr(_manylinux, name + "_compatible"))
427-
except (ImportError, AttributeError):
428-
# Fall through to heuristic check below.
447+
except ImportError:
429448
pass
430-
431-
return _have_compatible_glibc(*glibc_version)
449+
else:
450+
if hasattr(_manylinux, "manylinux_compatible"):
451+
result = _manylinux.manylinux_compatible(
452+
glibc_version[0], glibc_version[1], arch
453+
)
454+
if result is not None:
455+
return bool(result)
456+
else:
457+
if glibc_version == (2, 5):
458+
if hasattr(_manylinux, "manylinux1_compatible"):
459+
return bool(_manylinux.manylinux1_compatible)
460+
if glibc_version == (2, 12):
461+
if hasattr(_manylinux, "manylinux2010_compatible"):
462+
return bool(_manylinux.manylinux2010_compatible)
463+
if glibc_version == (2, 17):
464+
if hasattr(_manylinux, "manylinux2014_compatible"):
465+
return bool(_manylinux.manylinux2014_compatible)
466+
return True
432467

433468

434469
def _glibc_version_string():
@@ -505,10 +540,9 @@ def _glibc_version_string_ctypes():
505540
return version_str
506541

507542

508-
# Separated out from have_compatible_glibc for easier unit testing.
509-
def _check_glibc_version(version_str, required_major, minimum_minor):
510-
# type: (str, int, int) -> bool
511-
# Parse string and check against requested version.
543+
def _parse_glibc_version(version_str):
544+
# type: (str) -> Tuple[int, int]
545+
# Parse glibc version.
512546
#
513547
# We use a regexp instead of str.split because we want to discard any
514548
# random junk that might come after the minor version -- this might happen
@@ -521,19 +555,23 @@ def _check_glibc_version(version_str, required_major, minimum_minor):
521555
" got: %s" % version_str,
522556
RuntimeWarning,
523557
)
524-
return False
525-
return (
526-
int(m.group("major")) == required_major
527-
and int(m.group("minor")) >= minimum_minor
528-
)
558+
return -1, -1
559+
return (int(m.group("major")), int(m.group("minor")))
560+
529561

562+
_glibc_version = [] # type: List[Tuple[int, int]]
530563

531-
def _have_compatible_glibc(required_major, minimum_minor):
532-
# type: (int, int) -> bool
564+
565+
def _get_glibc_version():
566+
# type: () -> Tuple[int, int]
567+
if _glibc_version:
568+
return _glibc_version[0]
533569
version_str = _glibc_version_string()
534570
if version_str is None:
535-
return False
536-
return _check_glibc_version(version_str, required_major, minimum_minor)
571+
_glibc_version.append((-1, -1))
572+
else:
573+
_glibc_version.append(_parse_glibc_version(version_str))
574+
return _glibc_version[0]
537575

538576

539577
# Python does not provide platform information at sufficient granularity to
@@ -651,7 +689,42 @@ def _have_compatible_manylinux_abi(arch):
651689
return _is_linux_armhf()
652690
if arch == "i686":
653691
return _is_linux_i686()
654-
return True
692+
return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"}
693+
694+
695+
def _manylinux_tags(linux, arch):
696+
# type: (str, str) -> Iterator[str]
697+
# Oldest glibc to be supported regardless of architecture is (2, 17).
698+
too_old_glibc2 = glibcVersion(2, 16)
699+
if arch in {"x86_64", "i686"}:
700+
# On x86/i686 also oldest glibc to be supported is (2, 5).
701+
too_old_glibc2 = glibcVersion(2, 4)
702+
current_glibc = glibcVersion(*_get_glibc_version())
703+
glibc_max_list = [current_glibc]
704+
# We can assume compatibility across glibc major versions.
705+
# https://sourceware.org/bugzilla/show_bug.cgi?id=24636
706+
#
707+
# Build a list of maximum glibc versions so that we can
708+
# output the canonical list of all glibc from current_glibc
709+
# down to too_old_glibc2, including all intermediary versions.
710+
for glibc_major in range(current_glibc.major - 1, 1, -1):
711+
glibc_max_list.append(glibcVersion(glibc_major, _LAST_GLIBC_MINOR[glibc_major]))
712+
for glibc_max in glibc_max_list:
713+
if glibc_max.major == too_old_glibc2.major:
714+
min_minor = too_old_glibc2.minor
715+
else:
716+
# For other glibc major versions oldest supported is (x, 0).
717+
min_minor = -1
718+
for glibc_minor in range(glibc_max.minor, min_minor, -1):
719+
glibc_version = (glibc_max.major, glibc_minor)
720+
tag = "manylinux_{}_{}".format(*glibc_version)
721+
if _is_manylinux_compatible(tag, arch, glibc_version):
722+
yield linux.replace("linux", tag)
723+
# Handle the legacy manylinux1, manylinux2010, manylinux2014 tags.
724+
if glibc_version in _LEGACY_MANYLINUX_MAP:
725+
legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version]
726+
if _is_manylinux_compatible(legacy_tag, arch, glibc_version):
727+
yield linux.replace("linux", legacy_tag)
655728

656729

657730
def _linux_platforms(is_32bit=_32_BIT_INTERPRETER):
@@ -662,28 +735,10 @@ def _linux_platforms(is_32bit=_32_BIT_INTERPRETER):
662735
linux = "linux_i686"
663736
elif linux == "linux_aarch64":
664737
linux = "linux_armv7l"
665-
manylinux_support = []
666738
_, arch = linux.split("_", 1)
667739
if _have_compatible_manylinux_abi(arch):
668-
if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}:
669-
manylinux_support.append(
670-
("manylinux2014", (2, 17))
671-
) # CentOS 7 w/ glibc 2.17 (PEP 599)
672-
if arch in {"x86_64", "i686"}:
673-
manylinux_support.append(
674-
("manylinux2010", (2, 12))
675-
) # CentOS 6 w/ glibc 2.12 (PEP 571)
676-
manylinux_support.append(
677-
("manylinux1", (2, 5))
678-
) # CentOS 5 w/ glibc 2.5 (PEP 513)
679-
manylinux_support_iter = iter(manylinux_support)
680-
for name, glibc_version in manylinux_support_iter:
681-
if _is_manylinux_compatible(name, glibc_version):
682-
yield linux.replace("linux", name)
683-
break
684-
# Support for a later manylinux implies support for an earlier version.
685-
for name, _ in manylinux_support_iter:
686-
yield linux.replace("linux", name)
740+
for tag in _manylinux_tags(linux, arch):
741+
yield tag
687742
yield linux
688743

689744

0 commit comments

Comments
 (0)