Ffigen
FFI binding generator
Install / Use
/learn @dart-archive/FfigenREADME
This package has moved!
The package can now be found at https://github.com/dart-lang/native/tree/main/pkgs/ffigen
Binding generator for FFI bindings.
Note: ffigen only supports parsing
Cheaders, notC++headers.
This bindings generator can be used to call C code -- or code in another language that compiles to C modules that follow the C calling convention -- such as Go or Rust. For more details, see: https://dart.dev/guides/libraries/c-interop
ffigen also has experimental support for calling ObjC and Swift code; for details see: https://dart.dev/guides/libraries/objective-c-interop
Example
For some header file example.h:
int sum(int a, int b);
Add configurations to Pubspec File:
ffigen:
output: 'generated_bindings.dart'
headers:
entry-points:
- 'example.h'
Output (generated_bindings.dart).
import 'dart:ffi' as ffi;
class NativeLibrary {
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
NativeLibrary(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
NativeLibrary.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;
int sum(int a, int b) {
return _sum(a, b);
}
late final _sumPtr = _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int)>>('sum');
late final _sum = _sumPtr.asFunction<int Function(int, int)>();
}
}
Using this package
- Add
ffigenunderdev_dependenciesin yourpubspec.yaml(rundart pub add -d ffigen). - Add
package:ffiunderdependenciesin yourpubspec.yaml(rundart pub add ffi). - Install LLVM (see Installing LLVM).
- Configurations must be provided in
pubspec.yamlor in a custom YAML file (see configurations). - Run the tool-
dart run ffigen.
Jump to FAQ.
Installing LLVM
package:ffigen uses LLVM. Install LLVM (9+) in the following way.
Linux
-
Install libclangdev.
With apt-get:
sudo apt-get install libclang-dev.With dnf:
sudo dnf install clang-devel.
Windows
- Install Visual Studio with C++ development support.
- Install LLVM or
winget install -e --id LLVM.LLVM.
MacOS
- Install Xcode.
- Install Xcode command line tools -
xcode-select --install. - Install LLVM -
brew install llvm.
Configurations
Configurations can be provided in 2 ways-
- In the project's
pubspec.yamlfile under the keyffigen. - Via a custom YAML file, then specify this file while running -
dart run ffigen --config config.yaml
The following configuration options are available-
<table> <thead> <tr> <th>Key</th> <th>Explaination</th> <th>Example</th> </tr> <colgroup> <col> <col style="width: 100px;"> </colgroup> </thead> <tbody> <tr> <td>output<br><i><b>(Required)</b></i></td> <td>Output path of the generated bindings.</td> <td>output: 'generated_bindings.dart'
or
output:
bindings: 'generated_bindings.dart'
...
</td>
</tr>
<tr>
<td>llvm-path</td>
<td>Path to <i>llvm</i> folder.<br> ffigen will sequentially search
for `lib/libclang.so` on linux, `lib/libclang.dylib` on macOs and
`bin\libclang.dll` on windows, in the specified paths.<br><br>
Complete path to the dynamic library can also be supplied.<br>
<i>Required</i> if ffigen is unable to find this at default locations.</td>
<td>
llvm-path:
- '/usr/local/opt/llvm'
- 'C:\Program Files\llvm`
- '/usr/lib/llvm-11'
# Specify exact path to dylib
- '/usr/lib64/libclang.so'
</td>
</tr>
<tr>
<td>headers<br><i><b>(Required)</b></i></td>
<td>The header entry-points and include-directives. Glob syntax is allowed.<br>
If include-directives are not specified ffigen will generate everything directly/transitively under the entry-points.</td>
<td>
headers:
entry-points:
- 'folder/**.h'
- 'folder/specific_header.h'
include-directives:
- '**index.h'
- '**/clang-c/**'
- '/full/path/to/a/header.h'
</td>
</tr>
<tr>
<td>name<br><i>(Prefer)</i></td>
<td>Name of generated class.</td>
<td>
name: 'SQLite'
</td>
</tr>
<tr>
<td>description<br><i>(Prefer)</i></td>
<td>Dart Doc for generated class.</td>
<td>
description: 'Bindings to SQLite'
</td>
</tr>
<tr>
<td>compiler-opts</td>
<td>Pass compiler options to clang. You can also pass
these via the command line tool.</td>
<td>
compiler-opts:
- '-I/usr/lib/llvm-9/include/'
and/or via the command line -
dart run ffigen --compiler-opts "-I/headers
-L 'path/to/folder name/file'"
</td>
</tr>
<tr>
<td>compiler-opts-automatic -> macos -> include-c-standard-library</td>
<td>Tries to automatically find and add C standard library path to
compiler-opts on macos.<br>
<b>Default: true</b>
</td>
<td>
compiler-opts-automatic:
macos:
include-c-standard-library: false
</td>
</tr>
<tr>
<td>
functions<br><br>structs<br><br>unions<br><br>enums<br><br>
unnamed-enums<br><br>macros<br><br>globals
</td>
<td>Filters for declarations.<br><b>Default: all are included.</b><br><br>
Options -<br>
- Include/Exclude declarations.<br>
- Rename declarations.<br>
- Rename enum and struct members.<br>
- Expose symbol-address for functions and globals.<br>
</td>
<td>
functions:
include: # 'exclude' is also available.
# Matches using regexp.
- [a-z][a-zA-Z0-9]*
# '.' matches any character.
- prefix.*
# Matches with exact name
- someFuncName
# Full names have higher priority.
- anotherName
rename:
# Regexp groups based replacement.
'clang_(.*)': '$1'
'clang_dispose': 'dispose'
# Removes '_' from beginning.
'_(.*)': '$1'
symbol-address:
# Used to expose symbol address.
include:
- myFunc
structs:
rename:
# Removes prefix underscores
# from all structures.
'_(.*)': '$1'
member-rename:
'.*': # Matches any struct.
# Removes prefix underscores
# from members.
'_(.*)': '$1'
enums:
rename:
# Regexp groups based replacement.
'CXType_(.*)': '$1'
member-rename:
'(.*)': # Matches any enum.
# Removes '_' from beginning
# enum member name.
'_(.*)': '$1'
# Full names have higher priority.
'CXTypeKind':
# $1 keeps only the 1st
# group i.e only '(.*)'.
'CXType(.*)': '$1'
globals:
exclude:
- aGlobal
rename:
# Removes '_' from
# beginning of a name.
'_(.*)': '$1'
</td>
</tr>
<tr>
<td>typedefs</td>
<td>Filters for referred typedefs.<br><br>
Options -<br>
- Include/Exclude (referred typedefs only).<br>
- Rename typedefs.<br><br>
Note: Typedefs that are not referred to anywhere will not be generated.
</td>
<td>
typedefs:
exclude:
# Typedefs starting with `p` are not generated.
- 'p.*'
rename:
# Removes '_' from beginning of a typedef.
'_(.*)': '$1'
</td>
</tr>
<tr>
<td>functions -> expose-typedefs</td>
<td>Generate the typedefs to Native and Dart type of a function<br>
<b>Default: Inline types are used and no typedefs to Native/Dart
type are generated.</b>
</td>
<td>
functions:
expose-typedefs:
include:
# Match function name.
- 'myFunc'
# Do this to expose types for all function.
- '.*'
exclude:
# If you only use exclude, then everything
# not excluded is generated.
- 'dispose'
</td>
</tr>
<tr>
<td>functions -> leaf</td>
<td>Set isLeaf:true for functions.<br>
<b>Default: all functions are excluded.</b>
</td>
<td>
functions:
leaf:
include:
# Match function name.
- 'myFunc'
# Do this to set isLeaf:true for all functions.
- '.*'
exclude:
# If you only use exclude, then everything
# not excluded is generated.
- 'dispose'
</td>
</tr>
<tr>
<td>functions -> variadic-arguments</td>
<td>Generate multiple functions with different variadic arguments.<br>
<b>Default: var args for any function are ignored.</b>
</td>
<td>
functions:
variadic-arguments:
myfunc:
// Native C types are supported
- [int, unsigned char, long*, float**]
// Common C typedefs (stddef.h) are supported too
- [uint8_t, intptr_t, size_t, wchar_t*]
// Structs/Unions/Typedefs from generated code or a library import can be referred too.
- [MyStruct*, my_custom_lib.CustomUnion]
</td>
</tr>
<tr>
<td>structs -> pack</td>
<td>Override the @Packed(X) annotation for generated structs.<br><br>
<i>Options - none, 1, 2, 4, 8, 16</i><br>
You can use RegExp to match with the <b>generated</b> names.<br><br>
Note: Ffigen can only reliably identify packing specified using
__attribute__((__packed__)). However, structs packed using
`#pragma pack(...)` or any other way could <i>potentially</i> be incorrect
in which case you can override the generated annotations.
</td>
<td>
structs:
pack:
# Matches with the generated name.
'NoPackStruct': none # No packing
'.*': 1 # Pack all structs with value 1
</td>
</tr>
<tr