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 11KB

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