├── .gitignore ├── .metadata ├── .vscode └── launch.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── build.gradle ├── settings.gradle └── src │ └── main │ └── AndroidManifest.xml ├── cargokit ├── .github │ └── workflows │ │ ├── check_and_lint.yml │ │ └── test_example_plugin_build.yml ├── .gitignore ├── LICENSE ├── README ├── build_pod.sh ├── build_tool │ ├── README.md │ ├── analysis_options.yaml │ ├── bin │ │ └── build_tool.dart │ ├── lib │ │ ├── build_tool.dart │ │ └── src │ │ │ ├── android_environment.dart │ │ │ ├── artifacts_provider.dart │ │ │ ├── build_cmake.dart │ │ │ ├── build_gradle.dart │ │ │ ├── build_pod.dart │ │ │ ├── build_tool.dart │ │ │ ├── builder.dart │ │ │ ├── cargo.dart │ │ │ ├── crate_hash.dart │ │ │ ├── environment.dart │ │ │ ├── logging.dart │ │ │ ├── options.dart │ │ │ ├── precompile_binaries.dart │ │ │ ├── rustup.dart │ │ │ ├── target.dart │ │ │ ├── util.dart │ │ │ └── verify_binaries.dart │ ├── pubspec.lock │ ├── pubspec.yaml │ └── test │ │ ├── builder_test.dart │ │ ├── cargo_test.dart │ │ └── options_test.dart ├── cmake │ ├── cargokit.cmake │ └── resolve_symlinks.ps1 ├── docs │ ├── architecture.md │ └── precompiled_binaries.md ├── gradle │ └── plugin.gradle ├── run_build_tool.cmd └── run_build_tool.sh ├── example ├── .gitignore ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── hello_rust_ffi_plugin_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ └── README.md │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ └── RunnerTests │ │ └── RunnerTests.swift ├── lib │ └── main.dart ├── linux │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ │ ├── CMakeLists.txt │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugin_registrant.h │ │ └── generated_plugins.cmake │ ├── main.cc │ ├── my_application.cc │ └── my_application.h ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── app_icon_1024.png │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_512.png │ │ │ │ └── app_icon_64.png │ │ ├── Base.lproj │ │ │ └── MainMenu.xib │ │ ├── Configs │ │ │ ├── AppInfo.xcconfig │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements │ └── RunnerTests │ │ └── RunnerTests.swift ├── pubspec.lock ├── pubspec.yaml └── windows │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake │ └── runner │ ├── CMakeLists.txt │ ├── Runner.rc │ ├── flutter_window.cpp │ ├── flutter_window.h │ ├── main.cpp │ ├── resource.h │ ├── resources │ └── app_icon.ico │ ├── runner.exe.manifest │ ├── utils.cpp │ ├── utils.h │ ├── win32_window.cpp │ └── win32_window.h ├── ffigen.yaml ├── ios ├── lib ├── hello_rust_ffi_plugin.dart └── hello_rust_ffi_plugin_bindings_generated.dart ├── linux └── CMakeLists.txt ├── macos ├── Classes │ └── hello_rust_ffi_plugin.c └── hello_rust_ffi_plugin.podspec ├── pubspec.yaml ├── rust ├── Cargo.lock ├── Cargo.toml └── src │ └── lib.rs └── windows ├── .gitignore └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | 32 | target 33 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "bf94af1906719a4b1bde7743602a46b1d52c7402" 8 | channel: "main" 9 | 10 | project_type: plugin_ffi 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 17 | base_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 18 | - platform: android 19 | create_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 20 | base_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 21 | - platform: ios 22 | create_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 23 | base_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 24 | - platform: linux 25 | create_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 26 | base_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 27 | - platform: macos 28 | create_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 29 | base_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 30 | - platform: windows 31 | create_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 32 | base_revision: bf94af1906719a4b1bde7743602a46b1d52c7402 33 | 34 | # User provided section 35 | 36 | # List of Local paths (relative to this file) that should be 37 | # ignored by the migrate tool. 38 | # 39 | # Files that are not part of the templates will be ignored by default. 40 | unmanaged_files: 41 | - 'lib/main.dart' 42 | - 'ios/Runner.xcodeproj/project.pbxproj' 43 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug", 11 | "program": "${workspaceFolder}/", 12 | "args": [], 13 | "cwd": "${workspaceFolder}" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hello_rust_ffi_plugin 2 | 3 | A new Flutter FFI plugin project with Rust code in it. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter 8 | [FFI plugin](https://docs.flutter.dev/development/platform-integration/c-interop), 9 | a specialized package that includes native code directly invoked with Dart FFI. 10 | 11 | ## Project structure 12 | 13 | This template uses the following structure: 14 | 15 | * `rust`: Contains the native source code in Rust 16 | 17 | * `lib`: Contains the Dart code that defines the API of the plugin, and which 18 | calls into the native code using `dart:ffi`. 19 | 20 | * platform folders (`android`, `ios`, `windows`, etc.): Contains the build files 21 | for building and bundling the native code library with the platform application. 22 | 23 | ## Building and bundling native code 24 | 25 | The `pubspec.yaml` specifies FFI plugins as follows: 26 | 27 | ```yaml 28 | plugin: 29 | platforms: 30 | some_platform: 31 | ffiPlugin: true 32 | ``` 33 | 34 | This configuration invokes the native build for the various target platforms 35 | and bundles the binaries in Flutter applications using these FFI plugins. 36 | 37 | This can be combined with dartPluginClass, such as when FFI is used for the 38 | implementation of one platform in a federated plugin: 39 | 40 | ```yaml 41 | plugin: 42 | implements: some_other_plugin 43 | platforms: 44 | some_platform: 45 | dartPluginClass: SomeClass 46 | ffiPlugin: true 47 | ``` 48 | 49 | A plugin can have both FFI and method channels: 50 | 51 | ```yaml 52 | plugin: 53 | platforms: 54 | some_platform: 55 | pluginClass: SomeName 56 | ffiPlugin: true 57 | ``` 58 | 59 | For more information about how Cargo build is integrated with Flutter tooling see [Cargokit repository](https://github.com/irondash/cargokit). 60 | 61 | ## Binding to native code 62 | 63 | To use the native code, bindings in Dart are needed. 64 | To avoid writing these by hand, they are generated from the header file 65 | (`src/hello_rust_ffi_plugin.h`) by `package:ffigen`. 66 | Regenerate the bindings by running `flutter pub run ffigen --config ffigen.yaml`. 67 | 68 | ## Invoking native code 69 | 70 | Very short-running native functions can be directly invoked from any isolate. 71 | For example, see `sum` in `lib/hello_rust_ffi_plugin.dart`. 72 | 73 | Longer-running functions should be invoked on a helper isolate to avoid 74 | dropping frames in Flutter applications. 75 | For example, see `sumAsync` in `lib/hello_rust_ffi_plugin.dart`. 76 | 77 | ## Flutter help 78 | 79 | For help getting started with Flutter, view our 80 | [online documentation](https://flutter.dev/docs), which offers tutorials, 81 | samples, guidance on mobile development, and a full API reference. 82 | 83 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .cxx 10 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // The Android Gradle Plugin builds the native code with the Android NDK. 2 | 3 | group 'com.example.hello_rust_ffi_plugin' 4 | version '1.0' 5 | 6 | buildscript { 7 | repositories { 8 | google() 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | // The Android Gradle Plugin knows how to build native code with the NDK. 14 | classpath 'com.android.tools.build:gradle:7.3.0' 15 | } 16 | } 17 | 18 | rootProject.allprojects { 19 | repositories { 20 | google() 21 | mavenCentral() 22 | } 23 | } 24 | 25 | apply plugin: 'com.android.library' 26 | 27 | android { 28 | if (project.android.hasProperty("namespace")) { 29 | namespace 'com.example.hello_rust_ffi_plugin' 30 | } 31 | 32 | // Bumping the plugin compileSdkVersion requires all clients of this plugin 33 | // to bump the version in their app. 34 | compileSdkVersion 33 35 | 36 | ndkVersion flutter.ndkVersion 37 | 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_1_8 40 | targetCompatibility JavaVersion.VERSION_1_8 41 | } 42 | 43 | defaultConfig { 44 | minSdkVersion 19 45 | } 46 | } 47 | 48 | apply from: "../cargokit/gradle/plugin.gradle" 49 | 50 | cargokit { 51 | manifestDir = "../rust" 52 | libname = "hello_rust_ffi_plugin" 53 | } 54 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'hello_rust_ffi_plugin' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /cargokit/.github/workflows/check_and_lint.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | push: 4 | branches: 5 | - main 6 | 7 | name: Check and Lint 8 | 9 | jobs: 10 | Flutter: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 #v2.7.0 14 | - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d #1.6.0 15 | - name: Pub Get 16 | run: dart pub get --no-precompile 17 | working-directory: build_tool 18 | - name: Dart Format 19 | run: dart format . --output=none --set-exit-if-changed 20 | working-directory: build_tool 21 | - name: Analyze 22 | run: dart analyze 23 | working-directory: build_tool 24 | - name: Test 25 | run: dart test 26 | working-directory: build_tool 27 | -------------------------------------------------------------------------------- /cargokit/.github/workflows/test_example_plugin_build.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | push: 4 | branches: 5 | - main 6 | 7 | name: Test Example Plugin 8 | 9 | jobs: 10 | Build: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | os: 16 | - ubuntu-latest 17 | - macOS-latest 18 | - windows-latest 19 | build_mode: 20 | - debug 21 | - profile 22 | - release 23 | env: 24 | EXAMPLE_DIR: "a b/hello_rust_ffi_plugin/example" 25 | CARGOKIT_VERBOSE: 1 26 | steps: 27 | - name: Extract branch name 28 | shell: bash 29 | run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT 30 | id: extract_branch 31 | - name: Setup Repository 32 | shell: bash 33 | run: | 34 | mkdir "a b" # Space is intentional 35 | cd "a b" 36 | git config --global user.email "you@example.com" 37 | git config --global user.name "Your Name" 38 | # "advanced" branch has extra iOS flavor and uses rust nightly for release builds 39 | git clone -b advanced https://github.com/irondash/hello_rust_ffi_plugin 40 | cd hello_rust_ffi_plugin 41 | git subtree pull --prefix cargokit https://github.com/irondash/cargokit.git ${{ steps.extract_branch.outputs.branch }} --squash 42 | - uses: subosito/flutter-action@cc97e1648fff6ca5cc647fa67f47e70f7895510b # 2.11.0 43 | with: 44 | channel: "stable" 45 | - name: Install GTK 46 | if: (matrix.os == 'ubuntu-latest') 47 | run: sudo apt-get update && sudo apt-get install libgtk-3-dev 48 | - name: Install ninja-build 49 | if: (matrix.os == 'ubuntu-latest') 50 | run: sudo apt-get update && sudo apt-get install ninja-build 51 | - name: Build Linux (${{ matrix.build_mode }}) 52 | if: matrix.os == 'ubuntu-latest' 53 | shell: bash 54 | working-directory: ${{ env.EXAMPLE_DIR }} 55 | run: flutter build linux --${{ matrix.build_mode }} -v 56 | - name: Build macOS (${{ matrix.build_mode }}) 57 | if: matrix.os == 'macos-latest' 58 | shell: bash 59 | working-directory: ${{ env.EXAMPLE_DIR }} 60 | run: flutter build macos --${{ matrix.build_mode }} -v 61 | - name: Build iOS (${{ matrix.build_mode }}) 62 | if: matrix.os == 'macos-latest' 63 | shell: bash 64 | working-directory: ${{ env.EXAMPLE_DIR }} 65 | run: flutter build ios --${{ matrix.build_mode }} --no-codesign -v 66 | - name: Build iOS (${{ matrix.build_mode }}) - flavor1 67 | if: matrix.os == 'macos-latest' 68 | shell: bash 69 | working-directory: ${{ env.EXAMPLE_DIR }} 70 | run: flutter build ios --flavor flavor1 --${{ matrix.build_mode }} --no-codesign -v 71 | - name: Build Windows (${{ matrix.build_mode }}) 72 | if: matrix.os == 'windows-latest' 73 | shell: bash 74 | working-directory: ${{ env.EXAMPLE_DIR }} 75 | run: flutter build windows --${{ matrix.build_mode }} -v 76 | - name: Build Android (${{ matrix.build_mode }}) 77 | shell: bash 78 | working-directory: ${{ env.EXAMPLE_DIR }} 79 | run: | 80 | export JAVA_HOME=$JAVA_HOME_11_X64 81 | flutter build apk --${{ matrix.build_mode }} -v 82 | 83 | -------------------------------------------------------------------------------- /cargokit/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .dart_tool 3 | *.iml 4 | !pubspec.lock 5 | -------------------------------------------------------------------------------- /cargokit/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Matej Knopp 2 | 3 | ================================================================================ 4 | 5 | MIT LICENSE 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 copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | 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, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 20 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | ================================================================================ 25 | 26 | APACHE LICENSE, VERSION 2.0 27 | 28 | Licensed under the Apache License, Version 2.0 (the "License"); 29 | you may not use this file except in compliance with the License. 30 | You may obtain a copy of the License at 31 | 32 | http://www.apache.org/licenses/LICENSE-2.0 33 | 34 | Unless required by applicable law or agreed to in writing, software 35 | distributed under the License is distributed on an "AS IS" BASIS, 36 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 37 | See the License for the specific language governing permissions and 38 | limitations under the License. 39 | 40 | -------------------------------------------------------------------------------- /cargokit/README: -------------------------------------------------------------------------------- 1 | Experimental repository to provide glue for seamlessly integrating cargo build 2 | with flutter plugins and packages. 3 | 4 | See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/ 5 | for a tutorial on how to use Cargokit. 6 | 7 | Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin. 8 | 9 | -------------------------------------------------------------------------------- /cargokit/build_pod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | BASEDIR=$(dirname "$0") 5 | 6 | # Workaround for https://github.com/dart-lang/pub/issues/4010 7 | BASEDIR=$(cd "$BASEDIR" ; pwd -P) 8 | 9 | # Remove XCode SDK from path. Otherwise this breaks tool compilation when building iOS project 10 | NEW_PATH=`echo $PATH | tr ":" "\n" | grep -v "Contents/Developer/" | tr "\n" ":"` 11 | 12 | export PATH=${NEW_PATH%?} # remove trailing : 13 | 14 | env 15 | 16 | # Platform name (macosx, iphoneos, iphonesimulator) 17 | export CARGOKIT_DARWIN_PLATFORM_NAME=$PLATFORM_NAME 18 | 19 | # Arctive architectures (arm64, armv7, x86_64), space separated. 20 | export CARGOKIT_DARWIN_ARCHS=$ARCHS 21 | 22 | # Current build configuration (Debug, Release) 23 | export CARGOKIT_CONFIGURATION=$CONFIGURATION 24 | 25 | # Path to directory containing Cargo.toml. 26 | export CARGOKIT_MANIFEST_DIR=$PODS_TARGET_SRCROOT/$1 27 | 28 | # Temporary directory for build artifacts. 29 | export CARGOKIT_TARGET_TEMP_DIR=$TARGET_TEMP_DIR 30 | 31 | # Output directory for final artifacts. 32 | export CARGOKIT_OUTPUT_DIR=$PODS_CONFIGURATION_BUILD_DIR/$PRODUCT_NAME 33 | 34 | # Directory to store built tool artifacts. 35 | export CARGOKIT_TOOL_TEMP_DIR=$TARGET_TEMP_DIR/build_tool 36 | 37 | # Directory inside root project. Not necessarily the top level directory of root project. 38 | export CARGOKIT_ROOT_PROJECT_DIR=$SRCROOT 39 | 40 | FLUTTER_EXPORT_BUILD_ENVIRONMENT=( 41 | "$PODS_ROOT/../Flutter/ephemeral/flutter_export_environment.sh" # macOS 42 | "$PODS_ROOT/../Flutter/flutter_export_environment.sh" # iOS 43 | ) 44 | 45 | for path in "${FLUTTER_EXPORT_BUILD_ENVIRONMENT[@]}" 46 | do 47 | if [[ -f "$path" ]]; then 48 | source "$path" 49 | fi 50 | done 51 | 52 | "$BASEDIR/run_build_tool.sh" build-pod "$@" 53 | 54 | # Make a symlink from built framework to phony file, which will be used as input to 55 | # build script. This should force rebuild (podspec currently doesn't support alwaysOutOfDate 56 | # attribute on custom build phase) 57 | ln -fs "$OBJROOT/XCBuildData/build.db" "${BUILT_PRODUCTS_DIR}/cargokit_phony" 58 | ln -fs "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}" "${BUILT_PRODUCTS_DIR}/cargokit_phony_out" 59 | -------------------------------------------------------------------------------- /cargokit/build_tool/README.md: -------------------------------------------------------------------------------- 1 | A sample command-line application with an entrypoint in `bin/`, library code 2 | in `lib/`, and example unit test in `test/`. 3 | -------------------------------------------------------------------------------- /cargokit/build_tool/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | linter: 19 | rules: 20 | - prefer_relative_imports 21 | - directives_ordering 22 | 23 | # analyzer: 24 | # exclude: 25 | # - path/to/excluded/files/** 26 | 27 | # For more information about the core and recommended set of lints, see 28 | # https://dart.dev/go/core-lints 29 | 30 | # For additional information about configuring this file, see 31 | # https://dart.dev/guides/language/analysis-options 32 | -------------------------------------------------------------------------------- /cargokit/build_tool/bin/build_tool.dart: -------------------------------------------------------------------------------- 1 | import 'package:build_tool/build_tool.dart' as build_tool; 2 | 3 | void main(List arguments) { 4 | build_tool.runMain(arguments); 5 | } 6 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/build_tool.dart: -------------------------------------------------------------------------------- 1 | import 'src/build_tool.dart' as build_tool; 2 | 3 | Future runMain(List args) async { 4 | return build_tool.runMain(args); 5 | } 6 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/build_cmake.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:path/path.dart' as path; 4 | 5 | import 'artifacts_provider.dart'; 6 | import 'builder.dart'; 7 | import 'environment.dart'; 8 | import 'options.dart'; 9 | import 'target.dart'; 10 | 11 | class BuildCMake { 12 | final CargokitUserOptions userOptions; 13 | 14 | BuildCMake({required this.userOptions}); 15 | 16 | Future build() async { 17 | final targetPlatform = Environment.targetPlatform; 18 | final target = Target.forFlutterName(Environment.targetPlatform); 19 | if (target == null) { 20 | throw Exception("Unknown target platform: $targetPlatform"); 21 | } 22 | 23 | final environment = BuildEnvironment.fromEnvironment(isAndroid: false); 24 | final provider = 25 | ArtifactProvider(environment: environment, userOptions: userOptions); 26 | final artifacts = await provider.getArtifacts([target]); 27 | 28 | final libs = artifacts[target]!; 29 | 30 | for (final lib in libs) { 31 | if (lib.type == AritifactType.dylib) { 32 | File(lib.path) 33 | .copySync(path.join(Environment.outputDir, lib.finalFileName)); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/build_gradle.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:logging/logging.dart'; 4 | import 'package:path/path.dart' as path; 5 | 6 | import 'artifacts_provider.dart'; 7 | import 'builder.dart'; 8 | import 'environment.dart'; 9 | import 'options.dart'; 10 | import 'target.dart'; 11 | 12 | final log = Logger('build_gradle'); 13 | 14 | class BuildGradle { 15 | BuildGradle({required this.userOptions}); 16 | 17 | final CargokitUserOptions userOptions; 18 | 19 | Future build() async { 20 | final targets = Environment.targetPlatforms.map((arch) { 21 | final target = Target.forFlutterName(arch); 22 | if (target == null) { 23 | throw Exception( 24 | "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); 25 | } 26 | return target; 27 | }).toList(); 28 | 29 | final environment = BuildEnvironment.fromEnvironment(isAndroid: true); 30 | final provider = 31 | ArtifactProvider(environment: environment, userOptions: userOptions); 32 | final artifacts = await provider.getArtifacts(targets); 33 | 34 | for (final target in targets) { 35 | final libs = artifacts[target]!; 36 | final outputDir = path.join(Environment.outputDir, target.android!); 37 | Directory(outputDir).createSync(recursive: true); 38 | 39 | for (final lib in libs) { 40 | if (lib.type == AritifactType.dylib) { 41 | File(lib.path).copySync(path.join(outputDir, lib.finalFileName)); 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/build_pod.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:path/path.dart' as path; 4 | 5 | import 'artifacts_provider.dart'; 6 | import 'builder.dart'; 7 | import 'environment.dart'; 8 | import 'options.dart'; 9 | import 'target.dart'; 10 | import 'util.dart'; 11 | 12 | class BuildPod { 13 | BuildPod({required this.userOptions}); 14 | 15 | final CargokitUserOptions userOptions; 16 | 17 | Future build() async { 18 | final targets = Environment.darwinArchs.map((arch) { 19 | final target = Target.forDarwin( 20 | platformName: Environment.darwinPlatformName, darwinAarch: arch); 21 | if (target == null) { 22 | throw Exception( 23 | "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); 24 | } 25 | return target; 26 | }).toList(); 27 | 28 | final environment = BuildEnvironment.fromEnvironment(isAndroid: false); 29 | final provider = 30 | ArtifactProvider(environment: environment, userOptions: userOptions); 31 | final artifacts = await provider.getArtifacts(targets); 32 | 33 | void performLipo(String targetFile, Iterable sourceFiles) { 34 | runCommand("lipo", [ 35 | '-create', 36 | ...sourceFiles, 37 | '-output', 38 | targetFile, 39 | ]); 40 | } 41 | 42 | final outputDir = Environment.outputDir; 43 | 44 | Directory(outputDir).createSync(recursive: true); 45 | 46 | final staticLibs = artifacts.values 47 | .expand((element) => element) 48 | .where((element) => element.type == AritifactType.staticlib) 49 | .toList(); 50 | final dynamicLibs = artifacts.values 51 | .expand((element) => element) 52 | .where((element) => element.type == AritifactType.dylib) 53 | .toList(); 54 | 55 | final libName = environment.crateInfo.packageName; 56 | 57 | // If there is static lib, use it and link it with pod 58 | if (staticLibs.isNotEmpty) { 59 | final finalTargetFile = path.join(outputDir, "lib$libName.a"); 60 | performLipo(finalTargetFile, staticLibs.map((e) => e.path)); 61 | } else { 62 | // Otherwise try to replace bundle dylib with our dylib 63 | final bundlePaths = [ 64 | '$libName.framework/Versions/A/$libName', 65 | '$libName.framework/$libName', 66 | ]; 67 | 68 | for (final bundlePath in bundlePaths) { 69 | final targetFile = path.join(outputDir, bundlePath); 70 | if (File(targetFile).existsSync()) { 71 | performLipo(targetFile, dynamicLibs.map((e) => e.path)); 72 | 73 | // Replace absolute id with @rpath one so that it works properly 74 | // when moved to Frameworks. 75 | runCommand("install_name_tool", [ 76 | '-id', 77 | '@rpath/$bundlePath', 78 | targetFile, 79 | ]); 80 | return; 81 | } 82 | } 83 | throw Exception('Unable to find bundle for dynamic library'); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:logging/logging.dart'; 3 | import 'package:path/path.dart' as path; 4 | 5 | import 'android_environment.dart'; 6 | import 'cargo.dart'; 7 | import 'environment.dart'; 8 | import 'options.dart'; 9 | import 'rustup.dart'; 10 | import 'target.dart'; 11 | import 'util.dart'; 12 | 13 | final _log = Logger('builder'); 14 | 15 | enum BuildConfiguration { 16 | debug, 17 | release, 18 | profile, 19 | } 20 | 21 | extension on BuildConfiguration { 22 | bool get isDebug => this == BuildConfiguration.debug; 23 | String get rustName => switch (this) { 24 | BuildConfiguration.debug => 'debug', 25 | BuildConfiguration.release => 'release', 26 | BuildConfiguration.profile => 'release', 27 | }; 28 | } 29 | 30 | class BuildException implements Exception { 31 | final String message; 32 | 33 | BuildException(this.message); 34 | 35 | @override 36 | String toString() { 37 | return 'BuildException: $message'; 38 | } 39 | } 40 | 41 | class BuildEnvironment { 42 | final BuildConfiguration configuration; 43 | final CargokitCrateOptions crateOptions; 44 | final String targetTempDir; 45 | final String manifestDir; 46 | final CrateInfo crateInfo; 47 | 48 | final bool isAndroid; 49 | final String? androidSdkPath; 50 | final String? androidNdkVersion; 51 | final int? androidMinSdkVersion; 52 | final String? javaHome; 53 | 54 | BuildEnvironment({ 55 | required this.configuration, 56 | required this.crateOptions, 57 | required this.targetTempDir, 58 | required this.manifestDir, 59 | required this.crateInfo, 60 | required this.isAndroid, 61 | this.androidSdkPath, 62 | this.androidNdkVersion, 63 | this.androidMinSdkVersion, 64 | this.javaHome, 65 | }); 66 | 67 | static BuildConfiguration parseBuildConfiguration(String value) { 68 | // XCode configuration adds the flavor to configuration name. 69 | final firstSegment = value.split('-').first; 70 | final buildConfiguration = BuildConfiguration.values.firstWhereOrNull( 71 | (e) => e.name == firstSegment, 72 | ); 73 | if (buildConfiguration == null) { 74 | _log.warning('Unknown build configuraiton $value, will assume release'); 75 | return BuildConfiguration.release; 76 | } 77 | return buildConfiguration; 78 | } 79 | 80 | static BuildEnvironment fromEnvironment({ 81 | required bool isAndroid, 82 | }) { 83 | final buildConfiguration = 84 | parseBuildConfiguration(Environment.configuration); 85 | final manifestDir = Environment.manifestDir; 86 | final crateOptions = CargokitCrateOptions.load( 87 | manifestDir: manifestDir, 88 | ); 89 | final crateInfo = CrateInfo.load(manifestDir); 90 | return BuildEnvironment( 91 | configuration: buildConfiguration, 92 | crateOptions: crateOptions, 93 | targetTempDir: Environment.targetTempDir, 94 | manifestDir: manifestDir, 95 | crateInfo: crateInfo, 96 | isAndroid: isAndroid, 97 | androidSdkPath: isAndroid ? Environment.sdkPath : null, 98 | androidNdkVersion: isAndroid ? Environment.ndkVersion : null, 99 | androidMinSdkVersion: 100 | isAndroid ? int.parse(Environment.minSdkVersion) : null, 101 | javaHome: isAndroid ? Environment.javaHome : null, 102 | ); 103 | } 104 | } 105 | 106 | class RustBuilder { 107 | final Target target; 108 | final BuildEnvironment environment; 109 | 110 | RustBuilder({ 111 | required this.target, 112 | required this.environment, 113 | }); 114 | 115 | void prepare( 116 | Rustup rustup, 117 | ) { 118 | final toolchain = _toolchain; 119 | if (rustup.installedTargets(toolchain) == null) { 120 | rustup.installToolchain(toolchain); 121 | } 122 | if (toolchain == 'nightly') { 123 | rustup.installRustSrcForNightly(); 124 | } 125 | if (!rustup.installedTargets(toolchain)!.contains(target.rust)) { 126 | rustup.installTarget(target.rust, toolchain: toolchain); 127 | } 128 | } 129 | 130 | CargoBuildOptions? get _buildOptions => 131 | environment.crateOptions.cargo[environment.configuration]; 132 | 133 | String get _toolchain => _buildOptions?.toolchain.name ?? 'stable'; 134 | 135 | /// Returns the path of directory containing build artifacts. 136 | Future build() async { 137 | final extraArgs = _buildOptions?.flags ?? []; 138 | final manifestPath = path.join(environment.manifestDir, 'Cargo.toml'); 139 | runCommand( 140 | 'rustup', 141 | [ 142 | 'run', 143 | _toolchain, 144 | 'cargo', 145 | 'build', 146 | ...extraArgs, 147 | '--manifest-path', 148 | manifestPath, 149 | '-p', 150 | environment.crateInfo.packageName, 151 | if (!environment.configuration.isDebug) '--release', 152 | '--target', 153 | target.rust, 154 | '--target-dir', 155 | environment.targetTempDir, 156 | ], 157 | environment: await _buildEnvironment(), 158 | ); 159 | return path.join( 160 | environment.targetTempDir, 161 | target.rust, 162 | environment.configuration.rustName, 163 | ); 164 | } 165 | 166 | Future> _buildEnvironment() async { 167 | if (target.android == null) { 168 | return {}; 169 | } else { 170 | final sdkPath = environment.androidSdkPath; 171 | final ndkVersion = environment.androidNdkVersion; 172 | final minSdkVersion = environment.androidMinSdkVersion; 173 | if (sdkPath == null) { 174 | throw BuildException('androidSdkPath is not set'); 175 | } 176 | if (ndkVersion == null) { 177 | throw BuildException('androidNdkVersion is not set'); 178 | } 179 | if (minSdkVersion == null) { 180 | throw BuildException('androidMinSdkVersion is not set'); 181 | } 182 | final env = AndroidEnvironment( 183 | sdkPath: sdkPath, 184 | ndkVersion: ndkVersion, 185 | minSdkVersion: minSdkVersion, 186 | targetTempDir: environment.targetTempDir, 187 | target: target, 188 | ); 189 | if (!env.ndkIsInstalled() && environment.javaHome != null) { 190 | env.installNdk(javaHome: environment.javaHome!); 191 | } 192 | return env.buildEnvironment(); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/cargo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:path/path.dart' as path; 4 | import 'package:toml/toml.dart'; 5 | 6 | class ManifestException { 7 | ManifestException(this.message, {required this.fileName}); 8 | 9 | final String? fileName; 10 | final String message; 11 | 12 | @override 13 | String toString() { 14 | if (fileName != null) { 15 | return 'Failed to parse package manifest at $fileName: $message'; 16 | } else { 17 | return 'Failed to parse package manifest: $message'; 18 | } 19 | } 20 | } 21 | 22 | class CrateInfo { 23 | CrateInfo({required this.packageName}); 24 | 25 | final String packageName; 26 | 27 | static CrateInfo parseManifest(String manifest, {final String? fileName}) { 28 | final toml = TomlDocument.parse(manifest); 29 | final package = toml.toMap()['package']; 30 | if (package == null) { 31 | throw ManifestException('Missing package section', fileName: fileName); 32 | } 33 | final name = package['name']; 34 | if (name == null) { 35 | throw ManifestException('Missing package name', fileName: fileName); 36 | } 37 | return CrateInfo(packageName: name); 38 | } 39 | 40 | static CrateInfo load(String manifestDir) { 41 | final manifestFile = File(path.join(manifestDir, 'Cargo.toml')); 42 | final manifest = manifestFile.readAsStringSync(); 43 | return parseManifest(manifest, fileName: manifestFile.path); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/crate_hash.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:collection/collection.dart'; 6 | import 'package:convert/convert.dart'; 7 | import 'package:crypto/crypto.dart'; 8 | import 'package:path/path.dart' as path; 9 | 10 | class CrateHash { 11 | /// Computes a hash uniquely identifying crate content. This takes into account 12 | /// content all all .rs files inside the src directory, as well as Cargo.toml, 13 | /// Cargo.lock, build.rs and cargokit.yaml. 14 | /// 15 | /// If [tempStorage] is provided, computed hash is stored in a file in that directory 16 | /// and reused on subsequent calls if the crate content hasn't changed. 17 | static String compute(String manifestDir, {String? tempStorage}) { 18 | return CrateHash._( 19 | manifestDir: manifestDir, 20 | tempStorage: tempStorage, 21 | )._compute(); 22 | } 23 | 24 | CrateHash._({ 25 | required this.manifestDir, 26 | required this.tempStorage, 27 | }); 28 | 29 | String _compute() { 30 | final files = getFiles(); 31 | final tempStorage = this.tempStorage; 32 | if (tempStorage != null) { 33 | final quickHash = _computeQuickHash(files); 34 | final quickHashFolder = Directory(path.join(tempStorage, 'crate_hash')); 35 | quickHashFolder.createSync(recursive: true); 36 | final quickHashFile = File(path.join(quickHashFolder.path, quickHash)); 37 | if (quickHashFile.existsSync()) { 38 | return quickHashFile.readAsStringSync(); 39 | } 40 | final hash = _computeHash(files); 41 | quickHashFile.writeAsStringSync(hash); 42 | return hash; 43 | } else { 44 | return _computeHash(files); 45 | } 46 | } 47 | 48 | /// Computes a quick hash based on files stat (without reading contents). This 49 | /// is used to cache the real hash, which is slower to compute since it involves 50 | /// reading every single file. 51 | String _computeQuickHash(List files) { 52 | final output = AccumulatorSink(); 53 | final input = sha256.startChunkedConversion(output); 54 | 55 | final data = ByteData(8); 56 | for (final file in files) { 57 | input.add(utf8.encode(file.path)); 58 | final stat = file.statSync(); 59 | data.setUint64(0, stat.size); 60 | input.add(data.buffer.asUint8List()); 61 | data.setUint64(0, stat.modified.millisecondsSinceEpoch); 62 | input.add(data.buffer.asUint8List()); 63 | } 64 | 65 | input.close(); 66 | return base64Url.encode(output.events.single.bytes); 67 | } 68 | 69 | String _computeHash(List files) { 70 | final output = AccumulatorSink(); 71 | final input = sha256.startChunkedConversion(output); 72 | 73 | void addTextFile(File file) { 74 | // text Files are hashed by lines in case we're dealing with github checkout 75 | // that auto-converts line endings. 76 | final splitter = LineSplitter(); 77 | if (file.existsSync()) { 78 | final data = file.readAsStringSync(); 79 | final lines = splitter.convert(data); 80 | for (final line in lines) { 81 | input.add(utf8.encode(line)); 82 | } 83 | } 84 | } 85 | 86 | for (final file in files) { 87 | addTextFile(file); 88 | } 89 | 90 | input.close(); 91 | final res = output.events.single; 92 | 93 | // Truncate to 128bits. 94 | final hash = res.bytes.sublist(0, 16); 95 | return hex.encode(hash); 96 | } 97 | 98 | List getFiles() { 99 | final src = Directory(path.join(manifestDir, 'src')); 100 | final files = src 101 | .listSync(recursive: true, followLinks: false) 102 | .whereType() 103 | .toList(); 104 | files.sortBy((element) => element.path); 105 | void addFile(String relative) { 106 | final file = File(path.join(manifestDir, relative)); 107 | if (file.existsSync()) { 108 | files.add(file); 109 | } 110 | } 111 | 112 | addFile('Cargo.toml'); 113 | addFile('Cargo.lock'); 114 | addFile('build.rs'); 115 | addFile('cargokit.yaml'); 116 | return files; 117 | } 118 | 119 | final String manifestDir; 120 | final String? tempStorage; 121 | } 122 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/environment.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | extension on String { 4 | String resolveSymlink() => File(this).resolveSymbolicLinksSync(); 5 | } 6 | 7 | class Environment { 8 | /// Current build configuration (debug or release). 9 | static String get configuration => 10 | _getEnv("CARGOKIT_CONFIGURATION").toLowerCase(); 11 | 12 | static bool get isDebug => configuration == 'debug'; 13 | static bool get isRelease => configuration == 'release'; 14 | 15 | /// Temporary directory where Rust build artifacts are placed. 16 | static String get targetTempDir => _getEnv("CARGOKIT_TARGET_TEMP_DIR"); 17 | 18 | /// Final output directory where the build artifacts are placed. 19 | static String get outputDir => _getEnvPath('CARGOKIT_OUTPUT_DIR'); 20 | 21 | /// Path to the crate manifest (containing Cargo.toml). 22 | static String get manifestDir => _getEnvPath('CARGOKIT_MANIFEST_DIR'); 23 | 24 | /// Directory inside root project. Not necessarily root folder. Symlinks are 25 | /// not resolved on purpose. 26 | static String get rootProjectDir => _getEnv('CARGOKIT_ROOT_PROJECT_DIR'); 27 | 28 | // Pod 29 | 30 | /// Platform name (macosx, iphoneos, iphonesimulator). 31 | static String get darwinPlatformName => 32 | _getEnv("CARGOKIT_DARWIN_PLATFORM_NAME"); 33 | 34 | /// List of architectures to build for (arm64, armv7, x86_64). 35 | static List get darwinArchs => 36 | _getEnv("CARGOKIT_DARWIN_ARCHS").split(' '); 37 | 38 | // Gradle 39 | static String get minSdkVersion => _getEnv("CARGOKIT_MIN_SDK_VERSION"); 40 | static String get ndkVersion => _getEnv("CARGOKIT_NDK_VERSION"); 41 | static String get sdkPath => _getEnvPath("CARGOKIT_SDK_DIR"); 42 | static String get javaHome => _getEnvPath("CARGOKIT_JAVA_HOME"); 43 | static List get targetPlatforms => 44 | _getEnv("CARGOKIT_TARGET_PLATFORMS").split(','); 45 | 46 | // CMAKE 47 | static String get targetPlatform => _getEnv("CARGOKIT_TARGET_PLATFORM"); 48 | 49 | static String _getEnv(String key) { 50 | final res = Platform.environment[key]; 51 | if (res == null) { 52 | throw Exception("Missing environment variable $key"); 53 | } 54 | return res; 55 | } 56 | 57 | static String _getEnvPath(String key) { 58 | final res = _getEnv(key); 59 | if (Directory(res).existsSync()) { 60 | return res.resolveSymlink(); 61 | } else { 62 | return res; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/logging.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:logging/logging.dart'; 4 | 5 | const String kSeparator = "--"; 6 | const String kDoubleSeparator = "=="; 7 | 8 | bool _lastMessageWasSeparator = false; 9 | 10 | void _log(LogRecord rec) { 11 | final prefix = '${rec.level.name}: '; 12 | final out = rec.level == Level.SEVERE ? stderr : stdout; 13 | if (rec.message == kSeparator) { 14 | if (!_lastMessageWasSeparator) { 15 | out.write(prefix); 16 | out.writeln('-' * 80); 17 | _lastMessageWasSeparator = true; 18 | } 19 | return; 20 | } else if (rec.message == kDoubleSeparator) { 21 | out.write(prefix); 22 | out.writeln('=' * 80); 23 | _lastMessageWasSeparator = true; 24 | return; 25 | } 26 | out.write(prefix); 27 | out.writeln(rec.message); 28 | _lastMessageWasSeparator = false; 29 | } 30 | 31 | void initLogging() { 32 | Logger.root.level = Level.INFO; 33 | Logger.root.onRecord.listen((LogRecord rec) { 34 | final lines = rec.message.split('\n'); 35 | for (final line in lines) { 36 | if (line.isNotEmpty || lines.length == 1 || line != lines.last) { 37 | _log(LogRecord( 38 | rec.level, 39 | line, 40 | rec.loggerName, 41 | )); 42 | } 43 | } 44 | }); 45 | } 46 | 47 | void enableVerboseLogging() { 48 | Logger.root.level = Level.ALL; 49 | } 50 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/rustup.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:collection/collection.dart'; 4 | import 'package:path/path.dart' as path; 5 | 6 | import 'util.dart'; 7 | 8 | class _Toolchain { 9 | _Toolchain( 10 | this.name, 11 | this.targets, 12 | ); 13 | 14 | final String name; 15 | final List targets; 16 | } 17 | 18 | class Rustup { 19 | List? installedTargets(String toolchain) { 20 | final targets = _installedTargets(toolchain); 21 | return targets != null ? List.unmodifiable(targets) : null; 22 | } 23 | 24 | void installToolchain(String toolchain) { 25 | log.info("Installing Rust toolchain: $toolchain"); 26 | runCommand("rustup", ['toolchain', 'install', toolchain]); 27 | _installedToolchains 28 | .add(_Toolchain(toolchain, _getInstalledTargets(toolchain))); 29 | } 30 | 31 | void installTarget( 32 | String target, { 33 | required String toolchain, 34 | }) { 35 | log.info("Installing Rust target: $target"); 36 | runCommand("rustup", [ 37 | 'target', 38 | 'add', 39 | '--toolchain', 40 | toolchain, 41 | target, 42 | ]); 43 | _installedTargets(toolchain)?.add(target); 44 | } 45 | 46 | final List<_Toolchain> _installedToolchains; 47 | 48 | Rustup() : _installedToolchains = _getInstalledToolchains(); 49 | 50 | List? _installedTargets(String toolchain) => _installedToolchains 51 | .firstWhereOrNull( 52 | (e) => e.name == toolchain || e.name.startsWith('$toolchain-')) 53 | ?.targets; 54 | 55 | static List<_Toolchain> _getInstalledToolchains() { 56 | String extractToolchainName(String line) { 57 | // ignore (default) after toolchain name 58 | final parts = line.split(' '); 59 | return parts[0]; 60 | } 61 | 62 | final res = runCommand("rustup", ['toolchain', 'list']); 63 | final lines = res.stdout 64 | .toString() 65 | .split('\n') 66 | .where((e) => e.isNotEmpty) 67 | .map(extractToolchainName) 68 | .toList(growable: true); 69 | 70 | return lines 71 | .map( 72 | (name) => _Toolchain( 73 | name, 74 | _getInstalledTargets(name), 75 | ), 76 | ) 77 | .toList(growable: true); 78 | } 79 | 80 | static List _getInstalledTargets(String toolchain) { 81 | final res = runCommand("rustup", [ 82 | 'target', 83 | 'list', 84 | '--toolchain', 85 | toolchain, 86 | '--installed', 87 | ]); 88 | final lines = res.stdout 89 | .toString() 90 | .split('\n') 91 | .where((e) => e.isNotEmpty) 92 | .toList(growable: true); 93 | return lines; 94 | } 95 | 96 | bool _didInstallRustSrcForNightly = false; 97 | 98 | void installRustSrcForNightly() { 99 | if (_didInstallRustSrcForNightly) { 100 | return; 101 | } 102 | // Useful for -Z build-std 103 | runCommand( 104 | "rustup", 105 | ['component', 'add', 'rust-src', '--toolchain', 'nightly'], 106 | ); 107 | _didInstallRustSrcForNightly = true; 108 | } 109 | 110 | static String? executablePath() { 111 | final envPath = Platform.environment['PATH']; 112 | final envPathSeparator = Platform.isWindows ? ';' : ':'; 113 | final home = Platform.isWindows 114 | ? Platform.environment['USERPROFILE'] 115 | : Platform.environment['HOME']; 116 | final paths = [ 117 | if (home != null) path.join(home, '.cargo', 'bin'), 118 | if (envPath != null) ...envPath.split(envPathSeparator), 119 | ]; 120 | for (final p in paths) { 121 | final rustup = Platform.isWindows ? 'rustup.exe' : 'rustup'; 122 | final rustupPath = path.join(p, rustup); 123 | if (File(rustupPath).existsSync()) { 124 | return rustupPath; 125 | } 126 | } 127 | return null; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/target.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:collection/collection.dart'; 4 | 5 | import 'util.dart'; 6 | 7 | class Target { 8 | Target({ 9 | required this.rust, 10 | this.flutter, 11 | this.android, 12 | this.androidMinSdkVersion, 13 | this.darwinPlatform, 14 | this.darwinArch, 15 | }); 16 | 17 | static final all = [ 18 | Target( 19 | rust: 'armv7-linux-androideabi', 20 | flutter: 'android-arm', 21 | android: 'armeabi-v7a', 22 | androidMinSdkVersion: 16, 23 | ), 24 | Target( 25 | rust: 'aarch64-linux-android', 26 | flutter: 'android-arm64', 27 | android: 'arm64-v8a', 28 | androidMinSdkVersion: 21, 29 | ), 30 | Target( 31 | rust: 'i686-linux-android', 32 | flutter: 'android-x86', 33 | android: 'x86', 34 | androidMinSdkVersion: 16, 35 | ), 36 | Target( 37 | rust: 'x86_64-linux-android', 38 | flutter: 'android-x64', 39 | android: 'x86_64', 40 | androidMinSdkVersion: 21, 41 | ), 42 | Target( 43 | rust: 'x86_64-pc-windows-msvc', 44 | flutter: 'windows-x64', 45 | ), 46 | Target( 47 | rust: 'x86_64-unknown-linux-gnu', 48 | flutter: 'linux-x64', 49 | ), 50 | Target( 51 | rust: 'aarch64-unknown-linux-gnu', 52 | flutter: 'linux-arm64', 53 | ), 54 | Target( 55 | rust: 'x86_64-apple-darwin', 56 | darwinPlatform: 'macosx', 57 | darwinArch: 'x86_64', 58 | ), 59 | Target( 60 | rust: 'aarch64-apple-darwin', 61 | darwinPlatform: 'macosx', 62 | darwinArch: 'arm64', 63 | ), 64 | Target( 65 | rust: 'aarch64-apple-ios', 66 | darwinPlatform: 'iphoneos', 67 | darwinArch: 'arm64', 68 | ), 69 | Target( 70 | rust: 'aarch64-apple-ios-sim', 71 | darwinPlatform: 'iphonesimulator', 72 | darwinArch: 'arm64', 73 | ), 74 | Target( 75 | rust: 'x86_64-apple-ios', 76 | darwinPlatform: 'iphonesimulator', 77 | darwinArch: 'x86_64', 78 | ), 79 | ]; 80 | 81 | static Target? forFlutterName(String flutterName) { 82 | return all.firstWhereOrNull((element) => element.flutter == flutterName); 83 | } 84 | 85 | static Target? forDarwin({ 86 | required String platformName, 87 | required String darwinAarch, 88 | }) { 89 | return all.firstWhereOrNull((element) => // 90 | element.darwinPlatform == platformName && 91 | element.darwinArch == darwinAarch); 92 | } 93 | 94 | static Target? forRustTriple(String triple) { 95 | return all.firstWhereOrNull((element) => element.rust == triple); 96 | } 97 | 98 | static List androidTargets() { 99 | return all 100 | .where((element) => element.android != null) 101 | .toList(growable: false); 102 | } 103 | 104 | /// Returns buildable targets on current host platform ignoring Android targets. 105 | static List buildableTargets() { 106 | if (Platform.isLinux) { 107 | // Right now we don't support cross-compiling on Linux. So we just return 108 | // the host target. 109 | final arch = runCommand('arch', []).stdout as String; 110 | if (arch.trim() == 'aarch64') { 111 | return [Target.forRustTriple('aarch64-unknown-linux-gnu')!]; 112 | } else { 113 | return [Target.forRustTriple('x86_64-unknown-linux-gnu')!]; 114 | } 115 | } 116 | return all.where((target) { 117 | if (Platform.isWindows) { 118 | return target.rust.contains('-windows-'); 119 | } else if (Platform.isMacOS) { 120 | return target.darwinPlatform != null; 121 | } 122 | return false; 123 | }).toList(growable: false); 124 | } 125 | 126 | @override 127 | String toString() { 128 | return rust; 129 | } 130 | 131 | final String? flutter; 132 | final String rust; 133 | final String? android; 134 | final int? androidMinSdkVersion; 135 | final String? darwinPlatform; 136 | final String? darwinArch; 137 | } 138 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:logging/logging.dart'; 5 | import 'package:path/path.dart' as path; 6 | 7 | import 'logging.dart'; 8 | import 'rustup.dart'; 9 | 10 | final log = Logger("process"); 11 | 12 | class CommandFailedException implements Exception { 13 | final String executable; 14 | final List arguments; 15 | final ProcessResult result; 16 | 17 | CommandFailedException({ 18 | required this.executable, 19 | required this.arguments, 20 | required this.result, 21 | }); 22 | 23 | @override 24 | String toString() { 25 | final stdout = result.stdout.toString().trim(); 26 | final stderr = result.stderr.toString().trim(); 27 | return [ 28 | "External Command: $executable ${arguments.map((e) => '"$e"').join(' ')}", 29 | "Returned Exit Code: ${result.exitCode}", 30 | kSeparator, 31 | "STDOUT:", 32 | if (stdout.isNotEmpty) stdout, 33 | kSeparator, 34 | "STDERR:", 35 | if (stderr.isNotEmpty) stderr, 36 | ].join('\n'); 37 | } 38 | } 39 | 40 | ProcessResult runCommand( 41 | String executable, 42 | List arguments, { 43 | String? workingDirectory, 44 | Map? environment, 45 | bool includeParentEnvironment = true, 46 | bool runInShell = false, 47 | Encoding? stdoutEncoding = systemEncoding, 48 | Encoding? stderrEncoding = systemEncoding, 49 | }) { 50 | log.finer('Running command $executable ${arguments.join(' ')}'); 51 | final res = Process.runSync( 52 | _resolveExecutable(executable), 53 | arguments, 54 | workingDirectory: workingDirectory, 55 | environment: environment, 56 | includeParentEnvironment: includeParentEnvironment, 57 | runInShell: runInShell, 58 | stderrEncoding: stderrEncoding, 59 | stdoutEncoding: stdoutEncoding, 60 | ); 61 | if (res.exitCode != 0) { 62 | throw CommandFailedException( 63 | executable: executable, 64 | arguments: arguments, 65 | result: res, 66 | ); 67 | } else { 68 | return res; 69 | } 70 | } 71 | 72 | class RustupNotFoundException implements Exception { 73 | @override 74 | String toString() { 75 | return [ 76 | ' ', 77 | 'rustup not found in PATH.', 78 | ' ', 79 | 'Maybe you need to install Rust? It only takes a minute:', 80 | ' ', 81 | if (Platform.isWindows) 'https://www.rust-lang.org/tools/install', 82 | if (hasHomebrewRustInPath()) ...[ 83 | '\$ brew unlink rust # Unlink homebrew Rust from PATH', 84 | ], 85 | if (!Platform.isWindows) 86 | "\$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", 87 | ' ', 88 | ].join('\n'); 89 | } 90 | 91 | static bool hasHomebrewRustInPath() { 92 | if (!Platform.isMacOS) { 93 | return false; 94 | } 95 | final envPath = Platform.environment['PATH'] ?? ''; 96 | final paths = envPath.split(':'); 97 | return paths.any((p) { 98 | return p.contains('homebrew') && File(path.join(p, 'rustc')).existsSync(); 99 | }); 100 | } 101 | } 102 | 103 | String _resolveExecutable(String executable) { 104 | if (executable == 'rustup') { 105 | final resolved = Rustup.executablePath(); 106 | if (resolved != null) { 107 | return resolved; 108 | } 109 | throw RustupNotFoundException(); 110 | } else { 111 | return executable; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/verify_binaries.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:ed25519_edwards/ed25519_edwards.dart'; 4 | import 'package:http/http.dart'; 5 | 6 | import 'artifacts_provider.dart'; 7 | import 'cargo.dart'; 8 | import 'crate_hash.dart'; 9 | import 'options.dart'; 10 | import 'precompile_binaries.dart'; 11 | import 'target.dart'; 12 | 13 | class VerifyBinaries { 14 | VerifyBinaries({ 15 | required this.manifestDir, 16 | }); 17 | 18 | final String manifestDir; 19 | 20 | Future run() async { 21 | final crateInfo = CrateInfo.load(manifestDir); 22 | 23 | final config = CargokitCrateOptions.load(manifestDir: manifestDir); 24 | final precompiledBinaries = config.precompiledBinaries; 25 | if (precompiledBinaries == null) { 26 | stdout.writeln('Crate does not support precompiled binaries.'); 27 | } else { 28 | final crateHash = CrateHash.compute(manifestDir); 29 | stdout.writeln('Crate hash: $crateHash'); 30 | 31 | for (final target in Target.all) { 32 | final message = 'Checking ${target.rust}...'; 33 | stdout.write(message.padRight(40)); 34 | stdout.flush(); 35 | 36 | final artifacts = getArtifactNames( 37 | target: target, 38 | libraryName: crateInfo.packageName, 39 | remote: true, 40 | ); 41 | 42 | final prefix = precompiledBinaries.uriPrefix; 43 | 44 | bool ok = true; 45 | 46 | for (final artifact in artifacts) { 47 | final fileName = PrecompileBinaries.fileName(target, artifact); 48 | final signatureFileName = 49 | PrecompileBinaries.signatureFileName(target, artifact); 50 | 51 | final url = Uri.parse('$prefix$crateHash/$fileName'); 52 | final signatureUrl = 53 | Uri.parse('$prefix$crateHash/$signatureFileName'); 54 | 55 | final signature = await get(signatureUrl); 56 | if (signature.statusCode != 200) { 57 | stdout.writeln('MISSING'); 58 | ok = false; 59 | break; 60 | } 61 | final asset = await get(url); 62 | if (asset.statusCode != 200) { 63 | stdout.writeln('MISSING'); 64 | ok = false; 65 | break; 66 | } 67 | 68 | if (!verify(precompiledBinaries.publicKey, asset.bodyBytes, 69 | signature.bodyBytes)) { 70 | stdout.writeln('INVALID SIGNATURE'); 71 | ok = false; 72 | } 73 | } 74 | 75 | if (ok) { 76 | stdout.writeln('OK'); 77 | } 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /cargokit/build_tool/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: build_tool 2 | description: Cargokit build_tool. Facilitates the build of Rust crate during Flutter application build. 3 | publish_to: none 4 | version: 1.0.0 5 | 6 | environment: 7 | sdk: ">=3.0.0 <4.0.0" 8 | 9 | # Add regular dependencies here. 10 | dependencies: 11 | # these are pinned on purpose because the bundle_tool_runner doesn't have 12 | # pubspec.lock. See run_build_tool.sh 13 | logging: 1.2.0 14 | path: 1.8.0 15 | version: 3.0.0 16 | collection: 1.18.0 17 | ed25519_edwards: 0.3.1 18 | hex: 0.2.0 19 | yaml: 3.1.2 20 | source_span: 1.10.0 21 | github: 9.17.0 22 | args: 2.4.2 23 | crypto: 3.0.3 24 | convert: 3.1.1 25 | http: 1.1.0 26 | toml: 0.14.0 27 | 28 | dev_dependencies: 29 | lints: ^2.1.0 30 | test: ^1.24.0 31 | -------------------------------------------------------------------------------- /cargokit/build_tool/test/builder_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:build_tool/src/builder.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | test('parseBuildConfiguration', () { 6 | var b = BuildEnvironment.parseBuildConfiguration('debug'); 7 | expect(b, BuildConfiguration.debug); 8 | 9 | b = BuildEnvironment.parseBuildConfiguration('profile'); 10 | expect(b, BuildConfiguration.profile); 11 | 12 | b = BuildEnvironment.parseBuildConfiguration('release'); 13 | expect(b, BuildConfiguration.release); 14 | 15 | b = BuildEnvironment.parseBuildConfiguration('debug-dev'); 16 | expect(b, BuildConfiguration.debug); 17 | 18 | b = BuildEnvironment.parseBuildConfiguration('profile'); 19 | expect(b, BuildConfiguration.profile); 20 | 21 | b = BuildEnvironment.parseBuildConfiguration('profile-prod'); 22 | expect(b, BuildConfiguration.profile); 23 | 24 | // fallback to release 25 | b = BuildEnvironment.parseBuildConfiguration('unknown'); 26 | expect(b, BuildConfiguration.release); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /cargokit/build_tool/test/cargo_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:build_tool/src/cargo.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | final _cargoToml = """ 5 | [workspace] 6 | 7 | [profile.release] 8 | lto = true 9 | panic = "abort" 10 | opt-level = "z" 11 | # strip = "symbols" 12 | 13 | [package] 14 | name = "super_native_extensions" 15 | version = "0.1.0" 16 | edition = "2021" 17 | resolver = "2" 18 | 19 | [lib] 20 | crate-type = ["cdylib", "staticlib"] 21 | """; 22 | 23 | void main() { 24 | test('parseCargoToml', () { 25 | final info = CrateInfo.parseManifest(_cargoToml); 26 | expect(info.packageName, 'super_native_extensions'); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /cargokit/build_tool/test/options_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:build_tool/src/builder.dart'; 2 | import 'package:build_tool/src/options.dart'; 3 | import 'package:hex/hex.dart'; 4 | import 'package:test/test.dart'; 5 | import 'package:yaml/yaml.dart'; 6 | 7 | void main() { 8 | test('parseCargoBuildOptions', () { 9 | final yaml = """ 10 | toolchain: nightly 11 | extra_flags: 12 | - -Z 13 | # Comment here 14 | - build-std=panic_abort,std 15 | """; 16 | final node = loadYamlNode(yaml); 17 | final options = CargoBuildOptions.parse(node); 18 | expect(options.toolchain, Toolchain.nightly); 19 | expect(options.flags, ['-Z', 'build-std=panic_abort,std']); 20 | }); 21 | 22 | test('parsePrecompiledBinaries', () { 23 | final yaml = """ 24 | url_prefix: https://url-prefix 25 | public_key: a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445 26 | """; 27 | final precompiledBinaries = PrecompiledBinaries.parse(loadYamlNode(yaml)); 28 | final key = HEX.decode( 29 | 'a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445'); 30 | expect(precompiledBinaries.uriPrefix, 'https://url-prefix'); 31 | expect(precompiledBinaries.publicKey.bytes, key); 32 | }); 33 | 34 | test('parseCargokitOptions', () { 35 | const yaml = ''' 36 | cargo: 37 | # For smalles binaries rebuilt the standard library with panic=abort 38 | debug: 39 | toolchain: nightly 40 | extra_flags: 41 | - -Z 42 | # Comment here 43 | - build-std=panic_abort,std 44 | release: 45 | toolchain: beta 46 | 47 | precompiled_binaries: 48 | url_prefix: https://url-prefix 49 | public_key: a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445 50 | '''; 51 | final options = CargokitCrateOptions.parse(loadYamlNode(yaml)); 52 | expect(options.precompiledBinaries?.uriPrefix, 'https://url-prefix'); 53 | final key = HEX.decode( 54 | 'a4c3433798eb2c36edf2b94dbb4dd899d57496ca373a8982d8a792410b7f6445'); 55 | expect(options.precompiledBinaries?.publicKey.bytes, key); 56 | 57 | final debugOptions = options.cargo[BuildConfiguration.debug]!; 58 | expect(debugOptions.toolchain, Toolchain.nightly); 59 | expect(debugOptions.flags, ['-Z', 'build-std=panic_abort,std']); 60 | 61 | final releaseOptions = options.cargo[BuildConfiguration.release]!; 62 | expect(releaseOptions.toolchain, Toolchain.beta); 63 | expect(releaseOptions.flags, []); 64 | }); 65 | 66 | test('parseCargokitUserOptions', () { 67 | const yaml = ''' 68 | use_precompiled_binaries: false 69 | verbose_logging: true 70 | '''; 71 | final options = CargokitUserOptions.parse(loadYamlNode(yaml)); 72 | expect(options.usePrecompiledBinaries, false); 73 | expect(options.verboseLogging, true); 74 | }); 75 | } 76 | -------------------------------------------------------------------------------- /cargokit/cmake/cargokit.cmake: -------------------------------------------------------------------------------- 1 | SET(cargokit_cmake_root "${CMAKE_CURRENT_LIST_DIR}/..") 2 | 3 | # Workaround for https://github.com/dart-lang/pub/issues/4010 4 | get_filename_component(cargokit_cmake_root "${cargokit_cmake_root}" REALPATH) 5 | 6 | if(WIN32) 7 | # REALPATH does not properly resolve symlinks on windows :-/ 8 | execute_process(COMMAND powershell -ExecutionPolicy Bypass -File "${CMAKE_CURRENT_LIST_DIR}/resolve_symlinks.ps1" "${cargokit_cmake_root}" OUTPUT_VARIABLE cargokit_cmake_root OUTPUT_STRIP_TRAILING_WHITESPACE) 9 | endif() 10 | 11 | # Arguments 12 | # - target: CMAKE target to which rust library is linked 13 | # - manifest_dir: relative path from current folder to directory containing cargo manifest 14 | # - lib_name: cargo package name 15 | # - any_symbol_name: name of any exported symbol from the library. 16 | # used on windows to force linking with library. 17 | function(apply_cargokit target manifest_dir lib_name any_symbol_name) 18 | 19 | set(CARGOKIT_LIB_NAME "${lib_name}") 20 | set(CARGOKIT_LIB_FULL_NAME "${CMAKE_SHARED_MODULE_PREFIX}${CARGOKIT_LIB_NAME}${CMAKE_SHARED_MODULE_SUFFIX}") 21 | if (CMAKE_CONFIGURATION_TYPES) 22 | set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/$") 23 | set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/$/${CARGOKIT_LIB_FULL_NAME}") 24 | else() 25 | set(CARGOKIT_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") 26 | set(OUTPUT_LIB "${CMAKE_CURRENT_BINARY_DIR}/${CARGOKIT_LIB_FULL_NAME}") 27 | endif() 28 | set(CARGOKIT_TEMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/cargokit_build") 29 | 30 | if (FLUTTER_TARGET_PLATFORM) 31 | set(CARGOKIT_TARGET_PLATFORM "${FLUTTER_TARGET_PLATFORM}") 32 | else() 33 | set(CARGOKIT_TARGET_PLATFORM "windows-x64") 34 | endif() 35 | 36 | set(CARGOKIT_ENV 37 | "CARGOKIT_CMAKE=${CMAKE_COMMAND}" 38 | "CARGOKIT_CONFIGURATION=$" 39 | "CARGOKIT_MANIFEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${manifest_dir}" 40 | "CARGOKIT_TARGET_TEMP_DIR=${CARGOKIT_TEMP_DIR}" 41 | "CARGOKIT_OUTPUT_DIR=${CARGOKIT_OUTPUT_DIR}" 42 | "CARGOKIT_TARGET_PLATFORM=${CARGOKIT_TARGET_PLATFORM}" 43 | "CARGOKIT_TOOL_TEMP_DIR=${CARGOKIT_TEMP_DIR}/tool" 44 | "CARGOKIT_ROOT_PROJECT_DIR=${CMAKE_SOURCE_DIR}" 45 | ) 46 | 47 | if (WIN32) 48 | set(SCRIPT_EXTENSION ".cmd") 49 | set(IMPORT_LIB_EXTENSION ".lib") 50 | else() 51 | set(SCRIPT_EXTENSION ".sh") 52 | set(IMPORT_LIB_EXTENSION "") 53 | endif() 54 | 55 | # Using generators in custom command is only supported in CMake 3.20+ 56 | if (CMAKE_CONFIGURATION_TYPES AND ${CMAKE_VERSION} VERSION_LESS "3.20.0") 57 | foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES) 58 | add_custom_command( 59 | OUTPUT 60 | "${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${CARGOKIT_LIB_FULL_NAME}" 61 | "${CMAKE_CURRENT_BINARY_DIR}/_phony_" 62 | COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} 63 | "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake 64 | VERBATIM 65 | ) 66 | endforeach() 67 | else() 68 | add_custom_command( 69 | OUTPUT 70 | ${OUTPUT_LIB} 71 | "${CMAKE_CURRENT_BINARY_DIR}/_phony_" 72 | COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} 73 | "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake 74 | VERBATIM 75 | ) 76 | endif() 77 | 78 | set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/_phony_" PROPERTIES SYMBOLIC TRUE) 79 | 80 | if (TARGET ${target}) 81 | # If we have actual cmake target provided create target and make existing 82 | # target depend on it 83 | add_custom_target("${target}_cargokit" DEPENDS ${OUTPUT_LIB}) 84 | add_dependencies("${target}" "${target}_cargokit") 85 | target_link_libraries("${target}" PRIVATE "${OUTPUT_LIB}${IMPORT_LIB_EXTENSION}") 86 | if(WIN32) 87 | target_link_options(${target} PRIVATE "/INCLUDE:${any_symbol_name}") 88 | endif() 89 | else() 90 | # Otherwise (FFI) just use ALL to force building always 91 | add_custom_target("${target}_cargokit" ALL DEPENDS ${OUTPUT_LIB}) 92 | endif() 93 | 94 | # Allow adding the output library to plugin bundled libraries 95 | set("${target}_cargokit_lib" ${OUTPUT_LIB} PARENT_SCOPE) 96 | 97 | endfunction() -------------------------------------------------------------------------------- /cargokit/cmake/resolve_symlinks.ps1: -------------------------------------------------------------------------------- 1 | function Resolve-Symlinks { 2 | [CmdletBinding()] 3 | [OutputType([string])] 4 | param( 5 | [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] 6 | [string] $Path 7 | ) 8 | 9 | [string] $separator = '/' 10 | [string[]] $parts = $Path.Split($separator) 11 | 12 | [string] $realPath = '' 13 | foreach ($part in $parts) { 14 | if ($realPath -and !$realPath.EndsWith($separator)) { 15 | $realPath += $separator 16 | } 17 | $realPath += $part 18 | $item = Get-Item $realPath 19 | if ($item.Target) { 20 | $realPath = $item.Target.Replace('\', '/') 21 | } 22 | } 23 | $realPath 24 | } 25 | 26 | $path=Resolve-Symlinks -Path $args[0] 27 | Write-Host $path 28 | -------------------------------------------------------------------------------- /cargokit/docs/precompiled_binaries.md: -------------------------------------------------------------------------------- 1 | # Precompiled Binaries 2 | 3 | Because Cargokit builds the Rust crate during Flutter build, it is inherently 4 | dependend on the Rust toolchain being installed on the developer's machine. 5 | 6 | To decrease the friction, it is possible for Cargokit to use precompiled binaries instead. 7 | 8 | This is how the process of using precompiled binaries looks from the perspective of the build on developer machine: 9 | 10 | 1. Cargokit checks if there is `cargokit_options.yaml` file in the root folder of target application. If there is one, it will be checked for `use_precompiled_binaries` options to see if user opted out of using precompiled binaries. In which case Cargokit will insist on building from source. Cargokit will also build from source if the configuration file is absent, but user has Rustup installed. 11 | 12 | 2. Cargokit checks if there is `cargokit.yaml` file placed in the Rust crate. If there is one, it will be checked for `precompiled_binaries` section to see if crate supports precompiled binaries. The configuration section must contain a public key and URL prefix. 13 | 14 | 3. Cargokit computes a `crate-hash`. This is a SHA256 hash value computed from all Rust files inside crate, `Cargo.toml`, `Cargo.lock` and `cargokit.yaml`. This uniquely identifies the crate and it is used to find the correct precompiled binaries. 15 | 16 | 4. Cargokit will attempt to download the precompiled binaries for target platform and `crate_hash` combination and a signature file for each downloaded binary. If download succeeds, the binary content will be verified against the signature and public key included in `cargokit.yaml` (which is part of Rust crate and thus part of published Flutter package). 17 | 18 | 5. If the verification succeeds, the precompiled binaries will be used. Otherwise the binary will be discarded and Cargokit will insist on building from source. 19 | 20 | ## Providing precompiled binaries 21 | 22 | Note that this assumes that precompiled binaries will be generated during github actions and deployed as github releases. 23 | 24 | ### Use `build_tool` to generate a key-pair: 25 | 26 | ``` 27 | dart run build_tool gen-key 28 | ``` 29 | 30 | This will print the private key and public key. Store the private key securely. It needs to be provided as a secret to github action. 31 | 32 | The public key should be included in `cargokit.yaml` file in the Rust crate. 33 | 34 | ### Provide a `cargokit.yaml` file in the Rust crate 35 | 36 | The file must be placed alongside Cargo.toml. 37 | 38 | ```yaml 39 | precompiled_binaries: 40 | # Uri prefix used when downloading precompiled binaries. 41 | url_prefix: https://github.com///releases/download/precompiled_ 42 | 43 | # Public key for verifying downloaded precompiled binaries. 44 | public_key: 45 | ``` 46 | 47 | ### Configure a github action to build and upload precompiled binaries. 48 | 49 | The github action should be run at every commit to main branch (and possibly other branches). 50 | 51 | The action needs two secrets - private key for signing binaries and GitHub token for uploading binaries as releases. Here is example action that precompiles and uploads binaries for all supported targets. 52 | 53 | ```yaml 54 | on: 55 | push: 56 | branches: [ main ] 57 | 58 | name: Precompile Binaries 59 | 60 | jobs: 61 | Precompile: 62 | runs-on: ${{ matrix.os }} 63 | strategy: 64 | fail-fast: false 65 | matrix: 66 | os: 67 | - ubuntu-latest 68 | - macOS-latest 69 | - windows-latest 70 | steps: 71 | - uses: actions/checkout@v2 72 | - uses: dart-lang/setup-dart@v1 73 | - name: Install GTK 74 | if: (matrix.os == 'ubuntu-latest') 75 | run: sudo apt-get update && sudo apt-get install libgtk-3-dev 76 | - name: Precompile 77 | if: (matrix.os == 'macOS-latest') || (matrix.os == 'windows-latest') 78 | run: dart run build_tool precompile-binaries -v --manifest-dir=../../rust --repository=superlistapp/super_native_extensions 79 | working-directory: super_native_extensions/cargokit/build_tool 80 | env: 81 | GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }} 82 | PRIVATE_KEY: ${{ secrets.RELEASE_PRIVATE_KEY }} 83 | - name: Precompile (with Android) 84 | if: (matrix.os == 'ubuntu-latest') 85 | run: dart run build_tool precompile-binaries -v --manifest-dir=../../rust --repository=superlistapp/super_native_extensions --android-sdk-location=/usr/local/lib/android/sdk --android-ndk-version=24.0.8215888 --android-min-sdk-version=23 86 | working-directory: super_native_extensions/cargokit/build_tool 87 | env: 88 | GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }} 89 | PRIVATE_KEY: ${{ secrets.RELEASE_PRIVATE_KEY }} 90 | ``` 91 | 92 | By default the `built_tool precompile-binaries` commands build and uploads the binaries for all targets buildable from current host. This can be overriden using the `--target ` argument. 93 | 94 | Android binaries will be built when `--android-sdk-location` and `--android-ndk-version` arguments are provided. 95 | 96 | -------------------------------------------------------------------------------- /cargokit/gradle/plugin.gradle: -------------------------------------------------------------------------------- 1 | import java.nio.file.Paths 2 | import org.apache.tools.ant.taskdefs.condition.Os 3 | 4 | CargoKitPlugin.file = buildscript.sourceFile 5 | 6 | apply plugin: CargoKitPlugin 7 | 8 | class CargoKitExtension { 9 | String manifestDir; // Relative path to folder containing Cargo.toml 10 | String libname; // Library name within Cargo.toml. Must be a cdylib 11 | } 12 | 13 | abstract class CargoKitBuildTask extends DefaultTask { 14 | 15 | @Input 16 | String buildMode 17 | 18 | @Input 19 | String buildDir 20 | 21 | @Input 22 | String outputDir 23 | 24 | @Input 25 | String ndkVersion 26 | 27 | @Input 28 | String sdkDirectory 29 | 30 | @Input 31 | int compileSdkVersion; 32 | 33 | @Input 34 | int minSdkVersion; 35 | 36 | @Input 37 | String pluginFile 38 | 39 | @Input 40 | List targetPlatforms 41 | 42 | @TaskAction 43 | def build() { 44 | if (project.cargokit.manifestDir == null) { 45 | throw new GradleException("Property 'manifestDir' must be set on cargokit extension"); 46 | } 47 | 48 | if (project.cargokit.libname == null) { 49 | throw new GradleException("Property 'libname' must be set on cargokit extension"); 50 | } 51 | 52 | def executableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "run_build_tool.cmd" : "run_build_tool.sh" 53 | def path = Paths.get(new File(pluginFile).parent, "..", executableName); 54 | 55 | def manifestDir = Paths.get(project.buildscript.sourceFile.parent, project.cargokit.manifestDir) 56 | 57 | def rootProjectDir = project.rootProject.projectDir 58 | 59 | project.exec { 60 | executable path 61 | args "build-gradle" 62 | environment "CARGOKIT_ROOT_PROJECT_DIR", rootProjectDir 63 | environment "CARGOKIT_TOOL_TEMP_DIR", "${buildDir}/build_tool" 64 | environment "CARGOKIT_MANIFEST_DIR", manifestDir 65 | environment "CARGOKIT_CONFIGURATION", buildMode 66 | environment "CARGOKIT_TARGET_TEMP_DIR", buildDir 67 | environment "CARGOKIT_OUTPUT_DIR", outputDir 68 | environment "CARGOKIT_NDK_VERSION", ndkVersion 69 | environment "CARGOKIT_SDK_DIR", sdkDirectory 70 | environment "CARGOKIT_COMPILE_SDK_VERSION", compileSdkVersion 71 | environment "CARGOKIT_MIN_SDK_VERSION", minSdkVersion 72 | environment "CARGOKIT_TARGET_PLATFORMS", targetPlatforms.join(",") 73 | environment "CARGOKIT_JAVA_HOME", System.properties['java.home'] 74 | } 75 | } 76 | } 77 | 78 | class CargoKitPlugin implements Plugin { 79 | 80 | static String file; 81 | 82 | private Plugin findFlutterPlugin(Project rootProject) { 83 | _findFlutterPlugin(rootProject.childProjects) 84 | } 85 | 86 | private Plugin _findFlutterPlugin(Map projects) { 87 | for (project in projects) { 88 | for (plugin in project.value.getPlugins()) { 89 | if (plugin.class.name == "FlutterPlugin") { 90 | return plugin; 91 | } 92 | } 93 | def plugin = _findFlutterPlugin(project.value.childProjects); 94 | if (plugin != null) { 95 | return plugin; 96 | } 97 | } 98 | return null; 99 | } 100 | 101 | @Override 102 | void apply(Project project) { 103 | def plugin = findFlutterPlugin(project.rootProject); 104 | 105 | project.extensions.create("cargokit", CargoKitExtension) 106 | 107 | if (plugin == null) { 108 | print("Flutter plugin not found, CargoKit plugin will not be applied.") 109 | return; 110 | } 111 | 112 | def cargoBuildDir = "${project.buildDir}/build" 113 | 114 | plugin.project.android.applicationVariants.all { variant -> 115 | 116 | final buildType = variant.buildType.name 117 | 118 | def cargoOutputDir = "${project.buildDir}/jniLibs/${buildType}"; 119 | def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs; 120 | jniLibs.srcDir(new File(cargoOutputDir)) 121 | 122 | def platforms = plugin.getTargetPlatforms().collect() 123 | 124 | // Same thing addFlutterDependencies does in flutter.gradle 125 | if (buildType == "debug") { 126 | platforms.add("android-x86") 127 | platforms.add("android-x64") 128 | } 129 | 130 | // The task name depends on plugin properties, which are not available 131 | // at this point 132 | project.getGradle().afterProject { 133 | def taskName = "cargokitCargoBuild${project.cargokit.libname.capitalize()}${buildType.capitalize()}"; 134 | 135 | if (project.tasks.findByName(taskName)) { 136 | return 137 | } 138 | 139 | if (plugin.project.android.ndkVersion == null) { 140 | throw new GradleException("Please set 'android.ndkVersion' in 'app/build.gradle'.") 141 | } 142 | 143 | def task = project.tasks.create(taskName, CargoKitBuildTask.class) { 144 | buildMode = variant.name 145 | buildDir = cargoBuildDir 146 | outputDir = cargoOutputDir 147 | ndkVersion = plugin.project.android.ndkVersion 148 | sdkDirectory = plugin.project.android.sdkDirectory 149 | minSdkVersion = plugin.project.android.defaultConfig.minSdkVersion.apiLevel as int 150 | compileSdkVersion = plugin.project.android.compileSdkVersion.substring(8) as int 151 | targetPlatforms = platforms 152 | pluginFile = CargoKitPlugin.file 153 | } 154 | def onTask = { newTask -> 155 | if (newTask.name == "merge${buildType.capitalize()}NativeLibs") { 156 | newTask.dependsOn task 157 | // Fix gradle 7.4.2 not picking up JNI library changes 158 | newTask.outputs.upToDateWhen { false } 159 | } 160 | } 161 | project.tasks.each onTask 162 | project.tasks.whenTaskAdded onTask 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /cargokit/run_build_tool.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | setlocal ENABLEDELAYEDEXPANSION 5 | 6 | SET BASEDIR=%~dp0 7 | 8 | if not exist "%CARGOKIT_TOOL_TEMP_DIR%" ( 9 | mkdir "%CARGOKIT_TOOL_TEMP_DIR%" 10 | ) 11 | cd "%CARGOKIT_TOOL_TEMP_DIR%" 12 | 13 | SET BUILD_TOOL_PKG_DIR=%BASEDIR%build_tool 14 | SET DART=%FLUTTER_ROOT%\bin\cache\dart-sdk\bin\dart 15 | 16 | set BUILD_TOOL_PKG_DIR_POSIX=%BUILD_TOOL_PKG_DIR:\=/% 17 | 18 | ( 19 | echo name: build_tool_runner 20 | echo version: 1.0.0 21 | echo publish_to: none 22 | echo. 23 | echo environment: 24 | echo sdk: '^>=3.0.0 ^<4.0.0' 25 | echo. 26 | echo dependencies: 27 | echo build_tool: 28 | echo path: %BUILD_TOOL_PKG_DIR_POSIX% 29 | ) >pubspec.yaml 30 | 31 | if not exist bin ( 32 | mkdir bin 33 | ) 34 | 35 | ( 36 | echo import 'package:build_tool/build_tool.dart' as build_tool; 37 | echo void main^(List^ args^) ^{ 38 | echo build_tool.runMain^(args^); 39 | echo ^} 40 | ) >bin\build_tool_runner.dart 41 | 42 | SET PRECOMPILED=bin\build_tool_runner.dill 43 | 44 | REM To detect changes in package we compare output of DIR /s (recursive) 45 | set PREV_PACKAGE_INFO=.dart_tool\package_info.prev 46 | set CUR_PACKAGE_INFO=.dart_tool\package_info.cur 47 | 48 | DIR "%BUILD_TOOL_PKG_DIR%" /s > "%CUR_PACKAGE_INFO%_orig" 49 | 50 | REM Last line in dir output is free space on harddrive. That is bound to 51 | REM change between invocation so we need to remove it 52 | ( 53 | Set "Line=" 54 | For /F "UseBackQ Delims=" %%A In ("%CUR_PACKAGE_INFO%_orig") Do ( 55 | SetLocal EnableDelayedExpansion 56 | If Defined Line Echo !Line! 57 | EndLocal 58 | Set "Line=%%A") 59 | ) >"%CUR_PACKAGE_INFO%" 60 | DEL "%CUR_PACKAGE_INFO%_orig" 61 | 62 | REM Compare current directory listing with previous 63 | FC /B "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" > nul 2>&1 64 | 65 | If %ERRORLEVEL% neq 0 ( 66 | REM Changed - copy current to previous and remove precompiled kernel 67 | if exist "%PREV_PACKAGE_INFO%" ( 68 | DEL "%PREV_PACKAGE_INFO%" 69 | ) 70 | MOVE /Y "%CUR_PACKAGE_INFO%" "%PREV_PACKAGE_INFO%" 71 | if exist "%PRECOMPILED%" ( 72 | DEL "%PRECOMPILED%" 73 | ) 74 | ) 75 | 76 | REM There is no CUR_PACKAGE_INFO it was renamed in previous step to %PREV_PACKAGE_INFO% 77 | REM which means we need to do pub get and precompile 78 | if not exist "%PRECOMPILED%" ( 79 | "%DART%" pub get --no-precompile 80 | "%DART%" compile kernel bin/build_tool_runner.dart 81 | ) 82 | 83 | "%DART%" "%PRECOMPILED%" %* 84 | 85 | REM 253 means invalid snapshot version. 86 | If %ERRORLEVEL% equ 253 ( 87 | "%DART%" pub get --no-precompile 88 | "%DART%" compile kernel bin/build_tool_runner.dart 89 | "%DART%" "%PRECOMPILED%" %* 90 | ) 91 | -------------------------------------------------------------------------------- /cargokit/run_build_tool.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | BASEDIR=$(dirname "$0") 6 | 7 | mkdir -p "$CARGOKIT_TOOL_TEMP_DIR" 8 | 9 | cd "$CARGOKIT_TOOL_TEMP_DIR" 10 | 11 | # Write a very simple bin package in temp folder that depends on build_tool package 12 | # from Cargokit. This is done to ensure that we don't pollute Cargokit folder 13 | # with .dart_tool contents. 14 | 15 | BUILD_TOOL_PKG_DIR="$BASEDIR/build_tool" 16 | 17 | if [[ -z $FLUTTER_ROOT ]]; then # not defined 18 | DART=dart 19 | else 20 | DART="$FLUTTER_ROOT/bin/cache/dart-sdk/bin/dart" 21 | fi 22 | 23 | cat << EOF > "pubspec.yaml" 24 | name: build_tool_runner 25 | version: 1.0.0 26 | publish_to: none 27 | 28 | environment: 29 | sdk: '>=3.0.0 <4.0.0' 30 | 31 | dependencies: 32 | build_tool: 33 | path: "$BUILD_TOOL_PKG_DIR" 34 | EOF 35 | 36 | mkdir -p "bin" 37 | 38 | cat << EOF > "bin/build_tool_runner.dart" 39 | import 'package:build_tool/build_tool.dart' as build_tool; 40 | void main(List args) { 41 | build_tool.runMain(args); 42 | } 43 | EOF 44 | 45 | # Dart run will not cache any package that has a path dependency, which 46 | # is the case for our build_tool_runner. So instead we precompile the package 47 | # ourselves. 48 | # To invalidate the cached kernel we use the hash of ls -LR of the build_tool 49 | # package directory. This should be good enough, as the build_tool package 50 | # itself is not meant to have any path dependencies. 51 | 52 | if [[ "$OSTYPE" == "darwin"* ]]; then 53 | PACKAGE_HASH=$(ls -lTR "$BUILD_TOOL_PKG_DIR" | shasum) 54 | else 55 | PACKAGE_HASH=$(ls -lR --full-time "$BUILD_TOOL_PKG_DIR" | shasum) 56 | fi 57 | 58 | PACKAGE_HASH_FILE=".package_hash" 59 | 60 | if [ -f "$PACKAGE_HASH_FILE" ]; then 61 | EXISTING_HASH=$(cat "$PACKAGE_HASH_FILE") 62 | if [ "$PACKAGE_HASH" != "$EXISTING_HASH" ]; then 63 | rm "$PACKAGE_HASH_FILE" 64 | fi 65 | fi 66 | 67 | # Run pub get if needed. 68 | if [ ! -f "$PACKAGE_HASH_FILE" ]; then 69 | "$DART" pub get --no-precompile 70 | "$DART" compile kernel bin/build_tool_runner.dart 71 | echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE" 72 | fi 73 | 74 | set +e 75 | 76 | "$DART" bin/build_tool_runner.dill "$@" 77 | 78 | exit_code=$? 79 | 80 | # 253 means invalid snapshot version. 81 | if [ $exit_code == 253 ]; then 82 | "$DART" pub get --no-precompile 83 | "$DART" compile kernel bin/build_tool_runner.dart 84 | "$DART" bin/build_tool_runner.dill "$@" 85 | exit_code=$? 86 | fi 87 | 88 | exit $exit_code 89 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # hello_rust_ffi_plugin_example 2 | 3 | Demonstrates how to use the hello_rust_ffi_plugin plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace "com.example.hello_rust_ffi_plugin_example" 27 | compileSdkVersion flutter.compileSdkVersion 28 | ndkVersion flutter.ndkVersion 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.example.hello_rust_ffi_plugin_example" 46 | // You can update the following values to match your application needs. 47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 48 | minSdkVersion flutter.minSdkVersion 49 | targetSdkVersion flutter.targetSdkVersion 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | buildTypes { 55 | release { 56 | // TODO: Add your own signing config for the release build. 57 | // Signing with the debug keys for now, so `flutter run --release` works. 58 | signingConfig signingConfigs.debug 59 | } 60 | } 61 | } 62 | 63 | flutter { 64 | source '../..' 65 | } 66 | 67 | dependencies {} 68 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/hello_rust_ffi_plugin_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.hello_rust_ffi_plugin_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = '../build' 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(':app') 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | org.gradle.jvmargs=-Xmx4g -XX:+HeapDumpOnOutOfMemoryError 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip 6 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.1.0" apply false 22 | id "org.jetbrains.kotlin.android" version "1.7.10" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - hello_rust_ffi_plugin (0.0.1) 4 | 5 | DEPENDENCIES: 6 | - Flutter (from `Flutter`) 7 | - hello_rust_ffi_plugin (from `.symlinks/plugins/hello_rust_ffi_plugin/ios`) 8 | 9 | EXTERNAL SOURCES: 10 | Flutter: 11 | :path: Flutter 12 | hello_rust_ffi_plugin: 13 | :path: ".symlinks/plugins/hello_rust_ffi_plugin/ios" 14 | 15 | SPEC CHECKSUMS: 16 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 17 | hello_rust_ffi_plugin: 68384607453c42fe5f940b30dd7a9e58f7f65840 18 | 19 | PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 20 | 21 | COCOAPODS: 1.12.1 22 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Hello Rust Ffi Plugin 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | hello_rust_ffi_plugin_example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'dart:async'; 3 | 4 | import 'package:hello_rust_ffi_plugin/hello_rust_ffi_plugin.dart' as hello_rust_ffi_plugin; 5 | 6 | void main() { 7 | runApp(const MyApp()); 8 | } 9 | 10 | class MyApp extends StatefulWidget { 11 | const MyApp({super.key}); 12 | 13 | @override 14 | State createState() => _MyAppState(); 15 | } 16 | 17 | class _MyAppState extends State { 18 | late int sumResult; 19 | late Future sumAsyncResult; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | sumResult = hello_rust_ffi_plugin.sum(1, 2); 25 | sumAsyncResult = hello_rust_ffi_plugin.sumAsync(3, 4); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | const textStyle = TextStyle(fontSize: 25); 31 | const spacerSmall = SizedBox(height: 10); 32 | return MaterialApp( 33 | home: Scaffold( 34 | appBar: AppBar( 35 | title: const Text('Native Packages'), 36 | ), 37 | body: SingleChildScrollView( 38 | child: Container( 39 | padding: const EdgeInsets.all(10), 40 | child: Column( 41 | children: [ 42 | const Text( 43 | 'This calls a native function through FFI that is shipped as source in the package. ' 44 | 'The native code is built as part of the Flutter Runner build.', 45 | style: textStyle, 46 | textAlign: TextAlign.center, 47 | ), 48 | spacerSmall, 49 | Text( 50 | 'sum(1, 2) = $sumResult', 51 | style: textStyle, 52 | textAlign: TextAlign.center, 53 | ), 54 | spacerSmall, 55 | FutureBuilder( 56 | future: sumAsyncResult, 57 | builder: (BuildContext context, AsyncSnapshot value) { 58 | final displayValue = 59 | (value.hasData) ? value.data : 'loading'; 60 | return Text( 61 | 'await sumAsync(3, 4) = $displayValue', 62 | style: textStyle, 63 | textAlign: TextAlign.center, 64 | ); 65 | }, 66 | ), 67 | ], 68 | ), 69 | ), 70 | ), 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /example/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /example/linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.10) 3 | project(runner LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "hello_rust_ffi_plugin_example") 8 | # The unique GTK application identifier for this application. See: 9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID 10 | set(APPLICATION_ID "com.example.hello_rust_ffi_plugin") 11 | 12 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 13 | # versions of CMake. 14 | cmake_policy(SET CMP0063 NEW) 15 | 16 | # Load bundled libraries from the lib/ directory relative to the binary. 17 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 18 | 19 | # Root filesystem for cross-building. 20 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 21 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 22 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 24 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 25 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 26 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 27 | endif() 28 | 29 | # Define build configuration options. 30 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 31 | set(CMAKE_BUILD_TYPE "Debug" CACHE 32 | STRING "Flutter build mode" FORCE) 33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 34 | "Debug" "Profile" "Release") 35 | endif() 36 | 37 | # Compilation settings that should be applied to most targets. 38 | # 39 | # Be cautious about adding new options here, as plugins use this function by 40 | # default. In most cases, you should add new options to specific targets instead 41 | # of modifying this function. 42 | function(APPLY_STANDARD_SETTINGS TARGET) 43 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 44 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 45 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 46 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 47 | endfunction() 48 | 49 | # Flutter library and tool build rules. 50 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 51 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 52 | 53 | # System-level dependencies. 54 | find_package(PkgConfig REQUIRED) 55 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 56 | 57 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 58 | 59 | # Define the application target. To change its name, change BINARY_NAME above, 60 | # not the value here, or `flutter run` will no longer work. 61 | # 62 | # Any new source files that you add to the application should be added here. 63 | add_executable(${BINARY_NAME} 64 | "main.cc" 65 | "my_application.cc" 66 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 67 | ) 68 | 69 | # Apply the standard set of build settings. This can be removed for applications 70 | # that need different build settings. 71 | apply_standard_settings(${BINARY_NAME}) 72 | 73 | # Add dependency libraries. Add any application-specific dependencies here. 74 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 75 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 76 | 77 | # Run the Flutter tool portions of the build. This must not be removed. 78 | add_dependencies(${BINARY_NAME} flutter_assemble) 79 | 80 | # Only the install-generated bundle's copy of the executable will launch 81 | # correctly, since the resources must in the right relative locations. To avoid 82 | # people trying to run the unbundled copy, put it in a subdirectory instead of 83 | # the default top-level location. 84 | set_target_properties(${BINARY_NAME} 85 | PROPERTIES 86 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 87 | ) 88 | 89 | 90 | # Generated plugin build rules, which manage building the plugins and adding 91 | # them to the application. 92 | include(flutter/generated_plugins.cmake) 93 | 94 | 95 | # === Installation === 96 | # By default, "installing" just makes a relocatable bundle in the build 97 | # directory. 98 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 99 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 100 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 101 | endif() 102 | 103 | # Start with a clean build bundle directory every time. 104 | install(CODE " 105 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 106 | " COMPONENT Runtime) 107 | 108 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 109 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 110 | 111 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 112 | COMPONENT Runtime) 113 | 114 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 115 | COMPONENT Runtime) 116 | 117 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 118 | COMPONENT Runtime) 119 | 120 | foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) 121 | install(FILES "${bundled_library}" 122 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 123 | COMPONENT Runtime) 124 | endforeach(bundled_library) 125 | 126 | # Fully re-copy the assets directory on each build to avoid having stale files 127 | # from a previous install. 128 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 129 | install(CODE " 130 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 131 | " COMPONENT Runtime) 132 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 133 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 134 | 135 | # Install the AOT library on non-Debug builds only. 136 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 137 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 138 | COMPONENT Runtime) 139 | endif() 140 | -------------------------------------------------------------------------------- /example/linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void fl_register_plugins(FlPluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | hello_rust_ffi_plugin 10 | ) 11 | 12 | set(PLUGIN_BUNDLED_LIBRARIES) 13 | 14 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 15 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 16 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 19 | endforeach(plugin) 20 | 21 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 22 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 23 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 24 | endforeach(ffi_plugin) 25 | -------------------------------------------------------------------------------- /example/linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /example/linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "hello_rust_ffi_plugin_example"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "hello_rust_ffi_plugin_example"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GObject::dispose. 85 | static void my_application_dispose(GObject* object) { 86 | MyApplication* self = MY_APPLICATION(object); 87 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 88 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 89 | } 90 | 91 | static void my_application_class_init(MyApplicationClass* klass) { 92 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 93 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 94 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 95 | } 96 | 97 | static void my_application_init(MyApplication* self) {} 98 | 99 | MyApplication* my_application_new() { 100 | return MY_APPLICATION(g_object_new(my_application_get_type(), 101 | "application-id", APPLICATION_ID, 102 | "flags", G_APPLICATION_NON_UNIQUE, 103 | nullptr)); 104 | } 105 | -------------------------------------------------------------------------------- /example/linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | 9 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 10 | } 11 | -------------------------------------------------------------------------------- /example/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_macos_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /example/macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FlutterMacOS (1.0.0) 3 | - hello_rust_ffi_plugin (0.0.1) 4 | 5 | DEPENDENCIES: 6 | - FlutterMacOS (from `Flutter/ephemeral`) 7 | - hello_rust_ffi_plugin (from `Flutter/ephemeral/.symlinks/plugins/hello_rust_ffi_plugin/macos`) 8 | 9 | EXTERNAL SOURCES: 10 | FlutterMacOS: 11 | :path: Flutter/ephemeral 12 | hello_rust_ffi_plugin: 13 | :path: Flutter/ephemeral/.symlinks/plugins/hello_rust_ffi_plugin/macos 14 | 15 | SPEC CHECKSUMS: 16 | FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 17 | hello_rust_ffi_plugin: 68384607453c42fe5f940b30dd7a9e58f7f65840 18 | 19 | PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 20 | 21 | COCOAPODS: 1.12.1 22 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = hello_rust_ffi_plugin_example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.helloRustFfiPluginExample 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: hello_rust_ffi_plugin_example 2 | description: Demonstrates how to use the hello_rust_ffi_plugin plugin. 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | 7 | # The following defines the version and build number for your application. 8 | # A version number is three numbers separated by dots, like 1.2.43 9 | # followed by an optional build number separated by a +. 10 | # Both the version and the builder number may be overridden in flutter 11 | # build by specifying --build-name and --build-number, respectively. 12 | # In Android, build-name is used as versionName while build-number used as versionCode. 13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 | # Read more about iOS versioning at 16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 | # In Windows, build-name is used as the major, minor, and patch parts 18 | # of the product and file versions while build-number is used as the build suffix. 19 | version: 1.0.0+1 20 | 21 | environment: 22 | sdk: '>=3.0.0 <4.0.0' 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | flutter: 32 | sdk: flutter 33 | 34 | hello_rust_ffi_plugin: 35 | # When depending on this package from a real application you should use: 36 | # hello_rust_ffi_plugin: ^x.y.z 37 | # See https://dart.dev/tools/pub/dependencies#version-constraints 38 | # The example app is bundled with the plugin so we use a path dependency on 39 | # the parent directory to use the current plugin's version. 40 | path: ../ 41 | 42 | # The following adds the Cupertino Icons font to your application. 43 | # Use with the CupertinoIcons class for iOS style icons. 44 | cupertino_icons: ^1.0.2 45 | 46 | dev_dependencies: 47 | flutter_test: 48 | sdk: flutter 49 | 50 | # The "flutter_lints" package below contains a set of recommended lints to 51 | # encourage good coding practices. The lint set provided by the package is 52 | # activated in the `analysis_options.yaml` file located at the root of your 53 | # package. See that file for information about deactivating specific lint 54 | # rules and activating additional ones. 55 | flutter_lints: ^2.0.0 56 | 57 | # For information on the generic Dart part of this file, see the 58 | # following page: https://dart.dev/tools/pub/pubspec 59 | 60 | # The following section is specific to Flutter packages. 61 | flutter: 62 | 63 | # The following line ensures that the Material Icons font is 64 | # included with your application, so that you can use the icons in 65 | # the material Icons class. 66 | uses-material-design: true 67 | 68 | # To add assets to your application, add an assets section, like this: 69 | # assets: 70 | # - images/a_dot_burr.jpeg 71 | # - images/a_dot_ham.jpeg 72 | 73 | # An image asset can refer to one or more resolution-specific "variants", see 74 | # https://flutter.dev/assets-and-images/#resolution-aware 75 | 76 | # For details regarding adding assets from package dependencies, see 77 | # https://flutter.dev/assets-and-images/#from-packages 78 | 79 | # To add custom fonts to your application, add a fonts section here, 80 | # in this "flutter" section. Each entry in this list should have a 81 | # "family" key with the font family name, and a "fonts" key with a 82 | # list giving the asset and other descriptors for the font. For 83 | # example: 84 | # fonts: 85 | # - family: Schyler 86 | # fonts: 87 | # - asset: fonts/Schyler-Regular.ttf 88 | # - asset: fonts/Schyler-Italic.ttf 89 | # style: italic 90 | # - family: Trajan Pro 91 | # fonts: 92 | # - asset: fonts/TrajanPro.ttf 93 | # - asset: fonts/TrajanPro_Bold.ttf 94 | # weight: 700 95 | # 96 | # For details regarding fonts from package dependencies, 97 | # see https://flutter.dev/custom-fonts/#from-packages 98 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /example/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(hello_rust_ffi_plugin_example LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "hello_rust_ffi_plugin_example") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(VERSION 3.14...3.25) 12 | 13 | # Define build configuration option. 14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 15 | if(IS_MULTICONFIG) 16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 17 | CACHE STRING "" FORCE) 18 | else() 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | set(CMAKE_BUILD_TYPE "Debug" CACHE 21 | STRING "Flutter build mode" FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 23 | "Debug" "Profile" "Release") 24 | endif() 25 | endif() 26 | # Define settings for the Profile build mode. 27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 31 | 32 | # Use Unicode for all projects. 33 | add_definitions(-DUNICODE -D_UNICODE) 34 | 35 | # Compilation settings that should be applied to most targets. 36 | # 37 | # Be cautious about adding new options here, as plugins use this function by 38 | # default. In most cases, you should add new options to specific targets instead 39 | # of modifying this function. 40 | function(APPLY_STANDARD_SETTINGS TARGET) 41 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 43 | target_compile_options(${TARGET} PRIVATE /EHsc) 44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 46 | endfunction() 47 | 48 | # Flutter library and tool build rules. 49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 50 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 51 | 52 | # Application build; see runner/CMakeLists.txt. 53 | add_subdirectory("runner") 54 | 55 | 56 | # Generated plugin build rules, which manage building the plugins and adding 57 | # them to the application. 58 | include(flutter/generated_plugins.cmake) 59 | 60 | 61 | # === Installation === 62 | # Support files are copied into place next to the executable, so that it can 63 | # run in place. This is done instead of making a separate bundle (as on Linux) 64 | # so that building and running from within Visual Studio will work. 65 | set(BUILD_BUNDLE_DIR "$") 66 | # Make the "install" step default, as it's required to run. 67 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 68 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 69 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 70 | endif() 71 | 72 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 73 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 74 | 75 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 76 | COMPONENT Runtime) 77 | 78 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 79 | COMPONENT Runtime) 80 | 81 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 82 | COMPONENT Runtime) 83 | 84 | if(PLUGIN_BUNDLED_LIBRARIES) 85 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 86 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 87 | COMPONENT Runtime) 88 | endif() 89 | 90 | # Fully re-copy the assets directory on each build to avoid having stale files 91 | # from a previous install. 92 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 93 | install(CODE " 94 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 95 | " COMPONENT Runtime) 96 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 97 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 98 | 99 | # Install the AOT library on non-Debug builds only. 100 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 101 | CONFIGURATIONS Profile;Release 102 | COMPONENT Runtime) 103 | -------------------------------------------------------------------------------- /example/windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 12 | 13 | # === Flutter Library === 14 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 15 | 16 | # Published to parent scope for install step. 17 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 18 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 19 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 20 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 21 | 22 | list(APPEND FLUTTER_LIBRARY_HEADERS 23 | "flutter_export.h" 24 | "flutter_windows.h" 25 | "flutter_messenger.h" 26 | "flutter_plugin_registrar.h" 27 | "flutter_texture_registrar.h" 28 | ) 29 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 30 | add_library(flutter INTERFACE) 31 | target_include_directories(flutter INTERFACE 32 | "${EPHEMERAL_DIR}" 33 | ) 34 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 35 | add_dependencies(flutter flutter_assemble) 36 | 37 | # === Wrapper === 38 | list(APPEND CPP_WRAPPER_SOURCES_CORE 39 | "core_implementations.cc" 40 | "standard_codec.cc" 41 | ) 42 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 43 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 44 | "plugin_registrar.cc" 45 | ) 46 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 47 | list(APPEND CPP_WRAPPER_SOURCES_APP 48 | "flutter_engine.cc" 49 | "flutter_view_controller.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 52 | 53 | # Wrapper sources needed for a plugin. 54 | add_library(flutter_wrapper_plugin STATIC 55 | ${CPP_WRAPPER_SOURCES_CORE} 56 | ${CPP_WRAPPER_SOURCES_PLUGIN} 57 | ) 58 | apply_standard_settings(flutter_wrapper_plugin) 59 | set_target_properties(flutter_wrapper_plugin PROPERTIES 60 | POSITION_INDEPENDENT_CODE ON) 61 | set_target_properties(flutter_wrapper_plugin PROPERTIES 62 | CXX_VISIBILITY_PRESET hidden) 63 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 64 | target_include_directories(flutter_wrapper_plugin PUBLIC 65 | "${WRAPPER_ROOT}/include" 66 | ) 67 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 68 | 69 | # Wrapper sources needed for the runner. 70 | add_library(flutter_wrapper_app STATIC 71 | ${CPP_WRAPPER_SOURCES_CORE} 72 | ${CPP_WRAPPER_SOURCES_APP} 73 | ) 74 | apply_standard_settings(flutter_wrapper_app) 75 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 76 | target_include_directories(flutter_wrapper_app PUBLIC 77 | "${WRAPPER_ROOT}/include" 78 | ) 79 | add_dependencies(flutter_wrapper_app flutter_assemble) 80 | 81 | # === Flutter tool backend === 82 | # _phony_ is a non-existent file to force this command to run every time, 83 | # since currently there's no way to get a full input/output list from the 84 | # flutter tool. 85 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 86 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 87 | add_custom_command( 88 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 89 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 90 | ${CPP_WRAPPER_SOURCES_APP} 91 | ${PHONY_OUTPUT} 92 | COMMAND ${CMAKE_COMMAND} -E env 93 | ${FLUTTER_TOOL_ENVIRONMENT} 94 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 95 | windows-x64 $ 96 | VERBATIM 97 | ) 98 | add_custom_target(flutter_assemble DEPENDS 99 | "${FLUTTER_LIBRARY}" 100 | ${FLUTTER_LIBRARY_HEADERS} 101 | ${CPP_WRAPPER_SOURCES_CORE} 102 | ${CPP_WRAPPER_SOURCES_PLUGIN} 103 | ${CPP_WRAPPER_SOURCES_APP} 104 | ) 105 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void RegisterPlugins(flutter::PluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | hello_rust_ffi_plugin 10 | ) 11 | 12 | set(PLUGIN_BUNDLED_LIBRARIES) 13 | 14 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 15 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 16 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 19 | endforeach(plugin) 20 | 21 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 22 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 23 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 24 | endforeach(ffi_plugin) 25 | -------------------------------------------------------------------------------- /example/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /example/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "hello_rust_ffi_plugin_example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "hello_rust_ffi_plugin_example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "hello_rust_ffi_plugin_example.exe" "\0" 98 | VALUE "ProductName", "hello_rust_ffi_plugin_example" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | // flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /example/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"hello_rust_ffi_plugin_example", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /example/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /example/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irondash/hello_rust_ffi_plugin/eefae17c149ad0e418eb23ed3baf2950e0214ddf/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /example/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /example/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates a win32 window with |title| that is positioned and sized using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size this function will scale the inputted width and height as 35 | // as appropriate for the default monitor. The window is invisible until 36 | // |Show| is called. Returns true if the window was created successfully. 37 | bool Create(const std::wstring& title, const Point& origin, const Size& size); 38 | 39 | // Show the current window. Returns true if the window was successfully shown. 40 | bool Show(); 41 | 42 | // Release OS resources associated with window. 43 | void Destroy(); 44 | 45 | // Inserts |content| into the window tree. 46 | void SetChildContent(HWND content); 47 | 48 | // Returns the backing Window handle to enable clients to set icon and other 49 | // window properties. Returns nullptr if the window has been destroyed. 50 | HWND GetHandle(); 51 | 52 | // If true, closing this window will quit the application. 53 | void SetQuitOnClose(bool quit_on_close); 54 | 55 | // Return a RECT representing the bounds of the current client area. 56 | RECT GetClientArea(); 57 | 58 | protected: 59 | // Processes and route salient window messages for mouse handling, 60 | // size change and DPI. Delegates handling of these to member overloads that 61 | // inheriting classes can handle. 62 | virtual LRESULT MessageHandler(HWND window, 63 | UINT const message, 64 | WPARAM const wparam, 65 | LPARAM const lparam) noexcept; 66 | 67 | // Called when CreateAndShow is called, allowing subclass window-related 68 | // setup. Subclasses should return false if setup fails. 69 | virtual bool OnCreate(); 70 | 71 | // Called when Destroy is called. 72 | virtual void OnDestroy(); 73 | 74 | private: 75 | friend class WindowClassRegistrar; 76 | 77 | // OS callback called by message pump. Handles the WM_NCCREATE message which 78 | // is passed when the non-client area is being created and enables automatic 79 | // non-client DPI scaling so that the non-client area automatically 80 | // responds to changes in DPI. All other messages are handled by 81 | // MessageHandler. 82 | static LRESULT CALLBACK WndProc(HWND const window, 83 | UINT const message, 84 | WPARAM const wparam, 85 | LPARAM const lparam) noexcept; 86 | 87 | // Retrieves a class instance pointer for |window| 88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 89 | 90 | // Update the window frame's theme to match the system theme. 91 | static void UpdateTheme(HWND const window); 92 | 93 | bool quit_on_close_ = false; 94 | 95 | // window handle for top level window. 96 | HWND window_handle_ = nullptr; 97 | 98 | // window handle for hosted content. 99 | HWND child_content_ = nullptr; 100 | }; 101 | 102 | #endif // RUNNER_WIN32_WINDOW_H_ 103 | -------------------------------------------------------------------------------- /ffigen.yaml: -------------------------------------------------------------------------------- 1 | # Run with `flutter pub run ffigen --config ffigen.yaml`. 2 | name: HelloRustFfiPluginBindings 3 | description: | 4 | Bindings for `src/hello_rust_ffi_plugin.h`. 5 | 6 | Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`. 7 | output: 'lib/hello_rust_ffi_plugin_bindings_generated.dart' 8 | headers: 9 | entry-points: 10 | - 'src/hello_rust_ffi_plugin.h' 11 | include-directives: 12 | - 'src/hello_rust_ffi_plugin.h' 13 | preamble: | 14 | // ignore_for_file: always_specify_types 15 | // ignore_for_file: camel_case_types 16 | // ignore_for_file: non_constant_identifier_names 17 | comments: 18 | style: any 19 | length: full 20 | -------------------------------------------------------------------------------- /ios: -------------------------------------------------------------------------------- 1 | macos -------------------------------------------------------------------------------- /lib/hello_rust_ffi_plugin.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:async'; 3 | import 'dart:ffi'; 4 | import 'dart:io'; 5 | import 'dart:isolate'; 6 | 7 | import 'hello_rust_ffi_plugin_bindings_generated.dart'; 8 | 9 | /// A very short-lived native function. 10 | /// 11 | /// For very short-lived functions, it is fine to call them on the main isolate. 12 | /// They will block the Dart execution while running the native function, so 13 | /// only do this for native functions which are guaranteed to be short-lived. 14 | int sum(int a, int b) => _bindings.sum(a, b); 15 | 16 | /// A longer lived native function, which occupies the thread calling it. 17 | /// 18 | /// Do not call these kind of native functions in the main isolate. They will 19 | /// block Dart execution. This will cause dropped frames in Flutter applications. 20 | /// Instead, call these native functions on a separate isolate. 21 | /// 22 | /// Modify this to suit your own use case. Example use cases: 23 | /// 24 | /// 1. Reuse a single isolate for various different kinds of requests. 25 | /// 2. Use multiple helper isolates for parallel execution. 26 | Future sumAsync(int a, int b) async { 27 | final SendPort helperIsolateSendPort = await _helperIsolateSendPort; 28 | final int requestId = _nextSumRequestId++; 29 | final _SumRequest request = _SumRequest(requestId, a, b); 30 | final Completer completer = Completer(); 31 | _sumRequests[requestId] = completer; 32 | helperIsolateSendPort.send(request); 33 | return completer.future; 34 | } 35 | 36 | const String _libName = 'hello_rust_ffi_plugin'; 37 | 38 | /// The dynamic library in which the symbols for [HelloRustFfiPluginBindings] can be found. 39 | final DynamicLibrary _dylib = () { 40 | if (Platform.isMacOS || Platform.isIOS) { 41 | return DynamicLibrary.open('$_libName.framework/$_libName'); 42 | } 43 | if (Platform.isAndroid || Platform.isLinux) { 44 | return DynamicLibrary.open('lib$_libName.so'); 45 | } 46 | if (Platform.isWindows) { 47 | return DynamicLibrary.open('$_libName.dll'); 48 | } 49 | throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}'); 50 | }(); 51 | 52 | /// The bindings to the native functions in [_dylib]. 53 | final HelloRustFfiPluginBindings _bindings = HelloRustFfiPluginBindings(_dylib); 54 | 55 | 56 | /// A request to compute `sum`. 57 | /// 58 | /// Typically sent from one isolate to another. 59 | class _SumRequest { 60 | final int id; 61 | final int a; 62 | final int b; 63 | 64 | const _SumRequest(this.id, this.a, this.b); 65 | } 66 | 67 | /// A response with the result of `sum`. 68 | /// 69 | /// Typically sent from one isolate to another. 70 | class _SumResponse { 71 | final int id; 72 | final int result; 73 | 74 | const _SumResponse(this.id, this.result); 75 | } 76 | 77 | /// Counter to identify [_SumRequest]s and [_SumResponse]s. 78 | int _nextSumRequestId = 0; 79 | 80 | /// Mapping from [_SumRequest] `id`s to the completers corresponding to the correct future of the pending request. 81 | final Map> _sumRequests = >{}; 82 | 83 | /// The SendPort belonging to the helper isolate. 84 | Future _helperIsolateSendPort = () async { 85 | // The helper isolate is going to send us back a SendPort, which we want to 86 | // wait for. 87 | final Completer completer = Completer(); 88 | 89 | // Receive port on the main isolate to receive messages from the helper. 90 | // We receive two types of messages: 91 | // 1. A port to send messages on. 92 | // 2. Responses to requests we sent. 93 | final ReceivePort receivePort = ReceivePort() 94 | ..listen((dynamic data) { 95 | if (data is SendPort) { 96 | // The helper isolate sent us the port on which we can sent it requests. 97 | completer.complete(data); 98 | return; 99 | } 100 | if (data is _SumResponse) { 101 | // The helper isolate sent us a response to a request we sent. 102 | final Completer completer = _sumRequests[data.id]!; 103 | _sumRequests.remove(data.id); 104 | completer.complete(data.result); 105 | return; 106 | } 107 | throw UnsupportedError('Unsupported message type: ${data.runtimeType}'); 108 | }); 109 | 110 | // Start the helper isolate. 111 | await Isolate.spawn((SendPort sendPort) async { 112 | final ReceivePort helperReceivePort = ReceivePort() 113 | ..listen((dynamic data) { 114 | // On the helper isolate listen to requests and respond to them. 115 | if (data is _SumRequest) { 116 | final int result = _bindings.sum_long_running(data.a, data.b); 117 | final _SumResponse response = _SumResponse(data.id, result); 118 | sendPort.send(response); 119 | return; 120 | } 121 | throw UnsupportedError('Unsupported message type: ${data.runtimeType}'); 122 | }); 123 | 124 | // Send the port to the main isolate on which we can receive requests. 125 | sendPort.send(helperReceivePort.sendPort); 126 | }, receivePort.sendPort); 127 | 128 | // Wait until the helper isolate has sent us back the SendPort on which we 129 | // can start sending requests. 130 | return completer.future; 131 | }(); 132 | -------------------------------------------------------------------------------- /lib/hello_rust_ffi_plugin_bindings_generated.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: always_specify_types 2 | // ignore_for_file: camel_case_types 3 | // ignore_for_file: non_constant_identifier_names 4 | 5 | // AUTO GENERATED FILE, DO NOT EDIT. 6 | // 7 | // Generated by `package:ffigen`. 8 | import 'dart:ffi' as ffi; 9 | 10 | /// Bindings for `src/hello_rust_ffi_plugin.h`. 11 | /// 12 | /// Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`. 13 | /// 14 | class HelloRustFfiPluginBindings { 15 | /// Holds the symbol lookup function. 16 | final ffi.Pointer Function(String symbolName) 17 | _lookup; 18 | 19 | /// The symbols are looked up in [dynamicLibrary]. 20 | HelloRustFfiPluginBindings(ffi.DynamicLibrary dynamicLibrary) 21 | : _lookup = dynamicLibrary.lookup; 22 | 23 | /// The symbols are looked up with [lookup]. 24 | HelloRustFfiPluginBindings.fromLookup( 25 | ffi.Pointer Function(String symbolName) 26 | lookup) 27 | : _lookup = lookup; 28 | 29 | /// A very short-lived native function. 30 | /// 31 | /// For very short-lived functions, it is fine to call them on the main isolate. 32 | /// They will block the Dart execution while running the native function, so 33 | /// only do this for native functions which are guaranteed to be short-lived. 34 | int sum( 35 | int a, 36 | int b, 37 | ) { 38 | return _sum( 39 | a, 40 | b, 41 | ); 42 | } 43 | 44 | late final _sumPtr = 45 | _lookup>( 46 | 'sum'); 47 | late final _sum = _sumPtr.asFunction(); 48 | 49 | /// A longer lived native function, which occupies the thread calling it. 50 | /// 51 | /// Do not call these kind of native functions in the main isolate. They will 52 | /// block Dart execution. This will cause dropped frames in Flutter applications. 53 | /// Instead, call these native functions on a separate isolate. 54 | int sum_long_running( 55 | int a, 56 | int b, 57 | ) { 58 | return _sum_long_running( 59 | a, 60 | b, 61 | ); 62 | } 63 | 64 | late final _sum_long_runningPtr = 65 | _lookup>( 66 | 'sum_long_running'); 67 | late final _sum_long_running = 68 | _sum_long_runningPtr.asFunction(); 69 | } 70 | -------------------------------------------------------------------------------- /linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The Flutter tooling requires that developers have CMake 3.10 or later 2 | # installed. You should not increase this version, as doing so will cause 3 | # the plugin to fail to compile for some customers of the plugin. 4 | cmake_minimum_required(VERSION 3.10) 5 | 6 | # Project-level configuration. 7 | set(PROJECT_NAME "hello_rust_ffi_plugin") 8 | project(${PROJECT_NAME} LANGUAGES CXX) 9 | 10 | include("../cargokit/cmake/cargokit.cmake") 11 | apply_cargokit(${PROJECT_NAME} ../rust hello_rust_ffi_plugin "") 12 | 13 | # List of absolute paths to libraries that should be bundled with the plugin. 14 | # This list could contain prebuilt libraries, or libraries created by an 15 | # external build triggered from this build file. 16 | set(hello_rust_ffi_plugin_bundled_libraries 17 | # Defined in ../src/CMakeLists.txt. 18 | # This can be changed to accommodate different builds. 19 | "${${PROJECT_NAME}_cargokit_lib}" 20 | PARENT_SCOPE 21 | ) 22 | -------------------------------------------------------------------------------- /macos/Classes/hello_rust_ffi_plugin.c: -------------------------------------------------------------------------------- 1 | // This is an empty file to force CocoaPods to create a framework. 2 | -------------------------------------------------------------------------------- /macos/hello_rust_ffi_plugin.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint hello_rust_ffi_plugin.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'hello_rust_ffi_plugin' 7 | s.version = '0.0.1' 8 | s.summary = 'A new Flutter FFI plugin project.' 9 | s.description = <<-DESC 10 | A new Flutter FFI plugin project. 11 | DESC 12 | s.homepage = 'http://example.com' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'email@example.com' } 15 | 16 | # >>>>>> Everything after this line is new <<<<<<< 17 | 18 | s.source = { :path => '.' } 19 | s.source_files = 'Classes/**/*' 20 | 21 | s.script_phase = { 22 | :name => 'Build Rust library', 23 | # First argument is relative path to the `rust` folder, second is name of rust library 24 | :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../rust hello_rust_ffi_plugin', 25 | :execution_position=> :before_compile, 26 | :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], 27 | # Let XCode know that the static library referenced in -force_load below is 28 | # created by this build step. 29 | :output_files => ["${BUILT_PRODUCTS_DIR}/libhello_rust_ffi_plugin.a"], 30 | } 31 | s.pod_target_xcconfig = { 32 | 'DEFINES_MODULE' => 'YES', 33 | # Flutter.framework does not contain a i386 slice. 34 | 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', 35 | 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/libhello_rust_ffi_plugin.a', 36 | } 37 | end -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: hello_rust_ffi_plugin 2 | description: A new Flutter FFI plugin project. 3 | version: 0.0.1 4 | homepage: 5 | 6 | environment: 7 | sdk: '>=3.0.0 <4.0.0' 8 | flutter: '>=3.3.0' 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | plugin_platform_interface: ^2.0.2 14 | 15 | dev_dependencies: 16 | ffi: ^2.0.1 17 | ffigen: ^6.1.2 18 | flutter_test: 19 | sdk: flutter 20 | flutter_lints: ^2.0.0 21 | 22 | # For information on the generic Dart part of this file, see the 23 | # following page: https://dart.dev/tools/pub/pubspec 24 | 25 | # The following section is specific to Flutter packages. 26 | flutter: 27 | # This section identifies this Flutter project as a plugin project. 28 | # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) 29 | # which should be registered in the plugin registry. This is required for 30 | # using method channels. 31 | # The Android 'package' specifies package in which the registered class is. 32 | # This is required for using method channels on Android. 33 | # The 'ffiPlugin' specifies that native code should be built and bundled. 34 | # This is required for using `dart:ffi`. 35 | # All these are used by the tooling to maintain consistency when 36 | # adding or updating assets for this project. 37 | # 38 | # Please refer to README.md for a detailed explanation. 39 | plugin: 40 | platforms: 41 | android: 42 | ffiPlugin: true 43 | ios: 44 | ffiPlugin: true 45 | linux: 46 | ffiPlugin: true 47 | macos: 48 | ffiPlugin: true 49 | windows: 50 | ffiPlugin: true 51 | 52 | # To add assets to your plugin package, add an assets section, like this: 53 | # assets: 54 | # - images/a_dot_burr.jpeg 55 | # - images/a_dot_ham.jpeg 56 | # 57 | # For details regarding assets in packages, see 58 | # https://flutter.dev/assets-and-images/#from-packages 59 | # 60 | # An image asset can refer to one or more resolution-specific "variants", see 61 | # https://flutter.dev/assets-and-images/#resolution-aware 62 | 63 | # To add custom fonts to your plugin package, add a fonts section here, 64 | # in this "flutter" section. Each entry in this list should have a 65 | # "family" key with the font family name, and a "fonts" key with a 66 | # list giving the asset and other descriptors for the font. For 67 | # example: 68 | # fonts: 69 | # - family: Schyler 70 | # fonts: 71 | # - asset: fonts/Schyler-Regular.ttf 72 | # - asset: fonts/Schyler-Italic.ttf 73 | # style: italic 74 | # - family: Trajan Pro 75 | # fonts: 76 | # - asset: fonts/TrajanPro.ttf 77 | # - asset: fonts/TrajanPro_Bold.ttf 78 | # weight: 700 79 | # 80 | # For details regarding fonts in packages, see 81 | # https://flutter.dev/custom-fonts/#from-packages 82 | -------------------------------------------------------------------------------- /rust/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "hello_rust_ffi_plugin" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello_rust_ffi_plugin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | crate-type = ["cdylib", "staticlib"] 10 | 11 | [dependencies] 12 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{time::Duration, usize}; 2 | 3 | #[no_mangle] 4 | pub extern "C" fn sum(a: usize, b: usize) -> usize { 5 | a + b 6 | } 7 | 8 | #[no_mangle] 9 | pub extern "C" fn sum_long_running(a: usize, b: usize) -> usize { 10 | std::thread::sleep(Duration::from_secs(5)); 11 | a + b 12 | } 13 | 14 | #[cfg(test)] 15 | mod tests { 16 | use super::*; 17 | 18 | #[test] 19 | fn it_works() { 20 | let result = sum(2, 2); 21 | assert_eq!(result, 4); 22 | } 23 | } -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The Flutter tooling requires that developers have a version of Visual Studio 2 | # installed that includes CMake 3.14 or later. You should not increase this 3 | # version, as doing so will cause the plugin to fail to compile for some 4 | # customers of the plugin. 5 | cmake_minimum_required(VERSION 3.14) 6 | 7 | # Project-level configuration. 8 | set(PROJECT_NAME "hello_rust_ffi_plugin") 9 | project(${PROJECT_NAME} LANGUAGES CXX) 10 | 11 | include("../cargokit/cmake/cargokit.cmake") 12 | apply_cargokit(${PROJECT_NAME} ../rust hello_rust_ffi_plugin "") 13 | 14 | # List of absolute paths to libraries that should be bundled with the plugin. 15 | # This list could contain prebuilt libraries, or libraries created by an 16 | # external build triggered from this build file. 17 | set(hello_rust_ffi_plugin_bundled_libraries 18 | # Defined in ../src/CMakeLists.txt. 19 | # This can be changed to accommodate different builds. 20 | "${${PROJECT_NAME}_cargokit_lib}" 21 | PARENT_SCOPE 22 | ) 23 | --------------------------------------------------------------------------------