Skip to content

Commit 8504ff7

Browse files
committed
Enhanced C ABI integration
1 parent 2ae7dab commit 8504ff7

17 files changed

Lines changed: 1240 additions & 40 deletions

File tree

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,67 @@ destination = "project/lib/{shared_library}"
5050
Destination templates support `{module}`, `{target}`, `{profile}`, `{library}`,
5151
`{shared_library}`, and `{python_extension_name}`.
5252

53+
### Generated files and headers
54+
55+
`command` artifacts run an argv-list command and then validate explicit outputs.
56+
Outputs can be packaged into the wheel, installed as wheel shared data, or used
57+
only as required validation checks. `header` artifacts are a typed shorthand for
58+
validated header outputs and can also use `generator = "cbindgen"` in CLI or
59+
build-script mode.
60+
61+
```toml
62+
[[tool.hatch.build.hooks.hatch-rs.artifacts]]
63+
name = "generated-package-files"
64+
kind = "command"
65+
command = ["python", "scripts/write_generated_files.py"]
66+
inputs = ["scripts/write_generated_files.py"]
67+
68+
[[tool.hatch.build.hooks.hatch-rs.artifacts.outputs]]
69+
source = "project/generated/package.txt"
70+
destination = "project/generated/package.txt"
71+
install-scheme = "package"
72+
73+
[[tool.hatch.build.hooks.hatch-rs.artifacts]]
74+
name = "public-c-header"
75+
kind = "header"
76+
source = "project/include/project.h"
77+
destination = "include/project/project.h"
78+
install-scheme = "shared-data"
79+
```
80+
81+
### ABI validation and artifact metadata
82+
83+
Shared-library artifacts can validate the copied C ABI library before the wheel
84+
is finalized. The hook can check expected exported symbols, verify headers and
85+
ABI strings/macros, load the copied library with `ctypes.CDLL`, run project
86+
validation commands, include Windows import libraries, and emit a package-local
87+
artifact manifest.
88+
89+
```toml
90+
[tool.hatch.build.hooks.hatch-rs]
91+
module = "project"
92+
target-dir = "isolated"
93+
artifact-manifest = true
94+
95+
[[tool.hatch.build.hooks.hatch-rs.artifacts]]
96+
name = "c-abi"
97+
kind = "shared-library"
98+
manifest = "rust/Cargo.toml"
99+
library = "project_ffi"
100+
crate-type = "cdylib"
101+
destination = "project/lib/{shared_library}"
102+
expected-symbols = ["project_ffi_answer"]
103+
expected-headers = ["project/include/project.h"]
104+
expected-abi-strings = ["PROJECT_ABI_VERSION"]
105+
runtime-load = true
106+
include-import-lib = true
107+
108+
[[tool.hatch.build.hooks.hatch-rs.artifacts.validation-commands]]
109+
command = ["python", "scripts/validate_abi.py", "{destination}", "{header}"]
110+
```
111+
112+
`include-import-lib` only packages an import library on Windows targets, where
113+
Cargo emits `.dll.lib` or `.dll.a` files for downstream native linkers.
114+
53115
> [!NOTE]
54116
> 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).

hatch_rs/plugin.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ def initialize(self, version: str, build_data: dict[str, Any]) -> None:
6666
# Perform any cleanup actions
6767
build_plan.cleanup()
6868

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

7272
# force include libraries
7373
# for library in build_plan._libraries:
@@ -86,27 +86,33 @@ def initialize(self, version: str, build_data: dict[str, Any]) -> None:
8686
# build_data["tag"] = f"cp{version_major}{version_minor}-abi3-{os_name}_{machine}"
8787
# else:
8888
# build_data["tag"] = f"cp{version_major}{version_minor}-cp{version_major}{version_minor}-{os_name}_{machine}"
89-
build_data["pure_python"] = False
90-
machine = platform_machine().lower()
91-
version_major = version_info.major
92-
version_minor = version_info.minor
93-
94-
# TODO abi3
95-
if "darwin" in sys_platform:
96-
os_name = "macosx_11_0"
97-
elif "linux" in sys_platform:
98-
os_name = "linux"
99-
else:
100-
os_name = "win"
101-
if config.abi3:
102-
build_data["tag"] = f"cp{version_major}{version_minor}-abi3-{os_name}_{machine}"
103-
else:
104-
build_data["tag"] = f"cp{version_major}{version_minor}-cp{version_major}{version_minor}-{os_name}_{machine}"
89+
if build_plan.libraries:
90+
build_data["pure_python"] = False
91+
machine = platform_machine().lower()
92+
version_major = version_info.major
93+
version_minor = version_info.minor
94+
95+
# TODO abi3
96+
if "darwin" in sys_platform:
97+
os_name = "macosx_11_0"
98+
elif "linux" in sys_platform:
99+
os_name = "linux"
100+
else:
101+
os_name = "win"
102+
if config.abi3:
103+
build_data["tag"] = f"cp{version_major}{version_minor}-abi3-{os_name}_{machine}"
104+
else:
105+
build_data["tag"] = f"cp{version_major}{version_minor}-cp{version_major}{version_minor}-{os_name}_{machine}"
105106

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

112+
shared_data = build_data.setdefault("shared_data", {})
113+
shared_data.update(build_plan.shared_data)
114+
111115
for path in force_include:
112116
self._logger.warning(f"Force include: {path}")
117+
for source, destination in shared_data.items():
118+
self._logger.warning("Shared data: %s -> %s", source, destination)

0 commit comments

Comments
 (0)