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
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,67 @@ destination = "project/lib/{shared_library}"
Destination templates support `{module}`, `{target}`, `{profile}`, `{library}`,
`{shared_library}`, and `{python_extension_name}`.

### Generated files and headers

`command` artifacts run an argv-list command and then validate explicit outputs.
Outputs can be packaged into the wheel, installed as wheel shared data, or used
only as required validation checks. `header` artifacts are a typed shorthand for
validated header outputs and can also use `generator = "cbindgen"` in CLI or
build-script mode.

```toml
[[tool.hatch.build.hooks.hatch-rs.artifacts]]
name = "generated-package-files"
kind = "command"
command = ["python", "scripts/write_generated_files.py"]
inputs = ["scripts/write_generated_files.py"]

[[tool.hatch.build.hooks.hatch-rs.artifacts.outputs]]
source = "project/generated/package.txt"
destination = "project/generated/package.txt"
install-scheme = "package"

[[tool.hatch.build.hooks.hatch-rs.artifacts]]
name = "public-c-header"
kind = "header"
source = "project/include/project.h"
destination = "include/project/project.h"
install-scheme = "shared-data"
```

### ABI validation and artifact metadata

Shared-library artifacts can validate the copied C ABI library before the wheel
is finalized. The hook can check expected exported symbols, verify headers and
ABI strings/macros, load the copied library with `ctypes.CDLL`, run project
validation commands, include Windows import libraries, and emit a package-local
artifact manifest.

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

[[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}"
expected-symbols = ["project_ffi_answer"]
expected-headers = ["project/include/project.h"]
expected-abi-strings = ["PROJECT_ABI_VERSION"]
runtime-load = true
include-import-lib = true

[[tool.hatch.build.hooks.hatch-rs.artifacts.validation-commands]]
command = ["python", "scripts/validate_abi.py", "{destination}", "{header}"]
```

`include-import-lib` only packages an import library on Windows targets, where
Cargo emits `.dll.lib` or `.dll.a` files for downstream native linkers.

> [!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).
42 changes: 24 additions & 18 deletions hatch_rs/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ def initialize(self, version: str, build_data: dict[str, Any]) -> None:
# Perform any cleanup actions
build_plan.cleanup()

if not build_plan.libraries:
raise ValueError("No libraries were created by the build.")
if not build_plan.copied_artifacts and not build_plan.shared_data:
raise ValueError("No libraries or generated outputs were created by the build.")

# force include libraries
# for library in build_plan._libraries:
Expand All @@ -86,27 +86,33 @@ def initialize(self, version: str, build_data: dict[str, Any]) -> None:
# build_data["tag"] = f"cp{version_major}{version_minor}-abi3-{os_name}_{machine}"
# else:
# build_data["tag"] = f"cp{version_major}{version_minor}-cp{version_major}{version_minor}-{os_name}_{machine}"
build_data["pure_python"] = False
machine = platform_machine().lower()
version_major = version_info.major
version_minor = version_info.minor

# TODO abi3
if "darwin" in sys_platform:
os_name = "macosx_11_0"
elif "linux" in sys_platform:
os_name = "linux"
else:
os_name = "win"
if config.abi3:
build_data["tag"] = f"cp{version_major}{version_minor}-abi3-{os_name}_{machine}"
else:
build_data["tag"] = f"cp{version_major}{version_minor}-cp{version_major}{version_minor}-{os_name}_{machine}"
if build_plan.libraries:
build_data["pure_python"] = False
machine = platform_machine().lower()
version_major = version_info.major
version_minor = version_info.minor

# TODO abi3
if "darwin" in sys_platform:
os_name = "macosx_11_0"
elif "linux" in sys_platform:
os_name = "linux"
else:
os_name = "win"
if config.abi3:
build_data["tag"] = f"cp{version_major}{version_minor}-abi3-{os_name}_{machine}"
else:
build_data["tag"] = f"cp{version_major}{version_minor}-cp{version_major}{version_minor}-{os_name}_{machine}"

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

shared_data = build_data.setdefault("shared_data", {})
shared_data.update(build_plan.shared_data)

for path in force_include:
self._logger.warning(f"Force include: {path}")
for source, destination in shared_data.items():
self._logger.warning("Shared data: %s -> %s", source, destination)
Loading
Loading