feat: add the ability to declare safe tools in a cross-build environment. (#2317)
* Add the ability to declare safe tools in a cross-build environment. * Add an xfail if cmake isn't available on the test machine. * Placate linter regarding positional args. * Rework test to provide more robust confirmation of safe tools. * Remove a test skip condition that is no longer needed. Co-authored-by: Joe Rickerby <joerick@mac.com> * Rename the setting to xbuild-tools. * Add docs to clarify that xbuild-tools is transitive. * Raise a warning if xbuild-tools isn't defined. * Correct a bad copy-paste in the schema generator. * .. and now fix the indentation. * Move sentinel handling earlier into the parsing process. * Remove serialization from tests that won't start a test suite. --------- Co-authored-by: Joe Rickerby <joerick@mac.com>
This commit is contained in:
co-authored by
Joe Rickerby
parent
2aaa489371
commit
830e79e11c
@@ -94,6 +94,7 @@ class BuildOptions:
|
||||
environment: ParsedEnvironment
|
||||
before_all: str
|
||||
before_build: str | None
|
||||
xbuild_tools: list[str] | None
|
||||
repair_command: str
|
||||
manylinux_images: dict[str, str] | None
|
||||
musllinux_images: dict[str, str] | None
|
||||
@@ -718,6 +719,18 @@ class Options:
|
||||
|
||||
test_command = self.reader.get("test-command", option_format=ListFormat(sep=" && "))
|
||||
before_test = self.reader.get("before-test", option_format=ListFormat(sep=" && "))
|
||||
xbuild_tools: list[str] | None = shlex.split(
|
||||
self.reader.get(
|
||||
"xbuild-tools", option_format=ListFormat(sep=" ", quote=shlex.quote)
|
||||
)
|
||||
)
|
||||
# ["\u0000"] is a sentinel value used as a default, because TOML
|
||||
# doesn't have an explicit NULL value. If xbuild-tools is set to the
|
||||
# sentinel, it indicates that the user hasn't defined xbuild-tools
|
||||
# *at all* (not even an `xbuild-tools = []` definition).
|
||||
if xbuild_tools == ["\u0000"]:
|
||||
xbuild_tools = None
|
||||
|
||||
test_sources = shlex.split(
|
||||
self.reader.get(
|
||||
"test-sources", option_format=ListFormat(sep=" ", quote=shlex.quote)
|
||||
@@ -835,6 +848,7 @@ class Options:
|
||||
before_build=before_build,
|
||||
before_all=before_all,
|
||||
build_verbosity=build_verbosity,
|
||||
xbuild_tools=xbuild_tools,
|
||||
repair_command=repair_command,
|
||||
environment=environment,
|
||||
dependency_constraints=dependency_constraints,
|
||||
|
||||
@@ -5,6 +5,7 @@ import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
from collections.abc import Sequence, Set
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
@@ -151,6 +152,7 @@ def cross_virtualenv(
|
||||
build_python: Path,
|
||||
venv_path: Path,
|
||||
dependency_constraint_flags: Sequence[PathOrStr],
|
||||
xbuild_tools: Sequence[str] | None,
|
||||
) -> dict[str, str]:
|
||||
"""Create a cross-compilation virtual environment.
|
||||
|
||||
@@ -178,6 +180,8 @@ def cross_virtualenv(
|
||||
created.
|
||||
:param dependency_constraint_flags: Any flags that should be used when
|
||||
constraining dependencies in the environment.
|
||||
:param xbuild_tools: A list of executable names (without paths) that are
|
||||
on the path, but must be preserved in the cross environment.
|
||||
"""
|
||||
# Create an initial macOS virtual environment
|
||||
env = virtualenv(
|
||||
@@ -210,14 +214,52 @@ def cross_virtualenv(
|
||||
#
|
||||
# To prevent problems, set the PATH to isolate the build environment from
|
||||
# sources that could introduce incompatible binaries.
|
||||
#
|
||||
# However, there may be some tools on the path that are needed for the
|
||||
# build. Find their location on the path, and link the underlying binaries
|
||||
# (fully resolving symlinks) to a "safe" location that will *only* contain
|
||||
# those tools. This avoids needing to add *all* of Homebrew to the path just
|
||||
# to get access to (for example) cmake for build purposes. A value of None
|
||||
# means the user hasn't provided a list of xbuild tools.
|
||||
xbuild_tools_path = venv_path / "cibw_xbuild_tools"
|
||||
xbuild_tools_path.mkdir()
|
||||
if xbuild_tools is None:
|
||||
log.warning(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
Your project configuration does not define any cross-build tools.
|
||||
|
||||
iOS builds use an isolated build environment; if your build process requires any
|
||||
third-party tools (such as cmake, ninja, or rustc), you must explicitly declare
|
||||
that those tools are required using xbuild-tools/CIBW_XBUILD_TOOLS. This will
|
||||
likely manifest as a "somebuildtool: command not found" error.
|
||||
|
||||
If the build succeeds, you can silence this warning by setting adding
|
||||
`xbuild-tools = []` to your pyproject.toml configuration, or exporting
|
||||
CIBW_XBUILD_TOOLS as an empty string into your environment.
|
||||
"""
|
||||
)
|
||||
)
|
||||
else:
|
||||
for tool in xbuild_tools:
|
||||
tool_path = shutil.which(tool)
|
||||
if tool_path is None:
|
||||
msg = f"Could not find a {tool!r} executable on the path."
|
||||
raise errors.FatalError(msg)
|
||||
|
||||
# Link the binary into the safe tools directory
|
||||
original = Path(tool_path).resolve()
|
||||
print(f"{tool!r} will be included in the cross-build environment (using {original})")
|
||||
(xbuild_tools_path / tool).symlink_to(original)
|
||||
|
||||
env["PATH"] = os.pathsep.join(
|
||||
[
|
||||
# The target python's binary directory
|
||||
str(target_python.parent),
|
||||
# The cross-platform environments binary directory
|
||||
# The cross-platform environment's binary directory
|
||||
str(venv_path / "bin"),
|
||||
# Cargo's binary directory (to allow for Rust compilation)
|
||||
str(Path.home() / ".cargo" / "bin"),
|
||||
# The directory of cross-build tools
|
||||
str(xbuild_tools_path),
|
||||
# The bare minimum Apple system paths.
|
||||
"/usr/bin",
|
||||
"/bin",
|
||||
@@ -235,10 +277,12 @@ def cross_virtualenv(
|
||||
|
||||
def setup_python(
|
||||
tmp: Path,
|
||||
*,
|
||||
python_configuration: PythonConfiguration,
|
||||
dependency_constraint_flags: Sequence[PathOrStr],
|
||||
environment: ParsedEnvironment,
|
||||
build_frontend: BuildFrontendName,
|
||||
xbuild_tools: Sequence[str] | None,
|
||||
) -> tuple[Path, dict[str, str]]:
|
||||
if build_frontend == "build[uv]":
|
||||
msg = "uv doesn't support iOS"
|
||||
@@ -291,6 +335,7 @@ def setup_python(
|
||||
build_python=build_python,
|
||||
venv_path=venv_path,
|
||||
dependency_constraint_flags=dependency_constraint_flags,
|
||||
xbuild_tools=xbuild_tools,
|
||||
)
|
||||
venv_bin_path = venv_path / "bin"
|
||||
assert venv_bin_path.exists()
|
||||
@@ -414,10 +459,11 @@ def build(options: Options, tmp_path: Path) -> None:
|
||||
|
||||
target_install_path, env = setup_python(
|
||||
identifier_tmp_dir / "build",
|
||||
config,
|
||||
dependency_constraint_flags,
|
||||
build_options.environment,
|
||||
build_frontend.name,
|
||||
python_configuration=config,
|
||||
dependency_constraint_flags=dependency_constraint_flags,
|
||||
environment=build_options.environment,
|
||||
build_frontend=build_frontend.name,
|
||||
xbuild_tools=build_options.xbuild_tools,
|
||||
)
|
||||
pip_version = get_pip_version(env)
|
||||
|
||||
|
||||
@@ -397,6 +397,21 @@
|
||||
"description": "Specify alternative manylinux / musllinux container images",
|
||||
"title": "CIBW_MUSLLINUX_X86_64_IMAGE"
|
||||
},
|
||||
"xbuild-tools": {
|
||||
"description": "Binaries on the path that should be included in an isolated cross-build environment",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "CIBW_XBUILD_TOOLS"
|
||||
},
|
||||
"repair-wheel-command": {
|
||||
"description": "Execute a shell command to repair each built wheel.",
|
||||
"oneOf": [
|
||||
@@ -566,6 +581,9 @@
|
||||
"environment-pass": {
|
||||
"$ref": "#/$defs/inherit"
|
||||
},
|
||||
"xbuild-tools": {
|
||||
"$ref": "#/$defs/inherit"
|
||||
},
|
||||
"repair-wheel-command": {
|
||||
"$ref": "#/$defs/inherit"
|
||||
},
|
||||
@@ -991,6 +1009,9 @@
|
||||
"repair-wheel-command": {
|
||||
"$ref": "#/properties/repair-wheel-command"
|
||||
},
|
||||
"xbuild-tools": {
|
||||
"$ref": "#/properties/xbuild-tools"
|
||||
},
|
||||
"test-command": {
|
||||
"$ref": "#/properties/test-command"
|
||||
},
|
||||
|
||||
@@ -14,6 +14,8 @@ build-verbosity = 0
|
||||
|
||||
before-all = ""
|
||||
before-build = ""
|
||||
# TOML doesn't support explicit NULLs; use ["\u0000"] as a sentinel value.
|
||||
xbuild-tools = ["\u0000"]
|
||||
repair-wheel-command = ""
|
||||
|
||||
test-command = ""
|
||||
|
||||
Reference in New Issue
Block a user