Skip to content

Commit

Permalink
Fix vendor Rust: limit to manifests from backend
Browse files Browse the repository at this point in the history
Fromager was vendoring crates from all `Cargo.toml` files in a project.
This approach is causing issues for projects that have cargo files in
tests and example directories.

The `vendor_rust()` function now only vendors crates from `Cargo.toml`
in the project's root directory and additional cargo files listed in
`tools.maturin` or `tools.setuptools-rust` entries.

Fixes: python-wheel-build#529
Signed-off-by: Christian Heimes <[email protected]>
  • Loading branch information
tiran committed Jan 22, 2025
1 parent c84ee7a commit eec7e9c
Showing 1 changed file with 42 additions and 12 deletions.
54 changes: 42 additions & 12 deletions src/fromager/vendor_rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,28 +86,27 @@ def _cargo_config(project_dir: pathlib.Path) -> None:
tomlkit.dump(cfg, f)


def _should_vendor_rust(req: Requirement, project_dir: pathlib.Path) -> bool:
def _detect_rust_build_backend(
req: Requirement, pyproject_toml: dict[str, typing.Any]
) -> str | None:
"""Detect if project has build requirement on Rust
Detects setuptools-rust and maturin.
"""
pyproject_toml = dependencies.get_pyproject_contents(project_dir)
if not pyproject_toml:
logger.debug(f"{req.name}: has no pyproject.toml")
return False

build_backend = dependencies.get_build_backend(pyproject_toml)
if build_backend["build-backend"] == "maturin":
return "maturin"

for reqstring in build_backend["requires"]:
req = Requirement(reqstring)
if req.name in RUST_BUILD_REQUIRES:
logger.debug(
f"{req.name}: build-system requires {req.name}, vendoring crates"
f"{req.name}: build-system requires '{req.name}', vendoring crates"
)
return True
return req.name

logger.debug(f"{req.name}: no Rust build plugin detected")
return False
return None


def vendor_rust(
Expand All @@ -119,11 +118,42 @@ def vendor_rust(
``setuptools-rust`` or ``maturin``, and has a ``Cargo.toml``, otherwise
``False``.
"""
if not _should_vendor_rust(req, project_dir):
pyproject_toml = dependencies.get_pyproject_contents(project_dir)
if not pyproject_toml:
logger.debug(f"{req.name}: has no pyproject.toml")
return False

# check for Cargo.toml
manifests = list(project_dir.glob("**/Cargo.toml"))
backend = _detect_rust_build_backend(req, pyproject_toml)
if backend is None:
return False

manifests: list[pathlib.Path] = []
# check for Cargo.toml in project root
root_cargo_toml = project_dir / "Cargo.toml"
if root_cargo_toml.is_file():
manifests.append(root_cargo_toml)

# maturin and setuptools-rust can have Cargo.toml in other directories.
# tool.setuptools-rust and tool.maturin are optional.
if backend == "maturin":
try:
tool_maturin: dict[str, typing.Any] = pyproject_toml["tool"]["maturin"]
except KeyError as e:
logger.debug(f"{req.name}: No additional maturin settings: {e}")
else:
if "manifest-path" in tool_maturin:
manifests.append(project_dir / tool_maturin["manifest-path"])
elif backend == "setuptools-rust":
ext_modules: list[dict[str, typing.Any]]
try:
ext_modules = pyproject_toml["tool"]["setuptools-rust"]["ext-modules"]
except KeyError as e:
logger.debug(f"{req.name}: No additional setuptools-rust settings: {e}")
else:
for ext_module in ext_modules:
if "path" in ext_module:
manifests.append(project_dir / ext_module["path"])

if not manifests:
logger.debug(f"{req.name}: has no Cargo.toml files")
return False
Expand Down

0 comments on commit eec7e9c

Please sign in to comment.