1# This file originally from pip: 2# https://github.com/pypa/pip/blob/8f4f15a5a95d7d5b511ceaee9ed261176c181970/src/pip/_internal/pep425tags.py 3"""Generate and work with PEP 425 Compatibility Tags.""" 4from __future__ import absolute_import 5 6import distutils.util 7from distutils import log 8import platform 9import re 10import sys 11import sysconfig 12import warnings 13from collections import OrderedDict 14 15from . import glibc 16 17_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') 18 19 20def get_config_var(var): 21 try: 22 return sysconfig.get_config_var(var) 23 except IOError as e: # Issue #1074 24 warnings.warn("{}".format(e), RuntimeWarning) 25 return None 26 27 28def get_abbr_impl(): 29 """Return abbreviated implementation name.""" 30 if hasattr(sys, 'pypy_version_info'): 31 pyimpl = 'pp' 32 elif sys.platform.startswith('java'): 33 pyimpl = 'jy' 34 elif sys.platform == 'cli': 35 pyimpl = 'ip' 36 else: 37 pyimpl = 'cp' 38 return pyimpl 39 40 41def get_impl_ver(): 42 """Return implementation version.""" 43 impl_ver = get_config_var("py_version_nodot") 44 if not impl_ver or get_abbr_impl() == 'pp': 45 impl_ver = ''.join(map(str, get_impl_version_info())) 46 return impl_ver 47 48 49def get_impl_version_info(): 50 """Return sys.version_info-like tuple for use in decrementing the minor 51 version.""" 52 if get_abbr_impl() == 'pp': 53 # as per https://github.com/pypa/pip/issues/2882 54 return (sys.version_info[0], sys.pypy_version_info.major, 55 sys.pypy_version_info.minor) 56 else: 57 return sys.version_info[0], sys.version_info[1] 58 59 60def get_impl_tag(): 61 """ 62 Returns the Tag for this specific implementation. 63 """ 64 return "{}{}".format(get_abbr_impl(), get_impl_ver()) 65 66 67def get_flag(var, fallback, expected=True, warn=True): 68 """Use a fallback method for determining SOABI flags if the needed config 69 var is unset or unavailable.""" 70 val = get_config_var(var) 71 if val is None: 72 if warn: 73 log.debug("Config variable '%s' is unset, Python ABI tag may " 74 "be incorrect", var) 75 return fallback() 76 return val == expected 77 78 79def get_abi_tag(): 80 """Return the ABI tag based on SOABI (if available) or emulate SOABI 81 (CPython 2, PyPy).""" 82 soabi = get_config_var('SOABI') 83 impl = get_abbr_impl() 84 if not soabi and impl in {'cp', 'pp'} and hasattr(sys, 'maxunicode'): 85 d = '' 86 m = '' 87 u = '' 88 if get_flag('Py_DEBUG', 89 lambda: hasattr(sys, 'gettotalrefcount'), 90 warn=(impl == 'cp')): 91 d = 'd' 92 if get_flag('WITH_PYMALLOC', 93 lambda: impl == 'cp', 94 warn=(impl == 'cp')): 95 m = 'm' 96 if get_flag('Py_UNICODE_SIZE', 97 lambda: sys.maxunicode == 0x10ffff, 98 expected=4, 99 warn=(impl == 'cp' and 100 sys.version_info < (3, 3))) \ 101 and sys.version_info < (3, 3): 102 u = 'u' 103 abi = '%s%s%s%s%s' % (impl, get_impl_ver(), d, m, u) 104 elif soabi and soabi.startswith('cpython-'): 105 abi = 'cp' + soabi.split('-')[1] 106 elif soabi: 107 abi = soabi.replace('.', '_').replace('-', '_') 108 else: 109 abi = None 110 return abi 111 112 113def _is_running_32bit(): 114 return sys.maxsize == 2147483647 115 116 117def get_platform(): 118 """Return our platform name 'win32', 'linux_x86_64'""" 119 if sys.platform == 'darwin': 120 # distutils.util.get_platform() returns the release based on the value 121 # of MACOSX_DEPLOYMENT_TARGET on which Python was built, which may 122 # be significantly older than the user's current machine. 123 release, _, machine = platform.mac_ver() 124 split_ver = release.split('.') 125 126 if machine == "x86_64" and _is_running_32bit(): 127 machine = "i386" 128 elif machine == "ppc64" and _is_running_32bit(): 129 machine = "ppc" 130 131 return 'macosx_{}_{}_{}'.format(split_ver[0], split_ver[1], machine) 132 133 # XXX remove distutils dependency 134 result = distutils.util.get_platform().replace('.', '_').replace('-', '_') 135 if result == "linux_x86_64" and _is_running_32bit(): 136 # 32 bit Python program (running on a 64 bit Linux): pip should only 137 # install and run 32 bit compiled extensions in that case. 138 result = "linux_i686" 139 140 return result 141 142 143def is_manylinux1_compatible(): 144 # Only Linux, and only x86-64 / i686 145 if get_platform() not in {"linux_x86_64", "linux_i686"}: 146 return False 147 148 # Check for presence of _manylinux module 149 try: 150 import _manylinux 151 return bool(_manylinux.manylinux1_compatible) 152 except (ImportError, AttributeError): 153 # Fall through to heuristic check below 154 pass 155 156 # Check glibc version. CentOS 5 uses glibc 2.5. 157 return glibc.have_compatible_glibc(2, 5) 158 159 160def get_darwin_arches(major, minor, machine): 161 """Return a list of supported arches (including group arches) for 162 the given major, minor and machine architecture of an macOS machine. 163 """ 164 arches = [] 165 166 def _supports_arch(major, minor, arch): 167 # Looking at the application support for macOS versions in the chart 168 # provided by https://en.wikipedia.org/wiki/OS_X#Versions it appears 169 # our timeline looks roughly like: 170 # 171 # 10.0 - Introduces ppc support. 172 # 10.4 - Introduces ppc64, i386, and x86_64 support, however the ppc64 173 # and x86_64 support is CLI only, and cannot be used for GUI 174 # applications. 175 # 10.5 - Extends ppc64 and x86_64 support to cover GUI applications. 176 # 10.6 - Drops support for ppc64 177 # 10.7 - Drops support for ppc 178 # 179 # Given that we do not know if we're installing a CLI or a GUI 180 # application, we must be conservative and assume it might be a GUI 181 # application and behave as if ppc64 and x86_64 support did not occur 182 # until 10.5. 183 # 184 # Note: The above information is taken from the "Application support" 185 # column in the chart not the "Processor support" since I believe 186 # that we care about what instruction sets an application can use 187 # not which processors the OS supports. 188 if arch == 'ppc': 189 return (major, minor) <= (10, 5) 190 if arch == 'ppc64': 191 return (major, minor) == (10, 5) 192 if arch == 'i386': 193 return (major, minor) >= (10, 4) 194 if arch == 'x86_64': 195 return (major, minor) >= (10, 5) 196 if arch in groups: 197 for garch in groups[arch]: 198 if _supports_arch(major, minor, garch): 199 return True 200 return False 201 202 groups = OrderedDict([ 203 ("fat", ("i386", "ppc")), 204 ("intel", ("x86_64", "i386")), 205 ("fat64", ("x86_64", "ppc64")), 206 ("fat32", ("x86_64", "i386", "ppc")), 207 ]) 208 209 if _supports_arch(major, minor, machine): 210 arches.append(machine) 211 212 for garch in groups: 213 if machine in groups[garch] and _supports_arch(major, minor, garch): 214 arches.append(garch) 215 216 arches.append('universal') 217 218 return arches 219 220 221def get_supported(versions=None, noarch=False, platform=None, 222 impl=None, abi=None): 223 """Return a list of supported tags for each version specified in 224 `versions`. 225 226 :param versions: a list of string versions, of the form ["33", "32"], 227 or None. The first version will be assumed to support our ABI. 228 :param platform: specify the exact platform you want valid 229 tags for, or None. If None, use the local system platform. 230 :param impl: specify the exact implementation you want valid 231 tags for, or None. If None, use the local interpreter impl. 232 :param abi: specify the exact abi you want valid 233 tags for, or None. If None, use the local interpreter abi. 234 """ 235 supported = [] 236 237 # Versions must be given with respect to the preference 238 if versions is None: 239 versions = [] 240 version_info = get_impl_version_info() 241 major = version_info[:-1] 242 # Support all previous minor Python versions. 243 for minor in range(version_info[-1], -1, -1): 244 versions.append(''.join(map(str, major + (minor,)))) 245 246 impl = impl or get_abbr_impl() 247 248 abis = [] 249 250 abi = abi or get_abi_tag() 251 if abi: 252 abis[0:0] = [abi] 253 254 abi3s = set() 255 import imp 256 for suffix in imp.get_suffixes(): 257 if suffix[0].startswith('.abi'): 258 abi3s.add(suffix[0].split('.', 2)[1]) 259 260 abis.extend(sorted(list(abi3s))) 261 262 abis.append('none') 263 264 if not noarch: 265 arch = platform or get_platform() 266 if arch.startswith('macosx'): 267 # support macosx-10.6-intel on macosx-10.9-x86_64 268 match = _osx_arch_pat.match(arch) 269 if match: 270 name, major, minor, actual_arch = match.groups() 271 tpl = '{}_{}_%i_%s'.format(name, major) 272 arches = [] 273 for m in reversed(range(int(minor) + 1)): 274 for a in get_darwin_arches(int(major), m, actual_arch): 275 arches.append(tpl % (m, a)) 276 else: 277 # arch pattern didn't match (?!) 278 arches = [arch] 279 elif platform is None and is_manylinux1_compatible(): 280 arches = [arch.replace('linux', 'manylinux1'), arch] 281 else: 282 arches = [arch] 283 284 # Current version, current API (built specifically for our Python): 285 for abi in abis: 286 for arch in arches: 287 supported.append(('%s%s' % (impl, versions[0]), abi, arch)) 288 289 # abi3 modules compatible with older version of Python 290 for version in versions[1:]: 291 # abi3 was introduced in Python 3.2 292 if version in {'31', '30'}: 293 break 294 for abi in abi3s: # empty set if not Python 3 295 for arch in arches: 296 supported.append(("%s%s" % (impl, version), abi, arch)) 297 298 # Has binaries, does not use the Python API: 299 for arch in arches: 300 supported.append(('py%s' % (versions[0][0]), 'none', arch)) 301 302 # No abi / arch, but requires our implementation: 303 supported.append(('%s%s' % (impl, versions[0]), 'none', 'any')) 304 # Tagged specifically as being cross-version compatible 305 # (with just the major version specified) 306 supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any')) 307 308 # No abi / arch, generic Python 309 for i, version in enumerate(versions): 310 supported.append(('py%s' % (version,), 'none', 'any')) 311 if i == 0: 312 supported.append(('py%s' % (version[0]), 'none', 'any')) 313 314 return supported 315 316 317implementation_tag = get_impl_tag() 318