External Libraries and FFI
seed interoperates with platform libraries through explicit extern
declarations and linker inputs. Foreign calls are unsafe until an audited seed
wrapper establishes their type, ownership, lifetime, error, and synchronization
contracts.
Foreign declarations
The clean syntax is:
extern "c" fn c_sqrt(value: f64) -> f64 link_name "sqrt";
pub fn sqrt_value(value: f64) -> f64 {
unsafe { c_sqrt(value) }
}
The optional string after extern selects the calling convention. link_name
selects the foreign symbol when it differs from the seed declaration name.
Declarations may end with a semicolon.
An extern declaration does not identify or load a shared library by itself.
Direct compiler builds provide native inputs explicitly:
seed/compiler/llvm/build/seed \
--emit-binary app \
--link-library /usr/lib/x86_64-linux-gnu/libm.so.6 \
app.sd
The system-library path is host-specific and should be discovered by the
package/install procedure rather than hard-coded as a portable value.
--link-library accepts a path to a shared object/archive or the name of an
installed seed package at target levels that support linking. --link-object
adds an object file. The repository launcher forwards supported direct compiler
flags when an application needs explicit native inputs.
Package requirements
A package that contains extern declarations must:
- declare
[package].platformsinseed.toml; - provide a non-empty root
INSTALL.mddescribing system packages, required ABI versions, and non-pkg-config setup for each platform; - keep target/library selection consistent with the canonical target matrix;
- pass
seed publish --checkbefore publication or installation workflows.
Example manifest fragment:
[package]
name = "math-wrapper"
version = "0.1.0"
kind = "lib"
platforms = ["linux-x86-64", "linux-aarch64"]
The platform declaration is validation and compatibility metadata. It does not install the operating-system dependency.
Safe-wrapper checklist
Expose an ordinary safe pub fn only when its implementation enforces every
foreign precondition relevant to callers:
- pointer non-nullness, alignment, initialized extent, and pointee validity;
- buffer length and mutability;
- handle ownership and exact acquire/release pairing;
- borrowed-storage lifetime across the call and after any returned view;
- status, errno, and partial-operation handling;
- callback lifetime, calling convention, and thread/synchronization rules;
- allocator-domain compatibility for values crossing the ABI;
- target-specific layout and ABI assumptions.
If the wrapper cannot establish these conditions, expose it as unsafe fn and
document the caller obligations.
Pointers and handles
Raw pointers use explicit pointee mutability:
extern "c" fn foreign_open() -> *mut u8;
extern "c" fn foreign_close(handle: *mut u8);
Checking a raw value does not create safe provenance. Prefer an audited nominal owner that stores the handle, rejects null during construction, calls the foreign release function from its destructor, and exposes only operations whose preconditions it can maintain.
Do not represent a foreign owned handle as a copyable integer merely for convenience. That loses exact cleanup and permits duplicate release.
Borrowed foreign views
An ordinary foreign pointer or slice return has no safe lifetime proof.
@static_view is available only on an extern whose ABI guarantees that the
returned str or slice has process/static lifetime:
@static_view extern "c" fn foreign_version() -> str;
This attribute is a trusted provenance assertion, not a lifetime extension, and the call remains unsafe. Input-derived or handle-derived foreign views should be kept behind a wrapper owner rather than mislabeled static.
Resources across seed libraries
Native seed libraries use a profile-qualified runtime/allocator identity.
Passing string, bytes, shared_bytes, or destructor-owned values across a
seed library boundary is supported only when the producer and consumer
artifacts have compatible target, ABI, build profile, runtime profile, runtime
features, and interface identity.
An arbitrary C library does not understand those owned layouts. Prefer scalar, plain C-layout struct, pointer, and pointer/length ABIs at third-party boundaries, with conversion and ownership retained inside the seed wrapper.
Target limitations
Object emission is not proof of FFI or hosted linking. Consult
seed/compiler/llvm/tests/GATE14_TARGET_MATRIX.tsv before documenting an
external package as supported on a target. In particular, the current
wasm32-wasi row is object-only and does not provide an operational WASI FFI or
runtime.
Grove compatibility note
Many wrappers under the top-level grove/ tree use superseded foreign syntax,
capitalized types, implicit runtime helpers, or manual ownership conventions.
Their presence is not evidence that they compile with the operational LLVM
language. Port and verify a wrapper before listing it as a current external
library.