Fixing mac bot by explicitly setting the objective C sysroot (#414)

1 file changed
tree: c0ad08bef355a5568db95f60cfe4073a1b012eec
  1. .github/
  2. bin/
  3. example/
  4. lib/
  5. test/
  6. third_party/
  7. tool/
  8. .gitignore
  9. analysis_options.yaml
  10. AUTHORS
  11. CHANGELOG.md
  12. CONTRIBUTING.md
  13. LICENSE
  14. pubspec.yaml
  15. README.md
README.md

pub package Build Status Coverage Status

Binding generator for FFI bindings.

Note: ffigen only supports parsing C headers.

Example

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).

import 'package:ffi/ffi.dart' as pkg_ffi;
class NativeLibrary {
  final Pointer<T> Function<T extends NativeType>(String symbolName)
      _lookup;
  NativeLibrary(DynamicLibrary dynamicLibrary)
      : _lookup = dynamicLibrary.lookup;
  NativeLibrary.fromLookup(
      Pointer<T> Function<T extends NativeType>(String symbolName)
          lookup)
      : _lookup = lookup;

  int sum(int a, int b) {
    return _sum(a, b);
  }

  late final _sumPtr = _lookup<ffi.NativeFunction<pkg_ffi.Int Function(pkg_ffi.Int, pkg_ffi.Int)>>('sum');
  late final _sum = _sum_ptr.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).
  • Configurations must be provided in pubspec.yaml or in a custom YAML file (see configurations).
  • Run the tool- dart run ffigen.

Jump to FAQ.

Installing LLVM

package:ffigen uses LLVM. Install LLVM (9+) 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 or winget install -e --id LLVM.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 - dart run ffigen --config config.yaml

The following configuration options are available-

output: 'generated_bindings.dart'
llvm-path:
  - '/usr/local/opt/llvm'
  - 'C:\Program Files\llvm`
  - '/usr/lib/llvm-11'
  # Specify exact path to dylib
  - '/usr/lib64/libclang.so'
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/'

and/or via the command line -

dart run ffigen --compiler-opts "-I/headers
-L 'path/to/folder name/file'"
compiler-opts-automatic:
  macos:
    include-c-standard-library: false
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'
globals:
  exclude:
    - aGlobal
  rename:
    # Removes '_' from
    # beginning of a name.
    '_(.*)': '$1'
typedefs:
  exclude:
    # Typedefs starting with `p` are not generated.
    - 'p.*'
  rename:
    # Removes '_' from beginning of a typedef.
    '_(.*)': '$1'
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'
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'
structs:
  pack:
    # Matches with the generated name.
    'NoPackStruct': none # No packing
    '.*': 1 # Pack all structs with value 1
comments:
  style: any
  length: full
structs:
  dependency-only: opaque
unions:
  dependency-only: opaque
sort: true
use-supported-typedefs: true
use-dart-handle: true
preamble: |
  // ignore_for_file: camel_case_types, non_constant_identifier_names
library-imports:
  custom_lib: 'package:some_pkg/some_file.dart'
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'
language: 'objc'

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

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.

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.

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

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. 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

unnamed-enums:
  include:
    - 'CX_.*'
  exclude:
    - '.*Flag'
  rename:
    'CXType_(.*)': '$1'

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).

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 -

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 -

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.

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.