├── analysis_options.yaml ├── .travis.yml ├── pubspec.yaml ├── .github └── workflows │ └── dart.yml ├── .gitignore ├── LICENSE ├── CHANGELOG.md ├── lib ├── c.dart └── src │ └── c │ ├── generate_file.dart │ ├── global.dart │ ├── opaque.dart │ ├── config.dart │ ├── struct.dart │ ├── func.dart │ ├── dart_source_writer.dart │ └── library.dart ├── example └── example.dart └── README.md /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:pedantic/analysis_options.yaml -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: dart 2 | dart: 3 | - dev 4 | 5 | branches: 6 | only: [master] -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: ffi_tool 2 | version: 0.4.0 3 | description: A tool for generating 'dart:ffi' bindings for C and Objective-C libraries. 4 | homepage: https://github.com/dart-interop/ffi_tool 5 | 6 | environment: 7 | sdk: '>=2.12.0 <3.0.0' 8 | 9 | dev_dependencies: 10 | pedantic: ^1.9.0 11 | test: ^1.6.0 12 | -------------------------------------------------------------------------------- /.github/workflows/dart.yml: -------------------------------------------------------------------------------- 1 | name: Dart CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | container: 11 | image: google/dart:latest 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Install dependencies 16 | run: pub get 17 | - name: Run tests 18 | run: pub run test 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | .pub/ 7 | build/ 8 | # If you're building an application, you may want to check-in your pubspec.lock 9 | pubspec.lock 10 | 11 | # Directory created by dartdoc 12 | # If you don't generate documentation locally you can remove this line. 13 | doc/api/ 14 | 15 | # IDEs 16 | .idea/ 17 | .VSCode/ 18 | 19 | example/generated.dart -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 4 | terrier989 5 | EPNW 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.4.0 2 | * BREAKING: Null Safety 3 | * BREAKING: Removed dependency on the ffi package since dart:ffi is stable now (since dart 2.12) 4 | * BREAKING: Removed Utf8 type, use Uint8 instead 5 | * Added Opaque type 6 | 7 | # 0.3.0 8 | * Configure how a dynamic library should be loaded depending on the platform, 9 | explicitly give the load code, or make the user responsible for loading at runtime. 10 | 11 | * You can now add documentation to functions, structs and globals 12 | and preambles (e.g. for including licenses) to generated licenses. 13 | 14 | * BREAKING: DartSourceWriter and Parameter class removed from public API 15 | (although they were not meant to be used outside this package in the first place). 16 | 17 | * BREAKING: Renamed the class Field to StructField, since its only used for structs. 18 | 19 | * Further bug fixes 20 | 21 | # 0.2.5 22 | * Fixes issues caused by a breaking change after a pull request didn't go through enough code 23 | review. 24 | 25 | # 0.2.3 26 | * Adds support for disabling dartfmt. 27 | 28 | # 0.2.2 29 | * Bug fixes 30 | 31 | # 0.2.1 32 | * Bug fixes 33 | * Support for generating 'library', 'part of', and 'parts' directives. 34 | 35 | # 0.2.0 36 | * Support for pedantic 1.9 linter warning. 37 | * Added struct support. 38 | * Refactored the API. 39 | 40 | # 0.1.2 41 | * The package now works in the latest Dart SDK. 42 | * Removed cupertino_ffi stuff. 43 | 44 | # 0.1.1 45 | * Added Objective-C support. 46 | 47 | # 0.1.0 48 | * Initial release 49 | -------------------------------------------------------------------------------- /lib/c.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 ffi_tool authors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | // OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | /// Generates 'dart:ffi' bindings for C libraries. 22 | library ffi_tool.c; 23 | 24 | export 'src/c/config.dart'; 25 | export 'src/c/generate_file.dart'; 26 | export 'src/c/library.dart'; 27 | export 'src/c/func.dart' hide Parameter; 28 | export 'src/c/struct.dart'; 29 | export 'src/c/opaque.dart'; 30 | export 'src/c/global.dart'; 31 | export 'src/c/dart_source_writer.dart' hide DartSourceWriter; 32 | -------------------------------------------------------------------------------- /example/example.dart: -------------------------------------------------------------------------------- 1 | import 'package:ffi_tool/c.dart'; 2 | import 'dart:io'; 3 | 4 | void main() { 5 | // Generates source code and runs 'dartfmt' 6 | generateFile(File('generated.dart'), library); 7 | } 8 | 9 | final library = Library.platformAware( 10 | // Configure, how the dynamic library should be loaded depending on the platform 11 | dynamicLibraryConfig: DynamicLibraryConfig( 12 | windows: DynamicLibraryPlatformConfig.open('path/to/library.dll'), 13 | android: DynamicLibraryPlatformConfig.open('path/to/library.so'), 14 | iOS: DynamicLibraryPlatformConfig.process()), 15 | 16 | // Optional library preamble 17 | preamble: 18 | '// Licensed under MIT license\r\n// AUTOMATICALLY GENERATED. DO NOT EDIT.', 19 | 20 | // Optional imports 21 | importedUris: { 22 | ImportedUri('package:example/library.dart'), 23 | }, 24 | 25 | /// List of generated functions, structs, and global variables 26 | elements: [ 27 | // A definition for a function in C 28 | Func( 29 | name: 'Example', 30 | documentation: 'Takes parameters and does stuff.', 31 | parameterTypes: ['int32', 'float32', '*void'], 32 | returnType: 'void', 33 | ), 34 | 35 | // A definition for a struct in C 36 | // structs must at lease have one field, for 37 | // structs without fields use Opaque 38 | Struct( 39 | name: 'ExampleStruct', 40 | fields: [ 41 | StructField( 42 | name: 'length', 43 | type: 'size_t', 44 | ), 45 | ], 46 | ), 47 | 48 | // Opaque structs are structs without fields 49 | Opaque(name: 'ExampleOpaqueStruct'), 50 | 51 | // A definition for a global variable in C 52 | Global( 53 | name: 'ExampleGlobal', 54 | type: 'Int32', 55 | ), 56 | ], 57 | ); 58 | -------------------------------------------------------------------------------- /lib/src/c/generate_file.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 ffi_tool authors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the 'Software'), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | // OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | import 'dart:io'; 22 | 23 | import 'library.dart'; 24 | import 'dart_source_writer.dart'; 25 | 26 | /// Generates [file] by generating C bindings for [library]. 27 | /// If format is true 'dartfmt -w $PATH' will be run to format the generated file. 28 | void generateFile(File file, Library library, {bool format = true}) { 29 | final w = DartSourceWriter(); 30 | library.generateSource(w); 31 | file.writeAsStringSync(w.toString()); 32 | if (format) _dartFmt(file.path); 33 | } 34 | 35 | /// Formats a file with 'dartfmt' 36 | void _dartFmt(String path) { 37 | final result = 38 | Process.runSync('dartfmt', ['-w', path], runInShell: Platform.isWindows); 39 | print(result.stdout); 40 | print(result.stderr); 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/c/global.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 ffi_tool authors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the 'Software'), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | // OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | import 'library.dart'; 22 | import 'dart_source_writer.dart'; 23 | 24 | /// A definition for a C global. 25 | class Global extends Element { 26 | final String type; 27 | const Global( 28 | {required String name, required this.type, String? documentation}) 29 | : super(name: name, documentation: documentation); 30 | 31 | @override 32 | void generateInnerSource(DartSourceWriter w, Library library) { 33 | final dartType = w.getDartType(type); 34 | w.write('\n'); 35 | if (documentation == null) { 36 | w.write('/// C global `$name`.\n'); 37 | } else { 38 | w.write('/// '); 39 | w.writeAll(documentation!.split('\n'), '\n/// '); 40 | w.write('\n'); 41 | } 42 | w.write('final $dartType $name;\n'); 43 | } 44 | 45 | @override 46 | void generateOuterSource(DartSourceWriter w, Library library) {} 47 | 48 | @override 49 | bool generateConstructorSource(DartSourceWriter w, Library library) { 50 | final cType = w.getCType(type); 51 | w.write('$name = ${Library.dynamicLibraryIdentifier}.lookup<$cType>(\n'); 52 | w.write(' \'$name\',\n'); 53 | w.write(').value\n'); 54 | return true; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/c/opaque.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 ffi_tool authors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | // OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | import 'dart_source_writer.dart'; 22 | import 'library.dart'; 23 | 24 | /// A definition for a C opaque struct. 25 | /// 26 | /// ```dart 27 | /// import 'package:ffi_tool/c.dart'; 28 | /// import 'dart:io'; 29 | /// 30 | /// void main() { 31 | /// generateFile(File("generated.dart"), library); 32 | /// } 33 | /// 34 | /// const library = Library( 35 | /// dynamicLibraryPath: "path/to/library", 36 | /// 37 | /// elements: [ 38 | /// Opaque( 39 | /// name: "StructWithoutContent" 40 | /// ), 41 | /// ], 42 | /// ); 43 | /// ``` 44 | class Opaque extends Element { 45 | /// Optional source injected inside the generated class. 46 | final String? inject; 47 | 48 | const Opaque({ 49 | required String name, 50 | String? documentation, 51 | this.inject, 52 | }) : super(name: name, documentation: documentation); 53 | 54 | @override 55 | void generateOuterSource(DartSourceWriter w, Library library) { 56 | w.write('\n'); 57 | if (documentation == null) { 58 | w.write('/// C opaque struct `$name`.\n'); 59 | } else { 60 | w.write('/// '); 61 | w.writeAll(documentation!.split('\n'), '\n/// '); 62 | w.write('\n'); 63 | } 64 | 65 | // 66 | // Write injected source if any or just generate class 67 | // 68 | final inject = this.inject; 69 | if (inject != null) { 70 | w.write('class $name extends ffi.Opaque {'); 71 | w.write(' \n'); 72 | w.write(inject); 73 | w.write('}\n'); 74 | } else { 75 | w.write('class $name extends ffi.Opaque {}\n'); 76 | } 77 | } 78 | 79 | @override 80 | void generateInnerSource(DartSourceWriter w, Library library) {} 81 | 82 | @override 83 | bool generateConstructorSource(DartSourceWriter w, Library library) => false; 84 | } 85 | -------------------------------------------------------------------------------- /lib/src/c/config.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 ffi_tool authors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the 'Software'), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | // OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | ///Represents the different methods to create a `DynamicLibrary`. 22 | enum _DynamicLibraryCreationMode { executable, open, process } 23 | 24 | ///Configures how a `DynamicLibrary` should be created on a platform. 25 | ///The different constructors represents the different constructors of `DynamicLibrary`. 26 | ///See `DynamicLibrary` for more information. 27 | class DynamicLibraryPlatformConfig { 28 | final _DynamicLibraryCreationMode _creationMode; 29 | final String? _path; 30 | 31 | const DynamicLibraryPlatformConfig.executable() 32 | : _path = null, 33 | _creationMode = _DynamicLibraryCreationMode.executable; 34 | 35 | const DynamicLibraryPlatformConfig.process() 36 | : _path = null, 37 | _creationMode = _DynamicLibraryCreationMode.process; 38 | 39 | const DynamicLibraryPlatformConfig.open(String path) 40 | : _path = path, 41 | _creationMode = _DynamicLibraryCreationMode.open; 42 | 43 | @override 44 | String toString() { 45 | switch (_creationMode) { 46 | case _DynamicLibraryCreationMode.executable: 47 | return 'DynamicLibrary.executable()'; 48 | case _DynamicLibraryCreationMode.open: 49 | return 'DynamicLibrary.open(\'$_path\')'; 50 | case _DynamicLibraryCreationMode.process: 51 | return 'DynamicLibrary.process()'; 52 | default: 53 | return ''; 54 | } 55 | } 56 | } 57 | 58 | /// Defines, how the dynamic library should be loaded on each of darts known platforms. 59 | /// 60 | /// If the `DynamicLibraryPlatformConfig` is `null` for a platform, this platform will fallback to `other`. 61 | /// If `other` is `null`, executing on each platform falling back to it 62 | /// and on all platforms that do not match any known platform (e.g. windows, linux, ...), 63 | /// will throw an `UnsupportedError` 64 | class DynamicLibraryConfig { 65 | final DynamicLibraryPlatformConfig? windows; 66 | final DynamicLibraryPlatformConfig? linux; 67 | final DynamicLibraryPlatformConfig? macOS; 68 | final DynamicLibraryPlatformConfig? iOS; 69 | final DynamicLibraryPlatformConfig? android; 70 | final DynamicLibraryPlatformConfig? fuchsia; 71 | final DynamicLibraryPlatformConfig? other; 72 | 73 | const DynamicLibraryConfig( 74 | {this.windows, 75 | this.linux, 76 | this.macOS, 77 | this.iOS, 78 | this.android, 79 | this.fuchsia, 80 | this.other}); 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IMPORTANT NOTICE 2 | While this package continues to work, and was even migrated to null safety, there is a new, official tool to generate dart ffi bindings: [ffigen](https://pub.dev/packages/ffigen) 3 | 4 | # Overview 5 | This library helps developers to generate [dart:ffi](https://dart.dev/guides/libraries/c-interop) 6 | bindings. You can contribute at [github.com/dart-interop/ffi_tool](https://github.com/dart-interop/ffi_tool). 7 | 8 | The advantages over handwritten _dart:ffi_ code are: 9 | * __Less boilerplate__ 10 | * You don't have to define multiple types for each C function. 11 | * You can require the generated code to use [cupertino_ffi](https://pub.dev/packages/cupertino_ffi) 12 | reference counting methods (`arcPush`, `arcPop`, `arcReturn`). 13 | * __Possibly better readability__ 14 | * You can use the original identifiers (such as `*size_t` instead of `Pointer`). 15 | * You can define aliases, e.g. `const len_t = 'int32';`. 16 | * You can configure how a shared library is loaded at runtime. 17 | 18 | # Getting started 19 | ## 1.Add dependency 20 | The version of this package hosted on pub is outdated, use the newest version from github! 21 | In 'pubspec.yaml': 22 | ```yaml 23 | dev_dependencies: 24 | ffi_tool: 25 | git: 26 | url: git://github.com/dart-interop/ffi_tool.git 27 | ref: v0.4.0 28 | ``` 29 | 30 | Run `pub get`. 31 | 32 | ## 2.Write a script 33 | Create 'tool/generate_example.dart'. 34 | 35 | ### Example 36 | 37 | ```dart 38 | import 'package:ffi_tool/c.dart'; 39 | import 'dart:io'; 40 | 41 | void main() { 42 | // Generates source code and runs 'dartfmt' 43 | generateFile(File('generated.dart'), library); 44 | } 45 | 46 | final library = Library.platformAware( 47 | // Configure, how the dynamic library should be loaded depending on the platform 48 | dynamicLibraryConfig: DynamicLibraryConfig( 49 | windows: DynamicLibraryPlatformConfig.open('path/to/library.dll'), 50 | android: DynamicLibraryPlatformConfig.open('path/to/library.so'), 51 | iOS: DynamicLibraryPlatformConfig.process()), 52 | 53 | // Optional library preamble 54 | preamble: '// Licensed under MIT license\r\n// AUTOMATICALLY GENERATED. DO NOT EDIT.', 55 | 56 | // Optional imports 57 | importedUris: { 58 | ImportedUri('package:example/library.dart'), 59 | }, 60 | 61 | /// List of generated functions, structs, and global variables 62 | elements: [ 63 | // A definition for a function in C 64 | Func( 65 | name: 'Example', 66 | documentation: 'Takes parameters and does stuff.', 67 | parameterTypes: ['int32', 'float32', '*void'], 68 | returnType: 'void', 69 | ), 70 | 71 | // A definition for a struct in C 72 | // structs must at lease have one field, for 73 | // structs without fields use Opaque 74 | Struct( 75 | name: 'ExampleStruct', 76 | fields: [ 77 | StructField( 78 | name: 'length', 79 | type: 'size_t', 80 | ), 81 | ], 82 | ), 83 | 84 | // Opaque structs are structs without fields 85 | Opaque( 86 | name: 'ExampleOpaqueStruct' 87 | ), 88 | 89 | // A definition for a global variable in C 90 | Global( 91 | name: 'ExampleGlobal', 92 | type: 'Int32', 93 | ), 94 | ], 95 | ); 96 | ``` 97 | 98 | ## 3.Run the script 99 | With Dart SDK: 100 | ``` 101 | pub run tool/generate_example.dart 102 | ``` 103 | 104 | With Flutter SDK: 105 | ``` 106 | flutter pub run tool/generate_example.dart 107 | ``` 108 | 109 | ## 4.About Null safty 110 | Since version 0.4.0 the code generated by this package and this package itself are null safe. To achive this, functions and globals needed to be encapsulated in a container class. This on the other hand causes the newly generated code to have an other API than older versions of this package produced. -------------------------------------------------------------------------------- /lib/src/c/struct.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 ffi_tool authors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | // OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | import 'dart_source_writer.dart'; 22 | import 'library.dart'; 23 | 24 | /// A definition for a C struct. 25 | /// 26 | /// ```dart 27 | /// import 'package:ffi_tool/c.dart'; 28 | /// import 'dart:io'; 29 | /// 30 | /// void main() { 31 | /// generateFile(File("generated.dart"), library); 32 | /// } 33 | /// 34 | /// const library = Library( 35 | /// dynamicLibraryPath: "path/to/library", 36 | /// 37 | /// elements: [ 38 | /// Struct( 39 | /// name: "Coordinate", 40 | /// fields: [ 41 | /// StructField( 42 | /// type: 'double', 43 | /// name: 'latitude', 44 | /// ), 45 | /// StructField( 46 | /// type: 'double', 47 | /// name: 'longitude', 48 | /// ), 49 | /// ], 50 | /// ), 51 | /// ], 52 | /// ); 53 | /// ``` 54 | class Struct extends Element { 55 | final bool arc; 56 | final List fields; 57 | 58 | /// Optional source injected inside the generated class. 59 | final String? inject; 60 | 61 | const Struct({ 62 | required String name, 63 | this.arc = false, 64 | required this.fields, 65 | String? documentation, 66 | this.inject, 67 | }) : super(name: name, documentation: documentation); 68 | 69 | @override 70 | void generateOuterSource(DartSourceWriter w, Library library) { 71 | if (fields.isEmpty) { 72 | throw Exception( 73 | 'Structs may no longer by empty and must contain at least one field! Consider using a Opaque instead!'); 74 | } 75 | if (arc) { 76 | w.imports.add( 77 | const ImportedUri('package:cupertino_ffi/ffi.dart', prefix: 'ffi'), 78 | ); 79 | } 80 | 81 | w.write('\n'); 82 | if (documentation == null) { 83 | w.write('/// C struct `$name`.\n'); 84 | } else { 85 | w.write('/// '); 86 | w.writeAll(documentation!.split('\n'), '\n/// '); 87 | w.write('\n'); 88 | } 89 | w.write('class $name extends ffi.Struct {\n'); 90 | w.write(' \n'); 91 | 92 | // 93 | // Write fields 94 | // 95 | for (var field in fields) { 96 | // Some types (Int32, Float32, etc.) need to be annotated 97 | final annotationName = w.getPropertyAnnotationType(field.type); 98 | if (annotationName != null && annotationName != 'ffi.Pointer') { 99 | w.write(' @$annotationName()\n'); 100 | } 101 | w.write(' ${w.getDartType(field.type)} ${field.name};\n'); 102 | w.write(' \n'); 103 | } 104 | 105 | // 106 | // Write factory 107 | // 108 | w.write( 109 | ' static ffi.Pointer<$name> allocate(ffi.Allocator allocator) {\n'); 110 | if (arc) { 111 | w.write( 112 | ' final result = allocator.allocate<$name>(ffi.sizeOf<$name>());\n'); 113 | w.write(' ffi.arcAdd(result);\n'); 114 | w.write(' return result;\n'); 115 | } else { 116 | w.write(' return allocator.allocate<$name>(ffi.sizeOf<$name>());\n'); 117 | } 118 | w.write(' }\n'); 119 | w.write(' \n'); 120 | 121 | // 122 | // Write injected source 123 | // 124 | final inject = this.inject; 125 | if (inject != null) { 126 | w.write(inject); 127 | } 128 | 129 | w.write('}\n'); 130 | } 131 | 132 | @override 133 | void generateInnerSource(DartSourceWriter w, Library library) {} 134 | 135 | @override 136 | bool generateConstructorSource(DartSourceWriter w, Library library) => false; 137 | } 138 | 139 | class StructField { 140 | final String name; 141 | final String type; 142 | const StructField({required this.type, required this.name}); 143 | } 144 | -------------------------------------------------------------------------------- /lib/src/c/func.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 ffi_tool authors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the 'Software'), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | // OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | import 'library.dart'; 22 | import 'dart_source_writer.dart'; 23 | 24 | /// A definition for C function. 25 | class Func extends Element { 26 | /// Parameter types. 27 | final List parameterTypes; 28 | 29 | /// Parameter names. 30 | List get parameterNames => parameters.map((p) => p.name).toList(); 31 | 32 | final List? _parameterNames; 33 | 34 | /// Parameters. 35 | List get parameters => List.generate( 36 | parameterTypes.length, 37 | (i) { 38 | final type = parameterTypes[i]; 39 | final name = (_parameterNames != null && i < _parameterNames!.length) 40 | ? _parameterNames![i] 41 | : 'arg$i'; 42 | return Parameter(type: type, name: name); 43 | }, 44 | ); 45 | 46 | /// Return type. 47 | /// 48 | /// Examples: 49 | /// * 'void' 50 | /// * '*CFData'. 51 | final String returnType; 52 | 53 | /// Whether the return value should use Apple reference counting? The 54 | /// implementation uses [cupertino_ffi](https://pub.dev/packages/cupertino_ffi). 55 | final bool arc; 56 | 57 | const Func({ 58 | required String name, 59 | String? documentation, 60 | required this.parameterTypes, 61 | List? parameterNames, 62 | required this.returnType, 63 | this.arc = false, 64 | }) : _parameterNames = parameterNames, 65 | super(name: name, documentation: documentation); 66 | 67 | @override 68 | void generateInnerSource(DartSourceWriter w, Library library) { 69 | final typedefDart = '_${name}_Dart'; 70 | w.write('\n'); 71 | 72 | // Lookup 73 | if (documentation == null) { 74 | w.write('/// C function `$name`.\n'); 75 | } else { 76 | w.write('/// '); 77 | w.writeAll(documentation!.split('\n'), '\n/// '); 78 | w.write('\n'); 79 | } 80 | w.write('${w.getDartType(returnType)} $name('); 81 | if (parameters.isNotEmpty) { 82 | w.write('\n'); 83 | for (var parameter in parameters) { 84 | w.write(' ${w.getDartType(parameter.type)} ${parameter.name},\n'); 85 | } 86 | } 87 | w.write(') {\n'); 88 | if (w.getDartType(returnType) == 'void') { 89 | w.write('_$name('); 90 | w.writeAll(parameterNames, ', '); 91 | w.write(');\n'); 92 | } else { 93 | if (arc && returnType.startsWith('*')) { 94 | w.write(' final result = '); 95 | w.write('_$name('); 96 | w.writeAll(parameterNames, ', '); 97 | w.write(');\n'); 98 | w.write(' arcAdd(result);\n'); 99 | w.write(' return result;\n'); 100 | } else { 101 | w.write(' return '); 102 | w.write('_$name('); 103 | w.writeAll(parameterNames, ', '); 104 | w.write(');\n'); 105 | } 106 | } 107 | w.write('}\n'); 108 | w.write('final $typedefDart _$name;'); 109 | } 110 | 111 | @override 112 | bool generateConstructorSource(DartSourceWriter w, Library library) { 113 | final typedefC = '_${name}_C'; 114 | final typedefDart = '_${name}_Dart'; 115 | w.write( 116 | '_$name = ${Library.dynamicLibraryIdentifier}.lookupFunction<$typedefC, $typedefDart>(\n'); 117 | w.write(' \'$name\',\n'); 118 | w.write(')\n'); 119 | return true; 120 | } 121 | 122 | @override 123 | void generateOuterSource(DartSourceWriter w, Library library) { 124 | if (arc) { 125 | w.imports.add( 126 | const ImportedUri('package:cupertino_ffi/objc.dart', prefix: 'ffi'), 127 | ); 128 | } 129 | final typedefC = '_${name}_C'; 130 | final typedefDart = '_${name}_Dart'; 131 | // C type 132 | { 133 | w.write('typedef $typedefC = ${w.getCType(returnType)} Function('); 134 | if (parameters.isNotEmpty) { 135 | w.write('\n'); 136 | for (var parameter in parameters) { 137 | w.write(' ${w.getCType(parameter.type)} ${parameter.name},\n'); 138 | } 139 | } 140 | w.write(');\n'); 141 | } 142 | 143 | // Dart type 144 | { 145 | w.write('typedef $typedefDart = ${w.getDartType(returnType)} Function('); 146 | if (parameters.isNotEmpty) { 147 | w.write('\n'); 148 | for (var parameter in parameters) { 149 | w.write(' ${w.getDartType(parameter.type)} ${parameter.name},\n'); 150 | } 151 | } 152 | w.write(');\n'); 153 | } 154 | } 155 | } 156 | 157 | class Parameter { 158 | final String name; 159 | final String type; 160 | const Parameter({ 161 | required this.type, 162 | required this.name, 163 | }); 164 | } 165 | -------------------------------------------------------------------------------- /lib/src/c/dart_source_writer.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 ffi_tool authors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | // OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | /// Lowercase definition names to mappings. 22 | const _types = { 23 | '*void': _Type(ffi: 'ffi.Pointer', dart: 'ffi.Pointer'), 24 | 'void': _Type(ffi: 'ffi.Void', dart: 'void'), 25 | 26 | 'intptr': _Type(ffi: 'ffi.IntPtr', dart: 'int'), 27 | 28 | // For convenience 29 | 'size_t': _Type(ffi: 'ffi.IntPtr', dart: 'int'), 30 | 31 | 'char': _Type(ffi: 'ffi.Uint8', dart: 'int'), 32 | 'int8': _Type(ffi: 'ffi.Int8', dart: 'int'), 33 | 'int16': _Type(ffi: 'ffi.Int16', dart: 'int'), 34 | 'int32': _Type(ffi: 'ffi.Int32', dart: 'int'), 35 | 'int64': _Type(ffi: 'ffi.Int64', dart: 'int'), 36 | 'uint8': _Type(ffi: 'ffi.Uint8', dart: 'int'), 37 | 'uint16': _Type(ffi: 'ffi.Uint16', dart: 'int'), 38 | 'uint32': _Type(ffi: 'ffi.Uint32', dart: 'int'), 39 | 'uint64': _Type(ffi: 'ffi.Uint64', dart: 'int'), 40 | 'float': _Type(ffi: 'ffi.Float', dart: 'double'), 41 | 'double': _Type(ffi: 'ffi.Double', dart: 'double'), 42 | 'float32': _Type(ffi: 'ffi.Float', dart: 'double'), 43 | 'float64': _Type(ffi: 'ffi.Double', dart: 'double'), 44 | }; 45 | 46 | class DartSourceWriter { 47 | String? libraryName; 48 | String? partOf; 49 | String? preamble; 50 | final Set imports = {}; 51 | final Set parts = {}; 52 | final StringBuffer _sb = StringBuffer(); 53 | 54 | /// Returns Dart C type for the description type. 55 | /// 56 | /// Examples: 57 | /// * 'Int32' --> 'Int32' 58 | /// * '*CFString' --> 'Pointer' 59 | /// * '*void' --> 'Pointer' 60 | /// * 'void' --> 'Void' 61 | String getCType(String name) { 62 | final type = _types[name.toLowerCase()]; 63 | if (type != null) { 64 | final importUri = type.importInfo; 65 | if (importUri != null) { 66 | imports.add(importUri); 67 | } 68 | return type.ffi; 69 | } 70 | if (name.startsWith('*')) { 71 | return 'ffi.Pointer<${getCType(name.substring(1))}>'; 72 | } 73 | return name; 74 | } 75 | 76 | /// Converts description type to Dart type. 77 | /// 78 | /// Examples: 79 | /// * 'Int32' --> 'int' 80 | /// * 'Int64' --> 'int' 81 | /// * '*CFString' --> 'Pointer' 82 | String getDartType(String name) { 83 | final type = _types[name.toLowerCase()]; 84 | if (type != null) { 85 | final importUri = type.importInfo; 86 | if (importUri != null) { 87 | imports.add(importUri); 88 | } 89 | return type.dart; 90 | } 91 | if (name.startsWith('*')) { 92 | return 'ffi.Pointer<${getCType(name.substring(1))}>'; 93 | } 94 | return name; 95 | } 96 | 97 | /// Returns Dart C type for the description type. 98 | /// 99 | /// Examples: 100 | /// * 'Int32' --> 'Int32' 101 | /// * '*CFString' --> 'Pointer' 102 | /// * '*void' --> 'Pointer' 103 | /// * 'void' --> 'Void' 104 | String? getPropertyAnnotationType(String name) { 105 | if (name.startsWith('*')) { 106 | return 'ffi.Pointer'; 107 | } 108 | final type = _types[name.toLowerCase()]; 109 | if (type != null) { 110 | return type.ffi; 111 | } 112 | return null; 113 | } 114 | 115 | @override 116 | String toString() { 117 | final sb = StringBuffer(); 118 | sb.write(preamble); 119 | sb.write('\n'); 120 | 121 | // Library name 122 | if (libraryName != null) { 123 | sb.write('\n'); 124 | sb.write('library $libraryName;\n'); 125 | } 126 | 127 | // Part of 128 | if (partOf != null) { 129 | sb.write('\n'); 130 | sb.write('partOf $partOf;\n'); 131 | } 132 | 133 | // Imports 134 | if (imports.isNotEmpty) { 135 | sb.write('\n'); 136 | for (var importInfo in imports.toList()..sort()) { 137 | sb.write("import '${importInfo.uri}'"); 138 | final prefix = importInfo.prefix; 139 | final show = importInfo.show; 140 | final hide = importInfo.hide; 141 | if (prefix != null) { 142 | sb.write(' as $prefix'); 143 | } 144 | if (show != null) { 145 | sb.write(' show $show'); 146 | } 147 | if (hide != null) { 148 | sb.write(' hide $hide'); 149 | } 150 | sb.write(';\n'); 151 | } 152 | } 153 | 154 | // Parts 155 | if (parts.isNotEmpty) { 156 | for (var part in parts) { 157 | sb.write("part '$part';\n"); 158 | } 159 | } 160 | 161 | // Content 162 | sb.write('\n'); 163 | sb.write(_sb.toString()); 164 | return sb.toString(); 165 | } 166 | 167 | void write(Object obj) { 168 | _sb.write(obj); 169 | } 170 | 171 | void writeAll(Iterable objects, [String separator = '']) { 172 | _sb.writeAll(objects, separator); 173 | } 174 | } 175 | 176 | /// Describes an imported Dart package. 177 | class ImportedUri implements Comparable { 178 | final String uri; 179 | final String? prefix; 180 | final String? show; 181 | final String? hide; 182 | 183 | const ImportedUri(this.uri, {this.prefix, this.show, this.hide}); 184 | 185 | @override 186 | int get hashCode => uri.hashCode; 187 | 188 | @override 189 | bool operator ==(other) => 190 | other is ImportedUri && uri == other.uri && prefix == other.prefix; 191 | 192 | @override 193 | int compareTo(ImportedUri other) { 194 | { 195 | final r = uri.compareTo(other.uri); 196 | if (r != 0) { 197 | return r; 198 | } 199 | } 200 | return (prefix ?? '').compareTo(other.prefix ?? ''); 201 | } 202 | } 203 | 204 | class _Type { 205 | final String ffi; 206 | final String dart; 207 | final ImportedUri? importInfo; 208 | const _Type({required this.ffi, required this.dart, this.importInfo}); 209 | } 210 | -------------------------------------------------------------------------------- /lib/src/c/library.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 ffi_tool authors. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the 'Software'), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 14 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | // OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | import 'dart_source_writer.dart'; 22 | import 'config.dart'; 23 | 24 | /// A definition for a C library. 25 | class Library { 26 | /// The name for the class all functions and globals are placed in. 27 | /// 28 | /// This is required since version 0.4.0 of this package due to null safety. 29 | final String containerClassName; 30 | 31 | /// A preamble which is simply copied to the generated file. 32 | final String? preamble; 33 | 34 | /// Custom load code to load the underlying dynamic library, only non-null if created with the `Library.customLoadCode` constructor. 35 | final String? customLoadCode; 36 | 37 | /// If the library was created with the default constructor and is not platform aware this represents the path of the underlying dynamic library. 38 | final String? dynamicLibraryPath; 39 | 40 | ///If this library is platform aware, this represents the platform config, otherwise this is null. 41 | final DynamicLibraryConfig? dynamicLibraryConfig; 42 | 43 | /// Optional 'library' directive in the generated file. 44 | final String? libraryName; 45 | 46 | /// Optional 'part of' directive in the generated file. 47 | final String? partOf; 48 | 49 | /// Optional imported URIs. May be empty nut never null. 50 | final Set importedUris; 51 | 52 | /// Optional 'part' directives in the generated file. May be empty nut never null. 53 | final Set parts; 54 | 55 | /// Elements. 56 | final List elements; 57 | 58 | static const String dynamicLibraryIdentifier = '_dynamicLibrary'; 59 | 60 | ///The default constructor, which is not platform aware. 61 | /// 62 | ///The dynamic library will be created using the `DynamicLibrary.open` method and the given `dynamicLibraryPath`. 63 | const Library({ 64 | required this.dynamicLibraryPath, 65 | required this.elements, 66 | String? containerClassName, 67 | String? libraryName, 68 | this.partOf, 69 | this.preamble = '// AUTOMATICALLY GENERATED. DO NOT EDIT.', 70 | this.importedUris = const {}, 71 | this.parts = const {}, 72 | }) : dynamicLibraryConfig = null, 73 | customLoadCode = null, 74 | libraryName = libraryName, 75 | containerClassName = 76 | containerClassName ?? ((libraryName ?? '') + 'FunctionsAndGlobals'); 77 | 78 | ///Creates a library which is platform aware, meaning that the creation of the underlying dynamic library can depend on the platform. 79 | const Library.platformAware({ 80 | required this.dynamicLibraryConfig, 81 | required this.elements, 82 | String? containerClassName, 83 | String? libraryName, 84 | this.partOf, 85 | this.preamble = '// AUTOMATICALLY GENERATED. DO NOT EDIT.', 86 | this.importedUris = const {}, 87 | this.parts = const {}, 88 | }) : dynamicLibraryPath = null, 89 | customLoadCode = null, 90 | libraryName = libraryName, 91 | containerClassName = 92 | containerClassName ?? ((libraryName ?? '') + 'FunctionsAndGlobals'); 93 | 94 | ///Creates a library where the underlying dynamic library is explicitly loaded using the given `customLoadCode`. 95 | /// 96 | ///`customLoadCode` must be valid dart code and will be prefixed with "ffi.DynamicLibrary _open(){\n" and surfixed with "\n}". 97 | ///In other words, `customLoadCode` is the method body of a method with signatrure "DynamicLibrary _open()". 98 | const Library.customLoadCode({ 99 | required this.customLoadCode, 100 | required this.elements, 101 | String? containerClassName, 102 | String? libraryName, 103 | this.partOf, 104 | this.preamble = '// AUTOMATICALLY GENERATED. DO NOT EDIT.', 105 | this.importedUris = const {}, 106 | this.parts = const {}, 107 | }) : dynamicLibraryPath = null, 108 | dynamicLibraryConfig = null, 109 | libraryName = libraryName, 110 | containerClassName = 111 | containerClassName ?? ((libraryName ?? '') + 'FunctionsAndGlobals'); 112 | 113 | ///Creates a library without caring about the underlying library. You have to set it manually using the init() function of the generated file at runtime. 114 | const Library.withoutLoading({ 115 | required this.elements, 116 | String? containerClassName, 117 | String? libraryName, 118 | this.partOf, 119 | this.preamble = '// AUTOMATICALLY GENERATED. DO NOT EDIT.', 120 | this.importedUris = const {}, 121 | this.parts = const {}, 122 | }) : dynamicLibraryPath = null, 123 | dynamicLibraryConfig = null, 124 | customLoadCode = null, 125 | libraryName = libraryName, 126 | containerClassName = 127 | containerClassName ?? ((libraryName ?? '') + 'FunctionsAndGlobals'); 128 | 129 | bool get _platformAware => dynamicLibraryConfig != null; 130 | 131 | bool get _customLoadCode => customLoadCode != null; 132 | 133 | bool get _withoutLoading => 134 | dynamicLibraryConfig == null && 135 | customLoadCode == null && 136 | dynamicLibraryPath == null; 137 | 138 | void generateSource(DartSourceWriter w) { 139 | w.libraryName = libraryName; 140 | w.partOf = partOf; 141 | w.preamble = preamble; 142 | 143 | // Imports 144 | w.imports.add(const ImportedUri('dart:ffi', prefix: 'ffi')); 145 | if (_platformAware) { 146 | w.imports 147 | .add(const ImportedUri('dart:io', prefix: 'io', show: 'Platform')); 148 | } 149 | w.imports.addAll(importedUris); 150 | 151 | // Parts 152 | w.parts.addAll(parts); 153 | 154 | // Parts 155 | if (parts.isNotEmpty) { 156 | w.write('\n'); 157 | for (var part in parts) { 158 | w.write("part '$part';\n"); 159 | } 160 | w.write('\n'); 161 | } 162 | 163 | // Outer elements, meaning things not belonging into a class 164 | for (var element in elements) { 165 | element.generateOuterSource(w, this); 166 | } 167 | 168 | w.write('\n'); 169 | if (_customLoadCode) { 170 | w.write('ffi.DynamicLibrary _open(){\n'); 171 | w.write(customLoadCode!); 172 | w.write('\n}'); 173 | } else if (_platformAware) { 174 | final dynamicLibraryConfig = this.dynamicLibraryConfig!; 175 | w.write('ffi.DynamicLibrary _open(){\n\t'); 176 | if (dynamicLibraryConfig.windows != null) { 177 | w.write( 178 | 'if(io.Platform.isWindows) return ffi.${dynamicLibraryConfig.windows};\n\t'); 179 | } 180 | if (dynamicLibraryConfig.linux != null) { 181 | w.write( 182 | 'if(io.Platform.isLinux) return ffi.${dynamicLibraryConfig.linux};\n\t'); 183 | } 184 | if (dynamicLibraryConfig.android != null) { 185 | w.write( 186 | 'if(io.Platform.isAndroid) return ffi.${dynamicLibraryConfig.android};\n\t'); 187 | } 188 | if (dynamicLibraryConfig.macOS != null) { 189 | w.write( 190 | 'if(io.Platform.isMacOS) return ffi.${dynamicLibraryConfig.macOS};\n\t'); 191 | } 192 | if (dynamicLibraryConfig.iOS != null) { 193 | w.write( 194 | 'if(io.Platform.isIOS) return ffi.${dynamicLibraryConfig.iOS};\n\t'); 195 | } 196 | if (dynamicLibraryConfig.fuchsia != null) { 197 | w.write( 198 | 'if(io.Platform.isFuchsia) return ffi.${dynamicLibraryConfig.fuchsia};\n\t'); 199 | } 200 | var other = dynamicLibraryConfig.other == null 201 | ? 'throw UnsupportedError(\'This platform is not supported.\');\n' 202 | : 'return ffi.${dynamicLibraryConfig.other};\n'; 203 | w.write(other); 204 | w.write('}'); 205 | } else if (!_withoutLoading) { 206 | w.write('ffi.DynamicLibrary _open() => ffi.DynamicLibrary.open(\n'); 207 | w.write(' \'${dynamicLibraryPath}\',\n'); 208 | w.write(');\n'); 209 | } 210 | 211 | if (!_withoutLoading) { 212 | w.write('/// Dynamic library\n'); 213 | w.write( 214 | 'final ffi.DynamicLibrary ${dynamicLibraryIdentifier} = _open();'); 215 | } 216 | 217 | w.write('class $containerClassName {\n'); 218 | 219 | // Class constructor 220 | w.write('$containerClassName('); 221 | if (_withoutLoading) { 222 | w.write('ffi.DynamicLibrary $dynamicLibraryIdentifier'); 223 | } 224 | w.write(')'); 225 | var first = true; 226 | var needSeparator = true; 227 | for (var element in elements) { 228 | if (needSeparator) { 229 | if (first) { 230 | first = false; 231 | w.write(':\n'); 232 | } else { 233 | w.write(',\n'); 234 | } 235 | } 236 | needSeparator = element.generateConstructorSource(w, this); 237 | } 238 | w.write(';\n'); 239 | w.write('\n'); 240 | // Inner elements, the things that belong into a class 241 | for (var element in elements) { 242 | element.generateInnerSource(w, this); 243 | } 244 | 245 | w.write('}'); 246 | } 247 | 248 | @override 249 | String toString() { 250 | final w = DartSourceWriter(); 251 | generateSource(w); 252 | return w.toString(); 253 | } 254 | } 255 | 256 | /// Abstract base class for Func, Struct and Global. 257 | abstract class Element { 258 | /// Name of this element 259 | final String name; 260 | 261 | /// Optional documentation of this element 262 | final String? documentation; 263 | 264 | const Element({required this.name, this.documentation}); 265 | 266 | void generateOuterSource(DartSourceWriter w, Library library); 267 | void generateInnerSource(DartSourceWriter w, Library library); 268 | bool generateConstructorSource(DartSourceWriter w, Library library); 269 | } 270 | --------------------------------------------------------------------------------