Skip to content

Commit b17023d

Browse files
authored
[ffigen] Add more Objective-C documentation (#2707)
1 parent 81ff644 commit b17023d

File tree

10 files changed

+607
-302
lines changed

10 files changed

+607
-302
lines changed

pkgs/ffigen/README.md

Lines changed: 2 additions & 302 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Rust. For more details, see https://dart.dev/guides/libraries/c-interop.
1616
FFIgen also has experimental support for calling ObjC and Swift code;
1717
for details see https://dart.dev/guides/libraries/objective-c-interop.
1818

19+
More FFIgen documentation can be found [here](doc/README.md).
20+
1921
## Getting Started
2022

2123
This guide demonstrates how to call a custom C API from a standalone Dart
@@ -955,305 +957,3 @@ include-transitive-objc-categories: false
955957
</tr>
956958
</tbody>
957959
</table>
958-
959-
## FAQ
960-
961-
### Can FFIgen be used for removing underscores or renaming declarations?
962-
963-
FFIgen supports **regexp-based renaming**. The regexp must be a full match.
964-
For renaming you can use regexp groups (`$1` means group 1).
965-
966-
To renaming `clang_dispose_string` to `string_dispose` we can match it using
967-
`clang_(.*)_(.*)` and rename with `$2_$1`.
968-
969-
Here's an example of how to remove prefix underscores from any struct and its
970-
members.
971-
972-
```yaml
973-
structs:
974-
...
975-
rename:
976-
'_(.*)': '$1' # Removes prefix underscores from all structures.
977-
member-rename:
978-
'.*': # Matches any struct.
979-
'_(.*)': '$1' # Removes prefix underscores from members.
980-
```
981-
### How to generate declarations only from particular headers?
982-
983-
The default behavior is to include everything directly/transitively under
984-
each of the `entry-points` specified.
985-
986-
If you only want to have declarations directly particular header you can do so
987-
using `include-directives`. You can use **glob matching** to match header paths.
988-
989-
```yaml
990-
headers:
991-
entry-points:
992-
- 'path/to/my_header.h'
993-
include-directives:
994-
- '**my_header.h' # This glob pattern matches the header path.
995-
```
996-
### Can FFIgen filter declarations by name?
997-
998-
FFIgen supports including/excluding declarations using full regexp matching.
999-
1000-
Here's an example to filter functions using names:
1001-
1002-
```yaml
1003-
functions:
1004-
include:
1005-
- 'clang.*' # Include all functions starting with clang.
1006-
exclude:
1007-
- '.*dispose': # Exclude all functions ending with dispose.
1008-
```
1009-
1010-
This will include `clang_help`. But will exclude `clang_dispose`.
1011-
1012-
Note: exclude overrides include.
1013-
1014-
### How does FFIgen handle C Strings?
1015-
1016-
FFIgen treats `char*` just as any other pointer (`Pointer<Int8>`).
1017-
To convert these to/from `String`, you can use [package:ffi](https://pub.dev/packages/ffi).
1018-
Use `ptr.cast<Utf8>().toDartString()` to convert `char*` to dart `string` and
1019-
`"str".toNativeUtf8()` to convert `string` to `char*`.
1020-
1021-
### How are unnamed enums handled?
1022-
1023-
Unnamed enums are handled separately, under the key `unnamed-enums`, and are
1024-
generated as top level constants.
1025-
1026-
Here's an example that shows how to include/exclude/rename unnamed enums:
1027-
1028-
```yaml
1029-
unnamed-enums:
1030-
include:
1031-
- 'CX_.*'
1032-
exclude:
1033-
- '.*Flag'
1034-
rename:
1035-
'CXType_(.*)': '$1'
1036-
```
1037-
1038-
### How can I handle unexpected enum values?
1039-
1040-
Native enums are, by default, generated into Dart enums with `int get value` and
1041-
`fromValue(int)`. This works well in the case that your enums values are known
1042-
in advance and not going to change, and in return, you get the full benefits of
1043-
Dart enums like exhaustiveness checking.
1044-
1045-
However, if a native library adds another possible enum value after you generate
1046-
your bindings, and this new value is passed to your Dart code, this will result
1047-
in an `ArgumentError` at runtime. To fix this, you can regenerate the bindings
1048-
on the new header file, but if you wish to avoid this issue entirely, you can
1049-
tell FFIgen to generate plain Dart integers for your enum instead. To do this,
1050-
simply list your enum's name in the `as-int` section of your FFIgen config:
1051-
1052-
```yaml
1053-
enums:
1054-
as-int:
1055-
include:
1056-
- MyIntegerEnum
1057-
- '*IntegerEnum'
1058-
exclude:
1059-
- FakeIntegerEnum
1060-
```
1061-
1062-
Functions that accept or return these enums will now accept or return integers
1063-
instead, and it will be up to your code to map integer values to behavior and
1064-
handle invalid values. But your code will be future-proof against new additions
1065-
to the enums.
1066-
1067-
### Why are some struct/union declarations generated even after excluded them in config?
1068-
1069-
This happens when an excluded struct/union is a dependency to some included
1070-
declaration. (A dependency means a struct is being passed/returned by a function
1071-
or is member of another struct in some way.)
1072-
1073-
Note: If you supply `structs.dependency-only` as `opaque` FFIgen will generate
1074-
these struct dependencies as `Opaque` if they were only passed by reference
1075-
(pointer).
1076-
1077-
```yaml
1078-
structs:
1079-
dependency-only: opaque
1080-
unions:
1081-
dependency-only: opaque
1082-
```
1083-
1084-
### How to expose the native pointers?
1085-
1086-
By default, the native pointers are private, but you can use the
1087-
`symbol-address` subkey for functions/globals and make them public by matching
1088-
with its name. The pointers are then accessible via `nativeLibrary.addresses`.
1089-
1090-
Example:
1091-
1092-
```yaml
1093-
functions:
1094-
symbol-address:
1095-
include:
1096-
- 'myFunc' # Match function name.
1097-
- '.*' # Do this to expose all function pointers.
1098-
exclude: # If you only use exclude, then everything not excluded is generated.
1099-
- 'dispose'
1100-
```
1101-
1102-
### How to get typedefs to Native and Dart type of a function?
1103-
1104-
By default, these types are inline. But you can use the `expose-typedef` subkey
1105-
for functions to generate them. This will expose the Native and Dart type.
1106-
E.g. for a function named `hello` the generated typedefs are named as
1107-
`NativeHello` and `DartHello`.
1108-
1109-
Example:
1110-
1111-
```yaml
1112-
functions:
1113-
expose-typedefs:
1114-
include:
1115-
- 'myFunc' # Match function name.
1116-
- '.*' # Do this to expose types for all functions.
1117-
exclude: # If you only use exclude, then everything not excluded is generated.
1118-
- 'dispose'
1119-
```
1120-
1121-
### How are Structs/Unions/Enums that are referred to via typedefs handled?
1122-
1123-
Named declarations use their own names even when inside another typedef.
1124-
However, unnamed declarations inside typedefs take the name of the _first_
1125-
typedef that refers to them.
1126-
1127-
### Why are some typedefs not generated?
1128-
1129-
The following typedefs are not generated:
1130-
- They are not referred to anywhere in the included declarations.
1131-
- They refer to a struct/union having the same name as itself.
1132-
- They refer to a boolean, enum, inline array, Handle or any unsupported type.
1133-
1134-
### How are macros handled?
1135-
1136-
FFIgen uses `clang`'s own compiler frontend to parse and traverse the `C`
1137-
header files. FFIgen expands the macros using `clang`'s macro expansion and
1138-
then traverses the expanded code. To do this, FFIgen generates temporary files
1139-
in a system tmp directory.
1140-
1141-
A custom temporary directory can be specified by setting the `TEST_TMPDIR`
1142-
environment variable.
1143-
1144-
### What are these logs generated by FFIgen and how to fix them?
1145-
1146-
FFIgen can sometimes generate a lot of logs, especially when it's parsing a lot
1147-
of code.
1148-
- `SEVERE` logs are something you *definitely need to address*. They can be
1149-
caused due to syntax errors, or more generally missing header files
1150-
(which need to be specified using `compiler-opts` in config).
1151-
- `WARNING` logs are something *you can ignore*, but should probably look into.
1152-
These are mostly indications of declarations FFIgen couldn't generate due
1153-
to limitations of `dart:ffi`, private declarations (which can be resolved
1154-
by renaming them via FFIgen's config) or other minor issues in the config
1155-
file itself.
1156-
- Everything else can be safely ignored. Its purpose is to simply let you know
1157-
what FFIgen is doing.
1158-
- The verbosity of the logs can be changed by adding a flag with
1159-
the log level, e.g. `dart run ffigen --verbose <level>`.
1160-
Level options are `[all, fine, info (default), warning, severe]`.
1161-
The `all` and `fine` will print a ton of logs are meant for debugging
1162-
purposes only.
1163-
1164-
### How can type definitions be shared?
1165-
1166-
FFIgen can share type definitions using symbol files.
1167-
- A package can generate a symbol file using the `output.symbol-file` config.
1168-
- And another package can then import this, using `import.symbol-files` config.
1169-
- Doing so will reuse all the types such as Struct/Unions, and will automatically
1170-
exclude generating other types (E.g. functions, enums, macros).
1171-
1172-
Checkout `examples/shared_bindings` for details.
1173-
1174-
For manually reusing definitions from another package, the `library-imports`
1175-
and `type-map` config can be used.
1176-
1177-
### How does ObjC method filtering work?
1178-
1179-
Methods and properties on ObjC interfaces and protocols can be filtered using
1180-
the `member-filter` option under `objc-interfaces` and `objc-protocols`. For
1181-
simplicity we'll focus on interface methods, but the same rules apply to
1182-
properties and protocols. There are two parts to the filtering process: matching
1183-
the interface, and then filtering the method.
1184-
1185-
The syntax of `member-filter` is a YAML map from a pattern to some
1186-
`include`/`exclude` rules, and `include` and `exclude` are each a list of
1187-
patterns.
1188-
1189-
```yaml
1190-
objc-interfaces:
1191-
member-filter:
1192-
MyInterface: # Matches an interface.
1193-
include:
1194-
- "someMethod:withArg:" # Matches a method.
1195-
exclude:
1196-
- someOtherMethod # Matches a method.
1197-
```
1198-
1199-
The interface matching logic is the same as the matching logic for the
1200-
`member-rename` option:
1201-
1202-
- The pattern is compared against the original name of the interface (before any
1203-
renaming is applied).
1204-
- The pattern may be a string or a regexp, but in either case they must match
1205-
the entire interface name.
1206-
- If the pattern contains only alphanumeric characters, or `_`, it is treated as
1207-
a string rather than a regex.
1208-
- String patterns take precedence over regexps. That is, if an interface matches
1209-
both a regexp pattern, and a string pattern, it uses the string pattern's
1210-
`include`/`exclude` rules.
1211-
1212-
The method filtering logic uses the same `include`/`exclude` rules as the rest
1213-
of the config:
1214-
1215-
- `include` and `exclude` are a list of patterns.
1216-
- The patterns are compared against the original name of the method, before
1217-
renaming.
1218-
- The patterns can be strings or regexps, but must match the entire method name.
1219-
- The method name is in ObjC selector syntax, which means that the method name
1220-
and all the external parameter names are concatenated together with `:`
1221-
characters. This is the same name you'll see in ObjC's API documentation.
1222-
- **NOTE:** Since the pattern must match the entire method name, and most ObjC
1223-
method names end with a `:`, it's a good idea to surround the pattern with
1224-
quotes, `"`. Otherwise, YAML will think you're defining a map key.
1225-
- If no `include` or `exclude` rules are defined, all methods are included,
1226-
regardless of the top level `exclude-all-by-default` rule.
1227-
- If only `include` rules are `defined`, all non-matching methods are excluded.
1228-
- If only `exclude` rules are `defined`, all non-matching methods are included.
1229-
- If both `include` and `exclude` rules are defined, the `exclude` rules take
1230-
precedence. That is, if a method name matches both an `include` rule and an
1231-
`exclude` rule, the method is excluded. All non-matching methods are also
1232-
excluded.
1233-
1234-
The property filtering rules live in the same `objc-interfaces.member-filter`
1235-
option as the methods. There is no distinction between methods and properties in
1236-
the filters. The protocol filtering rules live in
1237-
`objc-protocols.member-filter`.
1238-
1239-
### How do I generate bindings for Apple APIs?
1240-
1241-
It can be tricky to locate header files containing Apple's ObjC frameworks, and
1242-
the paths can vary between computers depending on which version of Xcode you are
1243-
using and where it is installed. So FFIgen provides the following variable
1244-
substitutions that can be used in the `headers.entry-points` list:
1245-
1246-
- `$XCODE`: Replaced with the result of `xcode-select -p`, which is the
1247-
directory where Xcode's APIs are installed.
1248-
- `$IOS_SDK`: Replaced with `xcrun --show-sdk-path --sdk iphoneos`, which is the
1249-
directory within `$XCODE` where the iOS SDK is installed.
1250-
- `$MACOS_SDK`: Replaced with `xcrun --show-sdk-path --sdk macosx`, which is the
1251-
directory within `$XCODE` where the macOS SDK is installed.
1252-
1253-
For example:
1254-
1255-
```Yaml
1256-
headers:
1257-
entry-points:
1258-
- '$MACOS_SDK/System/Library/Frameworks/Foundation.framework/Headers/NSDate.h'
1259-
```

pkgs/ffigen/doc/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# FFIgen Documentation
2+
3+
## General documentation
4+
5+
- [FAQ](faq.md)
6+
- [Common errors](errors.md) in FFIgen and how to deal with them.
7+
8+
## Objective-C specific documentation
9+
10+
- [Objective-C memory management considerations](objc_gc.md)
11+
- [Dealing with OS differences](objc_os_differences.md)
12+
- [Runtime type checks in Objective-C](objc_runtime_types.md)
13+
- [Objective-C threading considerations](objc_threading.md)
14+
- [Objective-C method filtering](objc_method_filtering.md)
15+
- [Generating bindings for Apple APIs](apple_apis.md)

pkgs/ffigen/doc/apple_apis.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## Generating bindings for Apple APIs
2+
3+
It can be tricky to locate header files containing Apple's ObjC frameworks, and
4+
the paths can vary between computers depending on which version of Xcode you are
5+
using and where it is installed. So FFIgen provides the following variable
6+
substitutions that can be used in the `headers.entry-points` list:
7+
8+
- `$XCODE`: Replaced with the result of `xcode-select -p`, which is the
9+
directory where Xcode's APIs are installed.
10+
- `$IOS_SDK`: Replaced with `xcrun --show-sdk-path --sdk iphoneos`, which is the
11+
directory within `$XCODE` where the iOS SDK is installed.
12+
- `$MACOS_SDK`: Replaced with `xcrun --show-sdk-path --sdk macosx`, which is the
13+
directory within `$XCODE` where the macOS SDK is installed.
14+
15+
For example:
16+
17+
```Yaml
18+
headers:
19+
entry-points:
20+
- '$MACOS_SDK/System/Library/Frameworks/Foundation.framework/Headers/NSDate.h'
21+
```

0 commit comments

Comments
 (0)