blob: fd7f28fcd18175ded570ad0fd3b1408c38a46336 [file] [log] [blame] [view] [edit]
[![Build Status](https://github.com/dart-lang/native/actions/workflows/ffigen.yml/badge.svg)](https://github.com/dart-lang/native/actions/workflows/ffigen.yml)
[![Coverage Status](https://coveralls.io/repos/github/dart-lang/native/badge.svg?branch=main)](https://coveralls.io/github/dart-lang/native?branch=main)
[![pub package](https://img.shields.io/pub/v/ffigen.svg)](https://pub.dev/packages/ffigen)
[![package publisher](https://img.shields.io/pub/publisher/ffigen.svg)](https://pub.dev/packages/ffigen/publisher)
Binding generator for [FFI](https://dart.dev/guides/libraries/c-interop) 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 has experimental support for calling ObjC and Swift code;
for details see:
https://dart.dev/guides/libraries/objective-c-interop
## Example
For some header file _example.h_:
```C
int sum(int a, int b);
```
Add configurations to Pubspec File:
```yaml
ffigen:
output: 'generated_bindings.dart'
headers:
entry-points:
- 'example.h'
```
Output (_generated_bindings.dart_).
```dart
import 'dart:ffi' as ffi;
class NativeLibrary {
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
NativeLibrary(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
NativeLibrary.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;
int sum(int a, int b) {
return _sum(a, b);
}
late final _sumPtr = _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int)>>('sum');
late final _sum = _sumPtr.asFunction<int Function(int, int)>();
}
```
## Using this package
- Add `ffigen` under `dev_dependencies` in your `pubspec.yaml` (run `dart pub add -d ffigen`).
- Add `package:ffi` under `dependencies` in your `pubspec.yaml` (run `dart pub add ffi`).
- Install LLVM (see [Installing LLVM](#installing-llvm)).
- Configurations must be provided in `pubspec.yaml` or in a custom YAML file (see [configurations](#configurations)).
- Run the tool- `dart run ffigen`.
Jump to [FAQ](#faq).
## Installing LLVM
`package:ffigen` uses LLVM. Install LLVM (9+) 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](https://releases.llvm.org/download.html) or `winget install -e --id LLVM.LLVM`.
#### MacOS
1. Install Xcode.
2. Install Xcode command line tools - `xcode-select --install`.
## 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 -
`dart run ffigen --config config.yaml`
The following configuration options are available-
<table>
<thead>
<tr>
<th>Key</th>
<th>Explaination</th>
<th>Example</th>
</tr>
<colgroup>
<col>
<col style="width: 100px;">
</colgroup>
</thead>
<tbody>
<tr>
<td>output<br><i><b>(Required)</b></i></td>
<td>Output path of the generated bindings.</td>
<td>
```yaml
output: 'generated_bindings.dart'
```
or
```yaml
output:
bindings: 'generated_bindings.dart'
...
```
</td>
</tr>
<tr>
<td>llvm-path</td>
<td>Path to <i>llvm</i> folder.<br> 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.<br><br>
Complete path to the dynamic library can also be supplied.<br>
<i>Required</i> if ffigen is unable to find this at default locations.</td>
<td>
```yaml
llvm-path:
- '/usr/local/opt/llvm'
- 'C:\Program Files\llvm`
- '/usr/lib/llvm-11'
# Specify exact path to dylib
- '/usr/lib64/libclang.so'
```
</td>
</tr>
<tr>
<td>headers<br><i><b>(Required)</b></i></td>
<td>The header entry-points and include-directives. Glob syntax is allowed.<br>
If include-directives are not specified ffigen will generate everything directly/transitively under the entry-points.</td>
<td>
```yaml
headers:
entry-points:
- 'folder/**.h'
- 'folder/specific_header.h'
include-directives:
- '**index.h'
- '**/clang-c/**'
- '/full/path/to/a/header.h'
```
</td>
</tr>
<tr>
<td>name<br><i>(Prefer)</i></td>
<td>Name of generated class.</td>
<td>
```yaml
name: 'SQLite'
```
</td>
</tr>
<tr>
<td>description<br><i>(Prefer)</i></td>
<td>Dart Doc for generated class.</td>
<td>
```yaml
description: 'Bindings to SQLite'
```
</td>
</tr>
<tr>
<td>compiler-opts</td>
<td>Pass compiler options to clang. You can also pass
these via the command line tool.</td>
<td>
```yaml
compiler-opts:
- '-I/usr/lib/llvm-9/include/'
```
and/or via the command line -
```bash
dart run ffigen --compiler-opts "-I/headers
-L 'path/to/folder name/file'"
```
</td>
</tr>
<tr>
<td>compiler-opts-automatic.macos.include-c-standard-library</td>
<td>Tries to automatically find and add C standard library path to
compiler-opts on macos.<br>
<b>Default: true</b>
</td>
<td>
```yaml
compiler-opts-automatic:
macos:
include-c-standard-library: false
```
</td>
</tr>
<tr>
<td>
functions<br><br>structs<br><br>unions<br><br>enums<br><br>
unnamed-enums<br><br>macros<br><br>globals
</td>
<td>Filters for declarations.<br><b>Default: all are included.</b><br><br>
Options -<br>
- Include/Exclude declarations.<br>
- Rename declarations.<br>
- Rename enum, struct, and union members, function parameters, and ObjC
interface and protocol methods and properties.<br>
- Expose symbol-address for functions and globals.<br>
</td>
<td>
```yaml
functions:
include: # 'exclude' is also available.
# Matches using regexp.
- [a-z][a-zA-Z0-9]*
# '.' matches any character.
- prefix.*
# Matches with exact name
- someFuncName
# Full names have higher priority.
- anotherName
rename:
# Regexp groups based replacement.
'clang_(.*)': '$1'
'clang_dispose': 'dispose'
# Removes '_' from beginning.
'_(.*)': '$1'
symbol-address:
# Used to expose symbol address.
include:
- myFunc
structs:
rename:
# Removes prefix underscores
# from all structures.
'_(.*)': '$1'
member-rename:
'.*': # Matches any struct.
# Removes prefix underscores
# from members.
'_(.*)': '$1'
enums:
rename:
# Regexp groups based replacement.
'CXType_(.*)': '$1'
member-rename:
'(.*)': # Matches any enum.
# Removes '_' from beginning
# enum member name.
'_(.*)': '$1'
# Full names have higher priority.
'CXTypeKind':
# $1 keeps only the 1st
# group i.e only '(.*)'.
'CXType(.*)': '$1'
as-int:
# These enums will be generated as Dart integers instead of Dart enums
include:
- MyIntegerEnum
globals:
exclude:
- aGlobal
rename:
# Removes '_' from
# beginning of a name.
'_(.*)': '$1'
```
</td>
</tr>
<tr>
<td>typedefs</td>
<td>Filters for referred typedefs.<br><br>
Options -<br>
- Include/Exclude (referred typedefs only).<br>
- Rename typedefs.<br><br>
Note: By default, typedefs that are not referred to anywhere will not be generated.
</td>
<td>
```yaml
typedefs:
exclude:
# Typedefs starting with `p` are not generated.
- 'p.*'
rename:
# Removes '_' from beginning of a typedef.
'_(.*)': '$1'
```
</td>
</tr>
<tr>
<td>include-unused-typedefs</td>
<td>
Also generate typedefs that are not referred to anywhere.
<br>
<b>Default: false</b>
</td>
<td>
```yaml
include-unused-typedefs: true
```
</td>
</tr>
<tr>
<td>functions.expose-typedefs</td>
<td>Generate the typedefs to Native and Dart type of a function<br>
<b>Default: Inline types are used and no typedefs to Native/Dart
type are generated.</b>
</td>
<td>
```yaml
functions:
expose-typedefs:
include:
# Match function name.
- 'myFunc'
# Do this to expose types for all function.
- '.*'
exclude:
# If you only use exclude, then everything
# not excluded is generated.
- 'dispose'
```
</td>
</tr>
<tr>
<td>functions.leaf</td>
<td>Set isLeaf:true for functions.<br>
<b>Default: all functions are excluded.</b>
</td>
<td>
```yaml
functions:
leaf:
include:
# Match function name.
- 'myFunc'
# Do this to set isLeaf:true for all functions.
- '.*'
exclude:
# If you only use exclude, then everything
# not excluded is generated.
- 'dispose'
```
</td>
</tr>
<tr>
<td>functions.variadic-arguments</td>
<td>Generate multiple functions with different variadic arguments.<br>
<b>Default: var args for any function are ignored.</b>
</td>
<td>
```yaml
functions:
variadic-arguments:
myfunc:
// Native C types are supported
- [int, unsigned char, long*, float**]
// Common C typedefs (stddef.h) are supported too
- [uint8_t, intptr_t, size_t, wchar_t*]
// Structs/Unions/Typedefs from generated code or a library import can be referred too.
- [MyStruct*, my_custom_lib.CustomUnion]
```
</td>
</tr>
<tr>
<td>structs.pack</td>
<td>Override the @Packed(X) annotation for generated structs.<br><br>
<i>Options - none, 1, 2, 4, 8, 16</i><br>
You can use RegExp to match with the <b>generated</b> names.<br><br>
Note: Ffigen can only reliably identify packing specified using
__attribute__((__packed__)). However, structs packed using
`#pragma pack(...)` or any other way could <i>potentially</i> be incorrect
in which case you can override the generated annotations.
</td>
<td>
```yaml
structs:
pack:
# Matches with the generated name.
'NoPackStruct': none # No packing
'.*': 1 # Pack all structs with value 1
```
</td>
</tr>
<tr>
<td>comments</td>
<td>Extract documentation comments for declarations.<br>
The style and length of the comments recognized can be specified with the following options- <br>
<i>style: doxygen(default) | any </i><br>
<i>length: brief | full(default) </i><br>
If you want to disable all comments you can also pass<br>
comments: false.
</td>
<td>
```yaml
comments:
style: any
length: full
```
</td>
</tr>
<tr>
<td>structs.dependency-only<br><br>
unions.dependency-only
</td>
<td>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).<br>
<i>Options - full(default) | opaque</i><br>
</td>
<td>
```yaml
structs:
dependency-only: opaque
unions:
dependency-only: opaque
```
</td>
</tr>
<tr>
<td>sort</td>
<td>Sort the bindings according to name.<br>
<b>Default: false</b>, i.e keep the order as in the source files.
</td>
<td>
```yaml
sort: true
```
</td>
</tr>
<tr>
<td>use-supported-typedefs</td>
<td>Should automatically map typedefs, E.g uint8_t => Uint8, int16_t => Int16, size_t => Size etc.<br>
<b>Default: true</b>
</td>
<td>
```yaml
use-supported-typedefs: true
```
</td>
</tr>
<tr>
<td>use-dart-handle</td>
<td>Should map `Dart_Handle` to `Handle`.<br>
<b>Default: true</b>
</td>
<td>
```yaml
use-dart-handle: true
```
</td>
</tr>
<tr>
<td>ignore-source-errors</td>
<td>Where to ignore compiler warnings/errors in source header files.<br>
<b>Default: false</b>
</td>
<td>
```yaml
ignore-source-errors: true
```
and/or via the command line -
```bash
dart run ffigen --ignore-source-errors
```
</td>
</tr>
<tr>
<td>silence-enum-warning</td>
<td>Where to silence warning for enum integer type mimicking.<br>
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.<br>
<b>Default: false</b>
</td>
<td>
```yaml
silence-enum-warning: true
```
</td>
</tr>
<tr>
<td>exclude-all-by-default</td>
<td>
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.<br>
<b>Default: false</b>
</td>
<td>
```yaml
exclude-all-by-default: true
```
</td>
</tr>
<tr>
<td>preamble</td>
<td>Raw header of the file, pasted as-it-is.</td>
<td>
```yaml
preamble: |
// ignore_for_file: camel_case_types, non_constant_identifier_names
```
</td>
</tr>
<tr>
<td>library-imports</td>
<td>Specify library imports for use in type-map.<br><br>
Note: ffi (dart:ffi) is already available as a predefined import.
</td>
<td>
```yaml
library-imports:
custom_lib: 'package:some_pkg/some_file.dart'
```
</td>
</tr>
<tr>
<td>type-map</td>
<td>Map types like integers, typedefs, structs, unions to any other type.<br><br>
<b>Sub-fields</b> - <i>typedefs</i>, <i>structs</i>, <i>unions</i>, <i>ints</i><br><br>
<b><i>lib</i></b> must be specified in <i>library-imports</i> or be one of a predefined import.
</td>
<td>
```yaml
type-map:
'native-types': # Targets native types.
'char':
'lib': 'pkg_ffi' # predefined import.
'c-type': 'Char'
# For native-types dart-type can be be int, double or float
# but same otherwise.
'dart-type': 'int'
'int':
'lib': 'custom_lib'
'c-type': 'CustomType4'
'dart-type': 'int'
'typedefs': # Targets typedefs.
'my_type1':
'lib': 'custom_lib'
'c-type': 'CustomType'
'dart-type': 'CustomType'
'structs': # Targets structs.
'my_type2':
'lib': 'custom_lib'
'c-type': 'CustomType2'
'dart-type': 'CustomType2'
'unions': # Targets unions.
'my_type3':
'lib': 'custom_lib'
'c-type': 'CustomType3'
'dart-type': 'CustomType3'
```
</td>
</tr>
<tr>
<td>ffi-native</td>
<td>
<b>WARNING:</b> Native support is EXPERIMENTAL. The API may change
in a breaking way without notice.
<br><br>
Generate `@Native` bindings instead of bindings using `DynamicLibrary` or `lookup`.
</td>
<td>
```yaml
ffi-native:
asset-id: 'package:some_pkg/asset' # Optional, was assetId in previous versions
```
</td>
</tr>
<tr>
<td>language</td>
<td>
<b>WARNING:</b> Other language support is EXPERIMENTAL. The API may change
in a breaking way without notice.
<br><br>
Choose the input langauge. Must be one of 'c', or 'objc'. Defaults to 'c'.
</td>
<td>
```yaml
language: 'objc'
```
</td>
</tr>
<tr>
<td>output.objc-bindings</td>
<td>
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`.
<br><br>
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.
</td>
<td>
```yaml
output:
...
objc-bindings: 'generated_bindings.m'
```
</td>
</tr>
<tr>
<td>output.symbol-file</td>
<td>Generates a symbol file yaml containing all types defined in the generated output.</td>
<td>
```yaml
output:
...
symbol-file:
# Although file paths are supported here, prefer Package Uri's here
# so that other pacakges can use them.
output: 'package:some_pkg/symbols.yaml'
import-path: 'package:some_pkg/base.dart'
```
</td>
</tr>
<tr>
<td>import.symbol-files</td>
<td>Import symbols from a symbol file. Used for sharing type definitions from other pacakges.</td>
<td>
```yaml
import:
symbol-files:
# Both package Uri and file paths are supported here.
- 'package:some_pkg/symbols.yaml'
- 'path/to/some/symbol_file.yaml'
```
</td>
</tr>
<tr>
<td>
external-versions
</td>
<td>
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.
<br><br>
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.
<br><br>
Current support OS keys are ios and macos. If you have a use case for
version checking on other OSs, please file an issue.
</td>
<td>
```yaml
external-versions:
# See https://docs.flutter.dev/reference/supported-platforms.
ios:
min: 12.0.0
macos:
min: 10.14.0
```
</td>
</tr>
</tbody>
</table>
### Objective-C config options
<table>
<thead>
<tr>
<th>Key</th>
<th>Explaination</th>
<th>Example</th>
</tr>
<colgroup>
<col>
<col style="width: 100px;">
</colgroup>
</thead>
<tbody>
<tr>
<td>
objc-interfaces<br><br>
objc-protocols<br><br>
objc-categories
</td>
<td>
Filters for Objective C interface, protocol, and category declarations.
This option works the same as other declaration filters like `functions`
and `structs`.
</td>
<td>
```yaml
objc-interfaces:
include:
# Includes a specific interface.
- 'MyInterface'
# Includes all interfaces starting with "NS".
- 'NS.*'
exclude:
# Override the above NS.* inclusion, to exclude NSURL.
- 'NSURL'
rename:
# Removes '_' prefix from interface names.
'_(.*)': '$1'
objc-protocols:
include:
# Generates bindings for a specific protocol.
- MyProtocol
objc-categories:
include:
# Generates bindings for a specific category.
- MyCategory
```
</td>
</tr>
<tr>
<td>
objc-interfaces.module<br><br>
objc-protocols.module
</td>
<td>
Adds a module prefix to the interface/protocol name when loading it
from the dylib. This is only relevent for ObjC headers that are generated
wrappers for a Swift library. See example/swift for more information.
<br><br>
This is not necessary for objc-categories.
</td>
<td>
```yaml
headers:
entry-points:
# Generated by swiftc to wrap foo_lib.swift.
- 'foo_lib-Swift.h'
objc-interfaces:
include:
# Eg, foo_lib contains a set of classes prefixed with FL.
- 'FL.*'
module:
# Use 'foo_lib' as the module name for all the FL.* classes.
# We don't match .* here because other classes like NSString
# shouldn't be given a module prefix.
'FL.*': 'foo_lib'
```
</td>
</tr>
<tr>
<td>
objc-interfaces.member-filter<br><br>
objc-protocols.member-filter<br><br>
objc-categories.member-filter
</td>
<td>
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
<a href="#how-does-objc-method-filtering-work">below</a> for more details.
</td>
<td>
```yaml
objc-interfaces:
member-filter:
MyInterface:
include:
- "someMethod:withArg:"
# Since MyInterface has an include rule, all other methods
# are excluded by default.
objc-protocols:
member-filter:
NS.*: # Matches all protocols starting with NS.
exclude:
- copy.* # Remove all copy methods from these protocols.
objc-categories:
member-filter:
MyCategory:
include:
- init.* # Include all init methods.
```
</td>
</tr>
<tr>
<td>
include-transitive-objc-interfaces<br><br>
include-transitive-objc-protocols
</td>
<td>
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.
<br><br>
If these flags are enabled, transitively included interfaces and protocols
are fully code genned.
<br><br>
<b>Default: false</b>
</td>
<td>
```yaml
include-transitive-objc-interfaces: true
include-transitive-objc-protocols: true
```
</td>
</tr>
<tr>
<td>
include-transitive-objc-categories
</td>
<td>
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.
<br><br>
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.
<br><br>
<b>Default: true</b>
</td>
<td>
```yaml
include-transitive-objc-categories: false
```
</td>
</tr>
</tbody>
</table>
## Trying out examples
1. `cd examples/<example_u_want_to_run>`, Run `dart pub get`.
2. Run `dart run ffigen`.
## Running Tests
See [test/README.md](test/README.md)
## FAQ
### Can ffigen be used for removing underscores or renaming declarations?
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.
```yaml
structs:
...
rename:
'_(.*)': '$1' # Removes prefix underscores from all structures.
member-rename:
'.*': # Matches any struct.
'_(.*)': '$1' # Removes prefix underscores from members.
```
### How to generate declarations only from particular headers?
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.
```yaml
headers:
entry-points:
- 'path/to/my_header.h'
include-directives:
- '**my_header.h' # This glob pattern matches the header path.
```
### Can ffigen filter declarations by name?
Ffigen supports including/excluding declarations using full regexp matching.
Here's an example to filter functions using names
```yaml
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.
### How does ffigen handle C Strings?
Ffigen treats `char*` just as any other pointer,(`Pointer<Int8>`).
To convert these to/from `String`, you can use [package:ffi](https://pub.dev/packages/ffi). Use `ptr.cast<Utf8>().toDartString()` to convert `char*` to dart `string` and `"str".toNativeUtf8()` to convert `string` to `char*`.
### How are unnamed enums handled?
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
```yaml
unnamed-enums:
include:
- 'CX_.*'
exclude:
- '.*Flag'
rename:
'CXType_(.*)': '$1'
```
### How can I handle unexpected enum values?
Native enums are, by default, generated into Dart enums with `int get value` and `fromValue(int)`.
This works well in the case that your enums values are known in advance and not going to change,
and in return, you get the full benefits of Dart enums like exhaustiveness checking.
However, if a native library adds another possible enum value after you generate your bindings,
and this new value is passed to your Dart code, this will result in an `ArgumentError` at runtime.
To fix this, you can regenerate the bindings on the new header file, but if you wish to avoid this
issue entirely, you can tell ffigen to generate plain Dart integers for your enum instead. To do
this, simply list your enum's name in the `as-int` section of your ffigen config:
```yaml
enums:
as-int:
include:
- MyIntegerEnum
- '*IntegerEnum'
exclude:
- FakeIntegerEnum
```
Functions that accept or return these enums will now accept or return integers instead, and it will
be up to your code to map integer values to behavior and handle invalid values. But your code will
be future-proof against new additions to the enums.
### Why are some struct/union declarations generated even after excluded them in config?
This happens when an excluded struct/union 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).
```yaml
structs:
dependency-only: opaque
unions:
dependency-only: opaque
```
### How to expose the native pointers?
By default the native pointers are private, 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`.
Example -
```yaml
functions:
symbol-address:
include:
- 'myFunc' # Match function name.
- '.*' # Do this to expose all function pointers.
exclude: # If you only use exclude, then everything not excluded is generated.
- 'dispose'
```
### How to get typedefs to Native and Dart type of a function?
By default these types are inline. But you can use the `expose-typedef` subkey
for functions to generate them. This will expose the Native and Dart type.
E.g - for a function named `hello`, the generated typedefs are named
as `NativeHello` and `DartHello`.
Example -
```yaml
functions:
expose-typedefs:
include:
- 'myFunc' # Match function name.
- '.*' # Do this to expose types for all function.
exclude: # If you only use exclude, then everything not excluded is generated.
- 'dispose'
```
### How are Structs/Unions/Enums that are reffered to via typedefs handled?
Named declarations use their own names even when inside another typedef.
However, unnamed declarations inside typedefs take the name of the _first_ typedef
that refers to them.
### Why are some typedefs not generated?
The following typedefs are not generated -
- They are not referred to anywhere in the included declarations.
- They refer to a struct/union having the same name as itself.
- They refer to a boolean, enum, inline array, Handle or any unsupported type.
### How are macros handled?
`ffigen` uses `clang`'s own compiler frontend to parse and traverse the `C` header files. `ffigen` expands the macros using `clang`'s macro expansion and then traverses the expanded code. To do this, `ffigen` generates temporary files in a system tmp directory.
A custom temporary directory can be specified by setting the `TEST_TMPDIR` environment variable.
### What are these logs generated by ffigen and how to fix them?
Ffigen can sometimes generate a lot of logs, especially when it's parsing a lot of code.
- `SEVERE` logs are something you *definitely need to address*. They can be
caused due to syntax errors, or more generally missing header files
(which need to be specified using `compiler-opts` in config)
- `WARNING` logs are something *you can ignore*, but should probably look into.
These are mostly indications of declarations ffigen couldn't generate due
to limitations of dart:ffi, private declarations (which can be resolved
by renaming them via ffigen config) or other minor issues in the config
file itself.
- Everything else can be safely ignored. It's purpose is to simply
let you know what ffigen is doing.
- The verbosity of the logs can be changed by adding a flag with
the log level. E.g - `dart run ffigen --verbose <level>`.
Level options are - `[all, fine, info (default), warning, severe]`.
The `all` and `fine` will print a ton of logs are meant for debugging
purposes only.
### How can type definitions be shared?
Ffigen can share type definitions using symbol files.
- A package can generate a symbol file using the `output.symbol-file` config.
- And another package can then import this, using `import.symbol-files` config.
- Doing so will reuse all the types such as Struct/Unions, and will automatically
exclude generating other types (E.g functions, enums, macros).
Checkout `examples/shared_bindings` for details.
For manually reusing definitions from another package, the `library-imports`
and `type-map` config can be used.
### How does ObjC method filtering work?
Methods and properties on ObjC interfaces and protocols can be filtered using
the `member-filter` option under `objc-interfaces` and `objc-protocols`. For
simplicity we'll focus on interface methods, but the same rules apply to
properties and protocols. There are two parts to the filtering process: matching
the interface, and then filtering the method.
The syntax of `member-filter` is a YAML map from a pattern to some
`include`/`exclude` rules, and `include` and `exclude` are each a list of
patterns.
```yaml
objc-interfaces:
member-filter:
MyInterface: # Matches an interface.
include:
- "someMethod:withArg:" # Matches a method.
exclude:
- someOtherMethod # Matches a method.
```
The interface matching logic is the same as the matching logic for the
`member-rename` option:
- The pattern is compared against the original name of the interface (before any
renaming is applied).
- The pattern may be a string or a regexp, but in either case they must match
the entire interface name.
- If the pattern contains only alphanumeric characters, or `_`, it is treated as
a string rather than a regex.
- String patterns take precedence over regexps. That is, if an interface matches
both a regexp pattern, and a string pattern, it uses the string pattern's
`include`/`exclude` rules.
The method filtering logic uses the same `include`/`exclude` rules as the rest
of the config:
- `include` and `exclude` are a list of patterns.
- The patterns are compared against the original name of the method, before
renaming.
- The patterns can be strings or regexps, but must match the entire method name.
- The method name is in ObjC selector syntax, which means that the method name
and all the external parameter names are concatenated together with `:`
characters. This is the same name you'll see in ObjC's API documentation.
- **NOTE:** Since the pattern must match the entire method name, and most ObjC
method names end with a `:`, it's a good idea to surround the pattern with
quotes, `"`. Otherwise YAML will think you're defining a map key.
- If no `include` or `exclude` rules are defined, all methods are included,
regardless of the top level `exclude-all-by-default` rule.
- If only `include` rules are `defined`, all non-matching methods are excluded.
- If only `exclude` rules are `defined`, all non-matching methods are included.
- If both `include` and `exclude` rules are defined, the `exclude` rules take
precedence. That is, if a method name matches both an `include` rule and an
`exclude` rule, the method is excluded. All non-matching methods are also
excluded.
The property filtering rules live in the same `objc-interfaces.member-filter`
option as the methods. There is no distinction between methods and properties in
the filters. The protocol filtering rules live in
`objc-protocols.member-filter`.
### How do I generate bindings for Apple APIs?
It can be tricky to locate header files containing Apple's ObjC frameworks, and
the paths can vary between computers depending on which version of Xcode you are
using and where it is installed. So ffigen provides the following variable
substitutions that can be used in the `headers.entry-points` list:
- `$XCODE`: Replaced with the result of `xcode-select -p`, which is the
directory where Xcode's APIs are installed.
- `$IOS_SDK`: Replaced with `xcrun --show-sdk-path --sdk iphoneos`, which is the
directory within `$XCODE` where the iOS SDK is installed.
- `$MACOS_SDK`: Replaced with `xcrun --show-sdk-path --sdk macosx`, which is the
directory within `$XCODE` where the macOS SDK is installed.
For example:
```Yaml
headers:
entry-points:
- '$MACOS_SDK/System/Library/Frameworks/Foundation.framework/Headers/NSDate.h'
```