├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── cargokit ├── .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 ├── cmake │ ├── cargokit.cmake │ └── resolve_symlinks.ps1 ├── gradle │ └── plugin.gradle ├── run_build_tool.cmd └── run_build_tool.sh ├── example ├── .gitignore ├── README.md ├── analysis_options.yaml ├── integration_test │ └── simple_test.dart ├── 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 ├── flutter_rust_bridge.yaml ├── lib ├── src │ └── rust │ │ ├── api │ │ └── velopack.dart │ │ ├── frb_generated.dart │ │ ├── frb_generated.io.dart │ │ └── frb_generated.web.dart └── velopack_flutter.dart ├── linux └── CMakeLists.txt ├── macos ├── Classes │ └── dummy_file.c └── velopack_flutter.podspec ├── pubspec.yaml ├── rust ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ ├── api │ ├── mod.rs │ └── velopack.rs │ ├── frb_generated.rs │ └── lib.rs ├── test_driver └── integration_test.dart └── 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 | build/ 30 | -------------------------------------------------------------------------------- /.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: "b0850beeb25f6d5b10426284f506557f66181b36" 8 | channel: "stable" 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: b0850beeb25f6d5b10426284f506557f66181b36 17 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 18 | - platform: android 19 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 20 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 21 | - platform: ios 22 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 23 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 24 | - platform: linux 25 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 26 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 27 | - platform: macos 28 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 29 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 30 | - platform: windows 31 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 32 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 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 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.1.0 2 | 3 | * Upgrade Rust bridge to 2.6.0 4 | * Upgrade Velopack to 0.0.869 5 | 6 | ## 0.0.2 7 | 8 | * Upgrade Rust bridge to 2.2.0 and fix error because of bridge version mismatch 9 | 10 | ## 0.0.1 11 | 12 | * Initial release. 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Leon Kukuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # velopack_flutter 2 | 3 | [![pub package](https://img.shields.io/pub/v/velopack_flutter.svg)](https://pub.dartlang.org/packages/velopack_flutter) 4 | 5 | A Flutter implementation of Velopack using flutter_rust_bridge for automated desktop app updates. 6 | 7 | ## Why Velopack? 8 | 9 | Flutter currently lacks a robust solution for distributing auto-updates for desktop applications, aside from the Microsoft and Mac App Stores. Velopack addresses this gap by providing a cross-platform installation and auto-update framework for desktop applications. 10 | 11 | Learn more about Velopack at [https://velopack.io/](https://velopack.io/) 12 | 13 | This project leverages [flutter_rust_bridge](https://cjycode.com/flutter_rust_bridge/) to interface with the Rust implementation of Velopack. 14 | 15 | ## Getting Started 16 | 17 | 1. Make sure rust is installed: 18 | 2. Add the velopack_flutter dependency to your `pubspec.yaml`: 19 | 20 | ```yaml 21 | dependencies: 22 | velopack_flutter: ^0.1.0 23 | ``` 24 | 25 | 3. Import the package, initialize the Rust library and handle Velopack app hooks in your main.dart: 26 | 27 | ```dart 28 | import 'package:flutter/material.dart'; 29 | import 'package:velopack_flutter/velopack_flutter.dart'; 30 | 31 | Future main(List args) async { 32 | await VelopackRustLib.init(); 33 | 34 | final veloCommands = ['--veloapp-install', '--veloapp-updated', '--veloapp-obsolete', '--veloapp-uninstall']; 35 | if (veloCommands.any((cmd) => args.contains(cmd))) { 36 | exit(0); 37 | } 38 | 39 | /* You can now call the API functions shown in the API section. E.g isUpdateAvailable(url: ...); 40 | Do note that the API functions will only function correctly in a vpk packed release. 41 | isUpdateAvailable and the rest will just throw an exception if you call them while debugging. 42 | */ 43 | runApp(const MyApp()); 44 | } 45 | ``` 46 | 47 | ## Use with another rust library 48 | 49 | If your project have already existing rust bindings. You'll certainly need to have a distinct library class name to prevent name conflicts. 50 | 51 | This is what Velopack Flutter is doing by setting the following values in `flutter_rust_bridge.yaml` to make the class name "unique" and avoid conflicts. 52 | 53 | ```yaml 54 | dart_entrypoint_class_name: VelopackRustLib 55 | ``` 56 | 57 | **We recommend you to do the same on your own rust bindings.** 58 | 59 | ## API 60 | 61 | | Function | Description | 62 | |----------|------------------------------------------------------------------------------------------------------------------------------| 63 | | `isUpdateAvailable(String url)` | Checks the specified URL for an update and returns a boolean. | 64 | | `updateAndRestart(String url)` | Checks for an update, downloads and applies it, then restarts the app. | 65 | | `updateAndExit(String url)` | Checks for an update, downloads and applies it, then exits the app. | 66 | | `waitExitThenUpdate(String url)` | Checks for an update, downloads it, and applies it after the app has been closed. Will close automatically after 60 seconds. | 67 | 68 | ## Packaging 69 | 70 | 1. Install .NET Core SDK 6.0 and the `vpk` tool: 71 | 72 | ```shell 73 | dotnet tool update -g vpk 74 | ``` 75 | 76 | 2. Build your Flutter app: 77 | 78 | ```shell 79 | flutter build [windows|macos|linux] --release 80 | ``` 81 | 82 | 3. Navigate to your release build directory & package your app using `vpk`: 83 | 84 | ### Windows 85 | 86 | ```shell 87 | cd build/windows/x64/runner 88 | vpk pack --packId YourAppId --packVersion 1.0.0 --packDir Release --mainExe YourApp.exe 89 | ``` 90 | 91 | ### MacOS 92 | 93 | ```shell 94 | cd build/macos/Build/Products/Release 95 | vpk pack -u "xyz.appName.companyName" -v 1.0.0 \ 96 | -p "./YourApp.app" \ 97 | --channel "osx-channelName" \ 98 | --mainExe "Application Binary" \ 99 | --noPortable --packTitle "Application Title" --signEntitlements "$(pwd)/macos/Runner/Release.entitlements" \ 100 | --signAppIdentity "Developer ID Application: (Your Name)" \ 101 | --signInstallIdentity "Developer ID Installer: (Your Name)" \ 102 | --notaryProfile "notary-profile-name" \ 103 | ``` 104 | 105 | 106 | Your release package will be generated in the `Releases` directory. 107 | 108 | For more information on packaging and distribution, refer to: 109 | 110 | - [Velopack Packaging Documentation](https://docs.velopack.io/category/packaging) 111 | - [Velopack Distribution Documentation](https://docs.velopack.io/category/distributing) 112 | 113 | ## Using Github releases as your updates location 114 | 115 | There is an issue () in the velopack repo about adding full api support for github releases. In the meantime 116 | you can still use github releases with a few workarounds: 117 | 118 | ### Download step (before packing the release) 119 | 120 | Run the download command before packing to include the latest information from your github releases 121 | 122 | ```shell 123 | vpk download github --repoUrl https://github.com/{orgOrUser}/{repoName} 124 | ``` 125 | 126 | Then pack as normal. 127 | 128 | #### Uploading 129 | 130 | ```shell 131 | vpk upload github --repoUrl https://github.com/{orgOrUser}/{repoName} --publish --releaseName YourDesiredReleaseName --tag v1.0.0 --token your_github_token 132 | ``` 133 | 134 | #### Using the API in your flutter app 135 | 136 | velopack expects all the files to be available at the given url. One way to accomplish this with github releases is 137 | to specify `${repoUrl}releases/download/${tagName}/` as the url to isUpdateAvailable or any other api function. 138 | This does require you to parse out the tag for the latest release manually yourself, e.g.: 139 | 140 | ```dart 141 | final url = Uri.parse('${repoUrl}releases/latest/'); 142 | final response = await http.get(url); 143 | if (response.statusCode == 200) { 144 | final data = json.decode(response.body); 145 | final tag_name = data['tag_name'] 146 | } 147 | ``` 148 | 149 | ## Notes 150 | 151 | - The Linux implementation is currently untested. Contributions and feedback from Linux users are welcome. 152 | - The API may differ from Velopack implementations in other languages and is not feature-complete. In the long-term it would make sense to keep these consistent, however I didn't have time for this yet. Feel free to open a PR! 153 | 154 | ## Contributing 155 | 156 | If you encounter issues, have suggestions, or want to contribute code, please open an issue or submit a pull request on this GitHub repository. 157 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /cargokit/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .dart_tool 3 | *.iml 4 | !pubspec.lock 5 | -------------------------------------------------------------------------------- /cargokit/LICENSE: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | Copyright 2022 Matej Knopp 5 | 6 | ================================================================================ 7 | 8 | MIT LICENSE 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 14 | of the Software, and to permit persons to whom the Software is furnished to do 15 | so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 22 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 23 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 25 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | ================================================================================ 28 | 29 | APACHE LICENSE, VERSION 2.0 30 | 31 | Licensed under the Apache License, Version 2.0 (the "License"); 32 | you may not use this file except in compliance with the License. 33 | You may obtain a copy of the License at 34 | 35 | http://www.apache.org/licenses/LICENSE-2.0 36 | 37 | Unless required by applicable law or agreed to in writing, software 38 | distributed under the License is distributed on an "AS IS" BASIS, 39 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 40 | See the License for the specific language governing permissions and 41 | limitations under the License. 42 | 43 | -------------------------------------------------------------------------------- /cargokit/README: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | Experimental repository to provide glue for seamlessly integrating cargo build 5 | with flutter plugins and packages. 6 | 7 | See https://matejknopp.com/post/flutter_plugin_in_rust_with_no_prebuilt_binaries/ 8 | for a tutorial on how to use Cargokit. 9 | 10 | Example plugin available at https://github.com/irondash/hello_rust_ffi_plugin. 11 | 12 | -------------------------------------------------------------------------------- /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 | sh "$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 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | A sample command-line application with an entrypoint in `bin/`, library code 5 | in `lib/`, and example unit test in `test/`. 6 | -------------------------------------------------------------------------------- /cargokit/build_tool/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This is copied from Cargokit (which is the official way to use it currently) 2 | # Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | # This file configures the static analysis results for your project (errors, 5 | # warnings, and lints). 6 | # 7 | # This enables the 'recommended' set of lints from `package:lints`. 8 | # This set helps identify many issues that may lead to problems when running 9 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 10 | # style and format. 11 | # 12 | # If you want a smaller set of lints you can change this to specify 13 | # 'package:lints/core.yaml'. These are just the most critical lints 14 | # (the recommended set includes the core lints). 15 | # The core lints are also what is used by pub.dev for scoring packages. 16 | 17 | include: package:lints/recommended.yaml 18 | 19 | # Uncomment the following section to specify additional rules. 20 | 21 | linter: 22 | rules: 23 | - prefer_relative_imports 24 | - directives_ordering 25 | 26 | # analyzer: 27 | # exclude: 28 | # - path/to/excluded/files/** 29 | 30 | # For more information about the core and recommended set of lints, see 31 | # https://dart.dev/go/core-lints 32 | 33 | # For additional information about configuring this file, see 34 | # https://dart.dev/guides/language/analysis-options 35 | -------------------------------------------------------------------------------- /cargokit/build_tool/bin/build_tool.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'package:build_tool/build_tool.dart' as build_tool; 5 | 6 | void main(List arguments) { 7 | build_tool.runMain(arguments); 8 | } 9 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/build_tool.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'src/build_tool.dart' as build_tool; 5 | 6 | Future runMain(List args) async { 7 | return build_tool.runMain(args); 8 | } 9 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/android_environment.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | import 'dart:isolate'; 6 | import 'dart:math' as math; 7 | 8 | import 'package:collection/collection.dart'; 9 | import 'package:path/path.dart' as path; 10 | import 'package:version/version.dart'; 11 | 12 | import 'target.dart'; 13 | import 'util.dart'; 14 | 15 | class AndroidEnvironment { 16 | AndroidEnvironment({ 17 | required this.sdkPath, 18 | required this.ndkVersion, 19 | required this.minSdkVersion, 20 | required this.targetTempDir, 21 | required this.target, 22 | }); 23 | 24 | static void clangLinkerWrapper(List args) { 25 | final clang = Platform.environment['_CARGOKIT_NDK_LINK_CLANG']; 26 | if (clang == null) { 27 | throw Exception( 28 | "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_CLANG env var"); 29 | } 30 | final target = Platform.environment['_CARGOKIT_NDK_LINK_TARGET']; 31 | if (target == null) { 32 | throw Exception( 33 | "cargo-ndk rustc linker: didn't find _CARGOKIT_NDK_LINK_TARGET env var"); 34 | } 35 | 36 | runCommand(clang, [ 37 | target, 38 | ...args, 39 | ]); 40 | } 41 | 42 | /// Full path to Android SDK. 43 | final String sdkPath; 44 | 45 | /// Full version of Android NDK. 46 | final String ndkVersion; 47 | 48 | /// Minimum supported SDK version. 49 | final int minSdkVersion; 50 | 51 | /// Target directory for build artifacts. 52 | final String targetTempDir; 53 | 54 | /// Target being built. 55 | final Target target; 56 | 57 | bool ndkIsInstalled() { 58 | final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); 59 | final ndkPackageXml = File(path.join(ndkPath, 'package.xml')); 60 | return ndkPackageXml.existsSync(); 61 | } 62 | 63 | void installNdk({ 64 | required String javaHome, 65 | }) { 66 | final sdkManagerExtension = Platform.isWindows ? '.bat' : ''; 67 | final sdkManager = path.join( 68 | sdkPath, 69 | 'cmdline-tools', 70 | 'latest', 71 | 'bin', 72 | 'sdkmanager$sdkManagerExtension', 73 | ); 74 | 75 | log.info('Installing NDK $ndkVersion'); 76 | runCommand(sdkManager, [ 77 | '--install', 78 | 'ndk;$ndkVersion', 79 | ], environment: { 80 | 'JAVA_HOME': javaHome, 81 | }); 82 | } 83 | 84 | Future> buildEnvironment() async { 85 | final hostArch = Platform.isMacOS 86 | ? "darwin-x86_64" 87 | : (Platform.isLinux ? "linux-x86_64" : "windows-x86_64"); 88 | 89 | final ndkPath = path.join(sdkPath, 'ndk', ndkVersion); 90 | final toolchainPath = path.join( 91 | ndkPath, 92 | 'toolchains', 93 | 'llvm', 94 | 'prebuilt', 95 | hostArch, 96 | 'bin', 97 | ); 98 | 99 | final minSdkVersion = 100 | math.max(target.androidMinSdkVersion!, this.minSdkVersion); 101 | 102 | final exe = Platform.isWindows ? '.exe' : ''; 103 | 104 | final arKey = 'AR_${target.rust}'; 105 | final arValue = ['${target.rust}-ar', 'llvm-ar', 'llvm-ar.exe'] 106 | .map((e) => path.join(toolchainPath, e)) 107 | .firstWhereOrNull((element) => File(element).existsSync()); 108 | if (arValue == null) { 109 | throw Exception('Failed to find ar for $target in $toolchainPath'); 110 | } 111 | 112 | final targetArg = '--target=${target.rust}$minSdkVersion'; 113 | 114 | final ccKey = 'CC_${target.rust}'; 115 | final ccValue = path.join(toolchainPath, 'clang$exe'); 116 | final cfFlagsKey = 'CFLAGS_${target.rust}'; 117 | final cFlagsValue = targetArg; 118 | 119 | final cxxKey = 'CXX_${target.rust}'; 120 | final cxxValue = path.join(toolchainPath, 'clang++$exe'); 121 | final cxxFlagsKey = 'CXXFLAGS_${target.rust}'; 122 | final cxxFlagsValue = targetArg; 123 | 124 | final linkerKey = 125 | 'cargo_target_${target.rust.replaceAll('-', '_')}_linker'.toUpperCase(); 126 | 127 | final ranlibKey = 'RANLIB_${target.rust}'; 128 | final ranlibValue = path.join(toolchainPath, 'llvm-ranlib$exe'); 129 | 130 | final ndkVersionParsed = Version.parse(ndkVersion); 131 | final rustFlagsKey = 'CARGO_ENCODED_RUSTFLAGS'; 132 | final rustFlagsValue = _libGccWorkaround(targetTempDir, ndkVersionParsed); 133 | 134 | final runRustTool = 135 | Platform.isWindows ? 'run_build_tool.cmd' : 'run_build_tool.sh'; 136 | 137 | final packagePath = (await Isolate.resolvePackageUri( 138 | Uri.parse('package:build_tool/buildtool.dart')))! 139 | .toFilePath(); 140 | final selfPath = path.canonicalize(path.join( 141 | packagePath, 142 | '..', 143 | '..', 144 | '..', 145 | runRustTool, 146 | )); 147 | 148 | // Make sure that run_build_tool is working properly even initially launched directly 149 | // through dart run. 150 | final toolTempDir = 151 | Platform.environment['CARGOKIT_TOOL_TEMP_DIR'] ?? targetTempDir; 152 | 153 | return { 154 | arKey: arValue, 155 | ccKey: ccValue, 156 | cfFlagsKey: cFlagsValue, 157 | cxxKey: cxxValue, 158 | cxxFlagsKey: cxxFlagsValue, 159 | ranlibKey: ranlibValue, 160 | rustFlagsKey: rustFlagsValue, 161 | linkerKey: selfPath, 162 | // Recognized by main() so we know when we're acting as a wrapper 163 | '_CARGOKIT_NDK_LINK_TARGET': targetArg, 164 | '_CARGOKIT_NDK_LINK_CLANG': ccValue, 165 | 'CARGOKIT_TOOL_TEMP_DIR': toolTempDir, 166 | }; 167 | } 168 | 169 | // Workaround for libgcc missing in NDK23, inspired by cargo-ndk 170 | String _libGccWorkaround(String buildDir, Version ndkVersion) { 171 | final workaroundDir = path.join( 172 | buildDir, 173 | 'cargokit', 174 | 'libgcc_workaround', 175 | '${ndkVersion.major}', 176 | ); 177 | Directory(workaroundDir).createSync(recursive: true); 178 | if (ndkVersion.major >= 23) { 179 | File(path.join(workaroundDir, 'libgcc.a')) 180 | .writeAsStringSync('INPUT(-lunwind)'); 181 | } else { 182 | // Other way around, untested, forward libgcc.a from libunwind once Rust 183 | // gets updated for NDK23+. 184 | File(path.join(workaroundDir, 'libunwind.a')) 185 | .writeAsStringSync('INPUT(-lgcc)'); 186 | } 187 | 188 | var rustFlags = Platform.environment['CARGO_ENCODED_RUSTFLAGS'] ?? ''; 189 | if (rustFlags.isNotEmpty) { 190 | rustFlags = '$rustFlags\x1f'; 191 | } 192 | rustFlags = '$rustFlags-L\x1f$workaroundDir'; 193 | return rustFlags; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/artifacts_provider.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:ed25519_edwards/ed25519_edwards.dart'; 7 | import 'package:http/http.dart'; 8 | import 'package:logging/logging.dart'; 9 | import 'package:path/path.dart' as path; 10 | 11 | import 'builder.dart'; 12 | import 'crate_hash.dart'; 13 | import 'options.dart'; 14 | import 'precompile_binaries.dart'; 15 | import 'rustup.dart'; 16 | import 'target.dart'; 17 | 18 | class Artifact { 19 | /// File system location of the artifact. 20 | final String path; 21 | 22 | /// Actual file name that the artifact should have in destination folder. 23 | final String finalFileName; 24 | 25 | AritifactType get type { 26 | if (finalFileName.endsWith('.dll') || 27 | finalFileName.endsWith('.dll.lib') || 28 | finalFileName.endsWith('.pdb') || 29 | finalFileName.endsWith('.so') || 30 | finalFileName.endsWith('.dylib')) { 31 | return AritifactType.dylib; 32 | } else if (finalFileName.endsWith('.lib') || finalFileName.endsWith('.a')) { 33 | return AritifactType.staticlib; 34 | } else { 35 | throw Exception('Unknown artifact type for $finalFileName'); 36 | } 37 | } 38 | 39 | Artifact({ 40 | required this.path, 41 | required this.finalFileName, 42 | }); 43 | } 44 | 45 | final _log = Logger('artifacts_provider'); 46 | 47 | class ArtifactProvider { 48 | ArtifactProvider({ 49 | required this.environment, 50 | required this.userOptions, 51 | }); 52 | 53 | final BuildEnvironment environment; 54 | final CargokitUserOptions userOptions; 55 | 56 | Future>> getArtifacts(List targets) async { 57 | final result = await _getPrecompiledArtifacts(targets); 58 | 59 | final pendingTargets = List.of(targets); 60 | pendingTargets.removeWhere((element) => result.containsKey(element)); 61 | 62 | if (pendingTargets.isEmpty) { 63 | return result; 64 | } 65 | 66 | final rustup = Rustup(); 67 | for (final target in targets) { 68 | final builder = RustBuilder(target: target, environment: environment); 69 | builder.prepare(rustup); 70 | _log.info('Building ${environment.crateInfo.packageName} for $target'); 71 | final targetDir = await builder.build(); 72 | // For local build accept both static and dynamic libraries. 73 | final artifactNames = { 74 | ...getArtifactNames( 75 | target: target, 76 | libraryName: environment.crateInfo.packageName, 77 | aritifactType: AritifactType.dylib, 78 | remote: false, 79 | ), 80 | ...getArtifactNames( 81 | target: target, 82 | libraryName: environment.crateInfo.packageName, 83 | aritifactType: AritifactType.staticlib, 84 | remote: false, 85 | ) 86 | }; 87 | final artifacts = artifactNames 88 | .map((artifactName) => Artifact( 89 | path: path.join(targetDir, artifactName), 90 | finalFileName: artifactName, 91 | )) 92 | .where((element) => File(element.path).existsSync()) 93 | .toList(); 94 | result[target] = artifacts; 95 | } 96 | return result; 97 | } 98 | 99 | Future>> _getPrecompiledArtifacts( 100 | List targets) async { 101 | if (userOptions.usePrecompiledBinaries == false) { 102 | _log.info('Precompiled binaries are disabled'); 103 | return {}; 104 | } 105 | if (environment.crateOptions.precompiledBinaries == null) { 106 | _log.fine('Precompiled binaries not enabled for this crate'); 107 | return {}; 108 | } 109 | 110 | final start = Stopwatch()..start(); 111 | final crateHash = CrateHash.compute(environment.manifestDir, 112 | tempStorage: environment.targetTempDir); 113 | _log.fine( 114 | 'Computed crate hash $crateHash in ${start.elapsedMilliseconds}ms'); 115 | 116 | final downloadedArtifactsDir = 117 | path.join(environment.targetTempDir, 'precompiled', crateHash); 118 | Directory(downloadedArtifactsDir).createSync(recursive: true); 119 | 120 | final res = >{}; 121 | 122 | for (final target in targets) { 123 | final requiredArtifacts = getArtifactNames( 124 | target: target, 125 | libraryName: environment.crateInfo.packageName, 126 | remote: true, 127 | ); 128 | final artifactsForTarget = []; 129 | 130 | for (final artifact in requiredArtifacts) { 131 | final fileName = PrecompileBinaries.fileName(target, artifact); 132 | final downloadedPath = path.join(downloadedArtifactsDir, fileName); 133 | if (!File(downloadedPath).existsSync()) { 134 | final signatureFileName = 135 | PrecompileBinaries.signatureFileName(target, artifact); 136 | await _tryDownloadArtifacts( 137 | crateHash: crateHash, 138 | fileName: fileName, 139 | signatureFileName: signatureFileName, 140 | finalPath: downloadedPath, 141 | ); 142 | } 143 | if (File(downloadedPath).existsSync()) { 144 | artifactsForTarget.add(Artifact( 145 | path: downloadedPath, 146 | finalFileName: artifact, 147 | )); 148 | } else { 149 | break; 150 | } 151 | } 152 | 153 | // Only provide complete set of artifacts. 154 | if (artifactsForTarget.length == requiredArtifacts.length) { 155 | _log.fine('Found precompiled artifacts for $target'); 156 | res[target] = artifactsForTarget; 157 | } 158 | } 159 | 160 | return res; 161 | } 162 | 163 | static Future _get(Uri url, {Map? headers}) async { 164 | int attempt = 0; 165 | const maxAttempts = 10; 166 | while (true) { 167 | try { 168 | return await get(url, headers: headers); 169 | } on SocketException catch (e) { 170 | // Try to detect reset by peer error and retry. 171 | if (attempt++ < maxAttempts && 172 | (e.osError?.errorCode == 54 || e.osError?.errorCode == 10054)) { 173 | _log.severe( 174 | 'Failed to download $url: $e, attempt $attempt of $maxAttempts, will retry...'); 175 | await Future.delayed(Duration(seconds: 1)); 176 | continue; 177 | } else { 178 | rethrow; 179 | } 180 | } 181 | } 182 | } 183 | 184 | Future _tryDownloadArtifacts({ 185 | required String crateHash, 186 | required String fileName, 187 | required String signatureFileName, 188 | required String finalPath, 189 | }) async { 190 | final precompiledBinaries = environment.crateOptions.precompiledBinaries!; 191 | final prefix = precompiledBinaries.uriPrefix; 192 | final url = Uri.parse('$prefix$crateHash/$fileName'); 193 | final signatureUrl = Uri.parse('$prefix$crateHash/$signatureFileName'); 194 | _log.fine('Downloading signature from $signatureUrl'); 195 | final signature = await _get(signatureUrl); 196 | if (signature.statusCode == 404) { 197 | _log.warning( 198 | 'Precompiled binaries not available for crate hash $crateHash ($fileName)'); 199 | return; 200 | } 201 | if (signature.statusCode != 200) { 202 | _log.severe( 203 | 'Failed to download signature $signatureUrl: status ${signature.statusCode}'); 204 | return; 205 | } 206 | _log.fine('Downloading binary from $url'); 207 | final res = await _get(url); 208 | if (res.statusCode != 200) { 209 | _log.severe('Failed to download binary $url: status ${res.statusCode}'); 210 | return; 211 | } 212 | if (verify( 213 | precompiledBinaries.publicKey, res.bodyBytes, signature.bodyBytes)) { 214 | File(finalPath).writeAsBytesSync(res.bodyBytes); 215 | } else { 216 | _log.shout('Signature verification failed! Ignoring binary.'); 217 | } 218 | } 219 | } 220 | 221 | enum AritifactType { 222 | staticlib, 223 | dylib, 224 | } 225 | 226 | AritifactType artifactTypeForTarget(Target target) { 227 | if (target.darwinPlatform != null) { 228 | return AritifactType.staticlib; 229 | } else { 230 | return AritifactType.dylib; 231 | } 232 | } 233 | 234 | List getArtifactNames({ 235 | required Target target, 236 | required String libraryName, 237 | required bool remote, 238 | AritifactType? aritifactType, 239 | }) { 240 | aritifactType ??= artifactTypeForTarget(target); 241 | if (target.darwinArch != null) { 242 | if (aritifactType == AritifactType.staticlib) { 243 | return ['lib$libraryName.a']; 244 | } else { 245 | return ['lib$libraryName.dylib']; 246 | } 247 | } else if (target.rust.contains('-windows-')) { 248 | if (aritifactType == AritifactType.staticlib) { 249 | return ['$libraryName.lib']; 250 | } else { 251 | return [ 252 | '$libraryName.dll', 253 | '$libraryName.dll.lib', 254 | if (!remote) '$libraryName.pdb' 255 | ]; 256 | } 257 | } else if (target.rust.contains('-linux-')) { 258 | if (aritifactType == AritifactType.staticlib) { 259 | return ['lib$libraryName.a']; 260 | } else { 261 | return ['lib$libraryName.so']; 262 | } 263 | } else { 264 | throw Exception("Unsupported target: ${target.rust}"); 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/build_cmake.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:path/path.dart' as path; 7 | 8 | import 'artifacts_provider.dart'; 9 | import 'builder.dart'; 10 | import 'environment.dart'; 11 | import 'options.dart'; 12 | import 'target.dart'; 13 | 14 | class BuildCMake { 15 | final CargokitUserOptions userOptions; 16 | 17 | BuildCMake({required this.userOptions}); 18 | 19 | Future build() async { 20 | final targetPlatform = Environment.targetPlatform; 21 | final target = Target.forFlutterName(Environment.targetPlatform); 22 | if (target == null) { 23 | throw Exception("Unknown target platform: $targetPlatform"); 24 | } 25 | 26 | final environment = BuildEnvironment.fromEnvironment(isAndroid: false); 27 | final provider = 28 | ArtifactProvider(environment: environment, userOptions: userOptions); 29 | final artifacts = await provider.getArtifacts([target]); 30 | 31 | final libs = artifacts[target]!; 32 | 33 | for (final lib in libs) { 34 | if (lib.type == AritifactType.dylib) { 35 | File(lib.path) 36 | .copySync(path.join(Environment.outputDir, lib.finalFileName)); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/build_gradle.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:logging/logging.dart'; 7 | import 'package:path/path.dart' as path; 8 | 9 | import 'artifacts_provider.dart'; 10 | import 'builder.dart'; 11 | import 'environment.dart'; 12 | import 'options.dart'; 13 | import 'target.dart'; 14 | 15 | final log = Logger('build_gradle'); 16 | 17 | class BuildGradle { 18 | BuildGradle({required this.userOptions}); 19 | 20 | final CargokitUserOptions userOptions; 21 | 22 | Future build() async { 23 | final targets = Environment.targetPlatforms.map((arch) { 24 | final target = Target.forFlutterName(arch); 25 | if (target == null) { 26 | throw Exception( 27 | "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); 28 | } 29 | return target; 30 | }).toList(); 31 | 32 | final environment = BuildEnvironment.fromEnvironment(isAndroid: true); 33 | final provider = 34 | ArtifactProvider(environment: environment, userOptions: userOptions); 35 | final artifacts = await provider.getArtifacts(targets); 36 | 37 | for (final target in targets) { 38 | final libs = artifacts[target]!; 39 | final outputDir = path.join(Environment.outputDir, target.android!); 40 | Directory(outputDir).createSync(recursive: true); 41 | 42 | for (final lib in libs) { 43 | if (lib.type == AritifactType.dylib) { 44 | File(lib.path).copySync(path.join(outputDir, lib.finalFileName)); 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/build_pod.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:path/path.dart' as path; 7 | 8 | import 'artifacts_provider.dart'; 9 | import 'builder.dart'; 10 | import 'environment.dart'; 11 | import 'options.dart'; 12 | import 'target.dart'; 13 | import 'util.dart'; 14 | 15 | class BuildPod { 16 | BuildPod({required this.userOptions}); 17 | 18 | final CargokitUserOptions userOptions; 19 | 20 | Future build() async { 21 | final targets = Environment.darwinArchs.map((arch) { 22 | final target = Target.forDarwin( 23 | platformName: Environment.darwinPlatformName, darwinAarch: arch); 24 | if (target == null) { 25 | throw Exception( 26 | "Unknown darwin target or platform: $arch, ${Environment.darwinPlatformName}"); 27 | } 28 | return target; 29 | }).toList(); 30 | 31 | final environment = BuildEnvironment.fromEnvironment(isAndroid: false); 32 | final provider = 33 | ArtifactProvider(environment: environment, userOptions: userOptions); 34 | final artifacts = await provider.getArtifacts(targets); 35 | 36 | void performLipo(String targetFile, Iterable sourceFiles) { 37 | runCommand("lipo", [ 38 | '-create', 39 | ...sourceFiles, 40 | '-output', 41 | targetFile, 42 | ]); 43 | } 44 | 45 | final outputDir = Environment.outputDir; 46 | 47 | Directory(outputDir).createSync(recursive: true); 48 | 49 | final staticLibs = artifacts.values 50 | .expand((element) => element) 51 | .where((element) => element.type == AritifactType.staticlib) 52 | .toList(); 53 | final dynamicLibs = artifacts.values 54 | .expand((element) => element) 55 | .where((element) => element.type == AritifactType.dylib) 56 | .toList(); 57 | 58 | final libName = environment.crateInfo.packageName; 59 | 60 | // If there is static lib, use it and link it with pod 61 | if (staticLibs.isNotEmpty) { 62 | final finalTargetFile = path.join(outputDir, "lib$libName.a"); 63 | performLipo(finalTargetFile, staticLibs.map((e) => e.path)); 64 | } else { 65 | // Otherwise try to replace bundle dylib with our dylib 66 | final bundlePaths = [ 67 | '$libName.framework/Versions/A/$libName', 68 | '$libName.framework/$libName', 69 | ]; 70 | 71 | for (final bundlePath in bundlePaths) { 72 | final targetFile = path.join(outputDir, bundlePath); 73 | if (File(targetFile).existsSync()) { 74 | performLipo(targetFile, dynamicLibs.map((e) => e.path)); 75 | 76 | // Replace absolute id with @rpath one so that it works properly 77 | // when moved to Frameworks. 78 | runCommand("install_name_tool", [ 79 | '-id', 80 | '@rpath/$bundlePath', 81 | targetFile, 82 | ]); 83 | return; 84 | } 85 | } 86 | throw Exception('Unable to find bundle for dynamic library'); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/build_tool.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:args/command_runner.dart'; 7 | import 'package:ed25519_edwards/ed25519_edwards.dart'; 8 | import 'package:github/github.dart'; 9 | import 'package:hex/hex.dart'; 10 | import 'package:logging/logging.dart'; 11 | 12 | import 'android_environment.dart'; 13 | import 'build_cmake.dart'; 14 | import 'build_gradle.dart'; 15 | import 'build_pod.dart'; 16 | import 'logging.dart'; 17 | import 'options.dart'; 18 | import 'precompile_binaries.dart'; 19 | import 'target.dart'; 20 | import 'util.dart'; 21 | import 'verify_binaries.dart'; 22 | 23 | final log = Logger('build_tool'); 24 | 25 | abstract class BuildCommand extends Command { 26 | Future runBuildCommand(CargokitUserOptions options); 27 | 28 | @override 29 | Future run() async { 30 | final options = CargokitUserOptions.load(); 31 | 32 | if (options.verboseLogging || 33 | Platform.environment['CARGOKIT_VERBOSE'] == '1') { 34 | enableVerboseLogging(); 35 | } 36 | 37 | await runBuildCommand(options); 38 | } 39 | } 40 | 41 | class BuildPodCommand extends BuildCommand { 42 | @override 43 | final name = 'build-pod'; 44 | 45 | @override 46 | final description = 'Build cocoa pod library'; 47 | 48 | @override 49 | Future runBuildCommand(CargokitUserOptions options) async { 50 | final build = BuildPod(userOptions: options); 51 | await build.build(); 52 | } 53 | } 54 | 55 | class BuildGradleCommand extends BuildCommand { 56 | @override 57 | final name = 'build-gradle'; 58 | 59 | @override 60 | final description = 'Build android library'; 61 | 62 | @override 63 | Future runBuildCommand(CargokitUserOptions options) async { 64 | final build = BuildGradle(userOptions: options); 65 | await build.build(); 66 | } 67 | } 68 | 69 | class BuildCMakeCommand extends BuildCommand { 70 | @override 71 | final name = 'build-cmake'; 72 | 73 | @override 74 | final description = 'Build CMake library'; 75 | 76 | @override 77 | Future runBuildCommand(CargokitUserOptions options) async { 78 | final build = BuildCMake(userOptions: options); 79 | await build.build(); 80 | } 81 | } 82 | 83 | class GenKeyCommand extends Command { 84 | @override 85 | final name = 'gen-key'; 86 | 87 | @override 88 | final description = 'Generate key pair for signing precompiled binaries'; 89 | 90 | @override 91 | void run() { 92 | final kp = generateKey(); 93 | final private = HEX.encode(kp.privateKey.bytes); 94 | final public = HEX.encode(kp.publicKey.bytes); 95 | print("Private Key: $private"); 96 | print("Public Key: $public"); 97 | } 98 | } 99 | 100 | class PrecompileBinariesCommand extends Command { 101 | PrecompileBinariesCommand() { 102 | argParser 103 | ..addOption( 104 | 'repository', 105 | mandatory: true, 106 | help: 'Github repository slug in format owner/name', 107 | ) 108 | ..addOption( 109 | 'manifest-dir', 110 | mandatory: true, 111 | help: 'Directory containing Cargo.toml', 112 | ) 113 | ..addMultiOption('target', 114 | help: 'Rust target triple of artifact to build.\n' 115 | 'Can be specified multiple times or omitted in which case\n' 116 | 'all targets for current platform will be built.') 117 | ..addOption( 118 | 'android-sdk-location', 119 | help: 'Location of Android SDK (if available)', 120 | ) 121 | ..addOption( 122 | 'android-ndk-version', 123 | help: 'Android NDK version (if available)', 124 | ) 125 | ..addOption( 126 | 'android-min-sdk-version', 127 | help: 'Android minimum rquired version (if available)', 128 | ) 129 | ..addOption( 130 | 'temp-dir', 131 | help: 'Directory to store temporary build artifacts', 132 | ) 133 | ..addFlag( 134 | "verbose", 135 | abbr: "v", 136 | defaultsTo: false, 137 | help: "Enable verbose logging", 138 | ); 139 | } 140 | 141 | @override 142 | final name = 'precompile-binaries'; 143 | 144 | @override 145 | final description = 'Prebuild and upload binaries\n' 146 | 'Private key must be passed through PRIVATE_KEY environment variable. ' 147 | 'Use gen_key through generate priave key.\n' 148 | 'Github token must be passed as GITHUB_TOKEN environment variable.\n'; 149 | 150 | @override 151 | Future run() async { 152 | final verbose = argResults!['verbose'] as bool; 153 | if (verbose) { 154 | enableVerboseLogging(); 155 | } 156 | 157 | final privateKeyString = Platform.environment['PRIVATE_KEY']; 158 | if (privateKeyString == null) { 159 | throw ArgumentError('Missing PRIVATE_KEY environment variable'); 160 | } 161 | final githubToken = Platform.environment['GITHUB_TOKEN']; 162 | if (githubToken == null) { 163 | throw ArgumentError('Missing GITHUB_TOKEN environment variable'); 164 | } 165 | final privateKey = HEX.decode(privateKeyString); 166 | if (privateKey.length != 64) { 167 | throw ArgumentError('Private key must be 64 bytes long'); 168 | } 169 | final manifestDir = argResults!['manifest-dir'] as String; 170 | if (!Directory(manifestDir).existsSync()) { 171 | throw ArgumentError('Manifest directory does not exist: $manifestDir'); 172 | } 173 | String? androidMinSdkVersionString = 174 | argResults!['android-min-sdk-version'] as String?; 175 | int? androidMinSdkVersion; 176 | if (androidMinSdkVersionString != null) { 177 | androidMinSdkVersion = int.tryParse(androidMinSdkVersionString); 178 | if (androidMinSdkVersion == null) { 179 | throw ArgumentError( 180 | 'Invalid android-min-sdk-version: $androidMinSdkVersionString'); 181 | } 182 | } 183 | final targetStrigns = argResults!['target'] as List; 184 | final targets = targetStrigns.map((target) { 185 | final res = Target.forRustTriple(target); 186 | if (res == null) { 187 | throw ArgumentError('Invalid target: $target'); 188 | } 189 | return res; 190 | }).toList(growable: false); 191 | final precompileBinaries = PrecompileBinaries( 192 | privateKey: PrivateKey(privateKey), 193 | githubToken: githubToken, 194 | manifestDir: manifestDir, 195 | repositorySlug: RepositorySlug.full(argResults!['repository'] as String), 196 | targets: targets, 197 | androidSdkLocation: argResults!['android-sdk-location'] as String?, 198 | androidNdkVersion: argResults!['android-ndk-version'] as String?, 199 | androidMinSdkVersion: androidMinSdkVersion, 200 | tempDir: argResults!['temp-dir'] as String?, 201 | ); 202 | 203 | await precompileBinaries.run(); 204 | } 205 | } 206 | 207 | class VerifyBinariesCommand extends Command { 208 | VerifyBinariesCommand() { 209 | argParser.addOption( 210 | 'manifest-dir', 211 | mandatory: true, 212 | help: 'Directory containing Cargo.toml', 213 | ); 214 | } 215 | 216 | @override 217 | final name = "verify-binaries"; 218 | 219 | @override 220 | final description = 'Verifies published binaries\n' 221 | 'Checks whether there is a binary published for each targets\n' 222 | 'and checks the signature.'; 223 | 224 | @override 225 | Future run() async { 226 | final manifestDir = argResults!['manifest-dir'] as String; 227 | final verifyBinaries = VerifyBinaries( 228 | manifestDir: manifestDir, 229 | ); 230 | await verifyBinaries.run(); 231 | } 232 | } 233 | 234 | Future runMain(List args) async { 235 | try { 236 | // Init logging before options are loaded 237 | initLogging(); 238 | 239 | if (Platform.environment['_CARGOKIT_NDK_LINK_TARGET'] != null) { 240 | return AndroidEnvironment.clangLinkerWrapper(args); 241 | } 242 | 243 | final runner = CommandRunner('build_tool', 'Cargokit built_tool') 244 | ..addCommand(BuildPodCommand()) 245 | ..addCommand(BuildGradleCommand()) 246 | ..addCommand(BuildCMakeCommand()) 247 | ..addCommand(GenKeyCommand()) 248 | ..addCommand(PrecompileBinariesCommand()) 249 | ..addCommand(VerifyBinariesCommand()); 250 | 251 | await runner.run(args); 252 | } on ArgumentError catch (e) { 253 | stderr.writeln(e.toString()); 254 | exit(1); 255 | } catch (e, s) { 256 | log.severe(kDoubleSeparator); 257 | log.severe('Cargokit BuildTool failed with error:'); 258 | log.severe(kSeparator); 259 | log.severe(e); 260 | // This tells user to install Rust, there's no need to pollute the log with 261 | // stack trace. 262 | if (e is! RustupNotFoundException) { 263 | log.severe(kSeparator); 264 | log.severe(s); 265 | log.severe(kSeparator); 266 | log.severe('BuildTool arguments: $args'); 267 | } 268 | log.severe(kDoubleSeparator); 269 | exit(1); 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/builder.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'package:collection/collection.dart'; 5 | import 'package:logging/logging.dart'; 6 | import 'package:path/path.dart' as path; 7 | 8 | import 'android_environment.dart'; 9 | import 'cargo.dart'; 10 | import 'environment.dart'; 11 | import 'options.dart'; 12 | import 'rustup.dart'; 13 | import 'target.dart'; 14 | import 'util.dart'; 15 | 16 | final _log = Logger('builder'); 17 | 18 | enum BuildConfiguration { 19 | debug, 20 | release, 21 | profile, 22 | } 23 | 24 | extension on BuildConfiguration { 25 | bool get isDebug => this == BuildConfiguration.debug; 26 | String get rustName => switch (this) { 27 | BuildConfiguration.debug => 'debug', 28 | BuildConfiguration.release => 'release', 29 | BuildConfiguration.profile => 'release', 30 | }; 31 | } 32 | 33 | class BuildException implements Exception { 34 | final String message; 35 | 36 | BuildException(this.message); 37 | 38 | @override 39 | String toString() { 40 | return 'BuildException: $message'; 41 | } 42 | } 43 | 44 | class BuildEnvironment { 45 | final BuildConfiguration configuration; 46 | final CargokitCrateOptions crateOptions; 47 | final String targetTempDir; 48 | final String manifestDir; 49 | final CrateInfo crateInfo; 50 | 51 | final bool isAndroid; 52 | final String? androidSdkPath; 53 | final String? androidNdkVersion; 54 | final int? androidMinSdkVersion; 55 | final String? javaHome; 56 | 57 | BuildEnvironment({ 58 | required this.configuration, 59 | required this.crateOptions, 60 | required this.targetTempDir, 61 | required this.manifestDir, 62 | required this.crateInfo, 63 | required this.isAndroid, 64 | this.androidSdkPath, 65 | this.androidNdkVersion, 66 | this.androidMinSdkVersion, 67 | this.javaHome, 68 | }); 69 | 70 | static BuildConfiguration parseBuildConfiguration(String value) { 71 | // XCode configuration adds the flavor to configuration name. 72 | final firstSegment = value.split('-').first; 73 | final buildConfiguration = BuildConfiguration.values.firstWhereOrNull( 74 | (e) => e.name == firstSegment, 75 | ); 76 | if (buildConfiguration == null) { 77 | _log.warning('Unknown build configuraiton $value, will assume release'); 78 | return BuildConfiguration.release; 79 | } 80 | return buildConfiguration; 81 | } 82 | 83 | static BuildEnvironment fromEnvironment({ 84 | required bool isAndroid, 85 | }) { 86 | final buildConfiguration = 87 | parseBuildConfiguration(Environment.configuration); 88 | final manifestDir = Environment.manifestDir; 89 | final crateOptions = CargokitCrateOptions.load( 90 | manifestDir: manifestDir, 91 | ); 92 | final crateInfo = CrateInfo.load(manifestDir); 93 | return BuildEnvironment( 94 | configuration: buildConfiguration, 95 | crateOptions: crateOptions, 96 | targetTempDir: Environment.targetTempDir, 97 | manifestDir: manifestDir, 98 | crateInfo: crateInfo, 99 | isAndroid: isAndroid, 100 | androidSdkPath: isAndroid ? Environment.sdkPath : null, 101 | androidNdkVersion: isAndroid ? Environment.ndkVersion : null, 102 | androidMinSdkVersion: 103 | isAndroid ? int.parse(Environment.minSdkVersion) : null, 104 | javaHome: isAndroid ? Environment.javaHome : null, 105 | ); 106 | } 107 | } 108 | 109 | class RustBuilder { 110 | final Target target; 111 | final BuildEnvironment environment; 112 | 113 | RustBuilder({ 114 | required this.target, 115 | required this.environment, 116 | }); 117 | 118 | void prepare( 119 | Rustup rustup, 120 | ) { 121 | final toolchain = _toolchain; 122 | if (rustup.installedTargets(toolchain) == null) { 123 | rustup.installToolchain(toolchain); 124 | } 125 | if (toolchain == 'nightly') { 126 | rustup.installRustSrcForNightly(); 127 | } 128 | if (!rustup.installedTargets(toolchain)!.contains(target.rust)) { 129 | rustup.installTarget(target.rust, toolchain: toolchain); 130 | } 131 | } 132 | 133 | CargoBuildOptions? get _buildOptions => 134 | environment.crateOptions.cargo[environment.configuration]; 135 | 136 | String get _toolchain => _buildOptions?.toolchain.name ?? 'stable'; 137 | 138 | /// Returns the path of directory containing build artifacts. 139 | Future build() async { 140 | final extraArgs = _buildOptions?.flags ?? []; 141 | final manifestPath = path.join(environment.manifestDir, 'Cargo.toml'); 142 | runCommand( 143 | 'rustup', 144 | [ 145 | 'run', 146 | _toolchain, 147 | 'cargo', 148 | 'build', 149 | ...extraArgs, 150 | '--manifest-path', 151 | manifestPath, 152 | '-p', 153 | environment.crateInfo.packageName, 154 | if (!environment.configuration.isDebug) '--release', 155 | '--target', 156 | target.rust, 157 | '--target-dir', 158 | environment.targetTempDir, 159 | ], 160 | environment: await _buildEnvironment(), 161 | ); 162 | return path.join( 163 | environment.targetTempDir, 164 | target.rust, 165 | environment.configuration.rustName, 166 | ); 167 | } 168 | 169 | Future> _buildEnvironment() async { 170 | if (target.android == null) { 171 | return {}; 172 | } else { 173 | final sdkPath = environment.androidSdkPath; 174 | final ndkVersion = environment.androidNdkVersion; 175 | final minSdkVersion = environment.androidMinSdkVersion; 176 | if (sdkPath == null) { 177 | throw BuildException('androidSdkPath is not set'); 178 | } 179 | if (ndkVersion == null) { 180 | throw BuildException('androidNdkVersion is not set'); 181 | } 182 | if (minSdkVersion == null) { 183 | throw BuildException('androidMinSdkVersion is not set'); 184 | } 185 | final env = AndroidEnvironment( 186 | sdkPath: sdkPath, 187 | ndkVersion: ndkVersion, 188 | minSdkVersion: minSdkVersion, 189 | targetTempDir: environment.targetTempDir, 190 | target: target, 191 | ); 192 | if (!env.ndkIsInstalled() && environment.javaHome != null) { 193 | env.installNdk(javaHome: environment.javaHome!); 194 | } 195 | return env.buildEnvironment(); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/cargo.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:path/path.dart' as path; 7 | import 'package:toml/toml.dart'; 8 | 9 | class ManifestException { 10 | ManifestException(this.message, {required this.fileName}); 11 | 12 | final String? fileName; 13 | final String message; 14 | 15 | @override 16 | String toString() { 17 | if (fileName != null) { 18 | return 'Failed to parse package manifest at $fileName: $message'; 19 | } else { 20 | return 'Failed to parse package manifest: $message'; 21 | } 22 | } 23 | } 24 | 25 | class CrateInfo { 26 | CrateInfo({required this.packageName}); 27 | 28 | final String packageName; 29 | 30 | static CrateInfo parseManifest(String manifest, {final String? fileName}) { 31 | final toml = TomlDocument.parse(manifest); 32 | final package = toml.toMap()['package']; 33 | if (package == null) { 34 | throw ManifestException('Missing package section', fileName: fileName); 35 | } 36 | final name = package['name']; 37 | if (name == null) { 38 | throw ManifestException('Missing package name', fileName: fileName); 39 | } 40 | return CrateInfo(packageName: name); 41 | } 42 | 43 | static CrateInfo load(String manifestDir) { 44 | final manifestFile = File(path.join(manifestDir, 'Cargo.toml')); 45 | final manifest = manifestFile.readAsStringSync(); 46 | return parseManifest(manifest, fileName: manifestFile.path); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/crate_hash.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:convert'; 5 | import 'dart:io'; 6 | import 'dart:typed_data'; 7 | 8 | import 'package:collection/collection.dart'; 9 | import 'package:convert/convert.dart'; 10 | import 'package:crypto/crypto.dart'; 11 | import 'package:path/path.dart' as path; 12 | 13 | class CrateHash { 14 | /// Computes a hash uniquely identifying crate content. This takes into account 15 | /// content all all .rs files inside the src directory, as well as Cargo.toml, 16 | /// Cargo.lock, build.rs and cargokit.yaml. 17 | /// 18 | /// If [tempStorage] is provided, computed hash is stored in a file in that directory 19 | /// and reused on subsequent calls if the crate content hasn't changed. 20 | static String compute(String manifestDir, {String? tempStorage}) { 21 | return CrateHash._( 22 | manifestDir: manifestDir, 23 | tempStorage: tempStorage, 24 | )._compute(); 25 | } 26 | 27 | CrateHash._({ 28 | required this.manifestDir, 29 | required this.tempStorage, 30 | }); 31 | 32 | String _compute() { 33 | final files = getFiles(); 34 | final tempStorage = this.tempStorage; 35 | if (tempStorage != null) { 36 | final quickHash = _computeQuickHash(files); 37 | final quickHashFolder = Directory(path.join(tempStorage, 'crate_hash')); 38 | quickHashFolder.createSync(recursive: true); 39 | final quickHashFile = File(path.join(quickHashFolder.path, quickHash)); 40 | if (quickHashFile.existsSync()) { 41 | return quickHashFile.readAsStringSync(); 42 | } 43 | final hash = _computeHash(files); 44 | quickHashFile.writeAsStringSync(hash); 45 | return hash; 46 | } else { 47 | return _computeHash(files); 48 | } 49 | } 50 | 51 | /// Computes a quick hash based on files stat (without reading contents). This 52 | /// is used to cache the real hash, which is slower to compute since it involves 53 | /// reading every single file. 54 | String _computeQuickHash(List files) { 55 | final output = AccumulatorSink(); 56 | final input = sha256.startChunkedConversion(output); 57 | 58 | final data = ByteData(8); 59 | for (final file in files) { 60 | input.add(utf8.encode(file.path)); 61 | final stat = file.statSync(); 62 | data.setUint64(0, stat.size); 63 | input.add(data.buffer.asUint8List()); 64 | data.setUint64(0, stat.modified.millisecondsSinceEpoch); 65 | input.add(data.buffer.asUint8List()); 66 | } 67 | 68 | input.close(); 69 | return base64Url.encode(output.events.single.bytes); 70 | } 71 | 72 | String _computeHash(List files) { 73 | final output = AccumulatorSink(); 74 | final input = sha256.startChunkedConversion(output); 75 | 76 | void addTextFile(File file) { 77 | // text Files are hashed by lines in case we're dealing with github checkout 78 | // that auto-converts line endings. 79 | final splitter = LineSplitter(); 80 | if (file.existsSync()) { 81 | final data = file.readAsStringSync(); 82 | final lines = splitter.convert(data); 83 | for (final line in lines) { 84 | input.add(utf8.encode(line)); 85 | } 86 | } 87 | } 88 | 89 | for (final file in files) { 90 | addTextFile(file); 91 | } 92 | 93 | input.close(); 94 | final res = output.events.single; 95 | 96 | // Truncate to 128bits. 97 | final hash = res.bytes.sublist(0, 16); 98 | return hex.encode(hash); 99 | } 100 | 101 | List getFiles() { 102 | final src = Directory(path.join(manifestDir, 'src')); 103 | final files = src 104 | .listSync(recursive: true, followLinks: false) 105 | .whereType() 106 | .toList(); 107 | files.sortBy((element) => element.path); 108 | void addFile(String relative) { 109 | final file = File(path.join(manifestDir, relative)); 110 | if (file.existsSync()) { 111 | files.add(file); 112 | } 113 | } 114 | 115 | addFile('Cargo.toml'); 116 | addFile('Cargo.lock'); 117 | addFile('build.rs'); 118 | addFile('cargokit.yaml'); 119 | return files; 120 | } 121 | 122 | final String manifestDir; 123 | final String? tempStorage; 124 | } 125 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/environment.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | extension on String { 7 | String resolveSymlink() => File(this).resolveSymbolicLinksSync(); 8 | } 9 | 10 | class Environment { 11 | /// Current build configuration (debug or release). 12 | static String get configuration => 13 | _getEnv("CARGOKIT_CONFIGURATION").toLowerCase(); 14 | 15 | static bool get isDebug => configuration == 'debug'; 16 | static bool get isRelease => configuration == 'release'; 17 | 18 | /// Temporary directory where Rust build artifacts are placed. 19 | static String get targetTempDir => _getEnv("CARGOKIT_TARGET_TEMP_DIR"); 20 | 21 | /// Final output directory where the build artifacts are placed. 22 | static String get outputDir => _getEnvPath('CARGOKIT_OUTPUT_DIR'); 23 | 24 | /// Path to the crate manifest (containing Cargo.toml). 25 | static String get manifestDir => _getEnvPath('CARGOKIT_MANIFEST_DIR'); 26 | 27 | /// Directory inside root project. Not necessarily root folder. Symlinks are 28 | /// not resolved on purpose. 29 | static String get rootProjectDir => _getEnv('CARGOKIT_ROOT_PROJECT_DIR'); 30 | 31 | // Pod 32 | 33 | /// Platform name (macosx, iphoneos, iphonesimulator). 34 | static String get darwinPlatformName => 35 | _getEnv("CARGOKIT_DARWIN_PLATFORM_NAME"); 36 | 37 | /// List of architectures to build for (arm64, armv7, x86_64). 38 | static List get darwinArchs => 39 | _getEnv("CARGOKIT_DARWIN_ARCHS").split(' '); 40 | 41 | // Gradle 42 | static String get minSdkVersion => _getEnv("CARGOKIT_MIN_SDK_VERSION"); 43 | static String get ndkVersion => _getEnv("CARGOKIT_NDK_VERSION"); 44 | static String get sdkPath => _getEnvPath("CARGOKIT_SDK_DIR"); 45 | static String get javaHome => _getEnvPath("CARGOKIT_JAVA_HOME"); 46 | static List get targetPlatforms => 47 | _getEnv("CARGOKIT_TARGET_PLATFORMS").split(','); 48 | 49 | // CMAKE 50 | static String get targetPlatform => _getEnv("CARGOKIT_TARGET_PLATFORM"); 51 | 52 | static String _getEnv(String key) { 53 | final res = Platform.environment[key]; 54 | if (res == null) { 55 | throw Exception("Missing environment variable $key"); 56 | } 57 | return res; 58 | } 59 | 60 | static String _getEnvPath(String key) { 61 | final res = _getEnv(key); 62 | if (Directory(res).existsSync()) { 63 | return res.resolveSymlink(); 64 | } else { 65 | return res; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/logging.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:logging/logging.dart'; 7 | 8 | const String kSeparator = "--"; 9 | const String kDoubleSeparator = "=="; 10 | 11 | bool _lastMessageWasSeparator = false; 12 | 13 | void _log(LogRecord rec) { 14 | final prefix = '${rec.level.name}: '; 15 | final out = rec.level == Level.SEVERE ? stderr : stdout; 16 | if (rec.message == kSeparator) { 17 | if (!_lastMessageWasSeparator) { 18 | out.write(prefix); 19 | out.writeln('-' * 80); 20 | _lastMessageWasSeparator = true; 21 | } 22 | return; 23 | } else if (rec.message == kDoubleSeparator) { 24 | out.write(prefix); 25 | out.writeln('=' * 80); 26 | _lastMessageWasSeparator = true; 27 | return; 28 | } 29 | out.write(prefix); 30 | out.writeln(rec.message); 31 | _lastMessageWasSeparator = false; 32 | } 33 | 34 | void initLogging() { 35 | Logger.root.level = Level.INFO; 36 | Logger.root.onRecord.listen((LogRecord rec) { 37 | final lines = rec.message.split('\n'); 38 | for (final line in lines) { 39 | if (line.isNotEmpty || lines.length == 1 || line != lines.last) { 40 | _log(LogRecord( 41 | rec.level, 42 | line, 43 | rec.loggerName, 44 | )); 45 | } 46 | } 47 | }); 48 | } 49 | 50 | void enableVerboseLogging() { 51 | Logger.root.level = Level.ALL; 52 | } 53 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/precompile_binaries.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:ed25519_edwards/ed25519_edwards.dart'; 7 | import 'package:github/github.dart'; 8 | import 'package:logging/logging.dart'; 9 | import 'package:path/path.dart' as path; 10 | 11 | import 'artifacts_provider.dart'; 12 | import 'builder.dart'; 13 | import 'cargo.dart'; 14 | import 'crate_hash.dart'; 15 | import 'options.dart'; 16 | import 'rustup.dart'; 17 | import 'target.dart'; 18 | 19 | final _log = Logger('precompile_binaries'); 20 | 21 | class PrecompileBinaries { 22 | PrecompileBinaries({ 23 | required this.privateKey, 24 | required this.githubToken, 25 | required this.repositorySlug, 26 | required this.manifestDir, 27 | required this.targets, 28 | this.androidSdkLocation, 29 | this.androidNdkVersion, 30 | this.androidMinSdkVersion, 31 | this.tempDir, 32 | }); 33 | 34 | final PrivateKey privateKey; 35 | final String githubToken; 36 | final RepositorySlug repositorySlug; 37 | final String manifestDir; 38 | final List targets; 39 | final String? androidSdkLocation; 40 | final String? androidNdkVersion; 41 | final int? androidMinSdkVersion; 42 | final String? tempDir; 43 | 44 | static String fileName(Target target, String name) { 45 | return '${target.rust}_$name'; 46 | } 47 | 48 | static String signatureFileName(Target target, String name) { 49 | return '${target.rust}_$name.sig'; 50 | } 51 | 52 | Future run() async { 53 | final crateInfo = CrateInfo.load(manifestDir); 54 | 55 | final targets = List.of(this.targets); 56 | if (targets.isEmpty) { 57 | targets.addAll([ 58 | ...Target.buildableTargets(), 59 | if (androidSdkLocation != null) ...Target.androidTargets(), 60 | ]); 61 | } 62 | 63 | _log.info('Precompiling binaries for $targets'); 64 | 65 | final hash = CrateHash.compute(manifestDir); 66 | _log.info('Computed crate hash: $hash'); 67 | 68 | final String tagName = 'precompiled_$hash'; 69 | 70 | final github = GitHub(auth: Authentication.withToken(githubToken)); 71 | final repo = github.repositories; 72 | final release = await _getOrCreateRelease( 73 | repo: repo, 74 | tagName: tagName, 75 | packageName: crateInfo.packageName, 76 | hash: hash, 77 | ); 78 | 79 | final tempDir = this.tempDir != null 80 | ? Directory(this.tempDir!) 81 | : Directory.systemTemp.createTempSync('precompiled_'); 82 | 83 | tempDir.createSync(recursive: true); 84 | 85 | final crateOptions = CargokitCrateOptions.load( 86 | manifestDir: manifestDir, 87 | ); 88 | 89 | final buildEnvironment = BuildEnvironment( 90 | configuration: BuildConfiguration.release, 91 | crateOptions: crateOptions, 92 | targetTempDir: tempDir.path, 93 | manifestDir: manifestDir, 94 | crateInfo: crateInfo, 95 | isAndroid: androidSdkLocation != null, 96 | androidSdkPath: androidSdkLocation, 97 | androidNdkVersion: androidNdkVersion, 98 | androidMinSdkVersion: androidMinSdkVersion, 99 | ); 100 | 101 | final rustup = Rustup(); 102 | 103 | for (final target in targets) { 104 | final artifactNames = getArtifactNames( 105 | target: target, 106 | libraryName: crateInfo.packageName, 107 | remote: true, 108 | ); 109 | 110 | if (artifactNames.every((name) { 111 | final fileName = PrecompileBinaries.fileName(target, name); 112 | return (release.assets ?? []).any((e) => e.name == fileName); 113 | })) { 114 | _log.info("All artifacts for $target already exist - skipping"); 115 | continue; 116 | } 117 | 118 | _log.info('Building for $target'); 119 | 120 | final builder = 121 | RustBuilder(target: target, environment: buildEnvironment); 122 | builder.prepare(rustup); 123 | final res = await builder.build(); 124 | 125 | final assets = []; 126 | for (final name in artifactNames) { 127 | final file = File(path.join(res, name)); 128 | if (!file.existsSync()) { 129 | throw Exception('Missing artifact: ${file.path}'); 130 | } 131 | 132 | final data = file.readAsBytesSync(); 133 | final create = CreateReleaseAsset( 134 | name: PrecompileBinaries.fileName(target, name), 135 | contentType: "application/octet-stream", 136 | assetData: data, 137 | ); 138 | final signature = sign(privateKey, data); 139 | final signatureCreate = CreateReleaseAsset( 140 | name: signatureFileName(target, name), 141 | contentType: "application/octet-stream", 142 | assetData: signature, 143 | ); 144 | bool verified = verify(public(privateKey), data, signature); 145 | if (!verified) { 146 | throw Exception('Signature verification failed'); 147 | } 148 | assets.add(create); 149 | assets.add(signatureCreate); 150 | } 151 | _log.info('Uploading assets: ${assets.map((e) => e.name)}'); 152 | for (final asset in assets) { 153 | // This seems to be failing on CI so do it one by one 154 | int retryCount = 0; 155 | while (true) { 156 | try { 157 | await repo.uploadReleaseAssets(release, [asset]); 158 | break; 159 | } on Exception catch (e) { 160 | if (retryCount == 10) { 161 | rethrow; 162 | } 163 | ++retryCount; 164 | _log.shout( 165 | 'Upload failed (attempt $retryCount, will retry): ${e.toString()}'); 166 | await Future.delayed(Duration(seconds: 2)); 167 | } 168 | } 169 | } 170 | } 171 | 172 | _log.info('Cleaning up'); 173 | tempDir.deleteSync(recursive: true); 174 | } 175 | 176 | Future _getOrCreateRelease({ 177 | required RepositoriesService repo, 178 | required String tagName, 179 | required String packageName, 180 | required String hash, 181 | }) async { 182 | Release release; 183 | try { 184 | _log.info('Fetching release $tagName'); 185 | release = await repo.getReleaseByTagName(repositorySlug, tagName); 186 | } on ReleaseNotFound { 187 | _log.info('Release not found - creating release $tagName'); 188 | release = await repo.createRelease( 189 | repositorySlug, 190 | CreateRelease.from( 191 | tagName: tagName, 192 | name: 'Precompiled binaries ${hash.substring(0, 8)}', 193 | targetCommitish: null, 194 | isDraft: false, 195 | isPrerelease: false, 196 | body: 'Precompiled binaries for crate $packageName, ' 197 | 'crate hash $hash.', 198 | )); 199 | } 200 | return release; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/rustup.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:collection/collection.dart'; 7 | import 'package:path/path.dart' as path; 8 | 9 | import 'util.dart'; 10 | 11 | class _Toolchain { 12 | _Toolchain( 13 | this.name, 14 | this.targets, 15 | ); 16 | 17 | final String name; 18 | final List targets; 19 | } 20 | 21 | class Rustup { 22 | List? installedTargets(String toolchain) { 23 | final targets = _installedTargets(toolchain); 24 | return targets != null ? List.unmodifiable(targets) : null; 25 | } 26 | 27 | void installToolchain(String toolchain) { 28 | log.info("Installing Rust toolchain: $toolchain"); 29 | runCommand("rustup", ['toolchain', 'install', toolchain]); 30 | _installedToolchains 31 | .add(_Toolchain(toolchain, _getInstalledTargets(toolchain))); 32 | } 33 | 34 | void installTarget( 35 | String target, { 36 | required String toolchain, 37 | }) { 38 | log.info("Installing Rust target: $target"); 39 | runCommand("rustup", [ 40 | 'target', 41 | 'add', 42 | '--toolchain', 43 | toolchain, 44 | target, 45 | ]); 46 | _installedTargets(toolchain)?.add(target); 47 | } 48 | 49 | final List<_Toolchain> _installedToolchains; 50 | 51 | Rustup() : _installedToolchains = _getInstalledToolchains(); 52 | 53 | List? _installedTargets(String toolchain) => _installedToolchains 54 | .firstWhereOrNull( 55 | (e) => e.name == toolchain || e.name.startsWith('$toolchain-')) 56 | ?.targets; 57 | 58 | static List<_Toolchain> _getInstalledToolchains() { 59 | String extractToolchainName(String line) { 60 | // ignore (default) after toolchain name 61 | final parts = line.split(' '); 62 | return parts[0]; 63 | } 64 | 65 | final res = runCommand("rustup", ['toolchain', 'list']); 66 | 67 | // To list all non-custom toolchains, we need to filter out lines that 68 | // don't start with "stable", "beta", or "nightly". 69 | Pattern nonCustom = RegExp(r"^(stable|beta|nightly)"); 70 | final lines = res.stdout 71 | .toString() 72 | .split('\n') 73 | .where((e) => e.isNotEmpty && e.startsWith(nonCustom)) 74 | .map(extractToolchainName) 75 | .toList(growable: true); 76 | 77 | return lines 78 | .map( 79 | (name) => _Toolchain( 80 | name, 81 | _getInstalledTargets(name), 82 | ), 83 | ) 84 | .toList(growable: true); 85 | } 86 | 87 | static List _getInstalledTargets(String toolchain) { 88 | final res = runCommand("rustup", [ 89 | 'target', 90 | 'list', 91 | '--toolchain', 92 | toolchain, 93 | '--installed', 94 | ]); 95 | final lines = res.stdout 96 | .toString() 97 | .split('\n') 98 | .where((e) => e.isNotEmpty) 99 | .toList(growable: true); 100 | return lines; 101 | } 102 | 103 | bool _didInstallRustSrcForNightly = false; 104 | 105 | void installRustSrcForNightly() { 106 | if (_didInstallRustSrcForNightly) { 107 | return; 108 | } 109 | // Useful for -Z build-std 110 | runCommand( 111 | "rustup", 112 | ['component', 'add', 'rust-src', '--toolchain', 'nightly'], 113 | ); 114 | _didInstallRustSrcForNightly = true; 115 | } 116 | 117 | static String? executablePath() { 118 | final envPath = Platform.environment['PATH']; 119 | final envPathSeparator = Platform.isWindows ? ';' : ':'; 120 | final home = Platform.isWindows 121 | ? Platform.environment['USERPROFILE'] 122 | : Platform.environment['HOME']; 123 | final paths = [ 124 | if (home != null) path.join(home, '.cargo', 'bin'), 125 | if (envPath != null) ...envPath.split(envPathSeparator), 126 | ]; 127 | for (final p in paths) { 128 | final rustup = Platform.isWindows ? 'rustup.exe' : 'rustup'; 129 | final rustupPath = path.join(p, rustup); 130 | if (File(rustupPath).existsSync()) { 131 | return rustupPath; 132 | } 133 | } 134 | return null; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/target.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:collection/collection.dart'; 7 | 8 | import 'util.dart'; 9 | 10 | class Target { 11 | Target({ 12 | required this.rust, 13 | this.flutter, 14 | this.android, 15 | this.androidMinSdkVersion, 16 | this.darwinPlatform, 17 | this.darwinArch, 18 | }); 19 | 20 | static final all = [ 21 | Target( 22 | rust: 'armv7-linux-androideabi', 23 | flutter: 'android-arm', 24 | android: 'armeabi-v7a', 25 | androidMinSdkVersion: 16, 26 | ), 27 | Target( 28 | rust: 'aarch64-linux-android', 29 | flutter: 'android-arm64', 30 | android: 'arm64-v8a', 31 | androidMinSdkVersion: 21, 32 | ), 33 | Target( 34 | rust: 'i686-linux-android', 35 | flutter: 'android-x86', 36 | android: 'x86', 37 | androidMinSdkVersion: 16, 38 | ), 39 | Target( 40 | rust: 'x86_64-linux-android', 41 | flutter: 'android-x64', 42 | android: 'x86_64', 43 | androidMinSdkVersion: 21, 44 | ), 45 | Target( 46 | rust: 'x86_64-pc-windows-msvc', 47 | flutter: 'windows-x64', 48 | ), 49 | Target( 50 | rust: 'x86_64-unknown-linux-gnu', 51 | flutter: 'linux-x64', 52 | ), 53 | Target( 54 | rust: 'aarch64-unknown-linux-gnu', 55 | flutter: 'linux-arm64', 56 | ), 57 | Target( 58 | rust: 'x86_64-apple-darwin', 59 | darwinPlatform: 'macosx', 60 | darwinArch: 'x86_64', 61 | ), 62 | Target( 63 | rust: 'aarch64-apple-darwin', 64 | darwinPlatform: 'macosx', 65 | darwinArch: 'arm64', 66 | ), 67 | Target( 68 | rust: 'aarch64-apple-ios', 69 | darwinPlatform: 'iphoneos', 70 | darwinArch: 'arm64', 71 | ), 72 | Target( 73 | rust: 'aarch64-apple-ios-sim', 74 | darwinPlatform: 'iphonesimulator', 75 | darwinArch: 'arm64', 76 | ), 77 | Target( 78 | rust: 'x86_64-apple-ios', 79 | darwinPlatform: 'iphonesimulator', 80 | darwinArch: 'x86_64', 81 | ), 82 | ]; 83 | 84 | static Target? forFlutterName(String flutterName) { 85 | return all.firstWhereOrNull((element) => element.flutter == flutterName); 86 | } 87 | 88 | static Target? forDarwin({ 89 | required String platformName, 90 | required String darwinAarch, 91 | }) { 92 | return all.firstWhereOrNull((element) => // 93 | element.darwinPlatform == platformName && 94 | element.darwinArch == darwinAarch); 95 | } 96 | 97 | static Target? forRustTriple(String triple) { 98 | return all.firstWhereOrNull((element) => element.rust == triple); 99 | } 100 | 101 | static List androidTargets() { 102 | return all 103 | .where((element) => element.android != null) 104 | .toList(growable: false); 105 | } 106 | 107 | /// Returns buildable targets on current host platform ignoring Android targets. 108 | static List buildableTargets() { 109 | if (Platform.isLinux) { 110 | // Right now we don't support cross-compiling on Linux. So we just return 111 | // the host target. 112 | final arch = runCommand('arch', []).stdout as String; 113 | if (arch.trim() == 'aarch64') { 114 | return [Target.forRustTriple('aarch64-unknown-linux-gnu')!]; 115 | } else { 116 | return [Target.forRustTriple('x86_64-unknown-linux-gnu')!]; 117 | } 118 | } 119 | return all.where((target) { 120 | if (Platform.isWindows) { 121 | return target.rust.contains('-windows-'); 122 | } else if (Platform.isMacOS) { 123 | return target.darwinPlatform != null; 124 | } 125 | return false; 126 | }).toList(growable: false); 127 | } 128 | 129 | @override 130 | String toString() { 131 | return rust; 132 | } 133 | 134 | final String? flutter; 135 | final String rust; 136 | final String? android; 137 | final int? androidMinSdkVersion; 138 | final String? darwinPlatform; 139 | final String? darwinArch; 140 | } 141 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/util.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:convert'; 5 | import 'dart:io'; 6 | 7 | import 'package:logging/logging.dart'; 8 | import 'package:path/path.dart' as path; 9 | 10 | import 'logging.dart'; 11 | import 'rustup.dart'; 12 | 13 | final log = Logger("process"); 14 | 15 | class CommandFailedException implements Exception { 16 | final String executable; 17 | final List arguments; 18 | final ProcessResult result; 19 | 20 | CommandFailedException({ 21 | required this.executable, 22 | required this.arguments, 23 | required this.result, 24 | }); 25 | 26 | @override 27 | String toString() { 28 | final stdout = result.stdout.toString().trim(); 29 | final stderr = result.stderr.toString().trim(); 30 | return [ 31 | "External Command: $executable ${arguments.map((e) => '"$e"').join(' ')}", 32 | "Returned Exit Code: ${result.exitCode}", 33 | kSeparator, 34 | "STDOUT:", 35 | if (stdout.isNotEmpty) stdout, 36 | kSeparator, 37 | "STDERR:", 38 | if (stderr.isNotEmpty) stderr, 39 | ].join('\n'); 40 | } 41 | } 42 | 43 | class TestRunCommandArgs { 44 | final String executable; 45 | final List arguments; 46 | final String? workingDirectory; 47 | final Map? environment; 48 | final bool includeParentEnvironment; 49 | final bool runInShell; 50 | final Encoding? stdoutEncoding; 51 | final Encoding? stderrEncoding; 52 | 53 | TestRunCommandArgs({ 54 | required this.executable, 55 | required this.arguments, 56 | this.workingDirectory, 57 | this.environment, 58 | this.includeParentEnvironment = true, 59 | this.runInShell = false, 60 | this.stdoutEncoding, 61 | this.stderrEncoding, 62 | }); 63 | } 64 | 65 | class TestRunCommandResult { 66 | TestRunCommandResult({ 67 | this.pid = 1, 68 | this.exitCode = 0, 69 | this.stdout = '', 70 | this.stderr = '', 71 | }); 72 | 73 | final int pid; 74 | final int exitCode; 75 | final String stdout; 76 | final String stderr; 77 | } 78 | 79 | TestRunCommandResult Function(TestRunCommandArgs args)? testRunCommandOverride; 80 | 81 | ProcessResult runCommand( 82 | String executable, 83 | List arguments, { 84 | String? workingDirectory, 85 | Map? environment, 86 | bool includeParentEnvironment = true, 87 | bool runInShell = false, 88 | Encoding? stdoutEncoding = systemEncoding, 89 | Encoding? stderrEncoding = systemEncoding, 90 | }) { 91 | if (testRunCommandOverride != null) { 92 | final result = testRunCommandOverride!(TestRunCommandArgs( 93 | executable: executable, 94 | arguments: arguments, 95 | workingDirectory: workingDirectory, 96 | environment: environment, 97 | includeParentEnvironment: includeParentEnvironment, 98 | runInShell: runInShell, 99 | stdoutEncoding: stdoutEncoding, 100 | stderrEncoding: stderrEncoding, 101 | )); 102 | return ProcessResult( 103 | result.pid, 104 | result.exitCode, 105 | result.stdout, 106 | result.stderr, 107 | ); 108 | } 109 | log.finer('Running command $executable ${arguments.join(' ')}'); 110 | final res = Process.runSync( 111 | _resolveExecutable(executable), 112 | arguments, 113 | workingDirectory: workingDirectory, 114 | environment: environment, 115 | includeParentEnvironment: includeParentEnvironment, 116 | runInShell: runInShell, 117 | stderrEncoding: stderrEncoding, 118 | stdoutEncoding: stdoutEncoding, 119 | ); 120 | if (res.exitCode != 0) { 121 | throw CommandFailedException( 122 | executable: executable, 123 | arguments: arguments, 124 | result: res, 125 | ); 126 | } else { 127 | return res; 128 | } 129 | } 130 | 131 | class RustupNotFoundException implements Exception { 132 | @override 133 | String toString() { 134 | return [ 135 | ' ', 136 | 'rustup not found in PATH.', 137 | ' ', 138 | 'Maybe you need to install Rust? It only takes a minute:', 139 | ' ', 140 | if (Platform.isWindows) 'https://www.rust-lang.org/tools/install', 141 | if (hasHomebrewRustInPath()) ...[ 142 | '\$ brew unlink rust # Unlink homebrew Rust from PATH', 143 | ], 144 | if (!Platform.isWindows) 145 | "\$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", 146 | ' ', 147 | ].join('\n'); 148 | } 149 | 150 | static bool hasHomebrewRustInPath() { 151 | if (!Platform.isMacOS) { 152 | return false; 153 | } 154 | final envPath = Platform.environment['PATH'] ?? ''; 155 | final paths = envPath.split(':'); 156 | return paths.any((p) { 157 | return p.contains('homebrew') && File(path.join(p, 'rustc')).existsSync(); 158 | }); 159 | } 160 | } 161 | 162 | String _resolveExecutable(String executable) { 163 | if (executable == 'rustup') { 164 | final resolved = Rustup.executablePath(); 165 | if (resolved != null) { 166 | return resolved; 167 | } 168 | throw RustupNotFoundException(); 169 | } else { 170 | return executable; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /cargokit/build_tool/lib/src/verify_binaries.dart: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import 'dart:io'; 5 | 6 | import 'package:ed25519_edwards/ed25519_edwards.dart'; 7 | import 'package:http/http.dart'; 8 | 9 | import 'artifacts_provider.dart'; 10 | import 'cargo.dart'; 11 | import 'crate_hash.dart'; 12 | import 'options.dart'; 13 | import 'precompile_binaries.dart'; 14 | import 'target.dart'; 15 | 16 | class VerifyBinaries { 17 | VerifyBinaries({ 18 | required this.manifestDir, 19 | }); 20 | 21 | final String manifestDir; 22 | 23 | Future run() async { 24 | final crateInfo = CrateInfo.load(manifestDir); 25 | 26 | final config = CargokitCrateOptions.load(manifestDir: manifestDir); 27 | final precompiledBinaries = config.precompiledBinaries; 28 | if (precompiledBinaries == null) { 29 | stdout.writeln('Crate does not support precompiled binaries.'); 30 | } else { 31 | final crateHash = CrateHash.compute(manifestDir); 32 | stdout.writeln('Crate hash: $crateHash'); 33 | 34 | for (final target in Target.all) { 35 | final message = 'Checking ${target.rust}...'; 36 | stdout.write(message.padRight(40)); 37 | stdout.flush(); 38 | 39 | final artifacts = getArtifactNames( 40 | target: target, 41 | libraryName: crateInfo.packageName, 42 | remote: true, 43 | ); 44 | 45 | final prefix = precompiledBinaries.uriPrefix; 46 | 47 | bool ok = true; 48 | 49 | for (final artifact in artifacts) { 50 | final fileName = PrecompileBinaries.fileName(target, artifact); 51 | final signatureFileName = 52 | PrecompileBinaries.signatureFileName(target, artifact); 53 | 54 | final url = Uri.parse('$prefix$crateHash/$fileName'); 55 | final signatureUrl = 56 | Uri.parse('$prefix$crateHash/$signatureFileName'); 57 | 58 | final signature = await get(signatureUrl); 59 | if (signature.statusCode != 200) { 60 | stdout.writeln('MISSING'); 61 | ok = false; 62 | break; 63 | } 64 | final asset = await get(url); 65 | if (asset.statusCode != 200) { 66 | stdout.writeln('MISSING'); 67 | ok = false; 68 | break; 69 | } 70 | 71 | if (!verify(precompiledBinaries.publicKey, asset.bodyBytes, 72 | signature.bodyBytes)) { 73 | stdout.writeln('INVALID SIGNATURE'); 74 | ok = false; 75 | } 76 | } 77 | 78 | if (ok) { 79 | stdout.writeln('OK'); 80 | } 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /cargokit/build_tool/pubspec.yaml: -------------------------------------------------------------------------------- 1 | # This is copied from Cargokit (which is the official way to use it currently) 2 | # Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | name: build_tool 5 | description: Cargokit build_tool. Facilitates the build of Rust crate during Flutter application build. 6 | publish_to: none 7 | version: 1.0.0 8 | 9 | environment: 10 | sdk: ">=3.0.0 <4.0.0" 11 | 12 | # Add regular dependencies here. 13 | dependencies: 14 | # these are pinned on purpose because the bundle_tool_runner doesn't have 15 | # pubspec.lock. See run_build_tool.sh 16 | logging: 1.2.0 17 | path: 1.8.0 18 | version: 3.0.0 19 | collection: 1.18.0 20 | ed25519_edwards: 0.3.1 21 | hex: 0.2.0 22 | yaml: 3.1.2 23 | source_span: 1.10.0 24 | github: 9.17.0 25 | args: 2.4.2 26 | crypto: 3.0.3 27 | convert: 3.1.1 28 | http: 1.1.0 29 | toml: 0.14.0 30 | 31 | dev_dependencies: 32 | lints: ^2.1.0 33 | test: ^1.24.0 34 | -------------------------------------------------------------------------------- /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 | execute_process(COMMAND chmod +x "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}") 54 | endif() 55 | 56 | # Using generators in custom command is only supported in CMake 3.20+ 57 | if (CMAKE_CONFIGURATION_TYPES AND ${CMAKE_VERSION} VERSION_LESS "3.20.0") 58 | foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES) 59 | add_custom_command( 60 | OUTPUT 61 | "${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}/${CARGOKIT_LIB_FULL_NAME}" 62 | "${CMAKE_CURRENT_BINARY_DIR}/_phony_" 63 | COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} 64 | "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake 65 | VERBATIM 66 | ) 67 | endforeach() 68 | else() 69 | add_custom_command( 70 | OUTPUT 71 | ${OUTPUT_LIB} 72 | "${CMAKE_CURRENT_BINARY_DIR}/_phony_" 73 | COMMAND ${CMAKE_COMMAND} -E env ${CARGOKIT_ENV} 74 | "${cargokit_cmake_root}/run_build_tool${SCRIPT_EXTENSION}" build-cmake 75 | VERBATIM 76 | ) 77 | endif() 78 | 79 | 80 | set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/_phony_" PROPERTIES SYMBOLIC TRUE) 81 | 82 | if (TARGET ${target}) 83 | # If we have actual cmake target provided create target and make existing 84 | # target depend on it 85 | add_custom_target("${target}_cargokit" DEPENDS ${OUTPUT_LIB}) 86 | add_dependencies("${target}" "${target}_cargokit") 87 | target_link_libraries("${target}" PRIVATE "${OUTPUT_LIB}${IMPORT_LIB_EXTENSION}") 88 | if(WIN32) 89 | target_link_options(${target} PRIVATE "/INCLUDE:${any_symbol_name}") 90 | endif() 91 | else() 92 | # Otherwise (FFI) just use ALL to force building always 93 | add_custom_target("${target}_cargokit" ALL DEPENDS ${OUTPUT_LIB}) 94 | endif() 95 | 96 | # Allow adding the output library to plugin bundled libraries 97 | set("${target}_cargokit_lib" ${OUTPUT_LIB} PARENT_SCOPE) 98 | 99 | endfunction() 100 | -------------------------------------------------------------------------------- /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/gradle/plugin.gradle: -------------------------------------------------------------------------------- 1 | /// This is copied from Cargokit (which is the official way to use it currently) 2 | /// Details: https://fzyzcjy.github.io/flutter_rust_bridge/manual/integrate/builtin 3 | 4 | import java.nio.file.Paths 5 | import org.apache.tools.ant.taskdefs.condition.Os 6 | 7 | CargoKitPlugin.file = buildscript.sourceFile 8 | 9 | apply plugin: CargoKitPlugin 10 | 11 | class CargoKitExtension { 12 | String manifestDir; // Relative path to folder containing Cargo.toml 13 | String libname; // Library name within Cargo.toml. Must be a cdylib 14 | } 15 | 16 | abstract class CargoKitBuildTask extends DefaultTask { 17 | 18 | @Input 19 | String buildMode 20 | 21 | @Input 22 | String buildDir 23 | 24 | @Input 25 | String outputDir 26 | 27 | @Input 28 | String ndkVersion 29 | 30 | @Input 31 | String sdkDirectory 32 | 33 | @Input 34 | int compileSdkVersion; 35 | 36 | @Input 37 | int minSdkVersion; 38 | 39 | @Input 40 | String pluginFile 41 | 42 | @Input 43 | List targetPlatforms 44 | 45 | @TaskAction 46 | def build() { 47 | if (project.cargokit.manifestDir == null) { 48 | throw new GradleException("Property 'manifestDir' must be set on cargokit extension"); 49 | } 50 | 51 | if (project.cargokit.libname == null) { 52 | throw new GradleException("Property 'libname' must be set on cargokit extension"); 53 | } 54 | 55 | def executableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "run_build_tool.cmd" : "run_build_tool.sh" 56 | def path = Paths.get(new File(pluginFile).parent, "..", executableName); 57 | 58 | def manifestDir = Paths.get(project.buildscript.sourceFile.parent, project.cargokit.manifestDir) 59 | 60 | def rootProjectDir = project.rootProject.projectDir 61 | 62 | if (!Os.isFamily(Os.FAMILY_WINDOWS)) { 63 | project.exec { 64 | commandLine 'chmod', '+x', path 65 | } 66 | } 67 | 68 | project.exec { 69 | executable path 70 | args "build-gradle" 71 | environment "CARGOKIT_ROOT_PROJECT_DIR", rootProjectDir 72 | environment "CARGOKIT_TOOL_TEMP_DIR", "${buildDir}/build_tool" 73 | environment "CARGOKIT_MANIFEST_DIR", manifestDir 74 | environment "CARGOKIT_CONFIGURATION", buildMode 75 | environment "CARGOKIT_TARGET_TEMP_DIR", buildDir 76 | environment "CARGOKIT_OUTPUT_DIR", outputDir 77 | environment "CARGOKIT_NDK_VERSION", ndkVersion 78 | environment "CARGOKIT_SDK_DIR", sdkDirectory 79 | environment "CARGOKIT_COMPILE_SDK_VERSION", compileSdkVersion 80 | environment "CARGOKIT_MIN_SDK_VERSION", minSdkVersion 81 | environment "CARGOKIT_TARGET_PLATFORMS", targetPlatforms.join(",") 82 | environment "CARGOKIT_JAVA_HOME", System.properties['java.home'] 83 | } 84 | } 85 | } 86 | 87 | class CargoKitPlugin implements Plugin { 88 | 89 | static String file; 90 | 91 | private Plugin findFlutterPlugin(Project rootProject) { 92 | _findFlutterPlugin(rootProject.childProjects) 93 | } 94 | 95 | private Plugin _findFlutterPlugin(Map projects) { 96 | for (project in projects) { 97 | for (plugin in project.value.getPlugins()) { 98 | if (plugin.class.name == "FlutterPlugin") { 99 | return plugin; 100 | } 101 | } 102 | def plugin = _findFlutterPlugin(project.value.childProjects); 103 | if (plugin != null) { 104 | return plugin; 105 | } 106 | } 107 | return null; 108 | } 109 | 110 | @Override 111 | void apply(Project project) { 112 | def plugin = findFlutterPlugin(project.rootProject); 113 | 114 | project.extensions.create("cargokit", CargoKitExtension) 115 | 116 | if (plugin == null) { 117 | print("Flutter plugin not found, CargoKit plugin will not be applied.") 118 | return; 119 | } 120 | 121 | def cargoBuildDir = "${project.buildDir}/build" 122 | 123 | // Determine if the project is an application or library 124 | def isApplication = plugin.project.plugins.hasPlugin('com.android.application') 125 | def variants = isApplication ? plugin.project.android.applicationVariants : plugin.project.android.libraryVariants 126 | 127 | variants.all { variant -> 128 | 129 | final buildType = variant.buildType.name 130 | 131 | def cargoOutputDir = "${project.buildDir}/jniLibs/${buildType}"; 132 | def jniLibs = project.android.sourceSets.maybeCreate(buildType).jniLibs; 133 | jniLibs.srcDir(new File(cargoOutputDir)) 134 | 135 | def platforms = plugin.getTargetPlatforms().collect() 136 | 137 | // Same thing addFlutterDependencies does in flutter.gradle 138 | if (buildType == "debug") { 139 | platforms.add("android-x86") 140 | platforms.add("android-x64") 141 | } 142 | 143 | // The task name depends on plugin properties, which are not available 144 | // at this point 145 | project.getGradle().afterProject { 146 | def taskName = "cargokitCargoBuild${project.cargokit.libname.capitalize()}${buildType.capitalize()}"; 147 | 148 | if (project.tasks.findByName(taskName)) { 149 | return 150 | } 151 | 152 | if (plugin.project.android.ndkVersion == null) { 153 | throw new GradleException("Please set 'android.ndkVersion' in 'app/build.gradle'.") 154 | } 155 | 156 | def task = project.tasks.create(taskName, CargoKitBuildTask.class) { 157 | buildMode = variant.buildType.name 158 | buildDir = cargoBuildDir 159 | outputDir = cargoOutputDir 160 | ndkVersion = plugin.project.android.ndkVersion 161 | sdkDirectory = plugin.project.android.sdkDirectory 162 | minSdkVersion = plugin.project.android.defaultConfig.minSdkVersion.apiLevel as int 163 | compileSdkVersion = plugin.project.android.compileSdkVersion.substring(8) as int 164 | targetPlatforms = platforms 165 | pluginFile = CargoKitPlugin.file 166 | } 167 | def onTask = { newTask -> 168 | if (newTask.name == "merge${buildType.capitalize()}NativeLibs") { 169 | newTask.dependsOn task 170 | // Fix gradle 7.4.2 not picking up JNI library changes 171 | newTask.outputs.upToDateWhen { false } 172 | } 173 | } 174 | project.tasks.each onTask 175 | project.tasks.whenTaskAdded onTask 176 | } 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /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 /D "%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 | echo Running pub get in "%cd%" 80 | "%DART%" pub get --no-precompile 81 | "%DART%" compile kernel bin/build_tool_runner.dart 82 | ) 83 | 84 | "%DART%" "%PRECOMPILED%" %* 85 | 86 | REM 253 means invalid snapshot version. 87 | If %ERRORLEVEL% equ 253 ( 88 | "%DART%" pub get --no-precompile 89 | "%DART%" compile kernel bin/build_tool_runner.dart 90 | "%DART%" "%PRECOMPILED%" %* 91 | ) 92 | -------------------------------------------------------------------------------- /cargokit/run_build_tool.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env 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 | # Create alias for `shasum` if it does not exist and `sha1sum` exists 46 | if ! [ -x "$(command -v shasum)" ] && [ -x "$(command -v sha1sum)" ]; then 47 | shopt -s expand_aliases 48 | alias shasum="sha1sum" 49 | fi 50 | 51 | # Dart run will not cache any package that has a path dependency, which 52 | # is the case for our build_tool_runner. So instead we precompile the package 53 | # ourselves. 54 | # To invalidate the cached kernel we use the hash of ls -LR of the build_tool 55 | # package directory. This should be good enough, as the build_tool package 56 | # itself is not meant to have any path dependencies. 57 | 58 | if [[ "$OSTYPE" == "darwin"* ]]; then 59 | PACKAGE_HASH=$(ls -lTR "$BUILD_TOOL_PKG_DIR" | shasum) 60 | else 61 | PACKAGE_HASH=$(ls -lR --full-time "$BUILD_TOOL_PKG_DIR" | shasum) 62 | fi 63 | 64 | PACKAGE_HASH_FILE=".package_hash" 65 | 66 | if [ -f "$PACKAGE_HASH_FILE" ]; then 67 | EXISTING_HASH=$(cat "$PACKAGE_HASH_FILE") 68 | if [ "$PACKAGE_HASH" != "$EXISTING_HASH" ]; then 69 | rm "$PACKAGE_HASH_FILE" 70 | fi 71 | fi 72 | 73 | # Run pub get if needed. 74 | if [ ! -f "$PACKAGE_HASH_FILE" ]; then 75 | "$DART" pub get --no-precompile 76 | "$DART" compile kernel bin/build_tool_runner.dart 77 | echo "$PACKAGE_HASH" > "$PACKAGE_HASH_FILE" 78 | fi 79 | 80 | set +e 81 | 82 | "$DART" bin/build_tool_runner.dill "$@" 83 | 84 | exit_code=$? 85 | 86 | # 253 means invalid snapshot version. 87 | if [ $exit_code == 253 ]; then 88 | "$DART" pub get --no-precompile 89 | "$DART" compile kernel bin/build_tool_runner.dart 90 | "$DART" bin/build_tool_runner.dill "$@" 91 | exit_code=$? 92 | fi 93 | 94 | exit $exit_code 95 | -------------------------------------------------------------------------------- /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 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Symbolication related 35 | app.*.symbols 36 | 37 | # Obfuscation related 38 | app.*.map.json 39 | 40 | # Android Studio will place build artifacts here 41 | /android/app/debug 42 | /android/app/profile 43 | /android/app/release 44 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # velopack_flutter_example 2 | 3 | Demonstrates how to use the velopack_flutter 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/integration_test/simple_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:integration_test/integration_test.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:velopack_flutter/velopack_flutter.dart'; 4 | 5 | void main() { 6 | IntegrationTestWidgetsFlutterBinding.ensureInitialized(); 7 | setUpAll(() async => await RustLib.init()); 8 | } 9 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:velopack_flutter/velopack_flutter.dart'; 3 | 4 | Future main() async { 5 | await RustLib.init(); 6 | runApp(const MyApp()); 7 | } 8 | 9 | class MyApp extends StatelessWidget { 10 | const MyApp({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return MaterialApp( 15 | home: Scaffold( 16 | appBar: AppBar(title: const Text('flutter_rust_bridge quickstart')), 17 | body: Center( 18 | child: FutureBuilder( 19 | future: isUpdateAvailable(url: 'https://test.com'), 20 | builder: (context, snap) => 21 | Text('Update is available: ${snap.data}'), 22 | ), 23 | ), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /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 "velopack_flutter_example") 8 | # The unique GTK application identifier for this application. See: 9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID 10 | set(APPLICATION_ID "com.example.velopack_flutter") 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 | # Copy the native assets provided by the build.dart from all packages. 127 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") 128 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 129 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 130 | COMPONENT Runtime) 131 | 132 | # Fully re-copy the assets directory on each build to avoid having stale files 133 | # from a previous install. 134 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 135 | install(CODE " 136 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 137 | " COMPONENT Runtime) 138 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 139 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 140 | 141 | # Install the AOT library on non-Debug builds only. 142 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 143 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 144 | COMPONENT Runtime) 145 | endif() 146 | -------------------------------------------------------------------------------- /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 | velopack_flutter 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, "velopack_flutter_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, "velopack_flutter_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 GApplication::startup. 85 | static void my_application_startup(GApplication* application) { 86 | //MyApplication* self = MY_APPLICATION(object); 87 | 88 | // Perform any actions required at application startup. 89 | 90 | G_APPLICATION_CLASS(my_application_parent_class)->startup(application); 91 | } 92 | 93 | // Implements GApplication::shutdown. 94 | static void my_application_shutdown(GApplication* application) { 95 | //MyApplication* self = MY_APPLICATION(object); 96 | 97 | // Perform any actions required at application shutdown. 98 | 99 | G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); 100 | } 101 | 102 | // Implements GObject::dispose. 103 | static void my_application_dispose(GObject* object) { 104 | MyApplication* self = MY_APPLICATION(object); 105 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 106 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 107 | } 108 | 109 | static void my_application_class_init(MyApplicationClass* klass) { 110 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 111 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 112 | G_APPLICATION_CLASS(klass)->startup = my_application_startup; 113 | G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; 114 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 115 | } 116 | 117 | static void my_application_init(MyApplication* self) {} 118 | 119 | MyApplication* my_application_new() { 120 | return MY_APPLICATION(g_object_new(my_application_get_type(), 121 | "application-id", APPLICATION_ID, 122 | "flags", G_APPLICATION_NON_UNIQUE, 123 | nullptr)); 124 | } 125 | -------------------------------------------------------------------------------- /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 | - velopack_flutter (0.0.1): 4 | - FlutterMacOS 5 | 6 | DEPENDENCIES: 7 | - FlutterMacOS (from `Flutter/ephemeral`) 8 | - velopack_flutter (from `Flutter/ephemeral/.symlinks/plugins/velopack_flutter/macos`) 9 | 10 | EXTERNAL SOURCES: 11 | FlutterMacOS: 12 | :path: Flutter/ephemeral 13 | velopack_flutter: 14 | :path: Flutter/ephemeral/.symlinks/plugins/velopack_flutter/macos 15 | 16 | SPEC CHECKSUMS: 17 | FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 18 | velopack_flutter: 9d8a62354a23c75157e37a66ef691a47b4fe0e92 19 | 20 | PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 21 | 22 | COCOAPODS: 1.13.0 23 | -------------------------------------------------------------------------------- /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/GigaDroid/velopack_flutter/be50abcf73f279373c99802681189b8c5751739f/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/GigaDroid/velopack_flutter/be50abcf73f279373c99802681189b8c5751739f/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/GigaDroid/velopack_flutter/be50abcf73f279373c99802681189b8c5751739f/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/GigaDroid/velopack_flutter/be50abcf73f279373c99802681189b8c5751739f/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/GigaDroid/velopack_flutter/be50abcf73f279373c99802681189b8c5751739f/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/GigaDroid/velopack_flutter/be50abcf73f279373c99802681189b8c5751739f/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/GigaDroid/velopack_flutter/be50abcf73f279373c99802681189b8c5751739f/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 = velopack_flutter_example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.velopackFlutterExample 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2024 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 Cocoa 2 | import FlutterMacOS 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.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | args: 5 | dependency: transitive 6 | description: 7 | name: args 8 | sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.5.0" 12 | async: 13 | dependency: transitive 14 | description: 15 | name: async 16 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.11.0" 20 | boolean_selector: 21 | dependency: transitive 22 | description: 23 | name: boolean_selector 24 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.1.1" 28 | build_cli_annotations: 29 | dependency: transitive 30 | description: 31 | name: build_cli_annotations 32 | sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "2.1.0" 36 | characters: 37 | dependency: transitive 38 | description: 39 | name: characters 40 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.3.0" 44 | clock: 45 | dependency: transitive 46 | description: 47 | name: clock 48 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.1.1" 52 | collection: 53 | dependency: transitive 54 | description: 55 | name: collection 56 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.18.0" 60 | cupertino_icons: 61 | dependency: "direct main" 62 | description: 63 | name: cupertino_icons 64 | sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.0.8" 68 | fake_async: 69 | dependency: transitive 70 | description: 71 | name: fake_async 72 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "1.3.1" 76 | file: 77 | dependency: transitive 78 | description: 79 | name: file 80 | sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "7.0.0" 84 | flutter: 85 | dependency: "direct main" 86 | description: flutter 87 | source: sdk 88 | version: "0.0.0" 89 | flutter_driver: 90 | dependency: transitive 91 | description: flutter 92 | source: sdk 93 | version: "0.0.0" 94 | flutter_lints: 95 | dependency: "direct dev" 96 | description: 97 | name: flutter_lints 98 | sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" 99 | url: "https://pub.dev" 100 | source: hosted 101 | version: "3.0.2" 102 | flutter_rust_bridge: 103 | dependency: transitive 104 | description: 105 | name: flutter_rust_bridge 106 | sha256: fb9d3c9395eae3c71d4fe3ec343b9f30636c9988150c8bb33b60047549b34e3d 107 | url: "https://pub.dev" 108 | source: hosted 109 | version: "2.6.0" 110 | flutter_test: 111 | dependency: "direct dev" 112 | description: flutter 113 | source: sdk 114 | version: "0.0.0" 115 | fuchsia_remote_debug_protocol: 116 | dependency: transitive 117 | description: flutter 118 | source: sdk 119 | version: "0.0.0" 120 | integration_test: 121 | dependency: "direct dev" 122 | description: flutter 123 | source: sdk 124 | version: "0.0.0" 125 | leak_tracker: 126 | dependency: transitive 127 | description: 128 | name: leak_tracker 129 | sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" 130 | url: "https://pub.dev" 131 | source: hosted 132 | version: "10.0.5" 133 | leak_tracker_flutter_testing: 134 | dependency: transitive 135 | description: 136 | name: leak_tracker_flutter_testing 137 | sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" 138 | url: "https://pub.dev" 139 | source: hosted 140 | version: "3.0.5" 141 | leak_tracker_testing: 142 | dependency: transitive 143 | description: 144 | name: leak_tracker_testing 145 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 146 | url: "https://pub.dev" 147 | source: hosted 148 | version: "3.0.1" 149 | lints: 150 | dependency: transitive 151 | description: 152 | name: lints 153 | sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 154 | url: "https://pub.dev" 155 | source: hosted 156 | version: "3.0.0" 157 | matcher: 158 | dependency: transitive 159 | description: 160 | name: matcher 161 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 162 | url: "https://pub.dev" 163 | source: hosted 164 | version: "0.12.16+1" 165 | material_color_utilities: 166 | dependency: transitive 167 | description: 168 | name: material_color_utilities 169 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 170 | url: "https://pub.dev" 171 | source: hosted 172 | version: "0.11.1" 173 | meta: 174 | dependency: transitive 175 | description: 176 | name: meta 177 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 178 | url: "https://pub.dev" 179 | source: hosted 180 | version: "1.15.0" 181 | path: 182 | dependency: transitive 183 | description: 184 | name: path 185 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 186 | url: "https://pub.dev" 187 | source: hosted 188 | version: "1.9.0" 189 | platform: 190 | dependency: transitive 191 | description: 192 | name: platform 193 | sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" 194 | url: "https://pub.dev" 195 | source: hosted 196 | version: "3.1.5" 197 | plugin_platform_interface: 198 | dependency: transitive 199 | description: 200 | name: plugin_platform_interface 201 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 202 | url: "https://pub.dev" 203 | source: hosted 204 | version: "2.1.8" 205 | process: 206 | dependency: transitive 207 | description: 208 | name: process 209 | sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" 210 | url: "https://pub.dev" 211 | source: hosted 212 | version: "5.0.2" 213 | sky_engine: 214 | dependency: transitive 215 | description: flutter 216 | source: sdk 217 | version: "0.0.99" 218 | source_span: 219 | dependency: transitive 220 | description: 221 | name: source_span 222 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 223 | url: "https://pub.dev" 224 | source: hosted 225 | version: "1.10.0" 226 | stack_trace: 227 | dependency: transitive 228 | description: 229 | name: stack_trace 230 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 231 | url: "https://pub.dev" 232 | source: hosted 233 | version: "1.11.1" 234 | stream_channel: 235 | dependency: transitive 236 | description: 237 | name: stream_channel 238 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 239 | url: "https://pub.dev" 240 | source: hosted 241 | version: "2.1.2" 242 | string_scanner: 243 | dependency: transitive 244 | description: 245 | name: string_scanner 246 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 247 | url: "https://pub.dev" 248 | source: hosted 249 | version: "1.2.0" 250 | sync_http: 251 | dependency: transitive 252 | description: 253 | name: sync_http 254 | sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" 255 | url: "https://pub.dev" 256 | source: hosted 257 | version: "0.3.1" 258 | term_glyph: 259 | dependency: transitive 260 | description: 261 | name: term_glyph 262 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 263 | url: "https://pub.dev" 264 | source: hosted 265 | version: "1.2.1" 266 | test_api: 267 | dependency: transitive 268 | description: 269 | name: test_api 270 | sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" 271 | url: "https://pub.dev" 272 | source: hosted 273 | version: "0.7.2" 274 | vector_math: 275 | dependency: transitive 276 | description: 277 | name: vector_math 278 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 279 | url: "https://pub.dev" 280 | source: hosted 281 | version: "2.1.4" 282 | velopack_flutter: 283 | dependency: "direct main" 284 | description: 285 | path: ".." 286 | relative: true 287 | source: path 288 | version: "0.1.0" 289 | vm_service: 290 | dependency: transitive 291 | description: 292 | name: vm_service 293 | sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" 294 | url: "https://pub.dev" 295 | source: hosted 296 | version: "14.2.5" 297 | web: 298 | dependency: transitive 299 | description: 300 | name: web 301 | sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" 302 | url: "https://pub.dev" 303 | source: hosted 304 | version: "0.5.1" 305 | webdriver: 306 | dependency: transitive 307 | description: 308 | name: webdriver 309 | sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" 310 | url: "https://pub.dev" 311 | source: hosted 312 | version: "3.0.3" 313 | sdks: 314 | dart: ">=3.4.0 <4.0.0" 315 | flutter: ">=3.18.0-18.0.pre.54" 316 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: velopack_flutter_example 2 | description: "Demonstrates how to use the velopack_flutter 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.3.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 | velopack_flutter: 35 | # When depending on this package from a real application you should use: 36 | # velopack_flutter: ^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.6 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: ^3.0.0 56 | integration_test: 57 | sdk: flutter 58 | 59 | # For information on the generic Dart part of this file, see the 60 | # following page: https://dart.dev/tools/pub/pubspec 61 | 62 | # The following section is specific to Flutter packages. 63 | flutter: 64 | 65 | # The following line ensures that the Material Icons font is 66 | # included with your application, so that you can use the icons in 67 | # the material Icons class. 68 | uses-material-design: true 69 | 70 | # To add assets to your application, add an assets section, like this: 71 | # assets: 72 | # - images/a_dot_burr.jpeg 73 | # - images/a_dot_ham.jpeg 74 | 75 | # An image asset can refer to one or more resolution-specific "variants", see 76 | # https://flutter.dev/assets-and-images/#resolution-aware 77 | 78 | # For details regarding adding assets from package dependencies, see 79 | # https://flutter.dev/assets-and-images/#from-packages 80 | 81 | # To add custom fonts to your application, add a fonts section here, 82 | # in this "flutter" section. Each entry in this list should have a 83 | # "family" key with the font family name, and a "fonts" key with a 84 | # list giving the asset and other descriptors for the font. For 85 | # example: 86 | # fonts: 87 | # - family: Schyler 88 | # fonts: 89 | # - asset: fonts/Schyler-Regular.ttf 90 | # - asset: fonts/Schyler-Italic.ttf 91 | # style: italic 92 | # - family: Trajan Pro 93 | # fonts: 94 | # - asset: fonts/TrajanPro.ttf 95 | # - asset: fonts/TrajanPro_Bold.ttf 96 | # weight: 700 97 | # 98 | # For details regarding fonts from package dependencies, 99 | # see https://flutter.dev/custom-fonts/#from-packages 100 | -------------------------------------------------------------------------------- /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(velopack_flutter_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 "velopack_flutter_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 | # Copy the native assets provided by the build.dart from all packages. 91 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") 92 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 93 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 94 | COMPONENT Runtime) 95 | 96 | # Fully re-copy the assets directory on each build to avoid having stale files 97 | # from a previous install. 98 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 99 | install(CODE " 100 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 101 | " COMPONENT Runtime) 102 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 103 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 104 | 105 | # Install the AOT library on non-Debug builds only. 106 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 107 | CONFIGURATIONS Profile;Release 108 | COMPONENT Runtime) 109 | -------------------------------------------------------------------------------- /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 | # Set fallback configurations for older versions of the flutter tool. 14 | if (NOT DEFINED FLUTTER_TARGET_PLATFORM) 15 | set(FLUTTER_TARGET_PLATFORM "windows-x64") 16 | endif() 17 | 18 | # === Flutter Library === 19 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 20 | 21 | # Published to parent scope for install step. 22 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 23 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 24 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 25 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 26 | 27 | list(APPEND FLUTTER_LIBRARY_HEADERS 28 | "flutter_export.h" 29 | "flutter_windows.h" 30 | "flutter_messenger.h" 31 | "flutter_plugin_registrar.h" 32 | "flutter_texture_registrar.h" 33 | ) 34 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 35 | add_library(flutter INTERFACE) 36 | target_include_directories(flutter INTERFACE 37 | "${EPHEMERAL_DIR}" 38 | ) 39 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 40 | add_dependencies(flutter flutter_assemble) 41 | 42 | # === Wrapper === 43 | list(APPEND CPP_WRAPPER_SOURCES_CORE 44 | "core_implementations.cc" 45 | "standard_codec.cc" 46 | ) 47 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 48 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 49 | "plugin_registrar.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 52 | list(APPEND CPP_WRAPPER_SOURCES_APP 53 | "flutter_engine.cc" 54 | "flutter_view_controller.cc" 55 | ) 56 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 57 | 58 | # Wrapper sources needed for a plugin. 59 | add_library(flutter_wrapper_plugin STATIC 60 | ${CPP_WRAPPER_SOURCES_CORE} 61 | ${CPP_WRAPPER_SOURCES_PLUGIN} 62 | ) 63 | apply_standard_settings(flutter_wrapper_plugin) 64 | set_target_properties(flutter_wrapper_plugin PROPERTIES 65 | POSITION_INDEPENDENT_CODE ON) 66 | set_target_properties(flutter_wrapper_plugin PROPERTIES 67 | CXX_VISIBILITY_PRESET hidden) 68 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 69 | target_include_directories(flutter_wrapper_plugin PUBLIC 70 | "${WRAPPER_ROOT}/include" 71 | ) 72 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 73 | 74 | # Wrapper sources needed for the runner. 75 | add_library(flutter_wrapper_app STATIC 76 | ${CPP_WRAPPER_SOURCES_CORE} 77 | ${CPP_WRAPPER_SOURCES_APP} 78 | ) 79 | apply_standard_settings(flutter_wrapper_app) 80 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 81 | target_include_directories(flutter_wrapper_app PUBLIC 82 | "${WRAPPER_ROOT}/include" 83 | ) 84 | add_dependencies(flutter_wrapper_app flutter_assemble) 85 | 86 | # === Flutter tool backend === 87 | # _phony_ is a non-existent file to force this command to run every time, 88 | # since currently there's no way to get a full input/output list from the 89 | # flutter tool. 90 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 91 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 92 | add_custom_command( 93 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 94 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 95 | ${CPP_WRAPPER_SOURCES_APP} 96 | ${PHONY_OUTPUT} 97 | COMMAND ${CMAKE_COMMAND} -E env 98 | ${FLUTTER_TOOL_ENVIRONMENT} 99 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 100 | ${FLUTTER_TARGET_PLATFORM} $ 101 | VERBATIM 102 | ) 103 | add_custom_target(flutter_assemble DEPENDS 104 | "${FLUTTER_LIBRARY}" 105 | ${FLUTTER_LIBRARY_HEADERS} 106 | ${CPP_WRAPPER_SOURCES_CORE} 107 | ${CPP_WRAPPER_SOURCES_PLUGIN} 108 | ${CPP_WRAPPER_SOURCES_APP} 109 | ) 110 | -------------------------------------------------------------------------------- /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 | velopack_flutter 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", "velopack_flutter_example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "velopack_flutter_example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "velopack_flutter_example.exe" "\0" 98 | VALUE "ProductName", "velopack_flutter_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"velopack_flutter_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/GigaDroid/velopack_flutter/be50abcf73f279373c99802681189b8c5751739f/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 | unsigned 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.cpp: -------------------------------------------------------------------------------- 1 | #include "win32_window.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "resource.h" 7 | 8 | namespace { 9 | 10 | /// Window attribute that enables dark mode window decorations. 11 | /// 12 | /// Redefined in case the developer's machine has a Windows SDK older than 13 | /// version 10.0.22000.0. 14 | /// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute 15 | #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE 16 | #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 17 | #endif 18 | 19 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; 20 | 21 | /// Registry key for app theme preference. 22 | /// 23 | /// A value of 0 indicates apps should use dark mode. A non-zero or missing 24 | /// value indicates apps should use light mode. 25 | constexpr const wchar_t kGetPreferredBrightnessRegKey[] = 26 | L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; 27 | constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; 28 | 29 | // The number of Win32Window objects that currently exist. 30 | static int g_active_window_count = 0; 31 | 32 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); 33 | 34 | // Scale helper to convert logical scaler values to physical using passed in 35 | // scale factor 36 | int Scale(int source, double scale_factor) { 37 | return static_cast(source * scale_factor); 38 | } 39 | 40 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. 41 | // This API is only needed for PerMonitor V1 awareness mode. 42 | void EnableFullDpiSupportIfAvailable(HWND hwnd) { 43 | HMODULE user32_module = LoadLibraryA("User32.dll"); 44 | if (!user32_module) { 45 | return; 46 | } 47 | auto enable_non_client_dpi_scaling = 48 | reinterpret_cast( 49 | GetProcAddress(user32_module, "EnableNonClientDpiScaling")); 50 | if (enable_non_client_dpi_scaling != nullptr) { 51 | enable_non_client_dpi_scaling(hwnd); 52 | } 53 | FreeLibrary(user32_module); 54 | } 55 | 56 | } // namespace 57 | 58 | // Manages the Win32Window's window class registration. 59 | class WindowClassRegistrar { 60 | public: 61 | ~WindowClassRegistrar() = default; 62 | 63 | // Returns the singleton registrar instance. 64 | static WindowClassRegistrar* GetInstance() { 65 | if (!instance_) { 66 | instance_ = new WindowClassRegistrar(); 67 | } 68 | return instance_; 69 | } 70 | 71 | // Returns the name of the window class, registering the class if it hasn't 72 | // previously been registered. 73 | const wchar_t* GetWindowClass(); 74 | 75 | // Unregisters the window class. Should only be called if there are no 76 | // instances of the window. 77 | void UnregisterWindowClass(); 78 | 79 | private: 80 | WindowClassRegistrar() = default; 81 | 82 | static WindowClassRegistrar* instance_; 83 | 84 | bool class_registered_ = false; 85 | }; 86 | 87 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; 88 | 89 | const wchar_t* WindowClassRegistrar::GetWindowClass() { 90 | if (!class_registered_) { 91 | WNDCLASS window_class{}; 92 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 93 | window_class.lpszClassName = kWindowClassName; 94 | window_class.style = CS_HREDRAW | CS_VREDRAW; 95 | window_class.cbClsExtra = 0; 96 | window_class.cbWndExtra = 0; 97 | window_class.hInstance = GetModuleHandle(nullptr); 98 | window_class.hIcon = 99 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); 100 | window_class.hbrBackground = 0; 101 | window_class.lpszMenuName = nullptr; 102 | window_class.lpfnWndProc = Win32Window::WndProc; 103 | RegisterClass(&window_class); 104 | class_registered_ = true; 105 | } 106 | return kWindowClassName; 107 | } 108 | 109 | void WindowClassRegistrar::UnregisterWindowClass() { 110 | UnregisterClass(kWindowClassName, nullptr); 111 | class_registered_ = false; 112 | } 113 | 114 | Win32Window::Win32Window() { 115 | ++g_active_window_count; 116 | } 117 | 118 | Win32Window::~Win32Window() { 119 | --g_active_window_count; 120 | Destroy(); 121 | } 122 | 123 | bool Win32Window::Create(const std::wstring& title, 124 | const Point& origin, 125 | const Size& size) { 126 | Destroy(); 127 | 128 | const wchar_t* window_class = 129 | WindowClassRegistrar::GetInstance()->GetWindowClass(); 130 | 131 | const POINT target_point = {static_cast(origin.x), 132 | static_cast(origin.y)}; 133 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); 134 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); 135 | double scale_factor = dpi / 96.0; 136 | 137 | HWND window = CreateWindow( 138 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW, 139 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), 140 | Scale(size.width, scale_factor), Scale(size.height, scale_factor), 141 | nullptr, nullptr, GetModuleHandle(nullptr), this); 142 | 143 | if (!window) { 144 | return false; 145 | } 146 | 147 | UpdateTheme(window); 148 | 149 | return OnCreate(); 150 | } 151 | 152 | bool Win32Window::Show() { 153 | return ShowWindow(window_handle_, SW_SHOWNORMAL); 154 | } 155 | 156 | // static 157 | LRESULT CALLBACK Win32Window::WndProc(HWND const window, 158 | UINT const message, 159 | WPARAM const wparam, 160 | LPARAM const lparam) noexcept { 161 | if (message == WM_NCCREATE) { 162 | auto window_struct = reinterpret_cast(lparam); 163 | SetWindowLongPtr(window, GWLP_USERDATA, 164 | reinterpret_cast(window_struct->lpCreateParams)); 165 | 166 | auto that = static_cast(window_struct->lpCreateParams); 167 | EnableFullDpiSupportIfAvailable(window); 168 | that->window_handle_ = window; 169 | } else if (Win32Window* that = GetThisFromHandle(window)) { 170 | return that->MessageHandler(window, message, wparam, lparam); 171 | } 172 | 173 | return DefWindowProc(window, message, wparam, lparam); 174 | } 175 | 176 | LRESULT 177 | Win32Window::MessageHandler(HWND hwnd, 178 | UINT const message, 179 | WPARAM const wparam, 180 | LPARAM const lparam) noexcept { 181 | switch (message) { 182 | case WM_DESTROY: 183 | window_handle_ = nullptr; 184 | Destroy(); 185 | if (quit_on_close_) { 186 | PostQuitMessage(0); 187 | } 188 | return 0; 189 | 190 | case WM_DPICHANGED: { 191 | auto newRectSize = reinterpret_cast(lparam); 192 | LONG newWidth = newRectSize->right - newRectSize->left; 193 | LONG newHeight = newRectSize->bottom - newRectSize->top; 194 | 195 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, 196 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE); 197 | 198 | return 0; 199 | } 200 | case WM_SIZE: { 201 | RECT rect = GetClientArea(); 202 | if (child_content_ != nullptr) { 203 | // Size and position the child window. 204 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, 205 | rect.bottom - rect.top, TRUE); 206 | } 207 | return 0; 208 | } 209 | 210 | case WM_ACTIVATE: 211 | if (child_content_ != nullptr) { 212 | SetFocus(child_content_); 213 | } 214 | return 0; 215 | 216 | case WM_DWMCOLORIZATIONCOLORCHANGED: 217 | UpdateTheme(hwnd); 218 | return 0; 219 | } 220 | 221 | return DefWindowProc(window_handle_, message, wparam, lparam); 222 | } 223 | 224 | void Win32Window::Destroy() { 225 | OnDestroy(); 226 | 227 | if (window_handle_) { 228 | DestroyWindow(window_handle_); 229 | window_handle_ = nullptr; 230 | } 231 | if (g_active_window_count == 0) { 232 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); 233 | } 234 | } 235 | 236 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { 237 | return reinterpret_cast( 238 | GetWindowLongPtr(window, GWLP_USERDATA)); 239 | } 240 | 241 | void Win32Window::SetChildContent(HWND content) { 242 | child_content_ = content; 243 | SetParent(content, window_handle_); 244 | RECT frame = GetClientArea(); 245 | 246 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left, 247 | frame.bottom - frame.top, true); 248 | 249 | SetFocus(child_content_); 250 | } 251 | 252 | RECT Win32Window::GetClientArea() { 253 | RECT frame; 254 | GetClientRect(window_handle_, &frame); 255 | return frame; 256 | } 257 | 258 | HWND Win32Window::GetHandle() { 259 | return window_handle_; 260 | } 261 | 262 | void Win32Window::SetQuitOnClose(bool quit_on_close) { 263 | quit_on_close_ = quit_on_close; 264 | } 265 | 266 | bool Win32Window::OnCreate() { 267 | // No-op; provided for subclasses. 268 | return true; 269 | } 270 | 271 | void Win32Window::OnDestroy() { 272 | // No-op; provided for subclasses. 273 | } 274 | 275 | void Win32Window::UpdateTheme(HWND const window) { 276 | DWORD light_mode; 277 | DWORD light_mode_size = sizeof(light_mode); 278 | LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, 279 | kGetPreferredBrightnessRegValue, 280 | RRF_RT_REG_DWORD, nullptr, &light_mode, 281 | &light_mode_size); 282 | 283 | if (result == ERROR_SUCCESS) { 284 | BOOL enable_dark_mode = light_mode == 0; 285 | DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, 286 | &enable_dark_mode, sizeof(enable_dark_mode)); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /flutter_rust_bridge.yaml: -------------------------------------------------------------------------------- 1 | rust_input: crate::api 2 | rust_root: rust/ 3 | dart_output: lib/src/rust 4 | dart_entrypoint_class_name: VelopackRustLib 5 | -------------------------------------------------------------------------------- /lib/src/rust/api/velopack.dart: -------------------------------------------------------------------------------- 1 | // This file is automatically generated, so please do not edit it. 2 | // @generated by `flutter_rust_bridge`@ 2.7.0. 3 | 4 | // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import 5 | 6 | import '../frb_generated.dart'; 7 | import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; 8 | 9 | // These functions are ignored because they are not marked as `pub`: `check_and_download_updates`, `create_update_manager` 10 | 11 | Future isUpdateAvailable({required String url}) => 12 | VelopackRustLib.instance.api.crateApiVelopackIsUpdateAvailable(url: url); 13 | 14 | Future updateAndRestart({required String url}) => 15 | VelopackRustLib.instance.api.crateApiVelopackUpdateAndRestart(url: url); 16 | 17 | Future updateAndExit({required String url}) => 18 | VelopackRustLib.instance.api.crateApiVelopackUpdateAndExit(url: url); 19 | 20 | Future waitExitThenUpdate( 21 | {required String url, required bool silent, required bool restart}) => 22 | VelopackRustLib.instance.api.crateApiVelopackWaitExitThenUpdate( 23 | url: url, silent: silent, restart: restart); 24 | -------------------------------------------------------------------------------- /lib/src/rust/frb_generated.io.dart: -------------------------------------------------------------------------------- 1 | // This file is automatically generated, so please do not edit it. 2 | // @generated by `flutter_rust_bridge`@ 2.7.0. 3 | 4 | // ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field 5 | 6 | import 'api/velopack.dart'; 7 | import 'dart:async'; 8 | import 'dart:convert'; 9 | import 'dart:ffi' as ffi; 10 | import 'frb_generated.dart'; 11 | import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart'; 12 | 13 | abstract class VelopackRustLibApiImplPlatform 14 | extends BaseApiImpl { 15 | VelopackRustLibApiImplPlatform({ 16 | required super.handler, 17 | required super.wire, 18 | required super.generalizedFrbRustBinding, 19 | required super.portManager, 20 | }); 21 | 22 | @protected 23 | AnyhowException dco_decode_AnyhowException(dynamic raw); 24 | 25 | @protected 26 | String dco_decode_String(dynamic raw); 27 | 28 | @protected 29 | bool dco_decode_bool(dynamic raw); 30 | 31 | @protected 32 | Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); 33 | 34 | @protected 35 | int dco_decode_u_8(dynamic raw); 36 | 37 | @protected 38 | void dco_decode_unit(dynamic raw); 39 | 40 | @protected 41 | AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer); 42 | 43 | @protected 44 | String sse_decode_String(SseDeserializer deserializer); 45 | 46 | @protected 47 | bool sse_decode_bool(SseDeserializer deserializer); 48 | 49 | @protected 50 | Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); 51 | 52 | @protected 53 | int sse_decode_u_8(SseDeserializer deserializer); 54 | 55 | @protected 56 | void sse_decode_unit(SseDeserializer deserializer); 57 | 58 | @protected 59 | int sse_decode_i_32(SseDeserializer deserializer); 60 | 61 | @protected 62 | void sse_encode_AnyhowException( 63 | AnyhowException self, SseSerializer serializer); 64 | 65 | @protected 66 | void sse_encode_String(String self, SseSerializer serializer); 67 | 68 | @protected 69 | void sse_encode_bool(bool self, SseSerializer serializer); 70 | 71 | @protected 72 | void sse_encode_list_prim_u_8_strict( 73 | Uint8List self, SseSerializer serializer); 74 | 75 | @protected 76 | void sse_encode_u_8(int self, SseSerializer serializer); 77 | 78 | @protected 79 | void sse_encode_unit(void self, SseSerializer serializer); 80 | 81 | @protected 82 | void sse_encode_i_32(int self, SseSerializer serializer); 83 | } 84 | 85 | // Section: wire_class 86 | 87 | class VelopackRustLibWire implements BaseWire { 88 | factory VelopackRustLibWire.fromExternalLibrary(ExternalLibrary lib) => 89 | VelopackRustLibWire(lib.ffiDynamicLibrary); 90 | 91 | /// Holds the symbol lookup function. 92 | final ffi.Pointer Function(String symbolName) 93 | _lookup; 94 | 95 | /// The symbols are looked up in [dynamicLibrary]. 96 | VelopackRustLibWire(ffi.DynamicLibrary dynamicLibrary) 97 | : _lookup = dynamicLibrary.lookup; 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/rust/frb_generated.web.dart: -------------------------------------------------------------------------------- 1 | // This file is automatically generated, so please do not edit it. 2 | // @generated by `flutter_rust_bridge`@ 2.7.0. 3 | 4 | // ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field 5 | 6 | // Static analysis wrongly picks the IO variant, thus ignore this 7 | // ignore_for_file: argument_type_not_assignable 8 | 9 | import 'api/velopack.dart'; 10 | import 'dart:async'; 11 | import 'dart:convert'; 12 | import 'frb_generated.dart'; 13 | import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart'; 14 | 15 | abstract class VelopackRustLibApiImplPlatform 16 | extends BaseApiImpl { 17 | VelopackRustLibApiImplPlatform({ 18 | required super.handler, 19 | required super.wire, 20 | required super.generalizedFrbRustBinding, 21 | required super.portManager, 22 | }); 23 | 24 | @protected 25 | AnyhowException dco_decode_AnyhowException(dynamic raw); 26 | 27 | @protected 28 | String dco_decode_String(dynamic raw); 29 | 30 | @protected 31 | bool dco_decode_bool(dynamic raw); 32 | 33 | @protected 34 | Uint8List dco_decode_list_prim_u_8_strict(dynamic raw); 35 | 36 | @protected 37 | int dco_decode_u_8(dynamic raw); 38 | 39 | @protected 40 | void dco_decode_unit(dynamic raw); 41 | 42 | @protected 43 | AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer); 44 | 45 | @protected 46 | String sse_decode_String(SseDeserializer deserializer); 47 | 48 | @protected 49 | bool sse_decode_bool(SseDeserializer deserializer); 50 | 51 | @protected 52 | Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer); 53 | 54 | @protected 55 | int sse_decode_u_8(SseDeserializer deserializer); 56 | 57 | @protected 58 | void sse_decode_unit(SseDeserializer deserializer); 59 | 60 | @protected 61 | int sse_decode_i_32(SseDeserializer deserializer); 62 | 63 | @protected 64 | void sse_encode_AnyhowException( 65 | AnyhowException self, SseSerializer serializer); 66 | 67 | @protected 68 | void sse_encode_String(String self, SseSerializer serializer); 69 | 70 | @protected 71 | void sse_encode_bool(bool self, SseSerializer serializer); 72 | 73 | @protected 74 | void sse_encode_list_prim_u_8_strict( 75 | Uint8List self, SseSerializer serializer); 76 | 77 | @protected 78 | void sse_encode_u_8(int self, SseSerializer serializer); 79 | 80 | @protected 81 | void sse_encode_unit(void self, SseSerializer serializer); 82 | 83 | @protected 84 | void sse_encode_i_32(int self, SseSerializer serializer); 85 | } 86 | 87 | // Section: wire_class 88 | 89 | class VelopackRustLibWire implements BaseWire { 90 | VelopackRustLibWire.fromExternalLibrary(ExternalLibrary lib); 91 | } 92 | 93 | @JS('wasm_bindgen') 94 | external VelopackRustLibWasmModule get wasmModule; 95 | 96 | @JS() 97 | @anonymous 98 | extension type VelopackRustLibWasmModule._(JSObject _) implements JSObject {} 99 | -------------------------------------------------------------------------------- /lib/velopack_flutter.dart: -------------------------------------------------------------------------------- 1 | library velopack_flutter; 2 | 3 | export 'src/rust/api/velopack.dart'; 4 | export 'src/rust/frb_generated.dart' show VelopackRustLib; 5 | -------------------------------------------------------------------------------- /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 "velopack_flutter") 8 | project(${PROJECT_NAME} LANGUAGES CXX) 9 | 10 | include("../cargokit/cmake/cargokit.cmake") 11 | apply_cargokit(${PROJECT_NAME} ../rust velopack_flutter "") 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(velopack_flutter_bundled_libraries 17 | "${${PROJECT_NAME}_cargokit_lib}" 18 | PARENT_SCOPE 19 | ) 20 | -------------------------------------------------------------------------------- /macos/Classes/dummy_file.c: -------------------------------------------------------------------------------- 1 | // This is an empty file to force CocoaPods to create a framework. 2 | -------------------------------------------------------------------------------- /macos/velopack_flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint velopack_flutter.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'velopack_flutter' 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 | # This will ensure the source files in Classes/ are included in the native 17 | # builds of apps using this FFI plugin. Podspec does not support relative 18 | # paths, so Classes contains a forwarder C file that relatively imports 19 | # `../src/*` so that the C sources can be shared among all target platforms. 20 | s.source = { :path => '.' } 21 | s.source_files = 'Classes/**/*' 22 | s.dependency 'FlutterMacOS' 23 | 24 | s.platform = :osx, '10.11' 25 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } 26 | s.swift_version = '5.0' 27 | 28 | s.script_phase = { 29 | :name => 'Build Rust library', 30 | # First argument is relative path to the `rust` folder, second is name of rust library 31 | :script => 'sh "$PODS_TARGET_SRCROOT/../cargokit/build_pod.sh" ../rust velopack_flutter', 32 | :execution_position => :before_compile, 33 | :input_files => ['${BUILT_PRODUCTS_DIR}/cargokit_phony'], 34 | # Let XCode know that the static library referenced in -force_load below is 35 | # created by this build step. 36 | :output_files => ["${BUILT_PRODUCTS_DIR}/libvelopack_flutter.a"], 37 | } 38 | s.pod_target_xcconfig = { 39 | 'DEFINES_MODULE' => 'YES', 40 | # Flutter.framework does not contain a i386 slice. 41 | 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', 42 | 'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/libvelopack_flutter.a', 43 | } 44 | end -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: velopack_flutter 2 | description: "A flutter implementation of Velopack to enable easy distribution and auto-updates for flutter apps" 3 | version: 0.1.1 4 | repository: https://github.com/GigaDroid/velopack_flutter 5 | homepage: https://velopack.io/ 6 | environment: 7 | sdk: '>=3.3.0 <4.0.0' 8 | flutter: '>=3.3.0' 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | flutter_rust_bridge: 2.7.0 13 | plugin_platform_interface: ^2.0.2 14 | dev_dependencies: 15 | ffi: ^2.1.0 16 | ffigen: ^11.0.0 17 | flutter_test: 18 | sdk: flutter 19 | flutter_lints: ^3.0.0 20 | integration_test: 21 | sdk: flutter 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 | linux: 42 | ffiPlugin: true 43 | macos: 44 | ffiPlugin: true 45 | windows: 46 | ffiPlugin: true 47 | 48 | # To add assets to your plugin package, add an assets section, like this: 49 | # assets: 50 | # - images/a_dot_burr.jpeg 51 | # - images/a_dot_ham.jpeg 52 | # 53 | # For details regarding assets in packages, see 54 | # https://flutter.dev/assets-and-images/#from-packages 55 | # 56 | # An image asset can refer to one or more resolution-specific "variants", see 57 | # https://flutter.dev/assets-and-images/#resolution-aware 58 | 59 | # To add custom fonts to your plugin package, add a fonts section here, 60 | # in this "flutter" section. Each entry in this list should have a 61 | # "family" key with the font family name, and a "fonts" key with a 62 | # list giving the asset and other descriptors for the font. For 63 | # example: 64 | # fonts: 65 | # - family: Schyler 66 | # fonts: 67 | # - asset: fonts/Schyler-Regular.ttf 68 | # - asset: fonts/Schyler-Italic.ttf 69 | # style: italic 70 | # - family: Trajan Pro 71 | # fonts: 72 | # - asset: fonts/TrajanPro.ttf 73 | # - asset: fonts/TrajanPro_Bold.ttf 74 | # weight: 700 75 | # 76 | # For details regarding fonts in packages, see 77 | # https://flutter.dev/custom-fonts/#from-packages 78 | -------------------------------------------------------------------------------- /rust/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "velopack_flutter" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib", "staticlib"] 8 | 9 | [dependencies] 10 | flutter_rust_bridge = "=2.7.0" 11 | velopack = { version = "0.0", features= ["async"]} 12 | anyhow = "1.0.86" 13 | -------------------------------------------------------------------------------- /rust/src/api/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod velopack; 2 | -------------------------------------------------------------------------------- /rust/src/api/velopack.rs: -------------------------------------------------------------------------------- 1 | use velopack::*; 2 | use anyhow::Result; 3 | 4 | #[flutter_rust_bridge::frb(init)] 5 | pub fn init_app() { 6 | flutter_rust_bridge::setup_default_user_utils(); 7 | VelopackApp::build().run(); 8 | } 9 | 10 | fn create_update_manager(url: &str) -> Result { 11 | let source = sources::HttpSource::new(url); 12 | UpdateManager::new(source, None, None) 13 | } 14 | 15 | pub fn is_update_available(url: String) -> Result { 16 | let um = create_update_manager(&url)?; 17 | let update_check = um.check_for_updates()?; 18 | Ok(matches!(update_check, UpdateCheck::UpdateAvailable(..))) 19 | } 20 | 21 | fn check_and_download_updates(url: &str) -> Result> { 22 | let um = create_update_manager(url)?; 23 | if let UpdateCheck::UpdateAvailable(updates) = um.check_for_updates().unwrap() { 24 | um.download_updates(&updates, None)?; 25 | Ok(Some(updates)) 26 | } else { 27 | Ok(None) 28 | } 29 | } 30 | 31 | pub fn update_and_restart(url: String) -> Result<()> { 32 | if let Some(updates) = check_and_download_updates(&url)? { 33 | let um = create_update_manager(&url)?; 34 | um.apply_updates_and_restart(&updates)?; 35 | } 36 | Ok(()) 37 | } 38 | 39 | pub fn update_and_exit(url: String) -> Result<()> { 40 | if let Some(updates) = check_and_download_updates(&url)? { 41 | let um = create_update_manager(&url)?; 42 | um.apply_updates_and_exit(&updates)?; 43 | } 44 | Ok(()) 45 | } 46 | 47 | pub fn wait_exit_then_update(url: String, silent: bool, restart: bool) -> Result<()> { 48 | if let Some(updates) = check_and_download_updates(&url)? { 49 | let um = create_update_manager(&url)?; 50 | um.wait_exit_then_apply_updates(&updates, silent, restart,[""])?; 51 | } 52 | Ok(()) 53 | } 54 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod api; 2 | mod frb_generated; 3 | -------------------------------------------------------------------------------- /test_driver/integration_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:integration_test/integration_test_driver.dart'; 2 | 3 | Future main() => integrationDriver(); 4 | -------------------------------------------------------------------------------- /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 "velopack_flutter") 9 | project(${PROJECT_NAME} LANGUAGES CXX) 10 | 11 | include("../cargokit/cmake/cargokit.cmake") 12 | apply_cargokit(${PROJECT_NAME} ../rust velopack_flutter "") 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(velopack_flutter_bundled_libraries 18 | "${${PROJECT_NAME}_cargokit_lib}" 19 | PARENT_SCOPE 20 | ) 21 | --------------------------------------------------------------------------------