blob: ae93d7b690b1bdc0f79cc18249ed965c75465a7a [file] [log] [blame]
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import collections
import os
from . import cipd
# BINARY_VERSION_SUFFIX is a string added to the end of each version tag. This
# can be used to distinguish one build of a given package from another.
#
# Incrementing BINARY_VERSION only affects binary wheels; it is not applied to
# universal wheels. Changing this is a heavy operation, requiring the user to
# regenerate all wheels for all platforms so that they become available with the
# new suffix.
BINARY_VERSION_SUFFIX = None
_Spec = collections.namedtuple(
'_Spec',
(
'name',
'version',
# True if the wheel is universal, that is, pure Python.
'universal',
# An iterable containing the major Python versions the wheel is
# expected to be compatible with. Valid values are ["py2", "py3"].
'pyversions',
# default is true if this Spec should be built by default (i.e., when a
# user doesn't manually specify Specs to build).
'default',
# If set, this is appended to the CIPD version tag. It must include any
# separator.
'version_suffix'))
class Spec(_Spec):
@property
def tuple(self):
return (self.name, self.version)
@property
def tag(self):
if self.version:
ret = '%s-%s' % (self.name, self.version)
else:
ret = self.name
if self.version_suffix:
ret += self.version_suffix
if self.pyversions:
ret += '-%s' % '.'.join(sorted(self.pyversions))
return ret
@property
def is_py3_only(self):
return self.pyversions == ['py3']
def to_universal(self):
return self._replace(universal=True)
_Wheel = collections.namedtuple(
'_Wheel',
('spec', 'plat', 'download_plat', 'pyversion', 'filename', 'md_lines'))
class Wheel(_Wheel):
def __new__(cls, *args, **kwargs):
kwargs.setdefault('md_lines', [])
return super(Wheel, cls).__new__(cls, *args, **kwargs)
@property
def pyversion_str(self):
if self.spec.universal:
# We support py3-only or py2+py3. Wait for other requests
# to show up before adding more.
pyv = sorted(self.spec.pyversions or ['py2', 'py3'])
if pyv == ['py2', 'py3']:
return 'py2.py3'
elif pyv == ['py3']:
return pyv[0]
else:
raise ValueError('Unsupported versions: %r %r' % (pyv, self))
# We only generate wheels for "cpython" at the moment, and only
# for the specific wheel ABI the platform is configured for.
return 'cp%s' % (self.pyversion,)
@property
def abi(self):
if self.spec.universal or not self.plat.wheel_abi:
return 'none'
return self.plat.wheel_abi
@property
def platform(self):
return ['any'] if self.spec.universal else self.plat.wheel_plat
@property
def download_platform(self):
return ['any'] if self.spec.universal else self.download_plat.wheel_plat
@property
def primary_platform(self):
"""The platform to use when naming intermediate wheels and requesting
wheel from "pip". Generally, platforms that this doesn't work on (e.g.,
ARM) will not have wheels in PyPi, and platforms with wheels in
PyPi will have only one platform.
This is also used for naming when building wheels; this choice is
inconsequential in this context, as the wheel is renamed after the build.
"""
return self.platform[0]
@property
def build_id(self):
"""Returns a unique identifier for this build of the wheel."""
build_id = self.spec.version
if self.spec.version_suffix:
build_id += self.spec.version_suffix
return build_id
def default_filename(self):
return '%(name)s-%(version)s-%(pyversion)s-%(abi)s-%(platform)s.whl' % {
'name': self.spec.name.replace('-', '_'),
'version': self.spec.version,
'pyversion': self.pyversion_str,
'abi': self.abi,
'platform': '.'.join(self.platform),
}
def universal_filename(self):
"""This is a universal filename for the wheel, regardless of whether it's
binary or truly universal. See "A Note on Universality" at the top for
details on why we'd ever want to do this.
"""
wheel = self._replace(spec=self.spec.to_universal())
return wheel.default_filename()
def path(self, wheel_dir):
return os.path.join(wheel_dir, self.filename)
def cipd_package(self, git_revision=None, templated=False):
base_path = ['infra', 'python', 'wheels']
if self.spec.universal:
base_path += ['%s-%s' % (self.spec.name, self.pyversion_str)]
else:
base_path += [self.spec.name]
if not templated:
base_path += [
'%s_%s_%s' % (self.plat.cipd_platform, self.pyversion_str, self.abi)
]
else:
base_path += ['${vpython_platform}']
version_tag = 'version:%s' % (self.build_id,)
if not self.spec.universal and BINARY_VERSION_SUFFIX:
version_tag += BINARY_VERSION_SUFFIX
tags = [version_tag]
if git_revision is not None:
tags.append('git_revision:%s' % (git_revision,))
base = self.plat.dockcross_base
if base is not None and 'manylinux' in base:
tags.append('manylinux_version:%s' % (base,))
return cipd.Package(
name=('/'.join(p.replace('.', '_') for p in base_path)).lower(),
tags=tuple(tags),
install_mode=cipd.INSTALL_SYMLINK,
compress_level=cipd.COMPRESS_NONE,
)