Introduction
Bindings 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.cn/guides/libraries/c-interop.
FFIgen also has experimental support for calling ObjC and Swift code; for details see https://dart.cn/guides/libraries/objective-c-interop.
More FFIgen documentation can be found here.
Getting Started
This guide demonstrates how to call a custom C API from a standalone Dart
application. It assumes that Dart has been set up
(instructions) and that LLVM is installed on the
system (instructions). Furthermore, it assumes that the Dart
app has been created via dart create ffigen_example.
-
Add the utility package
package:ffias a dependency and the bindings generatorpackage:ffigenas a dev_dependency to the pubspec of your app by running:dart pub add ffi dev:ffigen. -
Write the C code and place it inside a subdirectory of your app. For this example we will place the following code in
src/add.handsrc/add.crespectively. It defines a simple API to add two integers in C.// in src/add.h: int add(int a, int b);// in src/add.c: int add(int a, int b) { return a + b; } -
To generate the bindings, we will write a script using
package:ffigenand place it undertool/ffigen.dart. The script instantiates and configures aFfiGenerator. Refer to the code comments below and the API docs to learn more about available configuration options.import 'dart:io'; import 'package:ffigen/ffigen.dart'; void main() { final packageRoot = Platform.script.resolve('../'); FfiGenerator( // Required. Output path for the generated bindings. output: Output(dartFile: packageRoot.resolve('lib/add.g.dart')), // Optional. Where to look for header files. headers: Headers(entryPoints: [packageRoot.resolve('src/add.h')]), // Optional. What functions to generate bindings for. functions: Functions.includeSet({'add'}), ).generate(); } -
Run the script with
dart run tool/ffigen.dartto generate the bindings. This will create the outputlib/add.g.dartfile, which can be imported by Dart code to access the C APIs. This command must be re-run whenever the FFIgen configuration (intool/ffigen.dart) or the C sources for which bindings are generated change. -
Import
add.g.dartin your Dart app and call the generated methods to access the native C API:import 'add.g.dart'; // ... void answerToLife() { print('The answer to the Ultimate Question is ${add(40, 2)}!'); } -
Before we can run the app, we need to compile the C sources. There are many ways to do that. For this example, we are using a build hook, which we define in
hook/build.dartas follows. This build hook also requires a dependency on thehooks,code_assets, andnative_toolchain_chelper packages, which we can add to our app by runningdart pub add hooks code_assets native_toolchain_c.import 'package:code_assets/code_assets.dart'; import 'package:hooks/hooks.dart'; import 'package:native_toolchain_c/native_toolchain_c.dart'; void main(List<String> args) async { await build(args, (input, output) async { if (input.config.buildCodeAssets) { final builder = CBuilder.library( name: 'add', assetName: 'add.g.dart', sources: ['src/add.c'], ); await builder.run(input: input, output: output); } }); }
That's it! Run your app with dart run to see it in action!
The complete and runnable example can be found in example/add.
More Examples
The code_asset package contains comprehensive examples
that showcase FFIgen. Additional examples that show how FFIgen can be used
in different scenarios can also be found in the example directory.
Requirements
LLVM (9+) must be installed on your system to use package:ffigen. Install it
in the following way:
Linux
- Install libclangdev:
- with apt-get:
sudo apt-get install libclang-dev. - with dnf:
sudo dnf install clang-devel.
- with apt-get:
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.
YAML Configuration Reference
In addition to the Dart API shown in the "Getting Started" section, FFIgen can also be configured via YAML. Support for the YAML configuration will be eventually phased out, and using the Dart API is recommended.
A YAML configuration can be either provided in the project's pubspec.yaml file
under the key ffigen or via a custom YAML file. To generate bindings
configured via YAML run either dart run ffigen if using the pubspec.yaml
file or run dart run ffigen --config config.yaml where config.yaml is the
path to your custom YAML file.
The following configuration options are available:
| Key | Explanation | Example |
|---|---|---|
| output (Required) |
Output path of the generated bindings. |
or
|
| 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. Complete path to the dynamic library can also be supplied. Required if FFIgen is unable to find this at default locations. |
|
| 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. |
|
| name (Prefer) |
Name of generated class. |
|
| description (Prefer) |
Dart Doc for generated class. |
|
| compiler-opts | Pass compiler options to clang. You can also pass these via the command line tool. |
and/or via the command line -
|
| 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 |
|
|
functions structs unions enums unnamed-enums macros globals |
Filters for declarations. Default: all are included. Options - - Include/Exclude declarations. - Rename declarations. - Rename enum, struct, and union members, function parameters, and ObjC interface and protocol methods and properties. - Expose symbol-address for functions and globals. |
|
| typedefs | Filters for referred typedefs. Options - - Include/Exclude (referred typedefs only). - Rename typedefs. Note: By default, typedefs that are not referred to anywhere will not be generated. |
|
| include-unused-typedefs |
Also generate typedefs that are not referred to anywhere.
Default: false |
|
| functions.expose-typedefs | Generate the typedefs to Native and Dart type of a function Default: Inline types are used and no typedefs to Native/Dart type are generated. |
|
| functions.leaf | Set isLeaf:true for functions. Default: all functions are excluded. |
|
| functions.variadic-arguments | Generate multiple functions with different variadic arguments. Default: var args for any function are ignored. |
|
| 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. |
|
| 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. |
|
| 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 |
|
| sort | Sort the bindings according to name. Default: false, i.e keep the order as in the source files. |
|
| use-supported-typedefs | Should automatically map typedefs, E.g uint8_t => Uint8, int16_t => Int16, size_t => Size etc. Default: true |
|
| use-dart-handle | Should map `Dart_Handle` to `Handle`. Default: true |
|
| ignore-source-errors | Where to ignore compiler warnings/errors in source header files. Default: false |
and/or via the command line -
|
| silence-enum-warning | Where to silence warning for enum integer type mimicking. The integer type used for enums is implementation-defined, and not part of the ABI. FFIgen tries to mimic the integer sizes chosen by the most common compilers for the various OS and architecture combinations. Default: false |
|
| exclude-all-by-default |
When a declaration filter (eg `functions:` or `structs:`) is empty or
unset, it defaults to including everything. If this flag is enabled, the
default behavior is to exclude everything instead. Default: false |
|
| preamble | Raw header of the file, pasted as-it-is. |
|
| library-imports | Specify library imports for use in type-map. Note: ffi (dart:ffi) is already available as a predefined import. |
|
| type-map | Map types like integers, typedefs, structs, unions to any other type. Sub-fields - typedefs, structs, unions, ints lib must be specified in library-imports or be one of a predefined import. |
|
| ffi-native |
WARNING: Native support is EXPERIMENTAL. The API may change
in a breaking way without notice.
Generate `@Native` bindings instead of bindings using `DynamicLibrary` or `lookup`. |
|
| language |
WARNING: Other language support is EXPERIMENTAL. The API may change
in a breaking way without notice.
Choose the input language. Must be one of 'c', or 'objc'. Defaults to 'c'. |
|
| output.objc-bindings |
Choose where the generated ObjC code (if any) is placed. The default path
is `'${output.bindings}.m'`, so if your Dart bindings are in
`generated_bindings.dart`, your ObjC code will be in
`generated_bindings.dart.m`.
This ObjC file will only be generated if it's needed. If it is generated, it must be compiled into your package, as part of a flutter plugin or build.dart script. If your package already has some sort of native build, you can simply add this generated ObjC file to that build. |
|
| output.symbol-file | Generates a symbol file yaml containing all types defined in the generated output. |
|
| import.symbol-files | Import symbols from a symbol file. Used for sharing type definitions from other pacakges. |
|
| external-versions |
Interfaces, methods, and other API elements may be marked with
deprecation annotations that indicate which platform version they were
deprecated in. If external-versions is set, APIs that were
deprecated as of the minimum version will be omitted from the
generated bindings.
The minimum version is specified per platform, and an API will be generated if it is available on *any* of the targeted platform versions. If a version is not specified for a particular platform, the API's inclusion will be based purely on the platforms that have a specified minimum version. Current support OS keys are ios and macos. If you have a use case for version checking on other OSs, please file an issue. |
|
Objective-C configuration options
| Key | Explanation | Example |
|---|---|---|
|
objc-interfaces objc-protocols objc-categories |
Filters for Objective-C interface, protocol, and category declarations. This option works the same as other declaration filters like `functions` and `structs`. |
|
|
objc-interfaces.module objc-protocols.module |
Adds a module prefix to the interface/protocol name when loading it
from the dylib. This is only relevant for ObjC headers that are generated
wrappers for a Swift library. See example/swift for more information.
This is not necessary for objc-categories. |
|
|
objc-interfaces.member-filter objc-protocols.member-filter objc-categories.member-filter |
Filters interface and protocol methods and properties. This is a map from interface name to a list of method include and exclude rules. The interface name can be a regexp. The include and exclude rules work exactly like any other declaration. See below for more details. |
|
|
include-transitive-objc-interfaces include-transitive-objc-protocols |
By default, Objective-C interfaces and protocols that are not directly
included by the inclusion rules, but are transitively depended on by
the inclusions, are not fully code genned. Transitively included
interfaces are generated as stubs, and transitive protocols are omitted.
If these flags are enabled, transitively included interfaces and protocols are fully code genned. Default: false |
|
| include-transitive-objc-categories |
By default, if an Objective-C interface is included in the bindings, all
the categories that extend it are also included. To filter them, set this
flag to false, then use objc-categories to include/exclude particular
categories.
Transitive categories are generated by default because it's not always obvious from the Apple documentation which interface methods are declared directly in the interface, and which are declared in categories. So it may appear that the interface is missing methods, when in fact those methods are part of a category. This would be a difficult problem to diagnose if transitive categories were not generated by default. Default: true |
|
Libraries
- ffigen Errors
- This is the Dart API for FFIgen. The main entrypoint is the FfiGenerator class.
Errors
Errors in FFIgen