Skip to content

jpnurmi/ffigen

 
 

Repository files navigation

Build Status

Experimental binding generator for FFI bindings.

Example

For some header file example.h:

int sum(int a, int b);

Add configurations to Pubspec File:

ffigen:
  output: 'generated_bindings.dart'
  headers:
    - 'example.h'

Output (generated_bindings.dart).

class NativeLibrary {
  final DynamicLibrary _dylib;

  NativeLibrary(DynamicLibrary dynamicLibrary) : _dylib = dynamicLibrary;

  int sum(int a, int b) {
    _sum ??= _dylib.lookupFunction<_c_sum, _dart_sum>('sum');
    return _sum(a, b);
  }
  _dart_sum _sum;;
}
typedef _c_sum = ffi.Int32 Function(Int32 a, Int32 b);
typedef _dart_sum = int Function(int a,int b);

Using this package

  • Add this package as dev_dependency in your pubspec.yaml.
  • Setup for use (see Setup).
  • Configurations must be provided in pubspec.yaml or in a custom YAML file (see configurations).
  • Run the tool- pub run ffigen.

Setup

package:ffigen uses LLVM. Install LLVM in the following way.

ubuntu/linux

  1. Install libclangdev - sudo apt-get install libclang-dev.

Windows

  1. Install Visual Studio with C++ development support.
  2. Install LLVM.

MacOS

  1. Install Xcode.
  2. Install LLVM - brew install llvm.

Configurations

Configurations can be provided in 2 ways-

  1. In the project's pubspec.yaml file under the key ffigen.
  2. Via a custom YAML file, then specify this file while running - pub run ffigen --config config.yaml

The following configuration options are available-

Key Required Explaination Example
output yes Output path of the generated bindings.
output: 'generated_bindings.dart'
headers yes List of C headers to use. Glob syntax is allowed.
headers:
  - 'folder/**.h'
  - 'folder/specific_header.h'
header-filter no Name of headers to include/exclude.
header-filter:
  include:
    - 'index.h'
    - 'platform.h'
name prefer Name of generated class.
name: 'SQLite'
description prefer Dart Doc for generated class.
description: 'Bindings to SQLite'
compiler-opts no Pass compiler options to clang.
compiler-opts: '-I/usr/lib/llvm-9/include/'
functions
structs
enums
no Filters for declarations.
Default: all are included
functions:
  include: # Exclude is also available.
    names: # Matches with exact name.
      - someFuncName
      - anotherName
    matches: # Matches using regexp.
      - prefix.*
      - [a-z][a-zA-Z0-9]*
  prefix: 'cx_' # Prefix added to all functions.
  prefix-replacement: # Replaces a functions's prefix.
    'clang_': ''
    '_': 'C'
array-workaround no Should generate workaround for fixed arrays in Structs. See Array Workaround
Default: false
array-workaround: true
comments no Extract documentation comments for declarations.
Options: brief/full/none
Default: brief
By default clang only parses documentation comments. To enable ordinary comments (starting with // or /*) add
-fparse-all-comments to compiler-opts.
comments: 'full'
sort no Sort the bindings according to name.
Default: false, i.e keep the order as in the source files.
sort: true
use-supported-typedefs no Should automatically map typedefs, E.g uint8_t => Uint8, int16_t => Int16 etc.
Default: true
use-supported-typedefs: true
preamble no Raw header of the file, pasted as-it-is.
preamble: |
  /// AUTO GENERATED FILE, DO NOT EDIT.
  ///
  /// Generated by `package:ffigen`.
size-map no 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

Array-Workaround

Fixed size array's in structs aren't currently supported by Dart. However we provide a workaround, using which array items can now be accessed using [] operator.

Here's a C structure from libclang-

typedef struct {
  unsigned long long data[3];
} CXFileUniqueID;

The generated code is -

class CXFileUniqueID extends ffi.Struct {
  @ffi.Uint64()
  int _unique_data_item_0;
  @ffi.Uint64()
  int _unique_data_item_1;
  @ffi.Uint64()
  int _unique_data_item_2;

  /// Helper for array `data`.
  ArrayHelper_CXFileUniqueID_data_level0 get data =>
      ArrayHelper_CXFileUniqueID_data_level0(this, [3], 0, 0);
}

/// Helper for array `data` in struct `CXFileUniqueID`.
class ArrayHelper_CXFileUniqueID_data_level0 {
  final CXFileUniqueID _struct;
  final List<int> dimensions;
  final int level;
  final int _absoluteIndex;
  int get length => dimensions[level];
  ArrayHelper_CXFileUniqueID_data_level0(
      this._struct, this.dimensions, this.level, this._absoluteIndex);
  void _checkBounds(int index) {
    if (index >= length || index < 0) {
      throw RangeError(
          'Dimension $level: index not in range 0..${length} exclusive.');
    }
  }

  int operator [](int index) {
    _checkBounds(index);
    switch (_absoluteIndex + index) {
      case 0:
        return _struct._unique_data_item_0;
      case 1:
        return _struct._unique_data_item_1;
      case 2:
        return _struct._unique_data_item_2;
      default:
        throw Exception('Invalid Array Helper generated.');
    }
  }

  void operator []=(int index, int value) {
    _checkBounds(index);
    switch (_absoluteIndex + index) {
      case 0:
        _struct._unique_data_item_0 = value;
        break;
      case 1:
        _struct._unique_data_item_1 = value;
        break;
      case 2:
        _struct._unique_data_item_2 = value;
        break;
      default:
        throw Exception('Invalid Array Helper generated.');
    }
  }
}

Limitations

  1. Multi OS support for types such as long. Issue #7
  2. Function's passing/returning structs by value are skipped. Issue #3
  3. Structs containing structs will have all their members removed. Issue #4

Trying out examples

  1. cd examples/<example_u_want_to_run>, Run pub get.
  2. Run pub run ffigen.

Running Tests

  1. Run setup to build the LLVM wrapper - pub run ffigen:setup.
  2. Dynamic library for some tests also need to be built before running the examples.
  3. cd test/native_test.
  4. Run dart build_test_dylib.dart.

Run tests from the root of the package with pub run test.

About

Experimental FFI binding generator

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Dart 98.5%
  • C 1.5%