Binding generator for FFI bindings.
Note: ffigen only supports parsing
C
headers.
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).
class NativeLibrary {
final Pointer<T> Function<T extends NativeType>(String symbolName)
_lookup;
NativeLibrary(DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
NativeLibrary.fromLookup(
Pointer<T> Function<T extends NativeType>(String symbolName)
lookup)
: _lookup = lookup;
int sum(int a, int b) {
return _sum(a, b);
}
late final _sum_ptr = _lookup<NativeFunction<_c_sum>>('sum');
late final _dart_sum _sum = _sum_ptr.asFunction<_dart_sum>();
}
typedef _c_sum = Int32 Function(Int32 a, Int32 b);
typedef _dart_sum = int Function(int a, int b);
- Add
ffigen
underdev_dependencies
in yourpubspec.yaml
. - Install LLVM (see Installing LLVM).
- Configurations must be provided in
pubspec.yaml
or in a custom YAML file (see configurations). - Run the tool-
dart run ffigen
.
Jump to FAQ.
package:ffigen
uses LLVM. Install LLVM (9+) in the following way.
- Install libclangdev -
sudo apt-get install libclang-dev
.
- Install Visual Studio with C++ development support.
- Install LLVM or
winget install -e --id LLVM.LLVM
.
- Install Xcode.
- Install LLVM -
brew install llvm
.
Configurations can be provided in 2 ways-
- In the project's
pubspec.yaml
file 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-
Key | Explaination | Example |
---|---|---|
output (Required) |
Output path of the generated bindings. |
output: 'generated_bindings.dart' |
llvm-path | Path to llvm folder. 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. Required if ffigen is unable to find this at default locations. |
llvm-path:
- '/usr/local/opt/llvm'
- 'C:\Program Files\llvm`
- '/usr/lib/llvm-11' |
headers (Required) |
The header entry-points and include-directives. Glob syntax is allowed. If include-directives are not specified ffigen will generate everything directly/transitively under the entry-points. |
headers:
entry-points:
- 'folder/**.h'
- 'folder/specific_header.h'
include-directives:
- '**index.h'
- '**/clang-c/**'
- '/full/path/to/a/header.h' |
name (Prefer) |
Name of generated class. |
name: 'SQLite' |
description (Prefer) |
Dart Doc for generated class. |
description: 'Bindings to SQLite' |
compiler-opts | Pass compiler options to clang. You can also pass these via the command line tool. |
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'" |
compiler-opts-automatic -> macos -> include-c-standard-library | Tries to automatically find and add C standard library path to
compiler-opts on macos. Default: true |
compiler-opts-automatic:
macos:
include-c-standard-library: false |
functions structs unions enums unnamed-enums macros globals |
Filters for declarations. Default: all are included. Options - - Include/Exclude declarations. - Rename declarations. - Rename enum and struct members. - Expose symbol-address and typedef for functions and globals. |
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 and typedef.
include:
- myFunc
enums:
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' |
structs -> pack | Override the @Packed(X) annotation for generated structs. Options - none, 1, 2, 4, 8, 16 You can use RegExp to match with the generated names. Note: Ffigen can only reliably identify packing specified using __attribute__((__packed__)). However, structs packed using `#pragma pack(...)` or any other way could potentially be incorrect in which case you can override the generated annotations. |
structs:
pack:
# Matches with the generated name.
'NoPackStruct': none # No packing
'.*': 1 # Pack all structs with value 1 |
comments | Extract documentation comments for declarations. The style and length of the comments recognized can be specified with the following options- style: doxygen(default) | any length: brief | full(default) If you want to disable all comments you can also pass comments: false. |
comments:
style: any
length: full |
structs -> dependency-only unions -> dependency-only |
If `opaque`, generates empty `Opaque` structs/unions if they
were not included in config (but were added since they are a dependency) and
only passed by reference(pointer). Options - full(default) | opaque |
structs:
dependency-only: opaque
unions:
dependency-only: opaque |
sort | Sort the bindings according to name. Default: false, i.e keep the order as in the source files. |
sort: true |
use-supported-typedefs | Should automatically map typedefs, E.g uint8_t => Uint8, int16_t => Int16 etc. Default: true |
use-supported-typedefs: true |
dart-bool | Should generate dart `bool` instead of Uint8 for c99 bool in functions. Default: true |
dart-bool: true |
use-dart-handle | Should map `Dart_Handle` to `Handle`. Default: true |
use-dart-handle: true |
preamble | Raw header of the file, pasted as-it-is. |
preamble: |
/// AUTO GENERATED FILE, DO NOT EDIT.
///
/// Generated by `package:ffigen`. |
typedef-map | Map typedefs to Native Types. Values can only be Void, Uint8, Int8, Uint16, Int16, Uint32, Int32, Uint64, Int64, IntPtr, Float and Double. |
typedef-map:
'my_custom_type': 'IntPtr'
'size_t': 'Int64' |
size-map | Size of integers to use (in bytes). The defaults (see example) may not be portable on all OS. Do not change these unless absolutely sure. |
# These are optional and also default,
# Omitting any and the default
# will be used.
size-map:
char: 1
unsigned char: 1
short: 2
unsigned short: 2
int: 4
unsigned int: 4
long: 8
unsigned long: 8
long long: 8
unsigned long long: 8
enum: 4 |
- Multi OS support for types such as long. Issue #7
cd examples/<example_u_want_to_run>
, Rundart pub get
.- Run
dart run ffigen
.
- Dynamic library for some tests need to be built before running the examples.
cd test/native_test
.- Run
dart build_test_dylib.dart
.
Run tests from the root of the package with dart run test
.
Note: If llvm is not installed in one of the default locations, tests may fail.
Ffigen supports regexp based renaming, the regexp must be a
full match, for renaming you can use regexp groups ($1
means group 1).
E.g - For renaming clang_dispose_string
to string_dispose
.
We can can match it using clang_(.*)_(.*)
and rename with $2_$1
.
Here's an example of how to remove prefix underscores from any struct and its members.
structs:
...
rename:
'_(.*)': '$1' # Removes prefix underscores from all structures.
member-rename:
'.*': # Matches any struct.
'_(.*)': '$1' # Removes prefix underscores from members.
The default behaviour is to include everything directly/transitively under
each of the entry-points
specified.
If you only want to have declarations directly particular header you can do so
using include-directives
. You can use glob matching to match header paths.
headers:
entry-points:
- 'path/to/my_header.h'
include-directives:
- '**my_header.h' # This glob pattern matches the header path.
Ffigen supports including/excluding declarations using full regexp matching.
Here's an example to filter functions using names
functions:
include:
- 'clang.*' # Include all functions starting with clang.
exclude:
- '.*dispose': # Exclude all functions ending with dispose.
This will include clang_help
. But will exclude clang_dispose
.
Note: exclude overrides include.
Ffigen treats char*
just as any other pointer,(Pointer<Int8>
).
To convert these to/from String
, you can use package:ffi. Use ptr.cast<Utf8>().toDartString()
to convert char*
to dart string
and "str".toNativeUtf8()
to convert string
to char*
.
Although dart:ffi
doesn't have a NativeType for bool
, they can be implemented as Uint8
.
Ffigen generates dart bool
for function parameters and return type by default.
To disable this, and use int
instead, set dart-bool: false
in configurations.
Unnamed enums are handled separately, under the key unnamed-enums
, and are generated as top level constants.
Here's an example that shows how to include/exclude/rename unnamed enums
unnamed-enums:
include:
- 'CX_.*'
exclude:
- '.*Flag'
rename:
'CXType_(.*)': '$1'
This happens when an excluded struct/union is a dependency to some included declaration. (A dependency means a struct is being passed/returned by a function or is member of another struct in some way)
Note: If you supply structs
-> dependency-only
as opaque
ffigen will generate
these struct dependencies as Opaque
if they were only passed by reference(pointer).
structs:
dependency-only: opaque
unions:
dependency-only: opaque
By default all native pointers and typedefs are hidden, but you can use the
symbol-address
subkey for functions/globals and make them public by matching with its name. The pointers are then accesible via nativeLibrary.addresses
and the native
typedef are prefixed with Native_
.
Example -
functions:
symbol-address:
include:
- 'myFunc'
- '.*' # Do this to expose all pointers.