commit | 02244360d5c0f28132062ac1de215cd48bc6d7b4 | [log] [tgz] |
---|---|---|
author | Prerak Mann <mannprerak2@gmail.com> | Mon Mar 08 02:23:25 2021 -0800 |
committer | GitHub <noreply@github.com> | Mon Mar 08 11:23:25 2021 +0100 |
tree | d6253943c96743dd90f2b9307cc1b736ebfb8e35 | |
parent | 749a828df87b55696a59387d1c6210f7aae1a4d8 [diff] |
Added option to generate dependency-only structs as opaque (#181)
Binding generator for FFI bindings.
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 DynamicLibrary _dylib; NativeLibrary(DynamicLibrary dynamicLibrary) : _dylib = dynamicLibrary; int sum(int a, int b) { return (_sum ??= _dylib.lookupFunction<_c_sum, _dart_sum>('sum'))(a, b); } _dart_sum? _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-lib: '/usr/local/opt/llvm/lib'
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/'
functions: include: # 'exclude' is also available. - [a-z][a-zA-Z0-9]* # Matches using regexp. - prefix.* # '.' matches any character. - someFuncName # Matches with exact name - anotherName # Full names have higher priority. rename: # Regexp groups based replacement. 'clang_(.*)': '$1' # full name matches have higher priority. 'clang_dispose': 'dispose' # Removes '_' from beginning of a name. '_(.*)': '$1' symbol-address: # Used to expose symbol and typedef. include: - myFunc enums: member-rename: '(.*)': # Matches any enum. # Removes '_' from beginning enum member name. '_(.*)': '$1' 'CXTypeKind': # Full names have higher priority. # $1 keeps only the 1st group i.e '(.*)'. 'CXType(.*)': '$1' globals: exclude: - aGlobal rename: # Removes '_' from beginning of a name. - '_(.*)': '$1'
array-workaround: true
comments: style: doxygen length: full
structs: 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
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() external int _unique_data_item_0; @ffi.Uint64() external int _unique_data_item_1; @ffi.Uint64() external 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.'); } } }
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 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
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.