Merge pull request #1091 from mayeut/abi3
feature: add support for ABI3 wheels
This commit is contained in:
+79
-63
@@ -12,6 +12,7 @@ from .typing import OrderedDict, PathOrStr, assert_never
|
|||||||
from .util import (
|
from .util import (
|
||||||
BuildSelector,
|
BuildSelector,
|
||||||
NonPlatformWheelError,
|
NonPlatformWheelError,
|
||||||
|
find_compatible_abi3_wheel,
|
||||||
get_build_verbosity_extra_flags,
|
get_build_verbosity_extra_flags,
|
||||||
prepare_command,
|
prepare_command,
|
||||||
read_python_configs,
|
read_python_configs,
|
||||||
@@ -132,6 +133,8 @@ def build_on_docker(
|
|||||||
)
|
)
|
||||||
docker.call(["sh", "-c", before_all_prepared], env=env)
|
docker.call(["sh", "-c", before_all_prepared], env=env)
|
||||||
|
|
||||||
|
built_wheels: List[PurePath] = []
|
||||||
|
|
||||||
for config in platform_configs:
|
for config in platform_configs:
|
||||||
log.build_start(config.identifier)
|
log.build_start(config.identifier)
|
||||||
build_options = options.build_options(config.identifier)
|
build_options = options.build_options(config.identifier)
|
||||||
@@ -174,74 +177,83 @@ def build_on_docker(
|
|||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if build_options.before_build:
|
abi3_wheel = find_compatible_abi3_wheel(built_wheels, config.identifier)
|
||||||
log.step("Running before_build...")
|
if abi3_wheel:
|
||||||
before_build_prepared = prepare_command(
|
log.step_end()
|
||||||
build_options.before_build,
|
print(
|
||||||
project=container_project_path,
|
f"\nFound previously built wheel {abi3_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
|
||||||
package=container_package_dir,
|
|
||||||
)
|
|
||||||
docker.call(["sh", "-c", before_build_prepared], env=env)
|
|
||||||
|
|
||||||
log.step("Building wheel...")
|
|
||||||
|
|
||||||
temp_dir = PurePath("/tmp/cibuildwheel")
|
|
||||||
built_wheel_dir = temp_dir / "built_wheel"
|
|
||||||
docker.call(["rm", "-rf", built_wheel_dir])
|
|
||||||
docker.call(["mkdir", "-p", built_wheel_dir])
|
|
||||||
|
|
||||||
verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
|
|
||||||
|
|
||||||
if build_options.build_frontend == "pip":
|
|
||||||
docker.call(
|
|
||||||
[
|
|
||||||
"python",
|
|
||||||
"-m",
|
|
||||||
"pip",
|
|
||||||
"wheel",
|
|
||||||
container_package_dir,
|
|
||||||
f"--wheel-dir={built_wheel_dir}",
|
|
||||||
"--no-deps",
|
|
||||||
*verbosity_flags,
|
|
||||||
],
|
|
||||||
env=env,
|
|
||||||
)
|
|
||||||
elif build_options.build_frontend == "build":
|
|
||||||
config_setting = " ".join(verbosity_flags)
|
|
||||||
docker.call(
|
|
||||||
[
|
|
||||||
"python",
|
|
||||||
"-m",
|
|
||||||
"build",
|
|
||||||
container_package_dir,
|
|
||||||
"--wheel",
|
|
||||||
f"--outdir={built_wheel_dir}",
|
|
||||||
f"--config-setting={config_setting}",
|
|
||||||
],
|
|
||||||
env=env,
|
|
||||||
)
|
)
|
||||||
|
repaired_wheels = [abi3_wheel]
|
||||||
else:
|
else:
|
||||||
assert_never(build_options.build_frontend)
|
|
||||||
|
|
||||||
built_wheel = docker.glob(built_wheel_dir, "*.whl")[0]
|
if build_options.before_build:
|
||||||
|
log.step("Running before_build...")
|
||||||
|
before_build_prepared = prepare_command(
|
||||||
|
build_options.before_build,
|
||||||
|
project=container_project_path,
|
||||||
|
package=container_package_dir,
|
||||||
|
)
|
||||||
|
docker.call(["sh", "-c", before_build_prepared], env=env)
|
||||||
|
|
||||||
repaired_wheel_dir = temp_dir / "repaired_wheel"
|
log.step("Building wheel...")
|
||||||
docker.call(["rm", "-rf", repaired_wheel_dir])
|
|
||||||
docker.call(["mkdir", "-p", repaired_wheel_dir])
|
|
||||||
|
|
||||||
if built_wheel.name.endswith("none-any.whl"):
|
temp_dir = PurePath("/tmp/cibuildwheel")
|
||||||
raise NonPlatformWheelError()
|
built_wheel_dir = temp_dir / "built_wheel"
|
||||||
|
docker.call(["rm", "-rf", built_wheel_dir])
|
||||||
|
docker.call(["mkdir", "-p", built_wheel_dir])
|
||||||
|
|
||||||
if build_options.repair_command:
|
verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
|
||||||
log.step("Repairing wheel...")
|
|
||||||
repair_command_prepared = prepare_command(
|
|
||||||
build_options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
|
|
||||||
)
|
|
||||||
docker.call(["sh", "-c", repair_command_prepared], env=env)
|
|
||||||
else:
|
|
||||||
docker.call(["mv", built_wheel, repaired_wheel_dir])
|
|
||||||
|
|
||||||
repaired_wheels = docker.glob(repaired_wheel_dir, "*.whl")
|
if build_options.build_frontend == "pip":
|
||||||
|
docker.call(
|
||||||
|
[
|
||||||
|
"python",
|
||||||
|
"-m",
|
||||||
|
"pip",
|
||||||
|
"wheel",
|
||||||
|
container_package_dir,
|
||||||
|
f"--wheel-dir={built_wheel_dir}",
|
||||||
|
"--no-deps",
|
||||||
|
*verbosity_flags,
|
||||||
|
],
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
elif build_options.build_frontend == "build":
|
||||||
|
config_setting = " ".join(verbosity_flags)
|
||||||
|
docker.call(
|
||||||
|
[
|
||||||
|
"python",
|
||||||
|
"-m",
|
||||||
|
"build",
|
||||||
|
container_package_dir,
|
||||||
|
"--wheel",
|
||||||
|
f"--outdir={built_wheel_dir}",
|
||||||
|
f"--config-setting={config_setting}",
|
||||||
|
],
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
assert_never(build_options.build_frontend)
|
||||||
|
|
||||||
|
built_wheel = docker.glob(built_wheel_dir, "*.whl")[0]
|
||||||
|
|
||||||
|
repaired_wheel_dir = temp_dir / "repaired_wheel"
|
||||||
|
docker.call(["rm", "-rf", repaired_wheel_dir])
|
||||||
|
docker.call(["mkdir", "-p", repaired_wheel_dir])
|
||||||
|
|
||||||
|
if built_wheel.name.endswith("none-any.whl"):
|
||||||
|
raise NonPlatformWheelError()
|
||||||
|
|
||||||
|
if build_options.repair_command:
|
||||||
|
log.step("Repairing wheel...")
|
||||||
|
repair_command_prepared = prepare_command(
|
||||||
|
build_options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
|
||||||
|
)
|
||||||
|
docker.call(["sh", "-c", repair_command_prepared], env=env)
|
||||||
|
else:
|
||||||
|
docker.call(["mv", built_wheel, repaired_wheel_dir])
|
||||||
|
|
||||||
|
repaired_wheels = docker.glob(repaired_wheel_dir, "*.whl")
|
||||||
|
|
||||||
if build_options.test_command and build_options.test_selector(config.identifier):
|
if build_options.test_command and build_options.test_selector(config.identifier):
|
||||||
log.step("Testing wheel...")
|
log.step("Testing wheel...")
|
||||||
@@ -292,8 +304,12 @@ def build_on_docker(
|
|||||||
docker.call(["rm", "-rf", venv_dir])
|
docker.call(["rm", "-rf", venv_dir])
|
||||||
|
|
||||||
# move repaired wheels to output
|
# move repaired wheels to output
|
||||||
docker.call(["mkdir", "-p", container_output_dir])
|
if abi3_wheel is None:
|
||||||
docker.call(["mv", *repaired_wheels, container_output_dir])
|
docker.call(["mkdir", "-p", container_output_dir])
|
||||||
|
docker.call(["mv", *repaired_wheels, container_output_dir])
|
||||||
|
built_wheels.extend(
|
||||||
|
container_output_dir / repaired_wheel.name for repaired_wheel in repaired_wheels
|
||||||
|
)
|
||||||
|
|
||||||
log.build_end()
|
log.build_end()
|
||||||
|
|
||||||
|
|||||||
+85
-70
@@ -23,6 +23,7 @@ from .util import (
|
|||||||
call,
|
call,
|
||||||
detect_ci_provider,
|
detect_ci_provider,
|
||||||
download,
|
download,
|
||||||
|
find_compatible_abi3_wheel,
|
||||||
get_build_verbosity_extra_flags,
|
get_build_verbosity_extra_flags,
|
||||||
get_pip_version,
|
get_pip_version,
|
||||||
install_certifi_script,
|
install_certifi_script,
|
||||||
@@ -291,6 +292,8 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
)
|
)
|
||||||
shell(before_all_prepared, env=env)
|
shell(before_all_prepared, env=env)
|
||||||
|
|
||||||
|
built_wheels: List[Path] = []
|
||||||
|
|
||||||
for config in python_configurations:
|
for config in python_configurations:
|
||||||
build_options = options.build_options(config.identifier)
|
build_options = options.build_options(config.identifier)
|
||||||
log.build_start(config.identifier)
|
log.build_start(config.identifier)
|
||||||
@@ -318,84 +321,94 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
build_options.build_frontend,
|
build_options.build_frontend,
|
||||||
)
|
)
|
||||||
|
|
||||||
if build_options.before_build:
|
abi3_wheel = find_compatible_abi3_wheel(built_wheels, config.identifier)
|
||||||
log.step("Running before_build...")
|
if abi3_wheel:
|
||||||
before_build_prepared = prepare_command(
|
log.step_end()
|
||||||
build_options.before_build, project=".", package=build_options.package_dir
|
print(
|
||||||
|
f"\nFound previously built wheel {abi3_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
|
||||||
)
|
)
|
||||||
shell(before_build_prepared, env=env)
|
repaired_wheel = abi3_wheel
|
||||||
|
else:
|
||||||
log.step("Building wheel...")
|
if build_options.before_build:
|
||||||
built_wheel_dir.mkdir()
|
log.step("Running before_build...")
|
||||||
|
before_build_prepared = prepare_command(
|
||||||
verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
|
build_options.before_build, project=".", package=build_options.package_dir
|
||||||
|
|
||||||
if build_options.build_frontend == "pip":
|
|
||||||
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
|
|
||||||
# see https://github.com/pypa/cibuildwheel/pull/369
|
|
||||||
call(
|
|
||||||
"python",
|
|
||||||
"-m",
|
|
||||||
"pip",
|
|
||||||
"wheel",
|
|
||||||
build_options.package_dir.resolve(),
|
|
||||||
f"--wheel-dir={built_wheel_dir}",
|
|
||||||
"--no-deps",
|
|
||||||
*verbosity_flags,
|
|
||||||
env=env,
|
|
||||||
)
|
|
||||||
elif build_options.build_frontend == "build":
|
|
||||||
config_setting = " ".join(verbosity_flags)
|
|
||||||
build_env = env.copy()
|
|
||||||
if build_options.dependency_constraints:
|
|
||||||
constraint_path = build_options.dependency_constraints.get_for_python_version(
|
|
||||||
config.version
|
|
||||||
)
|
)
|
||||||
build_env["PIP_CONSTRAINT"] = constraint_path.as_uri()
|
shell(before_build_prepared, env=env)
|
||||||
build_env["VIRTUALENV_PIP"] = get_pip_version(env)
|
|
||||||
call(
|
|
||||||
"python",
|
|
||||||
"-m",
|
|
||||||
"build",
|
|
||||||
build_options.package_dir,
|
|
||||||
"--wheel",
|
|
||||||
f"--outdir={built_wheel_dir}",
|
|
||||||
f"--config-setting={config_setting}",
|
|
||||||
env=build_env,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
assert_never(build_options.build_frontend)
|
|
||||||
|
|
||||||
built_wheel = next(built_wheel_dir.glob("*.whl"))
|
log.step("Building wheel...")
|
||||||
|
built_wheel_dir.mkdir()
|
||||||
|
|
||||||
repaired_wheel_dir.mkdir()
|
verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
|
||||||
|
|
||||||
if built_wheel.name.endswith("none-any.whl"):
|
if build_options.build_frontend == "pip":
|
||||||
raise NonPlatformWheelError()
|
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
|
||||||
|
# see https://github.com/pypa/cibuildwheel/pull/369
|
||||||
if build_options.repair_command:
|
call(
|
||||||
log.step("Repairing wheel...")
|
"python",
|
||||||
|
"-m",
|
||||||
if config_is_universal2:
|
"pip",
|
||||||
delocate_archs = "x86_64,arm64"
|
"wheel",
|
||||||
elif config_is_arm64:
|
build_options.package_dir.resolve(),
|
||||||
delocate_archs = "arm64"
|
f"--wheel-dir={built_wheel_dir}",
|
||||||
|
"--no-deps",
|
||||||
|
*verbosity_flags,
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
elif build_options.build_frontend == "build":
|
||||||
|
config_setting = " ".join(verbosity_flags)
|
||||||
|
build_env = env.copy()
|
||||||
|
if build_options.dependency_constraints:
|
||||||
|
constraint_path = (
|
||||||
|
build_options.dependency_constraints.get_for_python_version(
|
||||||
|
config.version
|
||||||
|
)
|
||||||
|
)
|
||||||
|
build_env["PIP_CONSTRAINT"] = constraint_path.as_uri()
|
||||||
|
build_env["VIRTUALENV_PIP"] = get_pip_version(env)
|
||||||
|
call(
|
||||||
|
"python",
|
||||||
|
"-m",
|
||||||
|
"build",
|
||||||
|
build_options.package_dir,
|
||||||
|
"--wheel",
|
||||||
|
f"--outdir={built_wheel_dir}",
|
||||||
|
f"--config-setting={config_setting}",
|
||||||
|
env=build_env,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
delocate_archs = "x86_64"
|
assert_never(build_options.build_frontend)
|
||||||
|
|
||||||
repair_command_prepared = prepare_command(
|
built_wheel = next(built_wheel_dir.glob("*.whl"))
|
||||||
build_options.repair_command,
|
|
||||||
wheel=built_wheel,
|
|
||||||
dest_dir=repaired_wheel_dir,
|
|
||||||
delocate_archs=delocate_archs,
|
|
||||||
)
|
|
||||||
shell(repair_command_prepared, env=env)
|
|
||||||
else:
|
|
||||||
shutil.move(str(built_wheel), repaired_wheel_dir)
|
|
||||||
|
|
||||||
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
|
repaired_wheel_dir.mkdir()
|
||||||
|
|
||||||
log.step_end()
|
if built_wheel.name.endswith("none-any.whl"):
|
||||||
|
raise NonPlatformWheelError()
|
||||||
|
|
||||||
|
if build_options.repair_command:
|
||||||
|
log.step("Repairing wheel...")
|
||||||
|
|
||||||
|
if config_is_universal2:
|
||||||
|
delocate_archs = "x86_64,arm64"
|
||||||
|
elif config_is_arm64:
|
||||||
|
delocate_archs = "arm64"
|
||||||
|
else:
|
||||||
|
delocate_archs = "x86_64"
|
||||||
|
|
||||||
|
repair_command_prepared = prepare_command(
|
||||||
|
build_options.repair_command,
|
||||||
|
wheel=built_wheel,
|
||||||
|
dest_dir=repaired_wheel_dir,
|
||||||
|
delocate_archs=delocate_archs,
|
||||||
|
)
|
||||||
|
shell(repair_command_prepared, env=env)
|
||||||
|
else:
|
||||||
|
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||||
|
|
||||||
|
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
|
||||||
|
|
||||||
|
log.step_end()
|
||||||
|
|
||||||
if build_options.test_command and build_options.test_selector(config.identifier):
|
if build_options.test_command and build_options.test_selector(config.identifier):
|
||||||
machine_arch = platform.machine()
|
machine_arch = platform.machine()
|
||||||
@@ -521,7 +534,9 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# we're all done here; move it to output (overwrite existing)
|
# we're all done here; move it to output (overwrite existing)
|
||||||
shutil.move(str(repaired_wheel), build_options.output_dir)
|
if abi3_wheel is None:
|
||||||
|
shutil.move(str(repaired_wheel), build_options.output_dir)
|
||||||
|
built_wheels.append(build_options.output_dir / repaired_wheel.name)
|
||||||
|
|
||||||
# clean up
|
# clean up
|
||||||
shutil.rmtree(identifier_tmp_dir)
|
shutil.rmtree(identifier_tmp_dir)
|
||||||
|
|||||||
+40
-1
@@ -13,7 +13,7 @@ import time
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from pathlib import Path
|
from pathlib import Path, PurePath
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
@@ -26,6 +26,7 @@ from typing import (
|
|||||||
Optional,
|
Optional,
|
||||||
Sequence,
|
Sequence,
|
||||||
TextIO,
|
TextIO,
|
||||||
|
TypeVar,
|
||||||
Union,
|
Union,
|
||||||
cast,
|
cast,
|
||||||
overload,
|
overload,
|
||||||
@@ -42,6 +43,7 @@ else:
|
|||||||
from filelock import FileLock
|
from filelock import FileLock
|
||||||
from packaging.requirements import InvalidRequirement, Requirement
|
from packaging.requirements import InvalidRequirement, Requirement
|
||||||
from packaging.specifiers import SpecifierSet
|
from packaging.specifiers import SpecifierSet
|
||||||
|
from packaging.utils import parse_wheel_filename
|
||||||
from packaging.version import Version
|
from packaging.version import Version
|
||||||
from platformdirs import user_cache_path
|
from platformdirs import user_cache_path
|
||||||
|
|
||||||
@@ -52,6 +54,7 @@ __all__ = [
|
|||||||
"MANYLINUX_ARCHS",
|
"MANYLINUX_ARCHS",
|
||||||
"call",
|
"call",
|
||||||
"shell",
|
"shell",
|
||||||
|
"find_compatible_abi3_wheel",
|
||||||
"format_safe",
|
"format_safe",
|
||||||
"prepare_command",
|
"prepare_command",
|
||||||
"get_build_verbosity_extra_flags",
|
"get_build_verbosity_extra_flags",
|
||||||
@@ -568,6 +571,42 @@ def virtualenv(
|
|||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar("T", bound=PurePath)
|
||||||
|
|
||||||
|
|
||||||
|
def find_compatible_abi3_wheel(wheels: Sequence[T], identifier: str) -> Optional[T]:
|
||||||
|
"""
|
||||||
|
Finds an ABI3 wheel in `wheels` compatible with the Python interpreter
|
||||||
|
specified by `identifier`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
interpreter, platform = identifier.split("-")
|
||||||
|
if not interpreter.startswith("cp3"):
|
||||||
|
return None
|
||||||
|
for wheel in wheels:
|
||||||
|
_, _, _, tags = parse_wheel_filename(wheel.name)
|
||||||
|
for tag in tags:
|
||||||
|
if tag.abi != "abi3":
|
||||||
|
continue
|
||||||
|
if not tag.interpreter.startswith("cp3"):
|
||||||
|
continue
|
||||||
|
if int(tag.interpreter[3:]) > int(interpreter[3:]):
|
||||||
|
continue
|
||||||
|
if platform.startswith(("manylinux", "musllinux", "macosx")):
|
||||||
|
# Linux, macOS
|
||||||
|
os_, arch = platform.split("_", 1)
|
||||||
|
if not tag.platform.startswith(os_):
|
||||||
|
continue
|
||||||
|
if not tag.platform.endswith("_" + arch):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# Windows
|
||||||
|
if not tag.platform == platform:
|
||||||
|
continue
|
||||||
|
return wheel
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info >= (3, 8):
|
if sys.version_info >= (3, 8):
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
else:
|
else:
|
||||||
|
|||||||
+81
-66
@@ -22,6 +22,7 @@ from .util import (
|
|||||||
NonPlatformWheelError,
|
NonPlatformWheelError,
|
||||||
call,
|
call,
|
||||||
download,
|
download,
|
||||||
|
find_compatible_abi3_wheel,
|
||||||
get_build_verbosity_extra_flags,
|
get_build_verbosity_extra_flags,
|
||||||
get_pip_version,
|
get_pip_version,
|
||||||
prepare_command,
|
prepare_command,
|
||||||
@@ -249,6 +250,8 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
)
|
)
|
||||||
shell(before_all_prepared, env=env)
|
shell(before_all_prepared, env=env)
|
||||||
|
|
||||||
|
built_wheels: List[Path] = []
|
||||||
|
|
||||||
for config in python_configurations:
|
for config in python_configurations:
|
||||||
build_options = options.build_options(config.identifier)
|
build_options = options.build_options(config.identifier)
|
||||||
log.build_start(config.identifier)
|
log.build_start(config.identifier)
|
||||||
@@ -274,83 +277,93 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
build_options.build_frontend,
|
build_options.build_frontend,
|
||||||
)
|
)
|
||||||
|
|
||||||
# run the before_build command
|
abi3_wheel = find_compatible_abi3_wheel(built_wheels, config.identifier)
|
||||||
if build_options.before_build:
|
if abi3_wheel:
|
||||||
log.step("Running before_build...")
|
log.step_end()
|
||||||
before_build_prepared = prepare_command(
|
print(
|
||||||
build_options.before_build, project=".", package=options.globals.package_dir
|
f"\nFound previously built wheel {abi3_wheel.name}, that's compatible with {config.identifier}. Skipping build step..."
|
||||||
)
|
)
|
||||||
shell(before_build_prepared, env=env)
|
repaired_wheel = abi3_wheel
|
||||||
|
else:
|
||||||
log.step("Building wheel...")
|
# run the before_build command
|
||||||
built_wheel_dir.mkdir()
|
if build_options.before_build:
|
||||||
|
log.step("Running before_build...")
|
||||||
verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
|
before_build_prepared = prepare_command(
|
||||||
|
build_options.before_build, project=".", package=options.globals.package_dir
|
||||||
if build_options.build_frontend == "pip":
|
|
||||||
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
|
|
||||||
# see https://github.com/pypa/cibuildwheel/pull/369
|
|
||||||
call(
|
|
||||||
"python",
|
|
||||||
"-m",
|
|
||||||
"pip",
|
|
||||||
"wheel",
|
|
||||||
options.globals.package_dir.resolve(),
|
|
||||||
f"--wheel-dir={built_wheel_dir}",
|
|
||||||
"--no-deps",
|
|
||||||
*get_build_verbosity_extra_flags(build_options.build_verbosity),
|
|
||||||
env=env,
|
|
||||||
)
|
|
||||||
elif build_options.build_frontend == "build":
|
|
||||||
config_setting = " ".join(verbosity_flags)
|
|
||||||
build_env = env.copy()
|
|
||||||
if build_options.dependency_constraints:
|
|
||||||
constraints_path = build_options.dependency_constraints.get_for_python_version(
|
|
||||||
config.version
|
|
||||||
)
|
)
|
||||||
# Bug in pip <= 21.1.3 - we can't have a space in the
|
shell(before_build_prepared, env=env)
|
||||||
# constraints file, and pip doesn't support drive letters
|
|
||||||
# in uhi. After probably pip 21.2, we can use uri. For
|
|
||||||
# now, use a temporary file.
|
|
||||||
if " " in str(constraints_path):
|
|
||||||
assert " " not in str(identifier_tmp_dir)
|
|
||||||
tmp_file = identifier_tmp_dir / "constraints.txt"
|
|
||||||
tmp_file.write_bytes(constraints_path.read_bytes())
|
|
||||||
constraints_path = tmp_file
|
|
||||||
|
|
||||||
build_env["PIP_CONSTRAINT"] = str(constraints_path)
|
log.step("Building wheel...")
|
||||||
build_env["VIRTUALENV_PIP"] = get_pip_version(env)
|
built_wheel_dir.mkdir()
|
||||||
|
|
||||||
|
verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
|
||||||
|
|
||||||
|
if build_options.build_frontend == "pip":
|
||||||
|
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
|
||||||
|
# see https://github.com/pypa/cibuildwheel/pull/369
|
||||||
call(
|
call(
|
||||||
"python",
|
"python",
|
||||||
"-m",
|
"-m",
|
||||||
"build",
|
"pip",
|
||||||
build_options.package_dir,
|
"wheel",
|
||||||
"--wheel",
|
options.globals.package_dir.resolve(),
|
||||||
f"--outdir={built_wheel_dir}",
|
f"--wheel-dir={built_wheel_dir}",
|
||||||
f"--config-setting={config_setting}",
|
"--no-deps",
|
||||||
env=build_env,
|
*get_build_verbosity_extra_flags(build_options.build_verbosity),
|
||||||
|
env=env,
|
||||||
)
|
)
|
||||||
else:
|
elif build_options.build_frontend == "build":
|
||||||
assert_never(build_options.build_frontend)
|
config_setting = " ".join(verbosity_flags)
|
||||||
|
build_env = env.copy()
|
||||||
|
if build_options.dependency_constraints:
|
||||||
|
constraints_path = (
|
||||||
|
build_options.dependency_constraints.get_for_python_version(
|
||||||
|
config.version
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# Bug in pip <= 21.1.3 - we can't have a space in the
|
||||||
|
# constraints file, and pip doesn't support drive letters
|
||||||
|
# in uhi. After probably pip 21.2, we can use uri. For
|
||||||
|
# now, use a temporary file.
|
||||||
|
if " " in str(constraints_path):
|
||||||
|
assert " " not in str(identifier_tmp_dir)
|
||||||
|
tmp_file = identifier_tmp_dir / "constraints.txt"
|
||||||
|
tmp_file.write_bytes(constraints_path.read_bytes())
|
||||||
|
constraints_path = tmp_file
|
||||||
|
|
||||||
built_wheel = next(built_wheel_dir.glob("*.whl"))
|
build_env["PIP_CONSTRAINT"] = str(constraints_path)
|
||||||
|
build_env["VIRTUALENV_PIP"] = get_pip_version(env)
|
||||||
|
call(
|
||||||
|
"python",
|
||||||
|
"-m",
|
||||||
|
"build",
|
||||||
|
build_options.package_dir,
|
||||||
|
"--wheel",
|
||||||
|
f"--outdir={built_wheel_dir}",
|
||||||
|
f"--config-setting={config_setting}",
|
||||||
|
env=build_env,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
assert_never(build_options.build_frontend)
|
||||||
|
|
||||||
# repair the wheel
|
built_wheel = next(built_wheel_dir.glob("*.whl"))
|
||||||
repaired_wheel_dir.mkdir()
|
|
||||||
|
|
||||||
if built_wheel.name.endswith("none-any.whl"):
|
# repair the wheel
|
||||||
raise NonPlatformWheelError()
|
repaired_wheel_dir.mkdir()
|
||||||
|
|
||||||
if build_options.repair_command:
|
if built_wheel.name.endswith("none-any.whl"):
|
||||||
log.step("Repairing wheel...")
|
raise NonPlatformWheelError()
|
||||||
repair_command_prepared = prepare_command(
|
|
||||||
build_options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
|
|
||||||
)
|
|
||||||
shell(repair_command_prepared, env=env)
|
|
||||||
else:
|
|
||||||
shutil.move(str(built_wheel), repaired_wheel_dir)
|
|
||||||
|
|
||||||
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
|
if build_options.repair_command:
|
||||||
|
log.step("Repairing wheel...")
|
||||||
|
repair_command_prepared = prepare_command(
|
||||||
|
build_options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir
|
||||||
|
)
|
||||||
|
shell(repair_command_prepared, env=env)
|
||||||
|
else:
|
||||||
|
shutil.move(str(built_wheel), repaired_wheel_dir)
|
||||||
|
|
||||||
|
repaired_wheel = next(repaired_wheel_dir.glob("*.whl"))
|
||||||
|
|
||||||
if build_options.test_command and options.globals.test_selector(config.identifier):
|
if build_options.test_command and options.globals.test_selector(config.identifier):
|
||||||
log.step("Testing wheel...")
|
log.step("Testing wheel...")
|
||||||
@@ -405,7 +418,9 @@ def build(options: Options, tmp_path: Path) -> None:
|
|||||||
shell(test_command_prepared, cwd="c:\\", env=virtualenv_env)
|
shell(test_command_prepared, cwd="c:\\", env=virtualenv_env)
|
||||||
|
|
||||||
# we're all done here; move it to output (remove if already exists)
|
# we're all done here; move it to output (remove if already exists)
|
||||||
shutil.move(str(repaired_wheel), build_options.output_dir)
|
if abi3_wheel is None:
|
||||||
|
shutil.move(str(repaired_wheel), build_options.output_dir)
|
||||||
|
built_wheels.append(build_options.output_dir / repaired_wheel.name)
|
||||||
|
|
||||||
# clean up
|
# clean up
|
||||||
# (we ignore errors because occasionally Windows fails to unlink a file and we
|
# (we ignore errors because occasionally Windows fails to unlink a file and we
|
||||||
|
|||||||
@@ -103,6 +103,12 @@ Linux), and the other architectures are emulated automatically.
|
|||||||
{% include "../examples/github-with-qemu.yml" %}
|
{% include "../examples/github-with-qemu.yml" %}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Building CPython ABI3 wheels (Limited API) {: #abi3}
|
||||||
|
|
||||||
|
The CPython Limited API is a subset of the Python C Extension API that's declared to be forward-compatible, meaning you can compile wheels for one version of Python, and they'll be compatible with future versions. Wheels that use the Limited API are known as ABI3 wheels.
|
||||||
|
|
||||||
|
To create a package that builds ABI3 wheels, you'll need to configure your build backend to compile libraries correctly create wheels with the right tags. [Check this repo](https://github.com/joerick/python-abi3-package-sample) for an example of how to do this with setuptools.
|
||||||
|
|
||||||
### Building packages with optional C extensions
|
### Building packages with optional C extensions
|
||||||
|
|
||||||
`cibuildwheel` defines the environment variable `CIBUILDWHEEL` to the value `1` allowing projects for which the C extension is optional to make it mandatory when building wheels.
|
`cibuildwheel` defines the environment variable `CIBUILDWHEEL` to the value `1` allowing projects for which the C extension is optional to make it mandatory when building wheels.
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ install_requires =
|
|||||||
bracex
|
bracex
|
||||||
certifi
|
certifi
|
||||||
filelock
|
filelock
|
||||||
packaging
|
packaging>=20.9
|
||||||
platformdirs
|
platformdirs
|
||||||
dataclasses;python_version < '3.7'
|
dataclasses;python_version < '3.7'
|
||||||
tomli;python_version < '3.11'
|
tomli;python_version < '3.11'
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import textwrap
|
||||||
|
|
||||||
|
from . import test_projects, utils
|
||||||
|
|
||||||
|
limited_api_project = test_projects.new_c_project(
|
||||||
|
setup_py_add=textwrap.dedent(
|
||||||
|
r"""
|
||||||
|
cmdclass = {}
|
||||||
|
extension_kwargs = {}
|
||||||
|
if sys.version_info[:2] >= (3, 8):
|
||||||
|
from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
|
||||||
|
|
||||||
|
class bdist_wheel_abi3(_bdist_wheel):
|
||||||
|
def finalize_options(self):
|
||||||
|
_bdist_wheel.finalize_options(self)
|
||||||
|
self.root_is_pure = False
|
||||||
|
|
||||||
|
def get_tag(self):
|
||||||
|
python, abi, plat = _bdist_wheel.get_tag(self)
|
||||||
|
return python, "abi3", plat
|
||||||
|
|
||||||
|
cmdclass["bdist_wheel"] = bdist_wheel_abi3
|
||||||
|
extension_kwargs["define_macros"] = [("Py_LIMITED_API", "0x03080000")]
|
||||||
|
extension_kwargs["py_limited_api"] = True
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
setup_py_extension_args_add="**extension_kwargs",
|
||||||
|
setup_py_setup_args_add="cmdclass=cmdclass",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test(tmp_path):
|
||||||
|
project_dir = tmp_path / "project"
|
||||||
|
limited_api_project.generate(project_dir)
|
||||||
|
|
||||||
|
# build the wheels
|
||||||
|
actual_wheels = utils.cibuildwheel_run(
|
||||||
|
project_dir,
|
||||||
|
add_env={
|
||||||
|
"CIBW_SKIP": "pp* ", # PyPy does not have a Py_LIMITED_API equivalent
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# check that the expected wheels are produced
|
||||||
|
expected_wheels = [
|
||||||
|
w.replace("cp38-cp38", "cp38-abi3")
|
||||||
|
for w in utils.expected_wheels("spam", "0.1.0")
|
||||||
|
if "-pp" not in w and "-cp39" not in w and "-cp310" not in w
|
||||||
|
]
|
||||||
|
assert set(actual_wheels) == set(expected_wheels)
|
||||||
@@ -54,6 +54,7 @@ setup(
|
|||||||
'spam',
|
'spam',
|
||||||
sources=['spam.c'],
|
sources=['spam.c'],
|
||||||
libraries=libraries,
|
libraries=libraries,
|
||||||
|
{{ setup_py_extension_args_add | indent(8) }}
|
||||||
)],
|
)],
|
||||||
{{ setup_py_setup_args_add | indent(4) }}
|
{{ setup_py_setup_args_add | indent(4) }}
|
||||||
)
|
)
|
||||||
@@ -73,6 +74,7 @@ def new_c_project(
|
|||||||
spam_c_top_level_add="",
|
spam_c_top_level_add="",
|
||||||
spam_c_function_add="",
|
spam_c_function_add="",
|
||||||
setup_py_add="",
|
setup_py_add="",
|
||||||
|
setup_py_extension_args_add="",
|
||||||
setup_py_setup_args_add="",
|
setup_py_setup_args_add="",
|
||||||
setup_cfg_add="",
|
setup_cfg_add="",
|
||||||
):
|
):
|
||||||
@@ -91,6 +93,7 @@ def new_c_project(
|
|||||||
"spam_c_top_level_add": spam_c_top_level_add,
|
"spam_c_top_level_add": spam_c_top_level_add,
|
||||||
"spam_c_function_add": spam_c_function_add,
|
"spam_c_function_add": spam_c_function_add,
|
||||||
"setup_py_add": setup_py_add,
|
"setup_py_add": setup_py_add,
|
||||||
|
"setup_py_extension_args_add": setup_py_extension_args_add,
|
||||||
"setup_py_setup_args_add": setup_py_setup_args_add,
|
"setup_py_setup_args_add": setup_py_setup_args_add,
|
||||||
"setup_cfg_add": setup_cfg_add,
|
"setup_cfg_add": setup_cfg_add,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user