ffigen 22.1.0
ffigen: ^22.1.0 copied to clipboard
Generator for FFI bindings, using LibClang to parse C, Objective-C, and Swift files.
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.dev/guides/libraries/c-interop.
FFIgen also supports calling ObjC code. For details see https://dart.dev/guides/libraries/objective-c-interop.
More FFIgen documentation can be found here.
Note
The YAML configuration format is deprecated and will be removed in a future version. Please migrate to the programmatic Dart generator API. You can use the migration skill in skills/ffigen-migrate-yaml-to-dart to automate the migration with an agent, but it also serves as good documentation if doing the migration manually.
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'; Future<void> main() async { final packageRoot = Platform.script.resolve('../'); final generator = FfiGenerator( // Required. Output path for the generated bindings. output: Output( dart: DartOutput(path: packageRoot.resolve('lib/add.g.dart')), ), // Optional. Where to look for header files. input: Input(entryPoints: [packageRoot.resolve('src/add.h')]), // Optional. Transform and filter AST nodes. visitors: [Visitor(func: (node) => node.isIncluded = node.name == 'add')], ); await generator.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 must be installed on your system to use package:ffigen. Version 18 or
newer is recommended, since that is the oldest version FFIgen is tested
against. 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.
Configuration #
FFIgen is configured using a Dart script, typically placed under tool/ffigen.dart and executed via dart run tool/ffigen.dart.
The script instantiates an FfiGenerator with your desired configuration and calls await generator.generate().
Example #
import 'dart:io';
import 'package:ffigen/ffigen.dart';
Future<void> main() async {
final packageRoot = Platform.script.resolve('../');
final generator = FfiGenerator(
// Required. Output path and options for the generated bindings.
output: Output(
dart: DartOutput(
path: packageRoot.resolve('lib/src/generated_bindings.dart'),
),
),
// Where to look for header files.
input: Input(entryPoints: [packageRoot.resolve('src/my_header.h')]),
// Visitors transform and filter AST nodes. By default, all top level APIs
// are excluded from the bindings. You must explicitly include the APIs
// you're interested in. Here we include all functions and structs.
visitors: [
Visitor(
func: (node) => node.isIncluded = true,
struct: (node) => node.isIncluded = true,
),
],
);
await generator.generate();
}
Run the script to generate bindings:
dart run tool/ffigen.dart
See the examples and API documentation for more information.