Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 38 additions & 35 deletions cibuildwheel/oci_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,43 +95,46 @@ def _check_engine_version(engine: OCIContainerEngineConfig) -> None:
try:
version_string = call(engine.name, "version", "-f", "{{json .}}", capture_stdout=True)
version_info = json.loads(version_string.strip())
if engine.name == "docker":
client_api_version = FlexibleVersion(version_info["Client"]["ApiVersion"])
server_api_version = FlexibleVersion(version_info["Server"]["ApiVersion"])
# --platform support was introduced in 1.32 as experimental, 1.41 removed the experimental flag
version = min(client_api_version, server_api_version)
minimum_version = FlexibleVersion("1.41")
minimum_version_str = "20.10.0" # docker version
error_msg = textwrap.dedent(
f"""
Build failed because {engine.name} is too old.

cibuildwheel requires {engine.name}>={minimum_version_str} running API version {minimum_version}.
The API version found by cibuildwheel is {version}.
"""
)
elif engine.name == "podman":
# podman uses the same version string for "Version" & "ApiVersion"
client_version = FlexibleVersion(version_info["Client"]["Version"])
if "Server" in version_info:
server_version = FlexibleVersion(version_info["Server"]["Version"])
else:
server_version = client_version
# --platform support was introduced in v3
version = min(client_version, server_version)
minimum_version = FlexibleVersion("3")
error_msg = textwrap.dedent(
f"""
Build failed because {engine.name} is too old.

cibuildwheel requires {engine.name}>={minimum_version}.
The version found by cibuildwheel is {version}.
"""
)
else:
assert_never(engine.name)
match engine.name:
case "docker":
client_api_version = FlexibleVersion(version_info["Client"]["ApiVersion"])
server_api_version = FlexibleVersion(version_info["Server"]["ApiVersion"])
# --platform support was introduced in 1.32 as experimental, 1.41 removed the experimental flag
version = min(client_api_version, server_api_version)
minimum_version = FlexibleVersion("1.41")
minimum_version_str = "20.10.0" # docker version
error_msg = textwrap.dedent(
f"""
Build failed because {engine.name} is too old.

cibuildwheel requires {engine.name}>={minimum_version_str} running API version {minimum_version}.
The API version found by cibuildwheel is {version}.
"""
)
case "podman":
# podman uses the same version string for "Version" & "ApiVersion"
client_version = FlexibleVersion(version_info["Client"]["Version"])
if "Server" in version_info:
server_version = FlexibleVersion(version_info["Server"]["Version"])
else:
server_version = client_version
# --platform support was introduced in v3
version = min(client_version, server_version)
minimum_version = FlexibleVersion("3")
error_msg = textwrap.dedent(
f"""
Build failed because {engine.name} is too old.

cibuildwheel requires {engine.name}>={minimum_version}.
The version found by cibuildwheel is {version}.
"""
)
case _:
assert_never(engine.name)

if version < minimum_version:
raise OCIEngineTooOldError(error_msg) from None

except (subprocess.CalledProcessError, KeyError, ValueError) as e:
msg = f"Build failed because {engine.name} is too old or is not working properly."
raise OCIEngineTooOldError(msg) from e
Expand Down
62 changes: 34 additions & 28 deletions cibuildwheel/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,40 +343,46 @@ def _apply_inherit_rule(
msg = f"Don't know how to merge {before!r} and {after!r} with {rule}"
raise OptionsReaderError(msg)

if rule == InheritRule.APPEND:
return option_format.merge_values(before, after)
if rule == InheritRule.PREPEND:
return option_format.merge_values(after, before)

assert_never(rule)
match rule:
case InheritRule.APPEND:
return option_format.merge_values(before, after)
case InheritRule.PREPEND:
return option_format.merge_values(after, before)
case _:
assert_never(rule)


def _stringify_setting(
setting: SettingValue,
option_format: OptionFormat | None,
) -> str:
if isinstance(setting, Mapping):
try:
if option_format is None:
raise OptionFormat.NotSupported
return option_format.format_table(setting)
except OptionFormat.NotSupported:
msg = f"Error converting {setting!r} to a string: this setting doesn't accept a table"
raise OptionsReaderError(msg) from None

if not isinstance(setting, str) and isinstance(setting, Sequence):
try:
if option_format is None:
raise OptionFormat.NotSupported
return option_format.format_list(setting)
except OptionFormat.NotSupported:
msg = f"Error converting {setting!r} to a string: this setting doesn't accept a list"
raise OptionsReaderError(msg) from None

if isinstance(setting, bool | int):
return str(setting)

return setting
match setting:
case {}:
assert isinstance(setting, Mapping) # MyPy 1.15 doesn't narrow this for us
try:
if option_format is None:
raise OptionFormat.NotSupported
return option_format.format_table(setting)
except OptionFormat.NotSupported:
msg = (
f"Error converting {setting!r} to a string: this setting doesn't accept a table"
)
raise OptionsReaderError(msg) from None
case bool() | int():
return str(setting)
case [*_]:
try:
if option_format is None:
raise OptionFormat.NotSupported
return option_format.format_list(setting)
except OptionFormat.NotSupported:
msg = (
f"Error converting {setting!r} to a string: this setting doesn't accept a list"
)
raise OptionsReaderError(msg) from None
case _:
assert isinstance(setting, str) # MyPy 1.15 doesn't narrow this for us
return setting


class OptionsReader:
Expand Down
86 changes: 44 additions & 42 deletions cibuildwheel/platforms/ios.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,20 +387,21 @@ def setup_python(
env.setdefault("IPHONEOS_DEPLOYMENT_TARGET", "13.0")

log.step("Installing build tools...")
if build_frontend == "pip":
# No additional build tools required
pass
elif build_frontend == "build":
call(
"pip",
"install",
"--upgrade",
"build[virtualenv]",
*constraint_flags(dependency_constraint),
env=env,
)
else:
assert_never(build_frontend)
match build_frontend:
case "pip":
# No additional build tools required
pass
case "build":
call(
"pip",
"install",
"--upgrade",
"build[virtualenv]",
*constraint_flags(dependency_constraint),
env=env,
)
case _:
assert_never(build_frontend)

return target_install_path, env

Expand Down Expand Up @@ -494,34 +495,35 @@ def build(options: Options, tmp_path: Path) -> None:
if constraints_path:
combine_constraints(build_env, constraints_path, None)

if build_frontend.name == "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",
*extra_flags,
env=build_env,
)
elif build_frontend.name == "build":
call(
"python",
"-m",
"build",
build_options.package_dir,
"--wheel",
f"--outdir={built_wheel_dir}",
*extra_flags,
env=build_env,
)
else:
assert_never(build_frontend)
match build_frontend.name:
case "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",
*extra_flags,
env=build_env,
)
case "build":
call(
"python",
"-m",
"build",
build_options.package_dir,
"--wheel",
f"--outdir={built_wheel_dir}",
*extra_flags,
env=build_env,
)
case _:
assert_never(build_frontend)

test_wheel = built_wheel = next(built_wheel_dir.glob("*.whl"))

Expand Down
63 changes: 32 additions & 31 deletions cibuildwheel/platforms/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,37 +275,38 @@ def build_in_container(
build_frontend, build_options.build_verbosity, build_options.config_settings
)

if build_frontend.name == "pip":
container.call(
[
"python",
"-m",
"pip",
"wheel",
container_package_dir,
f"--wheel-dir={built_wheel_dir}",
"--no-deps",
*extra_flags,
],
env=env,
)
elif build_frontend.name == "build" or build_frontend.name == "build[uv]":
if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags:
extra_flags += ["--installer=uv"]
container.call(
[
"python",
"-m",
"build",
container_package_dir,
"--wheel",
f"--outdir={built_wheel_dir}",
*extra_flags,
],
env=env,
)
else:
assert_never(build_frontend)
match build_frontend.name:
case "pip":
container.call(
[
"python",
"-m",
"pip",
"wheel",
container_package_dir,
f"--wheel-dir={built_wheel_dir}",
"--no-deps",
*extra_flags,
],
env=env,
)
case "build" | "build[uv]":
if use_uv and "--no-isolation" not in extra_flags and "-n" not in extra_flags:
extra_flags += ["--installer=uv"]
container.call(
[
"python",
"-m",
"build",
container_package_dir,
"--wheel",
f"--outdir={built_wheel_dir}",
*extra_flags,
],
env=env,
)
case _:
assert_never(build_frontend)

built_wheel = container.glob(built_wheel_dir, "*.whl")[0]

Expand Down
Loading