Skip to content
Open
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
19 changes: 16 additions & 3 deletions setuptools_rust/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,16 @@ def build_extension(
debug = self._is_debug_build(ext)
use_cargo_crate_type = _check_cargo_supports_crate_type_option(ext.env)

package_id = ext.metadata(quiet=quiet)["resolve"]["root"]
metadata = ext.metadata("--no-deps", quiet=quiet)
manifest_path = Path(ext.path).resolve()
package_id = next(
(
package["id"]
for package in metadata["packages"]
if Path(package["manifest_path"]).resolve() == manifest_path
),
None,
)
if package_id is None:
raise FileError(
f"manifest for Rust extention `{ext.name}` at path `{ext.path}` is a virtual manifest (a workspace root without a package).\n\n"
Expand Down Expand Up @@ -929,8 +938,12 @@ def _replace_cross_target_dir(path: str, ext: RustExtension, *, quiet: bool) ->
dockerfile; invoking `cargo metadata` we can work out the correct local
target directory.
"""
cross_target_dir = ext._metadata(cargo="cross", quiet=quiet)["target_directory"]
local_target_dir = ext._metadata(cargo="cargo", quiet=quiet)["target_directory"]
cross_target_dir = ext._metadata("cross", "--no-deps", quiet=quiet)[
"target_directory"
]
local_target_dir = ext._metadata("cargo", "--no-deps", quiet=quiet)[
"target_directory"
]
return path.replace(cross_target_dir, local_target_dir)


Expand Down
34 changes: 24 additions & 10 deletions setuptools_rust/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import warnings
from collections.abc import Sequence
from enum import IntEnum, auto
from functools import lru_cache
from pathlib import Path
from typing import (
TYPE_CHECKING,
Any,
Expand Down Expand Up @@ -194,11 +194,17 @@ def __init__(
DeprecationWarning,
)

self._metadata_cache: dict[tuple[str, ...], CargoMetadata] = {}

def get_lib_name(self, *, quiet: bool) -> str:
"""Parse Cargo.toml to get the name of the shared library."""
metadata = self.metadata(quiet=quiet)
root_key = metadata["resolve"]["root"]
[pkg] = [p for p in metadata["packages"] if p["id"] == root_key]
metadata = self.metadata("--no-deps", quiet=quiet)
manifest_path = Path(self.path).resolve()
(pkg,) = (
p
for p in metadata["packages"]
if Path(p["manifest_path"]).resolve() == manifest_path
)
name = pkg["targets"][0]["name"]
assert isinstance(name, str)
return re.sub(r"[./\\-]", "_", name)
Expand Down Expand Up @@ -253,27 +259,33 @@ def install_script(self, module_name: str, exe_path: str) -> None:
with open(file, "w") as f:
f.write(_SCRIPT_TEMPLATE.format(executable=repr(executable)))

def metadata(self, *, quiet: bool) -> CargoMetadata:
def metadata(self, *args: str, quiet: bool) -> CargoMetadata:
"""Returns cargo metadata for this extension package.

Cached - will only execute cargo on first invocation.
Cached - will only execute cargo on first invocation for each
combination of *args.
"""

return self._metadata(os.environ.get("CARGO", "cargo"), quiet)
return self._metadata(os.environ.get("CARGO", "cargo"), *args, quiet=quiet)

@lru_cache # noqa: B019
def _metadata(self, cargo: str, quiet: bool) -> CargoMetadata:
def _metadata(self, cargo: str, *args: str, quiet: bool) -> CargoMetadata:
metadata_command = [
cargo,
"metadata",
"--manifest-path",
self.path,
"--format-version",
"1",
*args,
]
if self.cargo_manifest_args:
metadata_command.extend(self.cargo_manifest_args)

command = tuple(metadata_command)

if (cached := self._metadata_cache.get(command)) is not None:
return cached

try:
# If quiet, capture stderr and only show it on exceptions
# If not quiet, let stderr be inherited
Expand All @@ -284,7 +296,9 @@ def _metadata(self, cargo: str, quiet: bool) -> CargoMetadata:
except subprocess.CalledProcessError as e:
raise SetupError(format_called_process_error(e))
try:
return cast(CargoMetadata, json.loads(payload))
metadata = cast(CargoMetadata, json.loads(payload))
self._metadata_cache[command] = metadata
return metadata
except json.decoder.JSONDecodeError as e:
raise SetupError(
f"""
Expand Down
Loading