ffigen 22.1.0 copy "ffigen: ^22.1.0" to clipboard
ffigen: ^22.1.0 copied to clipboard

Generator for FFI bindings, using LibClang to parse C, Objective-C, and Swift files.

Build Status Coverage Status pub package package publisher

Introduction #

Bindings generator for FFI bindings.

Note: FFIgen only supports parsing C headers, not C++ 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.

  1. Add the utility package package:ffi as a dependency and the bindings generator package:ffigen as a dev_dependency to the pubspec of your app by running: dart pub add ffi dev:ffigen.

  2. 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.h and src/add.c respectively. 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;
    }
    
  3. To generate the bindings, we will write a script using package:ffigen and place it under tool/ffigen.dart. The script instantiates and configures a FfiGenerator. 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();
    }
    
  4. Run the script with dart run tool/ffigen.dart to generate the bindings. This will create the output lib/add.g.dart file, which can be imported by Dart code to access the C APIs. This command must be re-run whenever the FFIgen configuration (in tool/ffigen.dart) or the C sources for which bindings are generated change.

  5. Import add.g.dart in 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)}!');
    }
    
  6. 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.dart as follows. This build hook also requires a dependency on the hooks, code_assets, and native_toolchain_c helper packages, which we can add to our app by running dart 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 #

  1. Install libclangdev:
    • with apt-get: sudo apt-get install libclang-dev.
    • with dnf: sudo dnf install clang-devel.

Windows #

  1. Install Visual Studio with C++ development support.
  2. Install LLVM or winget install -e --id LLVM.LLVM.

macOS

  1. Install Xcode.
  2. 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.

338
likes
160
points
1.14M
downloads

Documentation

API reference

Publisher

verified publishertools.dart.dev

Weekly Downloads

Generator for FFI bindings, using LibClang to parse C, Objective-C, and Swift files.

Repository (GitHub)
View/report issues
Contributing

Topics

#interop #ffi #codegen

License

BSD-3-Clause (license)

Dependencies

args, cli_util, code_assets, collection, dart_style, ffi, file, glob, hooks, logging, meta, native_toolchain_c, package_config, path, pub_semver, quiver, yaml, yaml_edit

More

Packages that depend on ffigen