-
Notifications
You must be signed in to change notification settings - Fork 27.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FFI plugins #96225
FFI plugins #96225
Conversation
d14b977
to
de71c8d
Compare
packages/flutter_tools/templates/plugin_ffi/android.tmpl/build.gradle.tmpl
Outdated
Show resolved
Hide resolved
Will support for prebuilt libraries be added in the future? It's not only important for closed-source libraries but also if the compiler is too difficult to setup on the users machine (for example for Rust libraries) |
It is the intention to support that use case as well. At this point we're not entirely sure whether that warrants a new template, or whether a sample and documentation is enough. (Some pointers based on the source template: add the precompiled dl path to Windows Linux Also, it would require package authors to upload the binaries for all (18) target platforms (hardware * OS combination) to pub. We're exploring if there are any obstacles for doing that. |
Building the binaries for all platforms is no problem but more documentation would be very helpful. Especially how to:
|
|
||
Future<TaskResult> call() async { | ||
final Directory tempDir = | ||
Directory.systemTemp.createTempSync('flutter_devicelab_plugin_test.'); | ||
// FFI plugins do not have support for `flutter test`. | ||
// `flutter test` does not do a native build. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated to this test, what is the user experience if I run flutter test
on an app with an FFI plugin? Does it just fail to lookup the symbol?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The FFI plugin template itself doesn't include any unit tests:
Test directory "test" not found.
When adding a test, opening the dynamic library fails:
Shell: Invalid argument(s): Failed to load dynamic library '<...>':
I created a tracking issue:
'Try creating a fresh project and migrating your existing code to ' | ||
'the new project manually.'); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could still be within the else {
, right? since if we've set template
on line 165 then the value of detectedProjectType
won't be used anyway, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it is in the else, the following code is dead code:
if (detectedProjectType != null && template != detectedProjectType && metadataExists) {
// We can only be definitive that this is the wrong type if the .metadata file
// exists and contains a type that doesn't match.
throwToolExit("The requested template type '${flutterProjectTypeToString(template)}' doesn't match the "
"existing template type of '${flutterProjectTypeToString(detectedProjectType)}'.");
}
template != detectedProjectType
will never be true.
That means we do not detect inconsistent project types. For example having method channel plugin
in the .metadata file and plugin_ffi
as an argument. Not detecting inconsistent project types results in a garbled project state with files from two different templates mixed together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of small comments, but overall this is looking really good!
packages/flutter_tools/test/commands.shard/permeable/create_test.dart
Outdated
Show resolved
Hide resolved
packages/flutter_tools/templates/plugin_ffi/src.tmpl/CMakeLists.txt.tmpl
Outdated
Show resolved
Hide resolved
packages/flutter_tools/templates/plugin_ffi/src.tmpl/CMakeLists.txt.tmpl
Outdated
Show resolved
Hide resolved
packages/flutter_tools/templates/plugin_ffi/src.tmpl/projectName.h.tmpl
Outdated
Show resolved
Hide resolved
packages/flutter_tools/templates/plugin_ffi/windows.tmpl/CMakeLists.txt.tmpl
Show resolved
Hide resolved
packages/flutter_tools/templates/plugin_shared/pubspec.yaml.tmpl
Outdated
Show resolved
Hide resolved
@stuartmorgan I've addressed all of them. PTAL. @christopherfujino I've replied to your comment above. Please let me know if you have any further comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Thanks for all the work here :)
Thanks Stuart!
Let's make our users happy! @christopherfujino waiting on your approval (or delegation) as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Thanks for landing this!
I have actually just ran into the issue @leisim prophesied; I am trying to build a Flutter library wrapping around a Rust library. Seeing as I cannot expect users of the library to have For macOS/iOS, my best attempt was to create an XCFramework from the compiled rust static libraries (quite a few @dcharkes mentions:
I'm most interested in the macOS/iOS part here (for now). Could you please provide a brief working example of how to go about this for macOS/iOS? I have tried for hours and cannot get the XCFramework & podspec file to work properly. Edit: just realized pub.dev has a 100 MB package limit, which is an issue. I guess users will need to install |
100 MB package version limit@jonasfj We have our first user hit the 100 MB limit. Precompiled static libraries on MacOS/iOS in a xcframework
I've got something slightly working for precompiled static libraries in an xcframework: mylib_staticlib/macos/mylib_staticlib.podspec #
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint mylib_staticlib.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'mylib_staticlib'
s.version = '0.0.1'
s.summary = 'A new flutter plugin project.'
s.description = <<-DESC
A new flutter plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'FlutterMacOS'
s.platform = :osx, '10.11'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
s.vendored_libraries = 'Frameworks/libmylib_staticlib.a'
s.pod_target_xcconfig = { "OTHER_LDFLAGS" => "-force_load $(PODS_TARGET_SRCROOT)/Frameworks/mylib_staticlib.xcframework/macos-arm64_x86_64/libmylib_staticlib.a" }
s.swift_version = '5.0'
end mylib_staticlib/macos/Classes/dummy_file.c // A dummy file so that the mylib_staticlib.framework is created. Path of the framework with static libraries: mylib_staticlib/ios/Frameworks/mylib_staticlib.xcframework/ios-arm64_x86_64-simulator/libmylib_staticlib.a One issue with doing it like this is that the ios_arm64 is compiled either against the iossimulator or against ios, and I wasn't able to figure out how to make that both work. |
Check out the Isar podspec and the build script for Rust. It works really well even without -force_load and supports all cpu/simulator configurations. That being said it is still a huge pain to ship native libraries for all platforms and I really hope there will be a nice solution by Flutter. Ideally allowing static compilation into the Flutter native lib. |
This looks like a better solution than mine, @GregoryConrad please take this as example instead. 😄
As the end goal, we would like static compilation into the compiled Dart code, that means we can use the native linker to get rid off all native code not referenced. Tracked in: |
@leisim Thanks for that! I didn't think of using a regular plugin package instead of an FFI plugin to actually integrate everything but it looks like it works smoothly. Also, thank you both for the speedy response! Static Linking w/ DartThis would be absolutely fantastic, as the generated static libraries I am dealing with are quite large (~100-200 mb per platform & architecture) and I am sure they have plenty of dead code not needed by my Dart library in them (caused by a large number of dependencies in the Rust library I am wrapping). 100 MB LimitAs I mentioned in the previous paragraph, the static libraries I am dealing with are exceedingly large, and the entire upload could easily be above 1 GB. If you do consider changing the pub limit, perhaps it would be worth considering having a separate upload for static/native libraries and then link to them in the main upload (or perhaps a different way to handle them altogether)? Related (above comment):
Maybe, if Dart does get static linking, the theoretical separate upload I mentioned above could just be for native libraries that are linked automatically by Flutter/Dart tooling? That could be a really elegant solution that is probably easier for package maintainers and the tooling alike, i.e.:
|
@GregoryConrad 100-200MB sounds incredibly large. Did you check the steps to reduce Rust binary size? It's quite expensive to load such a large binary at runtime. |
I was actually going to look into that later today since it did seem rather large; thanks for the link. |
In case anyone else stumbles into this, I got FFI working with my cross-platform XCFramework using
Pod::Spec.new do |spec|
spec.name = 'my_package'
spec.version = '0.0.1'
spec.license = { :file => '../LICENSE' }
spec.homepage = ''
spec.authors = { 'Your Name' => 'blabla@example.com' }
spec.summary = 'Some summary'
spec.source = { :path => '.' }
spec.source_files = 'Classes/**/*'
spec.public_header_files = 'Classes/**/*.h'
spec.vendored_frameworks = 'Frameworks/MyPackage.xcframework'
spec.ios.deployment_target = '11.0'
spec.osx.deployment_target = '10.11'
end
public func dummyMethodToEnforceBundling() {
enforce_binding() // disable tree shaking
}
/// Enforce the binding for this library (to prevent tree-shaking)
#[no_mangle]
pub extern "C" fn enforce_binding() {} |
I'm not sure we should tweak the 100MB limit. If you're things are really this big, then maybe:
|
Add support for
flutter create --template=plugin_ffi
andflutter run
support forffiPlugin: true
inpubspec.yaml
.This addresses 2 issues:
flutter create --template=plugin
. However, this generates boilerplate for plugin registration and example code for method channels which go unused.Design doc.
NDK versioning doc.
Some relevant issues:
Included in this PR:
package:ffigen
to generate the bindings.Not included in this PR:
flutter test
. This requires abin/setup.dart
to invoke a native build outsideflutter run
orflutter build
.bin/setup.dart
to invoke a native build.dart_api_dl.h
to be able to invoke functions fromdart_api.h
.dart_api.h
.plugin
andplugin_ffi
templates. The example apps (example/lib/main.dart
) are different.Test this PR locally without building Flutter by running the Flutter commands from source (replace platforms for your host OS).
For context, previous PR (reverted):
If you had to change anything in the flutter/tests repo, include a link to the migration guide as per the breaking change policy.
Pre-launch Checklist
///
).