diff --git a/documentation/docs/frequently-asked-questions.md b/documentation/docs/frequently-asked-questions.md index 4d18af4c..d64be28e 100644 --- a/documentation/docs/frequently-asked-questions.md +++ b/documentation/docs/frequently-asked-questions.md @@ -308,3 +308,25 @@ These links might be a help: - https://kazlauskas.me/entries/writing-proper-buildrs-scripts - https://github.com/RustAudio/rodio/issues/404 - https://github.com/breez/c-breez/issues/553 + +### How do I set the path to a compiled dynamic library? + +You might want to run your app on embedded devices. However, you may encounter this error when running your app on a non-major platform: + +``` +Failed to load dynamic library 'libhub.so': libhub.so: cannot open shared object file: No such file or directory +``` + +In this case, you can specify a path that points to the compiled Rust library. Simply provide a string path to your dynamic library file. + +```dart title="lib/main.dart" +import './messages/generated.dart'; + +async void main() { + await initializeRust(compiledLibPath: "/path/to/library/libhub.so"); + ... +} +... +``` + +This provided path will be used for finding dynamic library files on native platforms with Dart's `DynamicLibrary.open([compiledLibPath])`, and for loading the JavaScript module on the web with `import init, * as wasmBindings from "[compiledLibPath]"`. diff --git a/flutter_ffi_plugin/bin/src/message.dart b/flutter_ffi_plugin/bin/src/message.dart index 0353d83d..87bbfe14 100644 --- a/flutter_ffi_plugin/bin/src/message.dart +++ b/flutter_ffi_plugin/bin/src/message.dart @@ -504,7 +504,8 @@ hash_map.insert( import 'dart:typed_data'; import 'package:rinf/rinf.dart'; -Future initializeRust() async { +Future initializeRust({String? compiledLibPath}) async { + setCompiledLibPath(compiledLibPath); await prepareInterface(handleRustSignal); startRustLogic(); } diff --git a/flutter_ffi_plugin/lib/rinf.dart b/flutter_ffi_plugin/lib/rinf.dart index 4b5af0eb..debb3802 100755 --- a/flutter_ffi_plugin/lib/rinf.dart +++ b/flutter_ffi_plugin/lib/rinf.dart @@ -6,6 +6,16 @@ import 'src/exports.dart'; export 'src/interface.dart' show RustSignal; +/// Sets the exact file path of the dynamic library +/// compiled from the `hub` crate. +/// On the web, this function sets the path to the JavaScript module +/// that needs to be loaded. +/// This function might not be necessary for major platforms +/// but can be useful when the app runs on embedded devices. +void setCompiledLibPath(String? path) { + setCompiledLibPathExtern(path); +} + /// Prepares the native interface /// needed to communicate with Rust. Future prepareInterface(HandleRustSignal handleRustSignal) async { @@ -18,9 +28,9 @@ void startRustLogic() async { } /// Terminates all Rust tasks. -/// Doing so before closing the Flutter app -/// can prevent potential memory errors that may occur -/// when Rust attempts to send data after the Dart VM has been turned off. +/// Calling this function before closing the Flutter app +/// can prevent potential resource leaks that may occur +/// if the Rust side is abruptly terminated. /// Please note that on the web, this function does not have any effect, /// as tasks are managed by the JavaScript runtime, not Rust. void stopRustLogic() async { diff --git a/flutter_ffi_plugin/lib/src/interface_os.dart b/flutter_ffi_plugin/lib/src/interface_os.dart index 938d869c..7407ed6b 100644 --- a/flutter_ffi_plugin/lib/src/interface_os.dart +++ b/flutter_ffi_plugin/lib/src/interface_os.dart @@ -7,6 +7,10 @@ import 'dart:isolate'; import 'interface.dart'; import 'dart:convert'; +void setCompiledLibPathExtern(String? path) { + setDynamicLibPath(path); +} + Future prepareInterfaceExtern( HandleRustSignal handleRustSignal, ) async { diff --git a/flutter_ffi_plugin/lib/src/interface_web.dart b/flutter_ffi_plugin/lib/src/interface_web.dart index 05035d43..97642a4a 100644 --- a/flutter_ffi_plugin/lib/src/interface_web.dart +++ b/flutter_ffi_plugin/lib/src/interface_web.dart @@ -7,6 +7,10 @@ import 'interface.dart'; import 'dart:async'; import 'dart:convert'; +void setCompiledLibPathExtern(String? path) { + setJsLibPath(path); +} + Future prepareInterfaceExtern( HandleRustSignal handleRustSignal, ) async { diff --git a/flutter_ffi_plugin/lib/src/load_os.dart b/flutter_ffi_plugin/lib/src/load_os.dart index 870bd9ff..2b118966 100644 --- a/flutter_ffi_plugin/lib/src/load_os.dart +++ b/flutter_ffi_plugin/lib/src/load_os.dart @@ -1,9 +1,21 @@ import 'dart:io' as io; import 'dart:ffi'; +String? dynamicLibPath; final rustLibrary = loadRustLibrary(); +void setDynamicLibPath(String? path) { + dynamicLibPath = path; +} + DynamicLibrary loadRustLibrary() { + // Use provided dynamic library path if possible. + final path = dynamicLibPath; + if (path != null) { + return DynamicLibrary.open(path); + } + + // Otherewise, use the default path. if (io.Platform.isLinux) { return DynamicLibrary.open('libhub.so'); } else if (io.Platform.isAndroid) { diff --git a/flutter_ffi_plugin/lib/src/load_web.dart b/flutter_ffi_plugin/lib/src/load_web.dart index d54ba4cf..759492a2 100644 --- a/flutter_ffi_plugin/lib/src/load_web.dart +++ b/flutter_ffi_plugin/lib/src/load_web.dart @@ -4,11 +4,17 @@ import 'dart:js' as js; import 'dart:html'; import 'dart:async'; +String? jsLibPath; + // When Dart performs hot restart, // the `rinf` object is already defined // as a global JavaScript variable. final wasAlreadyLoaded = js.context.hasProperty("rinf"); +void setJsLibPath(String? path) { + jsLibPath = path; +} + Future loadJsFile() async { if (wasAlreadyLoaded) { return; @@ -17,10 +23,13 @@ Future loadJsFile() async { final loadCompleter = Completer(); js.context['completeRinfLoad'] = loadCompleter.complete; + // Use the default JavaScript path unless provided. + final path = jsLibPath ?? "/pkg/hub.js"; + final scriptElement = ScriptElement(); scriptElement.type = "module"; scriptElement.innerHtml = ''' -import init, * as wasmBindings from "/pkg/hub.js"; +import init, * as wasmBindings from "$path"; await init(); window.rinf = { ...wasmBindings }; completeRinfLoad();