You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pep425tags.py 13KB

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