From bb5501bc28aa693dcab1d445f9980ac306947ff8 Mon Sep 17 00:00:00 2001 From: MuZhou233 Date: Sun, 3 Dec 2023 01:46:55 +0000 Subject: [PATCH] feat: add rust process_runner integration --- Makefile | 2 + lib/bloc/gebura/gebura_bloc.dart | 30 ++- lib/bloc/gebura/gebura_event.dart | 6 + lib/bloc/gebura/gebura_state.dart | 17 ++ lib/ffi/native_ffi.dart | 21 +- lib/ffi/rust_ffi/rust_ffi.dart | 205 +++++++++++++++--- lib/view/pages/ffi_test_page.dart | 36 --- .../pages/gebura/gebura_library_detail.dart | 13 +- pubspec.yaml | 2 + rust_ffi/Cargo.toml | 10 +- rust_ffi/Makefile | 38 ++++ rust_ffi/rustfmt.toml | 15 ++ rust_ffi/src/api.rs | 188 +++++++++++++++- rust_ffi/src/bridge_generated.io.rs | 53 ++++- rust_ffi/src/bridge_generated.rs | 71 ++++-- rust_ffi/src/lib.rs | 4 +- 16 files changed, 615 insertions(+), 96 deletions(-) delete mode 100644 lib/view/pages/ffi_test_page.dart create mode 100644 rust_ffi/Makefile create mode 100644 rust_ffi/rustfmt.toml diff --git a/Makefile b/Makefile index c594668..d33da6c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ init: + cd rust_ffi && make init cargo install flutter_rust_bridge_codegen flutter pub add --dev ffigen && flutter pub add ffi @@ -6,5 +7,6 @@ generate_rust_bridge: flutter_rust_bridge_codegen -r rust_ffi/src/api.rs -d lib/ffi/rust_ffi/rust_ffi.dart format: + cd rust_ffi && make format dart fix --apply dart format . \ No newline at end of file diff --git a/lib/bloc/gebura/gebura_bloc.dart b/lib/bloc/gebura/gebura_bloc.dart index c14dbe5..910ca8e 100644 --- a/lib/bloc/gebura/gebura_bloc.dart +++ b/lib/bloc/gebura/gebura_bloc.dart @@ -1,10 +1,13 @@ import 'package:bloc/bloc.dart'; import 'package:bloc_concurrency/bloc_concurrency.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:path/path.dart'; import 'package:tuihub_protos/librarian/sephirah/v1/gebura.pb.dart'; import 'package:tuihub_protos/librarian/v1/common.pb.dart'; import '../../common/bloc_event_status_mixin.dart'; +import '../../ffi/native_ffi.dart'; import '../../model/gebura_model.dart'; import '../../repo/grpc/api_helper.dart'; import '../../repo/local/gebura.dart'; @@ -24,7 +27,6 @@ class GeburaBloc extends Bloc { }); on((event, emit) async { - debugPrint('GeburaPurchasedAppsLoadEvent'); emit(GeburaPurchasedAppsLoadState(state, EventStatus.processing)); final resp = await _api.doRequest( (client) => client.getPurchasedApps, @@ -177,6 +179,32 @@ class GeburaBloc extends Bloc { emit(GeburaAssignAppPackageState(state, EventStatus.success, msg: resp.error)); }, transformer: droppable()); + + on((event, emit) async { + emit(GeburaRunAppState(state, event.appID, EventStatus.processing)); + final setting = _repo.getAppLauncherSetting(event.appID.id.toInt()); + if (setting == null) { + emit(GeburaRunAppState(state, event.appID, EventStatus.failed, + msg: '请先设置应用路径')); + return; + } + try { + final (start, end, suceess) = await NativeFunc.processRunner( + '', setting.path, '', dirname(setting.path), 1, 1000); + if (!suceess) { + emit(GeburaRunAppState(state, event.appID, EventStatus.failed, + msg: '应用未正常退出')); + return; + } + emit(GeburaRunAppState(state, event.appID, EventStatus.success, + startTime: DateTime.fromMillisecondsSinceEpoch(start * 1000), + endTime: DateTime.fromMillisecondsSinceEpoch(end * 1000))); + } catch (e) { + emit(GeburaRunAppState(state, event.appID, EventStatus.failed, + msg: '启动器错误 ${e is FrbAnyhowException ? e.anyhow : e}')); + return; + } + }); } AppLauncherSetting? getAppLauncherSetting(InternalID id) { diff --git a/lib/bloc/gebura/gebura_event.dart b/lib/bloc/gebura/gebura_event.dart index 530615e..36394c4 100644 --- a/lib/bloc/gebura/gebura_event.dart +++ b/lib/bloc/gebura/gebura_event.dart @@ -61,3 +61,9 @@ final class GeburaAssignAppPackageEvent extends GeburaEvent { GeburaAssignAppPackageEvent(this.appPackageID, this.appID); } + +final class GeburaRunAppEvent extends GeburaEvent { + final InternalID appID; + + GeburaRunAppEvent(this.appID); +} diff --git a/lib/bloc/gebura/gebura_state.dart b/lib/bloc/gebura/gebura_state.dart index db47fda..31dd1f8 100644 --- a/lib/bloc/gebura/gebura_state.dart +++ b/lib/bloc/gebura/gebura_state.dart @@ -142,3 +142,20 @@ class GeburaAssignAppPackageState extends GeburaState with EventStatusMixin { @override final String? msg; } + +class GeburaRunAppState extends GeburaState with EventStatusMixin { + GeburaRunAppState(GeburaState state, this.appID, this.statusCode, + {this.startTime, this.endTime, this.msg}) + : super() { + _from(state); + } + + final InternalID appID; + final DateTime? startTime; + final DateTime? endTime; + + @override + final EventStatus? statusCode; + @override + final String? msg; +} diff --git a/lib/ffi/native_ffi.dart b/lib/ffi/native_ffi.dart index 18714af..7abba3e 100644 --- a/lib/ffi/native_ffi.dart +++ b/lib/ffi/native_ffi.dart @@ -1,5 +1,6 @@ import 'dart:ffi'; import 'dart:io'; + import 'rust_ffi/rust_ffi.dart'; class NativeFFI { @@ -30,8 +31,22 @@ class NativeFFI { class NativeFunc { static final _ffi = RustFfiImpl(NativeFFI.dyLib); - static Future add(int left, int right) async { - final int sum = await _ffi.add(left: left, right: right); - return sum; + static Future<(int, int, bool)> processRunner( + String name, + String executePath, + String monitorPath, + String workingDir, + int sleepCount, + int sleepMillis, + ) async { + return _ffi.processRunner( + name: name, + executePath: executePath, + monitorPath: monitorPath, + workingDir: workingDir, + sleepCount: sleepCount, + sleepMillis: sleepMillis, + mode: TraceMode.Simple, + ); } } diff --git a/lib/ffi/rust_ffi/rust_ffi.dart b/lib/ffi/rust_ffi/rust_ffi.dart index c598437..86346ab 100644 --- a/lib/ffi/rust_ffi/rust_ffi.dart +++ b/lib/ffi/rust_ffi/rust_ffi.dart @@ -11,9 +11,27 @@ import 'package:uuid/uuid.dart'; import 'dart:ffi' as ffi; abstract class RustFfi { - Future add({required int left, required int right, dynamic hint}); + /// find and trace stared process then get end_time and exit_code + Future<(int, int, bool)> processRunner( + {required TraceMode mode, + required String name, + required String executePath, + required String monitorPath, + required String workingDir, + required int sleepCount, + required int sleepMillis, + dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kProcessRunnerConstMeta; +} + +/// trace mode +enum TraceMode { + /// Wait child process to exit + Simple, - FlutterRustBridgeTaskConstMeta get kAddConstMeta; + /// Search process by given name and wait them all exit + ByName, } class RustFfiImpl implements RustFfi { @@ -25,23 +43,53 @@ class RustFfiImpl implements RustFfi { factory RustFfiImpl.wasm(FutureOr module) => RustFfiImpl(module as ExternalLibrary); RustFfiImpl.raw(this._platform); - Future add({required int left, required int right, dynamic hint}) { - var arg0 = api2wire_usize(left); - var arg1 = api2wire_usize(right); + Future<(int, int, bool)> processRunner( + {required TraceMode mode, + required String name, + required String executePath, + required String monitorPath, + required String workingDir, + required int sleepCount, + required int sleepMillis, + dynamic hint}) { + var arg0 = api2wire_trace_mode(mode); + var arg1 = _platform.api2wire_String(name); + var arg2 = _platform.api2wire_String(executePath); + var arg3 = _platform.api2wire_String(monitorPath); + var arg4 = _platform.api2wire_String(workingDir); + var arg5 = api2wire_i32(sleepCount); + var arg6 = _platform.api2wire_u64(sleepMillis); return _platform.executeNormal(FlutterRustBridgeTask( - callFfi: (port_) => _platform.inner.wire_add(port_, arg0, arg1), - parseSuccessData: _wire2api_usize, - parseErrorData: null, - constMeta: kAddConstMeta, - argValues: [left, right], + callFfi: (port_) => _platform.inner + .wire_process_runner(port_, arg0, arg1, arg2, arg3, arg4, arg5, arg6), + parseSuccessData: _wire2api___record__i64_i64_bool, + parseErrorData: _wire2api_FrbAnyhowException, + constMeta: kProcessRunnerConstMeta, + argValues: [ + mode, + name, + executePath, + monitorPath, + workingDir, + sleepCount, + sleepMillis + ], hint: hint, )); } - FlutterRustBridgeTaskConstMeta get kAddConstMeta => + FlutterRustBridgeTaskConstMeta get kProcessRunnerConstMeta => const FlutterRustBridgeTaskConstMeta( - debugName: "add", - argNames: ["left", "right"], + debugName: "process_runner", + argNames: [ + "mode", + "name", + "executePath", + "monitorPath", + "workingDir", + "sleepCount", + "sleepMillis" + ], ); void dispose() { @@ -49,17 +97,60 @@ class RustFfiImpl implements RustFfi { } // Section: wire2api - int _wire2api_usize(dynamic raw) { + FrbAnyhowException _wire2api_FrbAnyhowException(dynamic raw) { + return FrbAnyhowException(raw as String); + } + + String _wire2api_String(dynamic raw) { + return raw as String; + } + + (int, int, bool) _wire2api___record__i64_i64_bool(dynamic raw) { + final arr = raw as List; + if (arr.length != 3) { + throw Exception('Expected 3 elements, got ${arr.length}'); + } + return ( + _wire2api_i64(arr[0]), + _wire2api_i64(arr[1]), + _wire2api_bool(arr[2]), + ); + } + + bool _wire2api_bool(dynamic raw) { + return raw as bool; + } + + int _wire2api_i64(dynamic raw) { return castInt(raw); } + + int _wire2api_u8(dynamic raw) { + return raw as int; + } + + Uint8List _wire2api_uint_8_list(dynamic raw) { + return raw as Uint8List; + } } // Section: api2wire @protected -int api2wire_usize(int raw) { +int api2wire_i32(int raw) { return raw; } + +@protected +int api2wire_trace_mode(TraceMode raw) { + return api2wire_i32(raw.index); +} + +@protected +int api2wire_u8(int raw) { + return raw; +} + // Section: finalizer class RustFfiPlatform extends FlutterRustBridgeBase { @@ -67,6 +158,22 @@ class RustFfiPlatform extends FlutterRustBridgeBase { // Section: api2wire + @protected + ffi.Pointer api2wire_String(String raw) { + return api2wire_uint_8_list(utf8.encoder.convert(raw)); + } + + @protected + int api2wire_u64(int raw) { + return raw; + } + + @protected + ffi.Pointer api2wire_uint_8_list(Uint8List raw) { + final ans = inner.new_uint_8_list_0(raw.length); + ans.ref.ptr.asTypedList(raw.length).setAll(0, raw); + return ans; + } // Section: finalizer // Section: api_fill_to_wire @@ -168,23 +275,64 @@ class RustFfiWire implements FlutterRustBridgeWireBase { late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr .asFunction)>(); - void wire_add( + void wire_process_runner( int port_, - int left, - int right, + int mode, + ffi.Pointer name, + ffi.Pointer execute_path, + ffi.Pointer monitor_path, + ffi.Pointer working_dir, + int sleep_count, + int sleep_millis, ) { - return _wire_add( + return _wire_process_runner( port_, - left, - right, + mode, + name, + execute_path, + monitor_path, + working_dir, + sleep_count, + sleep_millis, ); } - late final _wire_addPtr = _lookup< + late final _wire_process_runnerPtr = _lookup< ffi.NativeFunction< - ffi.Void Function(ffi.Int64, ffi.UintPtr, ffi.UintPtr)>>('wire_add'); - late final _wire_add = - _wire_addPtr.asFunction(); + ffi.Void Function( + ffi.Int64, + ffi.Int32, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int32, + ffi.Uint64)>>('wire_process_runner'); + late final _wire_process_runner = _wire_process_runnerPtr.asFunction< + void Function( + int, + int, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + int)>(); + + ffi.Pointer new_uint_8_list_0( + int len, + ) { + return _new_uint_8_list_0( + len, + ); + } + + late final _new_uint_8_list_0Ptr = _lookup< + ffi + .NativeFunction Function(ffi.Int32)>>( + 'new_uint_8_list_0'); + late final _new_uint_8_list_0 = _new_uint_8_list_0Ptr + .asFunction Function(int)>(); void free_WireSyncReturn( WireSyncReturn ptr, @@ -203,6 +351,13 @@ class RustFfiWire implements FlutterRustBridgeWireBase { final class _Dart_Handle extends ffi.Opaque {} +final class wire_uint_8_list extends ffi.Struct { + external ffi.Pointer ptr; + + @ffi.Int32() + external int len; +} + typedef DartPostCObjectFnType = ffi.Pointer< ffi.NativeFunction< ffi.Bool Function(DartPort port_id, ffi.Pointer message)>>; diff --git a/lib/view/pages/ffi_test_page.dart b/lib/view/pages/ffi_test_page.dart deleted file mode 100644 index c8a08ac..0000000 --- a/lib/view/pages/ffi_test_page.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../ffi/native_ffi.dart'; - -class FfiTestPage extends StatefulWidget { - const FfiTestPage({super.key}); - - @override - State createState() => _FfiTestPageState(); -} - -class _FfiTestPageState extends State { - int _counter = 0; - - Future _incrementCounter() async { - _counter = await NativeFunc.add(_counter, 1); - setState(() {}); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Rust_Bridge Demo')), - body: Center( - child: Text( - 'Count: $_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), - ); - } -} diff --git a/lib/view/pages/gebura/gebura_library_detail.dart b/lib/view/pages/gebura/gebura_library_detail.dart index 7b5404c..cb8617e 100644 --- a/lib/view/pages/gebura/gebura_library_detail.dart +++ b/lib/view/pages/gebura/gebura_library_detail.dart @@ -3,10 +3,10 @@ import 'dart:async'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:open_file/open_file.dart'; import '../../../bloc/gebura/gebura_bloc.dart'; import '../../../common/platform.dart'; +import '../../components/toast.dart'; import '../../helper/spacing.dart'; import 'gebura_app_launcher_setting_dialog.dart'; @@ -21,6 +21,13 @@ class GeburaLibraryDetailPage extends StatelessWidget { if (state.selectedPurchasedAppIndex != lastIndex) { lastIndex = state.selectedPurchasedAppIndex; } + if (state is GeburaRunAppState && state.msg != null) { + Toast(title: '', message: state.msg!).show(context); + } + if (state is GeburaRunAppState && state.success) { + Toast(title: '', message: '${state.startTime} ${state.endTime}') + .show(context); + } }, builder: (context, state) { final app = state.purchasedApps != null && @@ -108,7 +115,9 @@ class GeburaLibraryDetailPage extends StatelessWidget { if (setting != null && setting.path.isNotEmpty) ElevatedButton( onPressed: () async { - await OpenFile.open(setting.path); + context + .read() + .add(GeburaRunAppEvent(app.id)); }, child: const Text('启动'), ) diff --git a/pubspec.yaml b/pubspec.yaml index 48105da..507ef0a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -96,9 +96,11 @@ dependencies: open_file: ^3.3.2 universal_io: ^2.2.2 package_info_plus: ^5.0.1 + path: ^1.8.3 # others json_annotation: ^4.8.1 + win32: ^5.1.0 dev_dependencies: flutter_test: diff --git a/rust_ffi/Cargo.toml b/rust_ffi/Cargo.toml index e268e28..b1bc4cd 100644 --- a/rust_ffi/Cargo.toml +++ b/rust_ffi/Cargo.toml @@ -7,7 +7,15 @@ edition = "2021" crate-type = ["staticlib", "cdylib"] [dependencies] +anyhow = "1.0" flutter_rust_bridge = "=1.82.1" +log = "0.4.14" +strum = "0.25.0" +strum_macros = "0.25.3" +sysinfo = "0.29.0" +time = "0.3.21" +walkdir = "2" +winapi = { version = "0.3.9", features = ["processthreadsapi"] } [build-dependencies] -flutter_rust_bridge_codegen = "=1.82.1" \ No newline at end of file +flutter_rust_bridge_codegen = "=1.82.1" diff --git a/rust_ffi/Makefile b/rust_ffi/Makefile new file mode 100644 index 0000000..81a5da1 --- /dev/null +++ b/rust_ffi/Makefile @@ -0,0 +1,38 @@ +SHELL := bash + +# init +init: + rustup toolchain add stable + rustup toolchain add nightly + +unset-override: + @# unset first in case of any previous overrides + @if rustup override list | grep `pwd` > /dev/null; then rustup override unset; fi + +pre-format: unset-override + @rustup component add rustfmt --toolchain nightly + @which cargo-sort &> /dev/null || cargo install -q cargo-sort + +# format code +format: pre-format + @cargo +nightly fmt + @cargo sort -w -c &>/dev/null || cargo sort -w >/dev/null + +# show help +help: + @echo '' + @echo 'Usage:' + @echo ' make [target]' + @echo '' + @echo 'Targets:' + @awk '/^[a-zA-Z\-\_0-9]+:/ { \ + helpMessage = match(lastLine, /^# (.*)/); \ + if (helpMessage) { \ + helpCommand = substr($$1, 0, index($$1, ":")-1); \ + helpMessage = substr(lastLine, RSTART + 2, RLENGTH); \ + printf "\033[36m%-22s\033[0m %s\n", helpCommand,helpMessage; \ + } \ + } \ + { lastLine = $$0 }' $(MAKEFILE_LIST) + +.DEFAULT_GOAL := help \ No newline at end of file diff --git a/rust_ffi/rustfmt.toml b/rust_ffi/rustfmt.toml new file mode 100644 index 0000000..3d7538e --- /dev/null +++ b/rust_ffi/rustfmt.toml @@ -0,0 +1,15 @@ +unstable_features = true + +comment_width = 80 +wrap_comments = true +format_code_in_doc_comments = true +format_macro_bodies = true +format_macro_matchers = true +normalize_comments = true +normalize_doc_attributes = true +condense_wildcard_suffixes = true +newline_style = "Unix" +use_field_init_shorthand = true +use_try_shorthand = true +imports_granularity = "Crate" +group_imports = "StdExternalCrate" \ No newline at end of file diff --git a/rust_ffi/src/api.rs b/rust_ffi/src/api.rs index 7d12d9a..45a5f1f 100644 --- a/rust_ffi/src/api.rs +++ b/rust_ffi/src/api.rs @@ -1,14 +1,184 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +// This is the entry point of your Rust library. +// When adding new code to your project, note that only items used +// here will be transformed to their Dart equivalents. + +use std::{ + ops::Add, + path::PathBuf, + process, + sync::{Arc, Mutex}, + thread, + thread::sleep, + time::{Duration, UNIX_EPOCH}, +}; + +use anyhow::{anyhow as err_msg, Result}; +use log::info; +use strum_macros::{Display, EnumString}; +use sysinfo::{PidExt, Process, ProcessExt, System, SystemExt}; +use time; + +/// trace mode +#[derive(Display, EnumString)] +#[strum(serialize_all = "snake_case")] +pub enum TraceMode { + /// Wait child process to exit + Simple, + /// Search process by given name and wait them all exit + ByName, +} + +/// find and trace stared process then get end_time and exit_code +pub fn process_runner( + mode: TraceMode, + name: String, + execute_path: String, + monitor_path: String, + working_dir: String, + sleep_count: i32, + sleep_millis: u64, +) -> Result<(i64, i64, bool)> { + let execute = + PathBuf::try_from(execute_path).map_err(|e| err_msg!("invalid execute path {}", e))?; + if !execute.is_file() { + return Err(err_msg!("invalid execute path")); + } + let working_dir = + PathBuf::try_from(&working_dir).map_err(|e| err_msg!("invalid working dir {}", e))?; + if !working_dir.is_dir() { + return Err(err_msg!("invalid working dir path")); + } + // return Err(err_msg!("break for test {}", + // working_dir.to_string_lossy().to_lowercase())); + let start_time = time::OffsetDateTime::now_utc(); + let mut child = process::Command::new(execute) + .current_dir(working_dir) + .spawn()?; + match mode { + TraceMode::Simple => { + let status = child.wait()?; + let end_time = time::OffsetDateTime::now_utc(); + Ok(( + start_time.unix_timestamp(), + end_time.unix_timestamp(), + status.success(), + )) + } + TraceMode::ByName => { + for _ in 0..sleep_count { + sleep(Duration::from_millis(sleep_millis)); + let s = System::new_all(); + let exit_code_mutex = Arc::new(Mutex::new(0)); + let processes: Vec<&Process> = s + .processes() + .values() + .filter(|val: &&Process| { + val.name().to_lowercase().contains(&name.to_lowercase()) + }) + .filter(|p| { + time::OffsetDateTime::from( + UNIX_EPOCH.add(time::Duration::seconds(p.start_time() as i64)), + ) + .add(time::Duration::seconds(1)) + > start_time + }) + .filter(|p| { + p.exe().to_string_lossy().to_lowercase() == monitor_path.to_lowercase() + }) + .collect(); + info!("processes num: {}", processes.len()); + let handles: Vec<_> = processes + .into_iter() + .map(|p| p.pid().as_u32()) + .map(|pid| { + let mu = Arc::clone(&exit_code_mutex); + thread::spawn(move || { + if let Ok(exit_code) = wait_for_process_exit(pid) { + if exit_code != 0 { + let mut guard = mu.lock().unwrap(); + *guard = exit_code; + } + } + }) + }) + .collect(); + if !handles.is_empty() { + for handle in handles { + handle.join().unwrap(); + } + let end_time = time::OffsetDateTime::now_utc(); + let exit_code = *exit_code_mutex.lock().unwrap(); + return Ok(( + start_time.unix_timestamp(), + end_time.unix_timestamp(), + exit_code == 0, + )); + } + } + Err(err_msg!("sleep time limit exceeded")) + } + } } -#[cfg(test)] -mod tests { - use super::*; +#[cfg(target_os = "linux")] +fn wait_for_process_exit(pid: u32) -> Result { + use libc::{c_int, pid_t, WEXITSTATUS, WIFEXITED, WIFSIGNALED, WTERMSIG}; + + extern "C" { + fn waitpid(pid: pid_t, status: *mut c_int, options: c_int) -> pid_t; + } + let mut status: c_int = 0; + unsafe { + let result = waitpid(pid as i32, &mut status as *mut c_int, 0); + if result == -1 { + return Err(err_msg!( + "An error occurred while waiting for the process to exit" + )); + } + } + + if WIFEXITED(status) { + let exit_code = WEXITSTATUS(status); + Ok(exit_code) + } else if WIFSIGNALED(status) { + let signal = WTERMSIG(status); + Err(err_msg!( + "Process terminated by signal, signal number: {}", + signal + )) + } else { + Err(err_msg!("Unknown error")) + } +} + +#[cfg(target_os = "windows")] +fn wait_for_process_exit(pid: u32) -> Result { + use winapi::{ + shared::minwindef::DWORD, + um::{ + minwinbase::STILL_ACTIVE, + processthreadsapi::{GetExitCodeProcess, OpenProcess}, + synchapi::WaitForSingleObject, + winnt::{PROCESS_QUERY_INFORMATION, SYNCHRONIZE}, + }, + }; + + unsafe { + let handle = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, 0, pid as DWORD); + if handle.is_null() { + return Err(err_msg!("can't get handle")); + } + + WaitForSingleObject(handle, winapi::um::winbase::INFINITE); + + let mut exit_code: DWORD = 0; + GetExitCodeProcess(handle, &mut exit_code); + + if exit_code == STILL_ACTIVE { + WaitForSingleObject(handle, winapi::um::winbase::INFINITE); + GetExitCodeProcess(handle, &mut exit_code); + } - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + Ok(exit_code as i32) } } diff --git a/rust_ffi/src/bridge_generated.io.rs b/rust_ffi/src/bridge_generated.io.rs index 89a2dda..39b7b36 100644 --- a/rust_ffi/src/bridge_generated.io.rs +++ b/rust_ffi/src/bridge_generated.io.rs @@ -2,18 +2,67 @@ use super::*; // Section: wire functions #[no_mangle] -pub extern "C" fn wire_add(port_: i64, left: usize, right: usize) { - wire_add_impl(port_, left, right) +pub extern "C" fn wire_process_runner( + port_: i64, + mode: i32, + name: *mut wire_uint_8_list, + execute_path: *mut wire_uint_8_list, + monitor_path: *mut wire_uint_8_list, + working_dir: *mut wire_uint_8_list, + sleep_count: i32, + sleep_millis: u64, +) { + wire_process_runner_impl( + port_, + mode, + name, + execute_path, + monitor_path, + working_dir, + sleep_count, + sleep_millis, + ) } // Section: allocate functions +#[no_mangle] +pub extern "C" fn new_uint_8_list_0(len: i32) -> *mut wire_uint_8_list { + let ans = wire_uint_8_list { + ptr: support::new_leak_vec_ptr(Default::default(), len), + len, + }; + support::new_leak_box_ptr(ans) +} + // Section: related functions // Section: impl Wire2Api +impl Wire2Api for *mut wire_uint_8_list { + fn wire2api(self) -> String { + let vec: Vec = self.wire2api(); + String::from_utf8_lossy(&vec).into_owned() + } +} + +impl Wire2Api> for *mut wire_uint_8_list { + fn wire2api(self) -> Vec { + unsafe { + let wrap = support::box_from_leak_ptr(self); + support::vec_from_leak_ptr(wrap.ptr, wrap.len) + } + } +} // Section: wire structs +#[repr(C)] +#[derive(Clone)] +pub struct wire_uint_8_list { + ptr: *mut u8, + len: i32, +} + // Section: impl NewWithNullPtr pub trait NewWithNullPtr { diff --git a/rust_ffi/src/bridge_generated.rs b/rust_ffi/src/bridge_generated.rs index 7776064..d6c7e91 100644 --- a/rust_ffi/src/bridge_generated.rs +++ b/rust_ffi/src/bridge_generated.rs @@ -11,32 +11,52 @@ // AUTO GENERATED FILE, DO NOT EDIT. // Generated by `flutter_rust_bridge`@ 1.82.1. -use crate::api::*; use core::panic::UnwindSafe; -use flutter_rust_bridge::rust2dart::IntoIntoDart; -use flutter_rust_bridge::*; -use std::ffi::c_void; -use std::sync::Arc; +use std::{ffi::c_void, sync::Arc}; + +use flutter_rust_bridge::{rust2dart::IntoIntoDart, *}; + +use crate::api::*; // Section: imports // Section: wire functions -fn wire_add_impl( +fn wire_process_runner_impl( port_: MessagePort, - left: impl Wire2Api + UnwindSafe, - right: impl Wire2Api + UnwindSafe, + mode: impl Wire2Api + UnwindSafe, + name: impl Wire2Api + UnwindSafe, + execute_path: impl Wire2Api + UnwindSafe, + monitor_path: impl Wire2Api + UnwindSafe, + working_dir: impl Wire2Api + UnwindSafe, + sleep_count: impl Wire2Api + UnwindSafe, + sleep_millis: impl Wire2Api + UnwindSafe, ) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, usize, _>( + FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, (i64, i64, bool), _>( WrapInfo { - debug_name: "add", + debug_name: "process_runner", port: Some(port_), mode: FfiCallMode::Normal, }, move || { - let api_left = left.wire2api(); - let api_right = right.wire2api(); - move |task_callback| Result::<_, ()>::Ok(add(api_left, api_right)) + let api_mode = mode.wire2api(); + let api_name = name.wire2api(); + let api_execute_path = execute_path.wire2api(); + let api_monitor_path = monitor_path.wire2api(); + let api_working_dir = working_dir.wire2api(); + let api_sleep_count = sleep_count.wire2api(); + let api_sleep_millis = sleep_millis.wire2api(); + move |task_callback| { + process_runner( + api_mode, + api_name, + api_execute_path, + api_monitor_path, + api_working_dir, + api_sleep_count, + api_sleep_millis, + ) + } }, ) } @@ -62,11 +82,32 @@ where (!self.is_null()).then(|| self.wire2api()) } } -impl Wire2Api for usize { - fn wire2api(self) -> usize { + +impl Wire2Api for i32 { + fn wire2api(self) -> i32 { + self + } +} +impl Wire2Api for i32 { + fn wire2api(self) -> TraceMode { + match self { + 0 => TraceMode::Simple, + 1 => TraceMode::ByName, + _ => unreachable!("Invalid variant for TraceMode: {}", self), + } + } +} +impl Wire2Api for u64 { + fn wire2api(self) -> u64 { self } } +impl Wire2Api for u8 { + fn wire2api(self) -> u8 { + self + } +} + // Section: impl IntoDart // Section: executor diff --git a/rust_ffi/src/lib.rs b/rust_ffi/src/lib.rs index fb65ee3..dfdd872 100644 --- a/rust_ffi/src/lib.rs +++ b/rust_ffi/src/lib.rs @@ -1,2 +1,2 @@ -mod bridge_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */ -mod api; \ No newline at end of file +mod api; +mod bridge_generated;