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
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,37 @@ path = "."
module = "project"
```

### Shared C ABI libraries

Projects can declare multiple Rust artifacts in one hook. A `python-extension`
artifact keeps the existing PyO3 extension flow, while a `shared-library`
artifact builds a standalone `cdylib`, copies the exact platform library name to
the configured destination, and includes it in the wheel.

```toml
[tool.hatch.build.hooks.hatch-rs]
verbose = true
path = "."
module = "project"
target-dir = "isolated"

[[tool.hatch.build.hooks.hatch-rs.artifacts]]
name = "python-extension"
kind = "python-extension"
manifest = "Cargo.toml"
library = "project"

[[tool.hatch.build.hooks.hatch-rs.artifacts]]
name = "c-abi"
kind = "shared-library"
manifest = "rust/Cargo.toml"
library = "project_ffi"
crate-type = "cdylib"
destination = "project/lib/{shared_library}"
```

Destination templates support `{module}`, `{target}`, `{profile}`, `{library}`,
`{shared_library}`, and `{python_extension_name}`.

> [!NOTE]
> This library was generated using [copier](https://copier.readthedocs.io/en/stable/) from the [Base Python Project Template repository](https://github.com/python-project-templates/base).
26 changes: 11 additions & 15 deletions hatch_rs/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from logging import getLogger
from os import getenv
from pathlib import Path
from platform import machine as platform_machine
from sys import platform as sys_platform, version_info
from typing import Any
Expand Down Expand Up @@ -61,12 +60,13 @@ def initialize(self, version: str, build_data: dict[str, Any]) -> None:
self._logger.warning(command)

# Execute build plan
build_plan.execute()
try:
build_plan.execute()
finally:
# Perform any cleanup actions
build_plan.cleanup()

# Perform any cleanup actions
build_plan.cleanup()

if not build_plan._libraries:
if not build_plan.libraries:
raise ValueError("No libraries were created by the build.")

# force include libraries
Expand Down Expand Up @@ -104,13 +104,9 @@ def initialize(self, version: str, build_data: dict[str, Any]) -> None:
build_data["tag"] = f"cp{version_major}{version_minor}-cp{version_major}{version_minor}-{os_name}_{machine}"

# force include libraries
for path in Path(".").rglob("*"):
if path.is_dir():
continue
if str(path).startswith("target") or str(path).startswith("dist") or not str(path).startswith(config.module):
continue
if path.suffix in (".pyd", ".dll", ".so", ".dylib"):
build_data["force_include"][str(path)] = str(path)

for path in build_data["force_include"]:
force_include = build_data.setdefault("force_include", {})
for artifact in build_plan.copied_artifacts:
force_include[artifact.distribution_path] = artifact.distribution_path

for path in force_include:
self._logger.warning(f"Force include: {path}")
Loading
Loading