blob: 2a0dff6f5f980147aefa3eeb26786490333ef55d [file] [log] [blame]
# Copyright 2017 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.
from . import build_platform
from . import source
from . import util
from .builder import Builder, BuildPackageFromPyPiWheel, BuildPackageFromSource
from .build_types import Spec
class ConditionalWheel(Builder):
"""Builds a wheel selected based on different conditions.
ConditionalWheel can help user select wheel based on platform without
match_tag
Args:
name (str): The name of the conditional wheel.
version (str): The conditional wheel version. Note there is no
patch_version for ConditionalWheel, as the bundle wheel version
can be set to any value we like.
select_fn (Callable[[System, Wheel], Wheel]): A function select the wheel
to be built.
pyversions (iterable or None): The list of "python" wheel fields (see
"Wheel.pyversion_str"). If None, a default Python version will be
used.
default (bool): If true, the wheel will be built by default.
only_plat: (See Builder's "only_plat" argument.)
skip_plat: (See Builder's "skip_plat" argument.)
patch_version (str or None): If set, this string is appended to the CIPD
version tag, for example if set to 'chromium.1', the version tag
for version 1.2.3 of the wheel would be 'version:1.2.3.chromium.1'.
"""
def __init__(self,
name,
version,
select_fn,
pyversions=None,
only_plat=None,
skip_plat=None,
default=True,
patch_version=None):
self._select_fn = select_fn
version_suffix = '.' + patch_version if patch_version else None
super(ConditionalWheel, self).__init__(
Spec(
name,
version,
universal=False,
pyversions=pyversions,
default=default,
version_suffix=version_suffix),
only_plat=only_plat,
skip_plat=skip_plat)
def build_fn(self, system, wheel, output_dir):
b = self._select_fn(system, wheel)
w = b.wheel(system, wheel.plat)
return b.build_wheel(w, system, output_dir, rebuild=True)
class SourceOrPrebuilt(Builder):
def __init__(self,
name,
version,
pyversions=None,
default=True,
patches=(),
patch_base=None,
patch_version=None,
build_deps=None,
tpp_libs_cb=None,
tpp_tools=None,
src_filter=None,
skip_auditwheel=False,
**kwargs):
"""General-purpose wheel builder.
If the wheel is "packaged" (see arg for description), it is expected that it
is resident in PyPi and will be downloaded; otherwise, it will be built from
source.
Args:
name (str): The wheel name.
version (str): The wheel version.
pyversions (iterable or None): The list of "python" wheel fields (see
"Wheel.pyversion_str"). If None, a default Python version will be
used.
default (bool): If true, the wheel will be built by default.
patches (tuple): Short patch names to apply to the source tree.
patch_base (str or None): Optionally override the base names for patches.
patch_version (str or None): If set, this string is appended to the CIPD
version tag, for example if set to 'chromium.1', the version tag
for version 1.2.3 of the wheel would be 'version:1.2.3.chromium.1'.
build_deps (dockerbuild.builder.BuildDependencies|None): Dependencies
required to build the wheel.
tpp_libs_cb (Callable[[Wheel], List[builder.TppLib]]|None): Callback that
returns 3pp libraries to install in the build environment.
tpp_tools (List[builder.TppTool]|None): 3pp tools (for the build platform)
which are needed to build the wheel.
src_filter (Callable[[str], bool]): Filtering files from the source. This
is a workaround for python < 3.6 on Windows to prevent failure caused
by 260 path length limit.
skip_auditwheel (bool): If True, do not run 'auditwheel' tool to fix up
manylinux wheel library dependencies. This option may be necessary
if the generated wheel does not contain any compiled code, since
this causes a hard error from auditwheel.
packaged (iterable or None): The names of platforms that have this wheel
available via PyPi. If None, a default set of packaged wheels will be
generated based on standard PyPi expectations, encoded with each
Platform's "packaged" property.
env_cb (Callable[[Wheel], Dict[str, str]]|None): Envvars to set when
building the wheel from source. The Wheel object is received as an
argument.
kwargs: Keyword arguments forwarded to Builder.
"""
self._pypi_src = source.pypi_sdist(
name, version, patches=patches, patch_base=patch_base)
self._packaged = set(
kwargs.pop('packaged', (p.name for p in build_platform.PACKAGED)))
self._build_deps = build_deps
self._tpp_libs_cb = tpp_libs_cb
self._tpp_tools = tpp_tools
self._src_filter = src_filter
self._skip_auditwheel = skip_auditwheel
self._env_cb = kwargs.pop('env_cb', None)
version_suffix = '.' + patch_version if patch_version else None
spec = Spec(
name,
self._pypi_src.version,
universal=False,
pyversions=pyversions,
default=default,
version_suffix=version_suffix)
for p in self._packaged:
assert p in build_platform.NAMES, (
"Wheel %r has invalid platform %s in packaged" % (spec, p))
super(SourceOrPrebuilt, self).__init__(spec, **kwargs)
def build_fn(self, system, wheel, output_dir):
if wheel.plat.name in self._packaged:
return BuildPackageFromPyPiWheel(system, wheel, output_dir)
wheel_env = self._env_cb(wheel) if self._env_cb else None
tpp_libs = self._tpp_libs_cb(wheel) if self._tpp_libs_cb else None
return BuildPackageFromSource(system, wheel, self._pypi_src, output_dir,
self._src_filter, self._build_deps, tpp_libs,
self._tpp_tools, wheel_env,
self._skip_auditwheel)
class MultiWheel(Builder):
def __init__(self,
name,
version,
wheels,
pyversions=None,
only_plat=None,
skip_plat=None,
default=True,
patch_version=None):
"""Builds a wheel consisting of multiple other wheels.
Bundles can be useful when a user always wants a common set of packages.
Args:
name (str): The name of the bundle wheel.
version (str): The bundle wheel version. Note there is no patch_version
for MultiWheels, as the bundle wheel version can be set to any value
we like.
wheels (iterable): A set of embedded wheel rules to add to the bundle.
pyversions (iterable or None): The list of "python" wheel fields (see
"Wheel.pyversion_str"). If None, a default Python version will be
used.
default (bool): If true, the wheel will be built by default.
only_plat: (See Builder's "only_plat" argument.)
skip_plat: (See Builder's "skip_plat" argument.)
patch_version (str or None): If set, this string is appended to the CIPD
version tag, for example if set to 'chromium.1', the version tag
for version 1.2.3 of the wheel would be 'version:1.2.3.chromium.1'.
"""
self._wheels = wheels
version_suffix = '.' + patch_version if patch_version else None
super(MultiWheel, self).__init__(
Spec(
name,
version,
universal=False,
pyversions=pyversions,
default=default,
version_suffix=version_suffix),
only_plat=only_plat,
skip_plat=skip_plat)
def build_fn(self, system, wheel, output_dir):
sub_wheels = []
for w in self._wheels:
subwheel_plat = (
build_platform.ALL[build_platform.UNIVERSAL[0]]
if w.spec.universal else wheel.plat)
sub_wheel = w.wheel(system, subwheel_plat)
util.LOGGER.info('Building sub-wheel: %s', sub_wheel)
# Any time the MultiWheel is built, rebuild all the subwheels.
sub_wheels += w.build_wheel(sub_wheel, system, output_dir, rebuild=True)
return sub_wheels
class Prebuilt(Builder):
"""Wheel builder for prepared wheels that must be downloaded from PyPi.
Args:
name (str): The wheel name.
version (str): The wheel version.
only_plat: (See Builder's "only_plat" argument.)
pyversions (iterable or None): The list of "python" wheel fields (see
"Wheel.pyversion_str"). If None, a default Python version will be
used.
default (bool): If true, the wheel will be built by default.
kwargs: Keyword arguments forwarded to Builder.
"""
def __init__(self,
name,
version,
only_plat,
pyversions=None,
default=True,
**kwargs):
kwargs['only_plat'] = only_plat
super(Prebuilt, self).__init__(
Spec(
name,
version,
universal=False,
pyversions=pyversions,
default=default,
version_suffix=None), **kwargs)
def build_fn(self, system, wheel, output_dir):
return BuildPackageFromPyPiWheel(system, wheel, output_dir)
class Universal(Builder):
def __init__(self, name, version, pyversions=None, default=True, **kwargs):
"""Universal wheel version of SourceOrPrebuilt.
Args:
name (str): The wheel name.
version (str): The wheel version.
pyversions (iterable or None): The list of "python" wheel fields (see
"Wheel.pyversion_str"). If None, a default Python version will be
used.
kwargs: Keyword arguments forwarded to Builder.
"""
super(Universal, self).__init__(
Spec(
name,
version,
universal=True,
pyversions=pyversions,
default=default,
version_suffix=None,
), **kwargs)
def build_fn(self, system, wheel, output_dir):
return BuildPackageFromPyPiWheel(system, wheel, output_dir)
class UniversalSource(Builder):
def __init__(self,
name,
pypi_version,
pyversions=None,
pypi_name=None,
patches=(),
patch_base=None,
patch_version=None,
**kwargs):
"""Universal wheel version of SourceOrPrebuilt that always builds from
source.
Args:
name (str): The wheel name.
version (str): The wheel version.
pyversions (iterable or None): The list of "python" wheel fields (see
"Wheel.pyversion_str"). If None, a default Python version will be
used.
pypi_name (str or None): Name of the package in PyPi. This can be useful
when translating between the CIPD package name (uses underscores) and
the PyPi package name (may use hyphens).
patches (tuple): Short patch names to apply to the source tree.
patch_base (str or None): Optionally override the base name for patches.
patch_version (str or None): If set, this string is appended to the CIPD
version tag, for example if set to 'chromium.1', the version tag
for version 1.2.3 of the wheel would be 'version:1.2.3.chromium.1'.
May not be used in combination with 'patches', since 'patches'
already appends a hash of the patches to the version.
kwargs: Keyword arguments forwarded to Builder.
Returns (Builder): A configured Builder for the specified wheel.
"""
self._pypi_src = source.pypi_sdist(
name=pypi_name or name,
version=pypi_version,
patches=patches,
patch_base=patch_base)
if patches:
if patch_version:
raise ValueError('patches and patch_version may not be used together.')
# For backward compatibility.
version_suffix = '-' + self._pypi_src.patches_hash
elif patch_version:
version_suffix = '.' + patch_version
else:
version_suffix = None
super(UniversalSource, self).__init__(
Spec(
name,
self._pypi_src.version,
universal=True,
pyversions=pyversions,
default=True,
version_suffix=version_suffix,
), **kwargs)
def build_fn(self, system, wheel, output_dir):
return BuildPackageFromSource(system, wheel, self._pypi_src, output_dir)
def md_data_fn(self):
if not self._pypi_src.patches:
return []
return ['\n* custom patches: %s' % (', '.join(self._pypi_src.patches),)]
class GitUniversalSource(Builder):
def __init__(self,
name,
version,
git_repo,
git_commit,
setup_path='',
**kwargs):
"""Version of UniversalSource that always builds a Universal wheel
from a git repo source rather than pypi.org.
Args:
name (str): The wheel name.
version (str): The wheel version.
git_repo (str): The git repository to fetch.
git_commit (str): The commit or tag to fetch.
setup_path (str): The repository-relative path to setup.py.
kwargs: Keyword arguments forwarded to Builder.
Returns (Builder): A configured Builder for the specified wheel.
"""
self._git_repo = git_repo
self._git_commit = git_commit
self._setup_path = setup_path
super(GitUniversalSource, self).__init__(
Spec(
name,
version,
universal=True,
pyversions=['py3'],
default=True,
version_suffix=None,
), **kwargs)
def build_fn(self, system, wheel, output_dir):
return BuildPackageFromSource(
system, wheel,
source.git_source(
self.spec.name, self.spec.version,
source.GitSource(system, self._git_repo, self._git_commit,
self._setup_path, self.spec.name)), output_dir)