[bug] External command spawns Tauri app clone and stalls, until clone app is closed
Describe the bug
Running a third-party function from src-tauri/srcmain.rs that spawns a call to an external command-line tool through an env var causes the Tauri app to …
- spawn a (fully functional) clone of itself
- stalls the execution of the external command until the spawned Tauri app is closed
Both of these are undesirable for obvious reasons.
The third-party function in this case is ProjectWorkspace.run_build_scripts(…) (docs) from rust-analyzer's ra_ap_project_model crate (docs) and the external command-line tool is cargo check with the env var being RUSTC_WRAPPER (cargo book).
The ProjectWorkspace.run_build_scripts(…) function works just fine outside of Tauri.
Reproduction
-
Create a barebones project:
yarn create tauri-app:yarn create v1.22.21 ... success Installed "[email protected]" with binaries: - create-tauri-app ✔ Project name · tauri-repro ✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm, bun) ✔ Choose your package manager · yarn ✔ Choose your UI template · Vanilla ✔ Choose your UI flavor · TypeScriptThen add the following dependencies:
[dependencies] ... fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs" } anyhow = "1.0.78" log = "0.4.20" env_logger = "0.10.1" ra_ap_load-cargo = "=0.0.190" ra_ap_paths = "=0.0.190" ra_ap_project_model = "=0.0.190" ra_ap_cfg = "=0.0.190" ra_ap_ide_db = "=0.0.190" -
And modify the
src-tauri/src/main.rsfile to look like this:// Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use std::path::{Path, PathBuf}; use ra_ap_load_cargo::{LoadCargoConfig, ProcMacroServerChoice}; use ra_ap_paths::AbsPathBuf; use ra_ap_project_model::{CargoConfig, ProjectManifest, ProjectWorkspace}; fn load_workspace(manifest_path: &Path) -> anyhow::Result<()> { let manifest_path = if !manifest_path.is_absolute() { std::env::current_dir()?.join(manifest_path) } else { manifest_path.to_owned() } .canonicalize()?; // Changing this to `true` causes the app to // 1. spawn a clone of itself // 2. stall itself, until the clone is closed const WRAP_RUSTC_IN_BUILD_SCRIPTS: bool = false; let cargo_config = CargoConfig { wrap_rustc_in_build_scripts: WRAP_RUSTC_IN_BUILD_SCRIPTS, ..Default::default() }; let load_config = LoadCargoConfig { load_out_dirs_from_check: true, prefill_caches: false, with_proc_macro_server: ProcMacroServerChoice::Sysroot, }; let mut project_workspace = { ProjectWorkspace::load( ProjectManifest::discover_single(AbsPathBuf::assert(manifest_path).as_path())?, &cargo_config, &|_| {}, ) }?; if load_config.load_out_dirs_from_check { log::debug!("🟡 Running build scripts ..."); if WRAP_RUSTC_IN_BUILD_SCRIPTS { log::debug!("🔴 Execution stalled, waiting for clone app to be closed."); } let build_scripts = project_workspace.run_build_scripts(&cargo_config, &|_| {})?; project_workspace.set_build_scripts(build_scripts); } let _ = ra_ap_load_cargo::load_workspace( project_workspace.clone(), &cargo_config.extra_env, &load_config, )?; log::debug!("🟢 Done."); Ok(()) } // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command #[tauri::command] fn greet(name: &str) -> String { let manifest_path = PathBuf::from(name); match load_workspace(&manifest_path) { Ok(()) => "It worked!".to_string(), Err(err) => format!("Error: {err:?}"), } } fn main() { let _ = fix_path_env::fix(); env_logger::init_from_env({ let env = env_logger::Env::default(); let key = env_logger::DEFAULT_FILTER_ENV; let filters = ["debug", "tao=trace", "salsa=warn"]; env.filter_or(key, filters.join(",")) }); tauri::Builder::default() .invoke_handler(tauri::generate_handler![greet]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } -
Create a dummy Rust project (e.g. via
cargo new "tauri-dummy"in a separate shell) somewhere else on your machine. -
Run the tauri app via
yarn tauri dev/npm run tauri dev. -
Pass the dummy project's manifest path (e.g.
/path/to/tauri-dummy/Cargo.toml) into the greet input field and click "Greet". -
The tauri web UI should show "It worked!" as the greeting and the logs should look something like this:
[… DEBUG tauri_repro] 🟡 Running build scripts ... [… DEBUG tauri_repro] 🟢 Done. -
Change the value of
const WRAP_RUSTC_IN_BUILD_SCRIPTS: boolinmain.rstotrue. -
Open your IDE's console and re-launch the tauri app.
-
Notice how the program execution seemingly stalls and how a second app instance has spawned all by itself, right on top of the original one, with the console's tail looking something like this:
[… DEBUG tauri_repro] 🟡 Running build scripts ... [… DEBUG tauri_repro] 🔴 Execution stalled, waiting for clone app to be closed. -
Close the clone app instance and notice how the original app's execution continues, and the UI shows "It worked!", with the console finally showing the following:
[… DEBUG tauri_repro] 🟢 Done.
Expected behavior
The app should neither …
- spawn a clone of itself
- stall its execution
Full tauri info output
yarn run v1.22.21
$ tauri info
[✔] Environment
- OS: Mac OS 14.3.0 X64
✔ Xcode Command Line Tools: installed
✔ rustc: 1.75.0 (82e1608df 2023-12-21)
✔ cargo: 1.75.0 (1d8b05cdd 2023-11-20)
✔ rustup: 1.26.0 (5af9b9484 2023-04-05)
✔ Rust toolchain: stable-x86_64-apple-darwin (default)
- node: 21.5.0
- yarn: 1.22.21
- npm: 10.2.5
[-] Packages
- tauri [RUST]: 1.5.4
- tauri-build [RUST]: 1.5.1
- wry [RUST]: 0.24.7
- tao [RUST]: 0.16.5
- @tauri-apps/api [NPM]: 1.5.3
- @tauri-apps/cli [NPM]: 1.5.9
[-] App
- build-type: bundle
- CSP: unset
- distDir: ../dist
- devPath: http://localhost:1420/
- bundler: Vite
✨ Done in 8.81s.
Stack trace
n/a
Additional context
Changing WRAP_RUSTC_IN_BUILD_SCRIPTS from false to true causes rust-analyzer (the library, aka the ra_ap_… dependencies we're linking) to wrap its invocation of cargo check through the RUSTC_WRAPPER env var.
RUSTC_WRAPPER— Instead of simply runningrustc, Cargo will execute this specified wrapper, passing as its command-line arguments therustcinvocation, with the first argument being the path to the actualrustc. Useful to set up a build cache tool such assccache. See build.rustc-wrapper to set via config. Setting this to the empty string overwrites the config and resets cargo to not use a wrapper.
My initial suspicion was that I was missing a call to let _ = fix_path_env::fix(); to make this work on macOS, but as evident by the reproduction above the problem persists, even with the env fix.
While #7681 doesn't mention the undesired spawning of an entire app clone (but merely of its window) they might still be connected, I reckon?
Yep. It looks like the similar prone