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);
ffigen
under dev_dependencies
in your pubspec.yaml
.pubspec.yaml
or in a custom YAML file (see configurations).dart run ffigen
.Jump to FAQ.
package:ffigen
uses LLVM. Install LLVM (9+) in the following way.
sudo apt-get install libclang-dev
.winget install -e --id LLVM.LLVM
.brew install llvm
.Configurations can be provided in 2 ways-
pubspec.yaml
file under the key ffigen
.dart run ffigen --config config.yaml
The following configuration options are available-
output: 'generated_bindings.dart'
llvm-path: - '/usr/local/opt/llvm' - 'C:\Program Files\llvm` - '/usr/lib/llvm-11'
headers: entry-points: - 'folder/**.h' - 'folder/specific_header.h' include-directives: - '**index.h' - '**/clang-c/**' - '/full/path/to/a/header.h'
name: 'SQLite'
description: 'Bindings to SQLite'
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: false
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: # Matches with the generated name. 'NoPackStruct': none # No packing '.*': 1 # Pack all structs with value 1
comments: style: any length: full
structs: dependency-only: opaque unions: dependency-only: opaque
sort: true
use-supported-typedefs: true
dart-bool: true
use-dart-handle: true
preamble: | /// AUTO GENERATED FILE, DO NOT EDIT. /// /// Generated by `package:ffigen`.
typedef-map: 'my_custom_type': 'IntPtr' 'size_t': 'Int64'
# 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
cd examples/<example_u_want_to_run>
, Run dart pub get
.dart run ffigen
.cd test/native_test
.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.