# C header bindings

> Generate Java Foreign Function and Memory API bindings from C headers with elide jextract, then compile and run them against a native shared library.

`elide jextract` reads C headers and generates Java source files using Java's Foreign Function and Memory
(FFM) API. It embeds OpenJDK jextract and shares Elide's Clang/LLVM native library with
the [embedded C compiler and linker](https://elide.help/docs/toolchain/native/cc), so generation needs no separate jextract, JDK or
libclang installation.

> [!NOTE] Distribution support
> Native tools are part of Elide 1.6.2. They are included on macOS arm64 and dynamically
> linked Linux amd64/arm64, and omitted on Windows, fully static Elide builds and custom builds using
> `--no-jvm-toolchain`.

Use this command when you want generated `.java` files to inspect, distribute or compile separately.
For an Elide Java project, [C header bindings in the Java compiler](https://elide.help/docs/toolchain/jvm/javac#c-header-bindings)
can generate and compile bindings in memory from Pkl declarations or ad hoc `elide javac` options.

## Generate Java source

Save a small library interface as `native/answer.h`:

```c
int answer(void);
```

Generate its Java bindings:

```bash
elide jextract -- --output generated \
  --target-package example.ffi \
  --header-class-name NativeApi \
  native/answer.h
```

The output includes `generated/example/ffi/NativeApi.java` and any supporting sources needed by the
bindings. The header's `answer` declaration becomes a Java accessor such as `NativeApi.answer()`.
Generation produces source code, not a native library, class files or JNI wrappers. It does not load
the application library, so its implementation does not need to be present at this step.

Without `--output`, sources go in the current directory. Without `--target-package`, they use the unnamed
package. Without `--header-class-name`, jextract derives a class name from the header filename, such as
`answer_h` for `answer.h`. Supply an explicit header class name when passing multiple headers.

## Configure parsing and select declarations

Pass jextract's arguments after `--`; its own help lists the full interface:

```bash
elide jextract -- --help
```

| Option | Purpose |
| --- | --- |
| `--output PATH` | Directory for generated Java sources. |
| `-t PACKAGE`, `--target-package PACKAGE` | Package for generated classes. |
| `--header-class-name NAME` | Generated header class name. |
| `-I DIR`, `--include-dir DIR` | Add a C header search directory; repeat for multiple directories. |
| `-D NAME=VALUE`, `--define-macro NAME=VALUE` | Define a preprocessor macro; repeat as needed. |
| `--include-function NAME` | Select a function for generation. |
| `--include-constant NAME` | Select a macro or enum constant. |
| `--include-struct NAME`, `--include-union NAME` | Select a struct or union. |
| `--include-typedef NAME`, `--include-var NAME` | Select a typedef or global variable. |
| `-l LIBSPEC`, `--library LIBSPEC` | Add a shared library to the generated runtime lookup. |
| `--use-system-load-library` | Use `System.loadLibrary` or `System.load` for those libraries. |

For example, add a library's include directory, set a macro and select one function:

```bash
elide jextract -- --output generated -t example.ffi \
  --header-class-name NativeApi \
  -I native/include -D ANSWER_API_VERSION=1 \
  --include-function answer native/answer.h
```

The standalone command also reads Clang arguments from `compile_flags.txt` in the current working directory,
one argument per line. This differs from the integrated Java compilation path, which uses its declared
binding configuration. Elide passes jextract's diagnostics and exit status through; a failed extraction
does not yield usable bindings.

## Call a shared library

Suppose `native/libanswer.so` on Linux, or `native/libanswer.dylib` on macOS, exports the implementation:

```c
int answer(void) {
  return 42;
}
```

The library must be built for the machine running the Java application. Obtain it from the library's build
or package, or compile `native/answer.c` with a separate full C compiler:

```bash
# Linux
clang -shared -fPIC native/answer.c -o native/libanswer.so

# macOS
clang -dynamiclib native/answer.c -o native/libanswer.dylib
```

These are external compiler commands. `elide cc` exposes object/IR output and in-memory JIT linking;
its current CLI does not expose shared-library output.

Regenerate the bindings with an explicit library path. On Linux:

```bash
elide jextract -- --output generated -t example.ffi \
  --header-class-name NativeApi \
  -l :./native/libanswer.so native/answer.h
```

On macOS, use `-l :./native/libanswer.dylib` instead. The leading `:` marks a path rather than a logical
library name. A relative library path is resolved when the generated code runs, so run this example from
the directory containing `native/`.

Save this caller as `Main.java`:

```java
import example.ffi.NativeApi;

public class Main {
  public static void main(String[] args) {
    System.out.println(NativeApi.answer());
  }
}
```

Compile the generated sources and caller with Elide's bundled Java 25 compiler, then enable native access
when running them:

```bash
elide javac -- -d out generated/example/ffi/*.java Main.java
elide java -- --enable-native-access=ALL-UNNAMED -cp out Main
```

```terminaloutput
42
```

`ALL-UNNAMED` applies to this classpath example. For a named module, enable native access for the module
containing the generated bindings. Native access is needed by the generated FFM code at runtime;
successful generation and Java compilation do not establish that the shared library can be loaded.

## Library discovery and memory ownership

By default, `-l answer` generates a `SymbolLookup.libraryLookup` for the platform-mapped library name, such
as `libanswer.so` or `libanswer.dylib`. Its discovery follows the platform loader's rules.
`-l :PATH` embeds the path instead. Repeat `-l` for multiple libraries. The generated lookup also falls back
to the class loader's symbols and the native linker's default symbols.

For Java's `java.library.path` lookup, generate with `--use-system-load-library -l answer`, then supply
`-Djava.library.path=./native` to the Java launcher. This emits `System.loadLibrary("answer")`; a path
library specification instead uses `System.load` and requires an absolute path. Header include directories
set with `-I` do not configure runtime library discovery.

Elide does not package the application library with generated sources or JARs. Keep the matching shared
library and its dependencies available at runtime. Generating FFM bindings also does not automatically
register those calls for an arbitrary Native Image application.

Generated bindings expose native pointers through `MemorySegment` and provide layouts for supported C
types. They do not infer ownership rules: use an appropriate `Arena` for Java-managed native allocations,
keep allocations and callback stubs alive while C uses them, and release library-owned memory through the
library's documented API. Native code runs with the process's privileges, and incorrect lifetimes or ABI
mismatches can crash the process.

## Headers and platform limits

Elide supplies jextract's builtin Clang headers. Library-specific headers and their dependencies still need
to be available through the supplied paths and include directories. This is a C header generator; C++
interfaces need a C-compatible API. Review warnings about unsupported declarations rather than assuming
every header construct becomes a Java type. Opaque structs can remain usable as pointer handles even when
their layouts cannot be generated.

The macOS distribution does not include an Apple SDK. SDK-dependent headers require a compatible installed
SDK and appropriate include configuration. Elide's packaged Native Image compiler/sysroot configuration
is a separate facility; it does not make every host header available to jextract. The self-contained header
above requires no system SDK.
