Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/thlorenz/wasmjsgen
Wasmjs binding generator adapted from ffigen
https://github.com/thlorenz/wasmjsgen
Last synced: 11 days ago
JSON representation
Wasmjs binding generator adapted from ffigen
- Host: GitHub
- URL: https://github.com/thlorenz/wasmjsgen
- Owner: thlorenz
- License: bsd-3-clause
- Created: 2021-07-20T22:18:23.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2021-07-20T22:18:56.000Z (over 3 years ago)
- Last Synced: 2024-12-26T13:23:10.842Z (23 days ago)
- Language: Dart
- Size: 1.69 MB
- Stars: 4
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
[![pub package](https://img.shields.io/pub/v/wasmjsgen.svg)](https://pub.dev/packages/wasmjsgen)
[![Build Status](https://github.com/thlorenz/wasmjsgen/workflows/Dart%20CI/badge.svg)](https://github.com/thlorenz/wasmjsgen/actions?query=workflow%3A"Dart+CI")Binding generator for Wasmjs flavor of [FFI](https://dart.dev/guides/libraries/c-interop) bindings.
> Note: wasmjsgen only supports parsing `C` headers.
**Status**: Alpha, not all features are supported, but small apps can be built with it, see
[this example](https://thlorenz.com/rid-examples/todo_cubit).The parameter and return types share native FFI names, i.e. `Pointer` in order to allow
consuming code to remain unchanged and switch from native FFI to WASM via conditional imports._The below Readme was modified from the _ffigen_ Readme and hasn't been fully updated yet_.
## Example
For some header file _example.h_:
```C
int sum(int a, int b);
```
Add configurations to Pubspec File:
```yaml
wasmjsgen:
allocate: 'my_malloc'
output: 'generated_bindings.dart'
headers:
entry-points:
- 'example.h'
```**NOTE**: that wasmjsgen adds _allocate_, _dealloate_ and _reallocate_ functions that are used
to manage memory. At the very least you will need to specify _allocate` if you want to send
`String`s from Dart to Wasm.Output (_generated_bindings.dart_).
```dart
class NativeLibrary {
/// The symbol lookup function.
T lookup(String name) {
return _wasmInstance.functions[name] as T;
}wasm_interop.Memory get memory {
return _wasmInstance.memories['memory']!;
}dart_typed.Uint8List get memView {
return _wasmInstance.memories['memory']!.buffer.asUint8List();
}[ .. ]
// --- dividePercision ---
/// Divides 2 floats, returns a pointer to double.
Pointer dividePercision(
Pointer a,
Pointer b,
) {
return Pointer.fromAddress(
Double(
_dividePercision(
a.address,
b.address,
),
),
);
}late final int Function(
int,
int,
) _dividePercision = lookup('dividePercision');
[ .. ]
}
```
## Using this package
- Add `wasmjsgen` under `dev_dependencies` in your `pubspec.yaml`.
- 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 wasmjsgen`.Jump to [FAQ](#faq).
## Installing LLVM
`package:wasmjsgen` 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](https://releases.llvm.org/download.html) or `winget install -e --id LLVM.LLVM`.#### MacOS
1. Install Xcode.
2. Install LLVM - `brew install llvm`.---
# From FFIgen Readme## Configurations
Configurations can be provided in 2 ways-
1. In the project's `pubspec.yaml` file under the key `wasmjsgen`.
2. Via a custom YAML file, then specify this file while running -
`dart run wasmjsgen --config config.yaml`The following configuration options are available-
Key
Explaination
Example
output
(Required)
Output path of the generated bindings.
```yaml
output: 'generated_bindings.dart'
```
llvm-path
Path to llvm folder.
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.
Complete path to the dynamic library can also be supplied.
Required if ffigen is unable to find this at default locations.
```yaml
llvm-path:
- '/usr/local/opt/llvm'
- 'C:\Program Files\llvm`
- '/usr/lib/llvm-11'
# Specify exact path to dylib
- '/usr/lib64/libclang.so'
```
headers
(Required)
The header entry-points and include-directives. Glob syntax is allowed.
If include-directives are not specified ffigen will generate everything directly/transitively under the entry-points.
```yaml
headers:
entry-points:
- 'folder/**.h'
- 'folder/specific_header.h'
include-directives:
- '**index.h'
- '**/clang-c/**'
- '/full/path/to/a/header.h'
```
name
(Prefer)
Name of generated class.
```yaml
name: 'SQLite'
```
description
(Prefer)
Dart Doc for generated class.
```yaml
description: 'Bindings to SQLite'
```
compiler-opts
Pass compiler options to clang. You can also pass
these via the command line tool.
```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'"
```
compiler-opts-automatic -> macos -> include-c-standard-library
Tries to automatically find and add C standard library path to
compiler-opts on macos.
Default: true
```yaml
compiler-opts-automatic:
macos:
include-c-standard-library: false
```
functions
structs
unions
enums
unnamed-enums
macros
globals
Filters for declarations.
Default: all are included.
Options -
- Include/Exclude declarations.
- Rename declarations.
- Rename enum and struct members.
- Expose symbol-address for functions and globals.
```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'
globals:
exclude:
- aGlobal
rename:
# Removes '_' from
# beginning of a name.
'_(.*)': '$1'
```
typedefs
Filters for referred typedefs.
Options -
- Include/Exclude (referred typedefs only).
- Rename typedefs.
Note: Typedefs that are not referred to anywhere will not be generated.
```yaml
typedefs:
exclude:
# Typedefs starting with `p` are not generated.
- 'p.*'
rename:
# Removes '_' from beginning of a typedef.
'_(.*)': '$1'
```
functions -> expose-typedefs
Generate the typedefs to Native and Dart type of a function
Default: Inline types are used and no typedefs to Native/Dart
type are generated.
```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'
```
structs -> pack
Override the @Packed(X) annotation for generated structs.
Options - none, 1, 2, 4, 8, 16
You can use RegExp to match with the generated names.
Note: Ffigen can only reliably identify packing specified using
__attribute__((__packed__)). However, structs packed using
`#pragma pack(...)` or any other way could potentially be incorrect
in which case you can override the generated annotations.
```yaml
structs:
pack:
# Matches with the generated name.
'NoPackStruct': none # No packing
'.*': 1 # Pack all structs with value 1
```
comments
Extract documentation comments for declarations.
The style and length of the comments recognized can be specified with the following options-
style: doxygen(default) | any
length: brief | full(default)
If you want to disable all comments you can also pass
comments: false.
```yaml
comments:
style: any
length: full
```
structs -> dependency-only
unions -> dependency-only
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).
Options - full(default) | opaque
```yaml
structs:
dependency-only: opaque
unions:
dependency-only: opaque
```
sort
Sort the bindings according to name.
Default: false, i.e keep the order as in the source files.
```yaml
sort: true
```
use-supported-typedefs
Should automatically map typedefs, E.g uint8_t => Uint8, int16_t => Int16 etc.
Default: true
```yaml
use-supported-typedefs: true
```
dart-bool
Should generate dart `bool` instead of Uint8 for c99 bool in functions.
Default: true
```yaml
dart-bool: true
```
use-dart-handle
Should map `Dart_Handle` to `Handle`.
Default: true
```yaml
use-dart-handle: true
```
preamble
Raw header of the file, pasted as-it-is.
```yaml
preamble: |
// ignore_for_file: camel_case_types, non_constant_identifier_names
```
typedef-map
Map typedefs to Native Types.
Values can only be
Void, Uint8, Int8, Uint16, Int16, Uint32, Int32, Uint64, Int64, IntPtr, Float and Double.
```yaml
typedef-map:
'my_custom_type': 'IntPtr'
'size_t': 'Int64'
```
size-map
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.
```yaml
# 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
```
## Limitations
1. Multi OS support for types such as long. [Issue #7](https://github.com/dart-lang/ffigen/issues/7)## Trying out examples
1. `cd examples/`, Run `dart pub get`.
2. Run `dart run ffigen`.## Running Tests
1. Dynamic library for some tests need to be built before running the examples.
1. `cd test/native_test`.
2. Run `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.
## 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`).
To convert these to/from `String`, you can use [package:ffi](https://pub.dev/packages/ffi). Use `ptr.cast().toDartString()` to convert `char*` to dart `string` and `"str".toNativeUtf8()` to convert `string` to `char*`.
### How does ffigen handle C99 bool data type?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.### 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'
```### 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.