Files
cibuildwheel/unit_test/options_test.py
T

743 lines
23 KiB
Python
Raw Normal View History

2026-05-28 00:20:08 +02:00
from __future__ import annotations
2022-09-17 09:58:19 +01:00
import os
2021-10-12 02:05:47 +01:00
import platform as platform_module
2022-09-06 00:56:20 -04:00
import textwrap
import unittest.mock
2022-09-17 09:58:19 +01:00
from pathlib import Path
from typing import Literal
2021-09-19 00:19:28 -04:00
2021-11-21 15:19:45 -05:00
import pytest
from cibuildwheel import errors
2022-09-17 09:58:19 +01:00
from cibuildwheel.bashlex_eval import local_environment_executor
2026-05-09 14:11:40 -04:00
from cibuildwheel.frontend import (
BuildFrontendConfig,
_split_config_settings,
2026-05-09 14:11:40 -04:00
get_build_frontend_extra_flags,
parse_config_settings,
2026-05-09 14:11:40 -04:00
prepare_config_settings,
)
from cibuildwheel.logger import Logger
2022-11-26 15:54:08 +00:00
from cibuildwheel.options import (
CommandLineArguments,
Options,
_get_pinned_container_images,
)
from cibuildwheel.platforms import ALL_PLATFORM_MODULES, get_build_identifiers
2025-03-10 21:45:54 +00:00
from cibuildwheel.util import resources
from cibuildwheel.util.packaging import DependencyConstraints
2021-09-19 00:19:28 -04:00
2026-05-28 00:20:08 +02:00
TYPE_CHECKING = False
if TYPE_CHECKING:
from collections.abc import Sequence
2021-09-19 00:19:28 -04:00
PYPROJECT_1 = """
[tool.cibuildwheel]
2025-02-25 20:23:01 +01:00
build = ["cp38-*", "cp313-*"]
2022-11-26 15:54:08 +00:00
skip = ["*musllinux*"]
archs = ["auto64", "auto32"]
2021-09-19 00:19:28 -04:00
environment = {FOO="BAR"}
test-command = "pyproject"
test-sources = ["test", "other dir"]
2021-09-19 00:19:28 -04:00
2025-03-22 17:26:38 +01:00
manylinux-x86_64-image = "manylinux_2_28"
2021-09-19 00:19:28 -04:00
2021-11-21 15:19:45 -05:00
environment-pass = ["EXAMPLE_ENV"]
pyodide-version = "0.29.4"
2021-09-19 00:19:28 -04:00
[tool.cibuildwheel.macos]
test-requires = "else"
[[tool.cibuildwheel.overrides]]
2025-02-25 20:23:01 +01:00
select = "cp313-*"
2021-09-19 00:19:28 -04:00
test-command = "pyproject-override"
2025-03-22 17:26:38 +01:00
manylinux-x86_64-image = "manylinux_2_34"
2021-09-19 00:19:28 -04:00
"""
2026-04-01 10:23:02 -04:00
def test_options_1(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
2021-09-19 00:19:28 -04:00
with tmp_path.joinpath("pyproject.toml").open("w") as f:
f.write(PYPROJECT_1)
2022-11-26 15:54:08 +00:00
args = CommandLineArguments.defaults()
2022-04-26 22:21:27 -04:00
args.package_dir = tmp_path
2021-10-12 02:05:47 +01:00
monkeypatch.setattr(platform_module, "machine", lambda: "x86_64")
2022-12-05 19:18:54 +00:00
options = Options(platform="linux", command_line_arguments=args, env={})
2021-09-19 00:19:28 -04:00
module = ALL_PLATFORM_MODULES["linux"]
2021-09-19 00:19:28 -04:00
identifiers = get_build_identifiers(
platform_module=module,
2021-10-12 02:05:47 +01:00
build_selector=options.globals.build_selector,
architectures=options.globals.architectures,
2021-09-19 00:19:28 -04:00
)
override_display = """\
2022-11-26 15:54:08 +00:00
*: pyproject
2025-02-25 20:23:01 +01:00
cp313-manylinux_x86_64, cp313-manylinux_i686: pyproject-override"""
2021-10-12 02:05:47 +01:00
print(options.summary(identifiers))
2021-09-19 00:19:28 -04:00
2021-10-12 02:05:47 +01:00
assert override_display in options.summary(identifiers)
default_build_options = options.build_options(identifier=None)
assert default_build_options.environment.as_dictionary(prev_environment={}) == {"FOO": "BAR"}
2021-09-19 00:19:28 -04:00
all_pinned_container_images = _get_pinned_container_images()
pinned_x86_64_container_image = all_pinned_container_images["x86_64"]
2021-09-19 00:19:28 -04:00
2021-10-12 02:05:47 +01:00
local = options.build_options("cp38-manylinux_x86_64")
2021-09-19 00:19:28 -04:00
assert local.manylinux_images is not None
assert local.test_command == "pyproject"
assert local.test_sources == ["test", "other dir"]
2025-03-22 17:26:38 +01:00
assert local.manylinux_images["x86_64"] == pinned_x86_64_container_image["manylinux_2_28"]
2021-09-19 00:19:28 -04:00
2025-02-25 20:23:01 +01:00
local = options.build_options("cp313-manylinux_x86_64")
2021-09-19 00:19:28 -04:00
assert local.manylinux_images is not None
assert local.test_command == "pyproject-override"
assert local.test_sources == ["test", "other dir"]
2025-03-22 17:26:38 +01:00
assert local.manylinux_images["x86_64"] == pinned_x86_64_container_image["manylinux_2_34"]
2021-11-21 15:19:45 -05:00
local = options.build_options("cp312-pyodide_wasm32")
assert local.pyodide_version == "0.29.4"
2021-11-21 15:19:45 -05:00
2026-04-01 10:23:02 -04:00
def test_passthrough(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
2021-11-21 15:19:45 -05:00
with tmp_path.joinpath("pyproject.toml").open("w") as f:
f.write(PYPROJECT_1)
2022-11-26 15:54:08 +00:00
args = CommandLineArguments.defaults()
2022-04-26 22:21:27 -04:00
args.package_dir = tmp_path
2021-11-21 15:19:45 -05:00
monkeypatch.setattr(platform_module, "machine", lambda: "x86_64")
2022-12-05 19:18:54 +00:00
options = Options(platform="linux", command_line_arguments=args, env={"EXAMPLE_ENV": "ONE"})
2021-11-21 15:19:45 -05:00
default_build_options = options.build_options(identifier=None)
assert default_build_options.environment.as_dictionary(prev_environment={}) == {
"FOO": "BAR",
"EXAMPLE_ENV": "ONE",
}
@pytest.mark.parametrize(
"env_var_value",
[
"normal value",
'"value wrapped in quotes"',
"an unclosed single-quote: '",
'an unclosed double-quote: "',
"string\nwith\ncarriage\nreturns\n",
"a trailing backslash \\",
],
)
2026-04-01 10:23:02 -04:00
def test_passthrough_evil(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, env_var_value: str
) -> None:
2022-11-26 15:54:08 +00:00
args = CommandLineArguments.defaults()
2022-04-26 22:21:27 -04:00
args.package_dir = tmp_path
2021-11-21 15:19:45 -05:00
monkeypatch.setattr(platform_module, "machine", lambda: "x86_64")
2022-11-26 15:54:08 +00:00
options = Options(
platform="linux",
command_line_arguments=args,
2022-12-05 19:18:54 +00:00
env={"CIBW_ENVIRONMENT_PASS_LINUX": "ENV_VAR", "ENV_VAR": env_var_value},
2022-11-26 15:54:08 +00:00
)
2021-11-21 15:19:45 -05:00
parsed_environment = options.build_options(identifier=None).environment
assert parsed_environment.as_dictionary(prev_environment={}) == {"ENV_VAR": env_var_value}
2022-09-06 00:56:20 -04:00
xfail_env_parse = pytest.mark.xfail(
raises=errors.ConfigurationError,
reason="until we can figure out the right way to quote these values",
)
2022-09-06 00:56:20 -04:00
@pytest.mark.parametrize(
"env_var_value",
[
"normal value",
pytest.param('"value wrapped in quotes"', marks=[xfail_env_parse]),
pytest.param('an unclosed double-quote: "', marks=[xfail_env_parse]),
2022-09-06 00:56:20 -04:00
"string\nwith\ncarriage\nreturns\n",
pytest.param("a trailing backslash \\", marks=[xfail_env_parse]),
2022-09-06 00:56:20 -04:00
],
)
2026-04-01 10:23:02 -04:00
def test_toml_environment_evil(tmp_path: Path, env_var_value: str) -> None:
2022-11-26 15:54:08 +00:00
args = CommandLineArguments.defaults()
2022-09-06 00:56:20 -04:00
args.package_dir = tmp_path
2022-09-17 09:58:19 +01:00
tmp_path.joinpath("pyproject.toml").write_text(
textwrap.dedent(
f"""\
[tool.cibuildwheel.environment]
EXAMPLE='''{env_var_value}'''
"""
2022-09-06 00:56:20 -04:00
)
2022-09-17 09:58:19 +01:00
)
2022-09-06 00:56:20 -04:00
2022-12-05 19:18:54 +00:00
options = Options(platform="linux", command_line_arguments=args, env={})
2022-09-06 00:56:20 -04:00
parsed_environment = options.build_options(identifier=None).environment
assert parsed_environment.as_dictionary(prev_environment={}) == {"EXAMPLE": env_var_value}
2022-09-17 09:58:19 +01:00
@pytest.mark.parametrize(
2023-01-30 15:53:44 -05:00
("toml_assignment", "result_value"),
2022-09-17 09:58:19 +01:00
[
('TEST_VAR="simple_value"', "simple_value"),
# spaces
('TEST_VAR="simple value"', "simple value"),
# env var
('TEST_VAR="$PARAM"', "spam"),
('TEST_VAR="$PARAM $PARAM"', "spam spam"),
# env var extension
('TEST_VAR="before:$PARAM:after"', "before:spam:after"),
# env var extension with spaces
('TEST_VAR="before $PARAM after"', "before spam after"),
# literal $ - this test is just for reference, I'm not sure if this
# syntax will work if we change the TOML quoting behaviour
(r'TEST_VAR="before\\$after"', "before$after"),
2022-09-17 09:58:19 +01:00
],
)
2024-10-22 10:16:59 -04:00
def test_toml_environment_quoting(tmp_path: Path, toml_assignment: str, result_value: str) -> None:
2022-11-26 15:54:08 +00:00
args = CommandLineArguments.defaults()
2022-09-17 09:58:19 +01:00
args.package_dir = tmp_path
tmp_path.joinpath("pyproject.toml").write_text(
textwrap.dedent(
f"""\
[tool.cibuildwheel.environment]
{toml_assignment}
"""
)
)
2022-12-05 19:18:54 +00:00
options = Options(platform="linux", command_line_arguments=args, env={})
2022-09-17 09:58:19 +01:00
parsed_environment = options.build_options(identifier=None).environment
environment_values = parsed_environment.as_dictionary(
prev_environment={**os.environ, "PARAM": "spam"},
executor=local_environment_executor,
)
assert environment_values["TEST_VAR"] == result_value
@pytest.mark.parametrize(
("toml_assignment", "result_name", "result_create_args", "result_disable_host_mount"),
[
(
'container-engine = "podman"',
"podman",
(),
False,
),
(
'container-engine = {name = "podman"}',
"podman",
(),
False,
),
(
'container-engine = "docker; create_args: --some-option"',
"docker",
("--some-option",),
False,
),
(
'container-engine = {name = "docker", create-args = ["--some-option"]}',
"docker",
("--some-option",),
False,
),
(
'container-engine = {name = "docker", create-args = ["--some-option", "value that contains spaces"]}',
"docker",
("--some-option", "value that contains spaces"),
False,
),
(
'container-engine = {name = "docker", create-args = ["--some-option", "value;that;contains;semicolons"]}',
"docker",
("--some-option", "value;that;contains;semicolons"),
False,
),
(
'container-engine = {name = "docker", disable-host-mount = true}',
"docker",
(),
True,
),
(
'container-engine = {name = "docker", disable_host_mount = true}',
"docker",
(),
True,
),
],
)
def test_container_engine_option(
2024-10-22 10:16:59 -04:00
tmp_path: Path,
toml_assignment: str,
result_name: str,
result_create_args: tuple[str, ...],
result_disable_host_mount: bool,
) -> None:
args = CommandLineArguments.defaults()
args.package_dir = tmp_path
tmp_path.joinpath("pyproject.toml").write_text(
textwrap.dedent(
f"""\
[tool.cibuildwheel]
{toml_assignment}
"""
)
)
options = Options(platform="linux", command_line_arguments=args, env={})
parsed_container_engine = options.build_options(None).container_engine
assert parsed_container_engine.name == result_name
assert parsed_container_engine.create_args == result_create_args
assert parsed_container_engine.disable_host_mount == result_disable_host_mount
2026-04-01 10:23:02 -04:00
def test_environment_pass_references() -> None:
options = Options(
platform="linux",
command_line_arguments=CommandLineArguments.defaults(),
env={
"CIBW_ENVIRONMENT_PASS_LINUX": "STARTER MAIN_COURSE",
"STARTER": "green eggs",
"MAIN_COURSE": "ham",
"CIBW_ENVIRONMENT": 'MEAL="$STARTER and $MAIN_COURSE"',
},
)
parsed_environment = options.build_options(identifier=None).environment
assert parsed_environment.as_dictionary(prev_environment={}) == {
"MEAL": "green eggs and ham",
"STARTER": "green eggs",
"MAIN_COURSE": "ham",
}
@pytest.mark.parametrize(
("toml_assignment", "result_name", "result_args"),
[
(
"",
None,
None,
),
(
'build-frontend = "build"',
"build",
[],
),
(
'build-frontend = "uv"',
"uv",
[],
),
(
'build-frontend = {name = "build"}',
"build",
[],
),
(
'build-frontend = "pip; args: --some-option"',
"pip",
["--some-option"],
),
(
'build-frontend = {name = "pip", args = ["--some-option"]}',
"pip",
["--some-option"],
),
],
)
2024-10-22 10:16:59 -04:00
def test_build_frontend_option(
tmp_path: Path, toml_assignment: str, result_name: str, result_args: list[str]
) -> None:
args = CommandLineArguments.defaults()
args.package_dir = tmp_path
tmp_path.joinpath("pyproject.toml").write_text(
textwrap.dedent(
f"""\
[tool.cibuildwheel]
{toml_assignment}
"""
)
)
options = Options(platform="linux", command_line_arguments=args, env={})
parsed_build_frontend = options.build_options(identifier=None).build_frontend
if toml_assignment:
assert parsed_build_frontend.name == result_name
assert parsed_build_frontend.args == result_args
else:
assert parsed_build_frontend.name == "build"
assert parsed_build_frontend.args == ()
2024-03-02 16:58:18 -05:00
2024-10-22 10:16:59 -04:00
def test_override_inherit_environment(tmp_path: Path) -> None:
2024-03-02 16:58:18 -05:00
args = CommandLineArguments.defaults()
args.package_dir = tmp_path
pyproject_toml: Path = tmp_path / "pyproject.toml"
pyproject_toml.write_text(
textwrap.dedent(
"""\
[tool.cibuildwheel]
environment = {FOO="BAR", "HAM"="EGGS"}
[[tool.cibuildwheel.overrides]]
select = "cp37*"
inherit.environment = "append"
environment = {FOO="BAZ", "PYTHON"="MONTY"}
"""
)
)
options = Options(platform="linux", command_line_arguments=args, env={})
parsed_environment = options.build_options(identifier=None).environment
assert parsed_environment.as_dictionary(prev_environment={}) == {
"FOO": "BAR",
"HAM": "EGGS",
}
assert options.build_options("cp37-manylinux_x86_64").environment.as_dictionary(
prev_environment={}
) == {
"FOO": "BAZ",
"HAM": "EGGS",
"PYTHON": "MONTY",
}
2024-10-22 10:16:59 -04:00
def test_override_inherit_environment_with_references(tmp_path: Path) -> None:
2024-03-02 16:58:18 -05:00
args = CommandLineArguments.defaults()
args.package_dir = tmp_path
pyproject_toml: Path = tmp_path / "pyproject.toml"
pyproject_toml.write_text(
textwrap.dedent(
"""\
[tool.cibuildwheel]
environment = {PATH="/opt/bin:$PATH"}
[[tool.cibuildwheel.overrides]]
select = "cp37*"
inherit.environment = "append"
environment = {PATH="/opt/local/bin:$PATH"}
"""
)
)
options = Options(platform="linux", command_line_arguments=args, env={"MONTY": "PYTHON"})
parsed_environment = options.build_options(identifier=None).environment
prev_environment = {"PATH": "/usr/bin:/bin"}
assert parsed_environment.as_dictionary(prev_environment=prev_environment) == {
"PATH": "/opt/bin:/usr/bin:/bin",
}
assert options.build_options("cp37-manylinux_x86_64").environment.as_dictionary(
prev_environment=prev_environment
) == {
"PATH": "/opt/local/bin:/opt/bin:/usr/bin:/bin",
}
2025-03-10 21:45:54 +00:00
@pytest.mark.parametrize(
("toml_assignment", "base_file_path", "packages"),
[
("", resources.CONSTRAINTS, []),
("dependency-versions = 'pinned'", resources.CONSTRAINTS, []),
("dependency-versions = 'latest'", None, []),
("dependency-versions = 'constraints file.txt'", Path("constraints file.txt"), []),
(
"dependency-versions = \"file:'constraints file.txt'\"",
Path("constraints file.txt"),
[],
),
(
"dependency-versions = {file = 'constraints file.txt'}",
Path("constraints file.txt"),
[],
),
(
"dependency-versions = 'packages: foo==1.2.3 bar==4.5.6'",
None,
["foo==1.2.3", "bar==4.5.6"],
),
],
)
def test_dependency_versions_toml(
tmp_path: Path,
toml_assignment: str,
base_file_path: Path | None,
packages: list[str] | None,
monkeypatch: pytest.MonkeyPatch,
) -> None:
args = CommandLineArguments.defaults()
args.package_dir = tmp_path
(tmp_path / "constraints file.txt").write_text("")
monkeypatch.chdir(tmp_path)
pyproject_toml: Path = tmp_path / "pyproject.toml"
pyproject_toml.write_text(
textwrap.dedent(
f"""\
[tool.cibuildwheel]
{toml_assignment}
"""
)
)
options = Options(platform="linux", command_line_arguments=args, env={})
parsed_dependency_constraints = options.build_options(None).dependency_constraints
if base_file_path is None and packages is None:
assert parsed_dependency_constraints == DependencyConstraints.latest()
else:
if parsed_dependency_constraints.base_file_path and base_file_path:
assert parsed_dependency_constraints.base_file_path.samefile(base_file_path)
else:
assert parsed_dependency_constraints.base_file_path == base_file_path
assert parsed_dependency_constraints.packages == packages
2025-03-12 09:41:51 +01:00
@pytest.mark.parametrize(
2025-03-22 17:26:38 +01:00
("image", "deprecated", "raises"),
2025-03-12 09:41:51 +01:00
[
2025-03-22 17:26:38 +01:00
("manylinux1", True, True),
("manylinux2010", True, True),
("manylinux2014", False, False),
("manylinux_2_24", True, True),
("manylinux_2_28", False, False),
("manylinux_2_34", False, False),
("musllinux_1_1", True, True),
("musllinux_1_2", False, False),
2025-03-12 09:41:51 +01:00
],
)
2025-03-22 17:26:38 +01:00
def test_deprecated_image(
image: str, deprecated: bool, raises: bool, capsys: pytest.CaptureFixture[str]
) -> None:
assert not raises or deprecated
2025-03-12 09:41:51 +01:00
args = CommandLineArguments.defaults()
env = {
"CIBW_ARCHS": "x86_64",
"CIBW_MANYLINUX_X86_64_IMAGE": image if image.startswith("manylinux") else "",
"CIBW_MUSLLINUX_X86_64_IMAGE": image if image.startswith("musllinux") else "",
}
options = Options(platform="linux", command_line_arguments=args, env=env)
2025-03-22 17:26:38 +01:00
try:
bo = options.build_options(None)
assert not raises
except errors.DeprecationError:
assert raises
return
2025-03-12 09:41:51 +01:00
images = bo.manylinux_images if image.startswith("manylinux") else bo.musllinux_images
assert images is not None
resolved_image = images["x86_64"]
captured = capsys.readouterr()
if deprecated:
assert f"Deprecated image {image!r}" in captured.err
assert f"{resolved_image!r}" in captured.err
else:
assert "Deprecated image" not in captured.err
@pytest.mark.parametrize(
("frontend", "verbosity", "result"),
[
("pip", 3, ["-Ca", "-Cb", "-1", "-vvv"]),
("pip", 2, ["-Ca", "-Cb", "-1", "-vv"]),
("pip", -1, ["-Ca", "-Cb", "-1", "-q"]),
2026-02-13 20:47:03 -05:00
("uv", -1, ["-Ca", "-Cb", "-1", "-q"]),
("build", -1, ["-Ca", "-Cb", "-1", "-q"]),
("build[uv]", -1, ["-Ca", "-Cb", "-1", "-q"]),
("build", 0, ["-Ca", "-Cb", "-1"]),
("build", 1, ["-Ca", "-Cb", "-1"]),
("build", 2, ["-Ca", "-Cb", "-1", "-v"]),
("build", 3, ["-Ca", "-Cb", "-1", "-vv"]),
("build[uv]", 3, ["-Ca", "-Cb", "-1", "-vv"]),
2026-02-13 20:47:03 -05:00
("uv", 3, ["-Ca", "-Cb", "-1", "-vv"]),
],
)
def test_get_build_frontend_extra_flags(
frontend: Literal["pip", "build", "build[uv]"],
verbosity: int,
result: list[str],
monkeypatch: pytest.MonkeyPatch,
) -> None:
mock_warning = unittest.mock.MagicMock()
monkeypatch.setattr(Logger, "warning", mock_warning)
build_frontend = BuildFrontendConfig(frontend, ["-1"])
args = get_build_frontend_extra_flags(
2026-05-11 03:19:36 +02:00
build_frontend=build_frontend, verbosity_level=verbosity, config_settings="a b"
)
assert args == result
mock_warning.assert_not_called()
2026-05-09 14:11:40 -04:00
@pytest.mark.parametrize(
("config_settings", "expected"),
[
(
"setup-args=--cross-file={project}/meson_cross_files/windows-386.ini",
"setup-args=--cross-file=C:/project/meson_cross_files/windows-386.ini",
),
(
"setup-args=--cross-file={package}/meson_cross_files/windows-386.ini",
"setup-args=--cross-file=C:/project/pkg/meson_cross_files/windows-386.ini",
),
],
)
def test_prepare_config_settings(config_settings: str, expected: str) -> None:
assert (
prepare_config_settings(config_settings, project="C:/project", package="C:/project/pkg")
== expected
)
@pytest.mark.parametrize(
("project", "package"),
[
(R"C:\Users\John Doe\project", R"C:\Users\John Doe\project\pkg"),
("/home/me/my project", "/home/me/my project/pkg"),
],
)
def test_prepare_config_settings_special_chars(project: str, package: str) -> None:
# Paths with spaces or backslashes must survive the round-trip through the
# downstream shlex.split intact (regression test for the {package}/{project}
# substitution being mangled by re-parsing).
config_settings = "setup-args=--cross-file={package}/cross.ini other-setting={project}"
prepared = prepare_config_settings(config_settings, project=project, package=package)
assert _split_config_settings(prepared) == [
f"-Csetup-args=--cross-file={package}/cross.ini",
f"-Cother-setting={project}",
]
assert parse_config_settings(prepared) == {
"setup-args": f"--cross-file={package}/cross.ini",
"other-setting": project,
}
@pytest.mark.parametrize(
("definition", "expected_args"),
[
("", ()),
('test-runtime = ""', ()),
("test-runtime = {}", ()),
('test-runtime = {args = ""}', []),
('test-runtime = "args: --simulator foo"', ["--simulator", "foo"]),
('test-runtime = {args = ["--simulator", "foo"]}', ["--simulator", "foo"]),
],
)
def test_test_runtime_handling(
tmp_path: Path, definition: str, expected_args: Sequence[str] | None
) -> None:
args = CommandLineArguments.defaults()
args.package_dir = tmp_path
pyproject_toml: Path = tmp_path / "pyproject.toml"
pyproject_toml.write_text(
textwrap.dedent(
f"""\
[tool.cibuildwheel]
{definition}
"""
)
)
options = Options(platform="ios", command_line_arguments=args, env={})
local = options.build_options("cp313-ios_13_0_arm64_iphoneos")
assert local.test_runtime.args == expected_args
@pytest.mark.parametrize(
("definition", "expected"),
[
("", None),
("xbuild-tools = []", []),
('xbuild-tools = ["cmake", "rustc"]', ["cmake", "rustc"]),
],
)
def test_xbuild_tools_handling(tmp_path: Path, definition: str, expected: list[str] | None) -> None:
args = CommandLineArguments.defaults()
args.package_dir = tmp_path
pyproject_toml: Path = tmp_path / "pyproject.toml"
pyproject_toml.write_text(
textwrap.dedent(
f"""\
[tool.cibuildwheel]
{definition}
"""
)
)
options = Options(platform="ios", command_line_arguments=args, env={})
local = options.build_options("cp313-ios_13_0_arm64_iphoneos")
assert local.xbuild_tools == expected
DEFAULT_XBUILD_FILES = {
"numpy": [
"numpy/_core/include/numpy/_numpyconfig.h",
"numpy/_core/include/numpy/numpyconfig.h",
"numpy/_core/lib/libnpymath.a",
"numpy/random/lib/libnpyrandom.a",
]
}
@pytest.mark.parametrize(
("definition", "expected"),
[
("", DEFAULT_XBUILD_FILES),
('xbuild-files = ""', {}),
('xbuild-files = "package1: file1"', {"package1": ["file1"]}),
(
"xbuild-files = \"package1: 'file1 with space'; package2: file2a file2b\"",
{"package1": ["file1 with space"], "package2": ["file2a", "file2b"]},
),
],
)
def test_xbuild_files(tmp_path: Path, definition: str, expected: dict[str, list[str]]) -> None:
args = CommandLineArguments.defaults()
args.package_dir = tmp_path
pyproject_toml: Path = tmp_path / "pyproject.toml"
pyproject_toml.write_text(
textwrap.dedent(
f"""\
[tool.cibuildwheel]
{definition}
"""
)
)
options = Options(platform="ios", command_line_arguments=args, env={})
assert options.build_options(None).xbuild_files == expected
pyproject_toml.write_text(
"[tool.cibuildwheel.xbuild-files]\n"
+ "\n".join(f"{package} = {files}" for package, files in expected.items())
)
options = Options(platform="ios", command_line_arguments=args, env={})
assert options.build_options(None).xbuild_files == expected