Skip to content

Commit a87b2dd

Browse files
committed
docs: specify one-call polyglot exports
1 parent 40bf5e7 commit a87b2dd

1 file changed

Lines changed: 117 additions & 0 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# One-Call Polyglot Exports Design
2+
3+
## Goal
4+
5+
Equilibrium should make polyglot FFI feel like one library call. A user should be able to point Equilibrium at a source file and get a C ABI artifact plus usable bindings without needing to hand-write a build graph or run the CLI.
6+
7+
The default API remains library-first:
8+
9+
```rust
10+
let module = equilibrium_ffi::load("src/native/math.rs")?;
11+
```
12+
13+
That call should detect the source language, build the C ABI output, generate bindings or import metadata, and return a module handle that tells the caller what was produced.
14+
15+
## Export Discovery
16+
17+
Equilibrium should prefer explicit export markers when they exist:
18+
19+
- Rust functions marked with the `equilibrium-rust` macro.
20+
- Zig functions declared with `pub export`.
21+
- Nim procedures using C export pragmas or Equilibrium helpers.
22+
- D functions declared with `extern(C) export` or marked by the helper attribute.
23+
- C and C++ declarations already present in a header.
24+
25+
If no explicit markers are found and the caller did not provide an export list in the API call or `equilibrium.toml`, Equilibrium should export every top-level function in that source file.
26+
27+
That fallback should be intentionally simple. It is a convenience path for small files and demos, not a promise to infer complex language-specific visibility rules across an entire project.
28+
29+
## API Shape
30+
31+
The one-call path should support three levels of control:
32+
33+
```rust
34+
equilibrium_ffi::load("src/native/math.zig")?;
35+
```
36+
37+
This uses detection, explicit markers if present, and all-functions fallback when no markers exist.
38+
39+
```rust
40+
equilibrium_ffi::load_with_options(
41+
"src/native/math.zig",
42+
LoadOptions::default().exports(["add", "multiply"]),
43+
)?;
44+
```
45+
46+
This uses caller-provided exports and skips implicit all-function export.
47+
48+
```toml
49+
[target.math]
50+
language = "zig"
51+
sources = ["src/native/math.zig"]
52+
exports = ["add", "multiply"]
53+
```
54+
55+
This gives repeatable project configuration when the caller wants it.
56+
57+
## Artifact Model
58+
59+
Every supported language should lower to the same artifact model:
60+
61+
- source language
62+
- generated C ABI library or object
63+
- generated or discovered C header
64+
- generated consumer bindings
65+
- discovered exports
66+
- warnings
67+
68+
Rust remains the first-class consumer because this crate is Rust, but the artifact model should not assume Rust is the only consumer. Zig, Nim, D, and future helpers can import the same C ABI surface through generated wrappers.
69+
70+
## CLI Role
71+
72+
The CLI should stay optional. It can generate bindings, inspect projects, or precompute a build graph, but the default workflow should not require it.
73+
74+
Useful CLI command surfaces:
75+
76+
```bash
77+
eq generate src/native/math.zig
78+
eq graph
79+
eq build-graph
80+
```
81+
82+
These commands should expose the same behavior as the library, not define a separate architecture.
83+
84+
## Build Time and Runtime
85+
86+
The same export discovery and artifact generation should work from `build.rs` and at runtime:
87+
88+
- In `build.rs`, generated artifacts should land in Cargo's output directory or a caller-provided directory.
89+
- At runtime, generated artifacts should land in a cache directory or caller-provided directory.
90+
- Outputs should be deterministic enough that repeated calls can reuse existing artifacts when inputs have not changed.
91+
92+
## Error Handling
93+
94+
Errors should say which stage failed:
95+
96+
- language detection
97+
- export discovery
98+
- compiler discovery
99+
- C ABI artifact generation
100+
- header generation
101+
- binding generation
102+
- dynamic loading
103+
104+
Implicit all-function export should produce warnings when a function cannot be represented safely in the C ABI instead of silently creating invalid bindings.
105+
106+
## Testing
107+
108+
The implementation should add focused coverage for:
109+
110+
- explicit export marker detection
111+
- all-functions fallback when no markers are present
112+
- API-provided export list overriding fallback
113+
- `equilibrium.toml` export list overriding fallback
114+
- generated artifact metadata
115+
- at least one Rust-to-Zig or Zig-to-Rust consumer wrapper path through the C ABI
116+
117+
Compiler-dependent integration tests should skip cleanly when a host compiler is missing.

0 commit comments

Comments
 (0)