refactor tauri-cli (#14836)

* refactor(tauri-cli): use OsString where possible

* refactor(tauri-cli): remove needless scoping blocks

* refactor(tauri-cli): make return type concrete

* refactor(tauri-cli): use ?

* refactor(tauri-cli): coerce later to trait object

* refactor(tauri-cli): remove clone

* refactor(tauri-cli): make better use of static OnceLock

* fix(tauri-cli): upgrade atomics to SeqCst

* Add change file

* Update .changes/change-pr-14836.md

Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
This commit is contained in:
sftse
2026-01-29 03:39:00 +01:00
committed by GitHub
parent d453e2e06a
commit e3fdcb5002
11 changed files with 106 additions and 131 deletions

View File

@@ -0,0 +1,6 @@
---
"@tauri-apps/cli": patch:changes
"tauri-cli": patch:changes
---
Continued refactors of tauri-cli, fix too weak atomics.

View File

@@ -25,13 +25,13 @@ use std::{
process::{exit, Command, Stdio}, process::{exit, Command, Stdio},
sync::{ sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
Arc, Mutex, OnceLock, OnceLock,
}, },
}; };
mod builtin_dev_server; mod builtin_dev_server;
static BEFORE_DEV: OnceLock<Mutex<Arc<SharedChild>>> = OnceLock::new(); static BEFORE_DEV: OnceLock<SharedChild> = OnceLock::new();
static KILL_BEFORE_DEV_FLAG: AtomicBool = AtomicBool::new(false); static KILL_BEFORE_DEV_FLAG: AtomicBool = AtomicBool::new(false);
#[cfg(unix)] #[cfg(unix)]
@@ -205,21 +205,18 @@ pub fn setup(
let child = SharedChild::spawn(&mut command) let child = SharedChild::spawn(&mut command)
.unwrap_or_else(|_| panic!("failed to run `{before_dev}`")); .unwrap_or_else(|_| panic!("failed to run `{before_dev}`"));
let child = Arc::new(child);
let child_ = child.clone();
let child = BEFORE_DEV.get_or_init(move || child);
std::thread::spawn(move || { std::thread::spawn(move || {
let status = child_ let status = child
.wait() .wait()
.expect("failed to wait on \"beforeDevCommand\""); .expect("failed to wait on \"beforeDevCommand\"");
if !(status.success() || KILL_BEFORE_DEV_FLAG.load(Ordering::Relaxed)) { if !(status.success() || KILL_BEFORE_DEV_FLAG.load(Ordering::SeqCst)) {
log::error!("The \"beforeDevCommand\" terminated with a non-zero status code."); log::error!("The \"beforeDevCommand\" terminated with a non-zero status code.");
exit(status.code().unwrap_or(1)); exit(status.code().unwrap_or(1));
} }
}); });
BEFORE_DEV.set(Mutex::new(child)).unwrap();
let _ = ctrlc::set_handler(move || { let _ = ctrlc::set_handler(move || {
kill_before_dev_process(); kill_before_dev_process();
exit(130); exit(130);
@@ -336,11 +333,10 @@ pub fn on_app_exit(code: Option<i32>, reason: ExitReason, exit_on_panic: bool, n
pub fn kill_before_dev_process() { pub fn kill_before_dev_process() {
if let Some(child) = BEFORE_DEV.get() { if let Some(child) = BEFORE_DEV.get() {
let child = child.lock().unwrap(); if KILL_BEFORE_DEV_FLAG.load(Ordering::SeqCst) {
if KILL_BEFORE_DEV_FLAG.load(Ordering::Relaxed) {
return; return;
} }
KILL_BEFORE_DEV_FLAG.store(true, Ordering::Relaxed); KILL_BEFORE_DEV_FLAG.store(true, Ordering::SeqCst);
#[cfg(windows)] #[cfg(windows)]
{ {
let powershell_path = std::env::var("SYSTEMROOT").map_or_else( let powershell_path = std::env::var("SYSTEMROOT").map_or_else(

View File

@@ -210,7 +210,7 @@ impl Rust {
if options.no_watch { if options.no_watch {
let (tx, rx) = sync_channel(1); let (tx, rx) = sync_channel(1);
self.run_dev(options, run_args, move |status, reason| { self.run_dev(options, &run_args, move |status, reason| {
on_exit(status, reason); on_exit(status, reason);
tx.send(()).unwrap(); tx.send(()).unwrap();
})?; })?;
@@ -225,9 +225,11 @@ impl Rust {
&merge_configs, &merge_configs,
|rust: &mut Rust, _config| { |rust: &mut Rust, _config| {
let on_exit = on_exit.clone(); let on_exit = on_exit.clone();
rust.run_dev(options.clone(), run_args.clone(), move |status, reason| { rust
on_exit(status, reason) .run_dev(options.clone(), &run_args, move |status, reason| {
}) on_exit(status, reason)
})
.map(|child| Box::new(child) as Box<dyn DevProcess + Send>)
}, },
dirs, dirs,
) )
@@ -361,7 +363,7 @@ fn build_ignore_matcher(dir: &Path) -> IgnoreMatcher {
ignore_builder.add(path); ignore_builder.add(path);
if let Ok(ignore_file) = std::env::var("TAURI_CLI_WATCHER_IGNORE_FILENAME") { if let Some(ignore_file) = std::env::var_os("TAURI_CLI_WATCHER_IGNORE_FILENAME") {
ignore_builder.add(dir.join(ignore_file)); ignore_builder.add(dir.join(ignore_file));
} }
@@ -393,7 +395,7 @@ fn lookup<F: FnMut(FileType, PathBuf)>(dir: &Path, mut f: F) {
let mut builder = ignore::WalkBuilder::new(dir); let mut builder = ignore::WalkBuilder::new(dir);
builder.add_custom_ignore_filename(".taurignore"); builder.add_custom_ignore_filename(".taurignore");
let _ = builder.add_ignore(default_gitignore); let _ = builder.add_ignore(default_gitignore);
if let Ok(ignore_file) = std::env::var("TAURI_CLI_WATCHER_IGNORE_FILENAME") { if let Some(ignore_file) = std::env::var_os("TAURI_CLI_WATCHER_IGNORE_FILENAME") {
builder.add_ignore(ignore_file); builder.add_ignore(ignore_file);
} }
builder.require_git(false).ignore(false).max_depth(Some(1)); builder.require_git(false).ignore(false).max_depth(Some(1));
@@ -490,9 +492,9 @@ impl Rust {
fn run_dev<F: Fn(Option<i32>, ExitReason) + Send + Sync + 'static>( fn run_dev<F: Fn(Option<i32>, ExitReason) + Send + Sync + 'static>(
&mut self, &mut self,
options: Options, options: Options,
run_args: Vec<String>, run_args: &[String],
on_exit: F, on_exit: F,
) -> crate::Result<Box<dyn DevProcess + Send>> { ) -> crate::Result<desktop::DevChild> {
desktop::run_dev( desktop::run_dev(
options, options,
run_args, run_args,
@@ -500,7 +502,6 @@ impl Rust {
self.config_features.clone(), self.config_features.clone(),
on_exit, on_exit,
) )
.map(|c| Box::new(c) as Box<dyn DevProcess + Send>)
} }
fn run_dev_watcher< fn run_dev_watcher<
@@ -1380,7 +1381,7 @@ fn tauri_config_to_bundle_settings(
if enabled_features.contains(&"tray-icon".into()) if enabled_features.contains(&"tray-icon".into())
|| enabled_features.contains(&"tauri/tray-icon".into()) || enabled_features.contains(&"tauri/tray-icon".into())
{ {
let (tray_kind, path) = std::env::var("TAURI_LINUX_AYATANA_APPINDICATOR") let (tray_kind, path) = std::env::var_os("TAURI_LINUX_AYATANA_APPINDICATOR")
.map(|ayatana| { .map(|ayatana| {
if ayatana == "true" || ayatana == "1" { if ayatana == "true" || ayatana == "1" {
( (
@@ -1402,7 +1403,7 @@ fn tauri_config_to_bundle_settings(
) )
} }
}) })
.unwrap_or_else(|_| pkgconfig_utils::get_appindicator_library_path()); .unwrap_or_else(pkgconfig_utils::get_appindicator_library_path);
match tray_kind { match tray_kind {
pkgconfig_utils::TrayKind::Ayatana => { pkgconfig_utils::TrayKind::Ayatana => {
depends_deb.push("libayatana-appindicator3-1".into()); depends_deb.push("libayatana-appindicator3-1".into());

View File

@@ -29,7 +29,7 @@ pub struct DevChild {
impl DevProcess for DevChild { impl DevProcess for DevChild {
fn kill(&self) -> std::io::Result<()> { fn kill(&self) -> std::io::Result<()> {
self.dev_child.kill()?; self.dev_child.kill()?;
self.manually_killed_app.store(true, Ordering::Relaxed); self.manually_killed_app.store(true, Ordering::SeqCst);
Ok(()) Ok(())
} }
@@ -42,17 +42,17 @@ impl DevProcess for DevChild {
} }
fn manually_killed_process(&self) -> bool { fn manually_killed_process(&self) -> bool {
self.manually_killed_app.load(Ordering::Relaxed) self.manually_killed_app.load(Ordering::SeqCst)
} }
} }
pub fn run_dev<F: Fn(Option<i32>, ExitReason) + Send + Sync + 'static>( pub fn run_dev<F: Fn(Option<i32>, ExitReason) + Send + Sync + 'static>(
options: Options, options: Options,
run_args: Vec<String>, run_args: &[String],
available_targets: &mut Option<Vec<RustupTarget>>, available_targets: &mut Option<Vec<RustupTarget>>,
config_features: Vec<String>, config_features: Vec<String>,
on_exit: F, on_exit: F,
) -> crate::Result<impl DevProcess> { ) -> crate::Result<DevChild> {
let mut dev_cmd = cargo_command(true, options, available_targets, config_features)?; let mut dev_cmd = cargo_command(true, options, available_targets, config_features)?;
let runner = dev_cmd.get_program().to_string_lossy().into_owned(); let runner = dev_cmd.get_program().to_string_lossy().into_owned();
@@ -137,7 +137,7 @@ pub fn run_dev<F: Fn(Option<i32>, ExitReason) + Send + Sync + 'static>(
status.code(), status.code(),
if status.code() == Some(101) && is_cargo_compile_error { if status.code() == Some(101) && is_cargo_compile_error {
ExitReason::CompilationFailed ExitReason::CompilationFailed
} else if manually_killed_app_.load(Ordering::Relaxed) { } else if manually_killed_app_.load(Ordering::SeqCst) {
ExitReason::TriggeredKill ExitReason::TriggeredKill
} else { } else {
ExitReason::NormalExit ExitReason::NormalExit
@@ -163,7 +163,7 @@ pub fn build(
let out_dir = app_settings.out_dir(&options, tauri_dir)?; let out_dir = app_settings.out_dir(&options, tauri_dir)?;
let bin_path = app_settings.app_binary_path(&options, tauri_dir)?; let bin_path = app_settings.app_binary_path(&options, tauri_dir)?;
if !std::env::var("STATIC_VCRUNTIME").is_ok_and(|v| v == "false") { if !std::env::var_os("STATIC_VCRUNTIME").is_some_and(|v| v == "false") {
std::env::set_var("STATIC_VCRUNTIME", "true"); std::env::set_var("STATIC_VCRUNTIME", "true");
} }

View File

@@ -62,20 +62,17 @@ pub fn command(options: Options) -> Result<()> {
)? )?
}; };
let (config, metadata) = { let (config, metadata) = get_config(
let (config, metadata) = get_config( &get_app(
&get_app( MobileTarget::Android,
MobileTarget::Android,
&tauri_config,
&AppInterface::new(&tauri_config, None, dirs.tauri)?,
dirs.tauri,
),
&tauri_config, &tauri_config,
&[], &AppInterface::new(&tauri_config, None, dirs.tauri)?,
&cli_options, dirs.tauri,
); ),
(config, metadata) &tauri_config,
}; &[],
&cli_options,
);
ensure_init( ensure_init(
&tauri_config, &tauri_config,

View File

@@ -153,19 +153,16 @@ pub fn run(
.unwrap(); .unwrap();
build_options.target = Some(first_target.triple.into()); build_options.target = Some(first_target.triple.into());
let (interface, config, metadata) = { let interface = AppInterface::new(tauri_config, build_options.target.clone(), dirs.tauri)?;
let interface = AppInterface::new(tauri_config, build_options.target.clone(), dirs.tauri)?; interface.build_options(&mut Vec::new(), &mut build_options.features, true);
interface.build_options(&mut Vec::new(), &mut build_options.features, true);
let app = get_app(MobileTarget::Android, tauri_config, &interface, dirs.tauri); let app = get_app(MobileTarget::Android, tauri_config, &interface, dirs.tauri);
let (config, metadata) = get_config( let (config, metadata) = get_config(
&app, &app,
tauri_config, tauri_config,
&build_options.features, &build_options.features,
&Default::default(), &Default::default(),
); );
(interface, config, metadata)
};
let profile = if options.debug { let profile = if options.debug {
Profile::Debug Profile::Debug

View File

@@ -183,18 +183,15 @@ fn run_command(options: Options, noise_level: NoiseLevel, dirs: Dirs) -> Result<
.unwrap_or_else(|| Target::all().values().next().unwrap().triple.into()); .unwrap_or_else(|| Target::all().values().next().unwrap().triple.into());
dev_options.target = Some(target_triple); dev_options.target = Some(target_triple);
let (interface, config, metadata) = { let interface = AppInterface::new(&tauri_config, dev_options.target.clone(), dirs.tauri)?;
let interface = AppInterface::new(&tauri_config, dev_options.target.clone(), dirs.tauri)?;
let app = get_app(MobileTarget::Android, &tauri_config, &interface, dirs.tauri); let app = get_app(MobileTarget::Android, &tauri_config, &interface, dirs.tauri);
let (config, metadata) = get_config( let (config, metadata) = get_config(
&app, &app,
&tauri_config, &tauri_config,
dev_options.features.as_ref(), dev_options.features.as_ref(),
&Default::default(), &Default::default(),
); );
(interface, config, metadata)
};
set_current_dir(dirs.tauri).context("failed to set current directory to Tauri directory")?; set_current_dir(dirs.tauri).context("failed to set current directory to Tauri directory")?;

View File

@@ -194,20 +194,17 @@ pub fn run(options: Options, noise_level: NoiseLevel, dirs: &Dirs) -> Result<Bui
&options.config.iter().map(|c| &c.0).collect::<Vec<_>>(), &options.config.iter().map(|c| &c.0).collect::<Vec<_>>(),
dirs.tauri, dirs.tauri,
)?; )?;
let (interface, mut config) = { let interface = AppInterface::new(&tauri_config, build_options.target.clone(), dirs.tauri)?;
let interface = AppInterface::new(&tauri_config, build_options.target.clone(), dirs.tauri)?; interface.build_options(&mut Vec::new(), &mut build_options.features, true);
interface.build_options(&mut Vec::new(), &mut build_options.features, true);
let app = get_app(MobileTarget::Ios, &tauri_config, &interface, dirs.tauri); let app = get_app(MobileTarget::Ios, &tauri_config, &interface, dirs.tauri);
let (config, _metadata) = get_config( let (mut config, _) = get_config(
&app, &app,
&tauri_config, &tauri_config,
&build_options.features, &build_options.features,
&Default::default(), &Default::default(),
dirs.tauri, dirs.tauri,
)?; )?;
(interface, config)
};
set_current_dir(dirs.tauri).context("failed to set current directory")?; set_current_dir(dirs.tauri).context("failed to set current directory")?;

View File

@@ -188,20 +188,16 @@ fn run_command(options: Options, noise_level: NoiseLevel, dirs: Dirs) -> Result<
&options.config.iter().map(|c| &c.0).collect::<Vec<_>>(), &options.config.iter().map(|c| &c.0).collect::<Vec<_>>(),
dirs.tauri, dirs.tauri,
)?; )?;
let (interface, config) = { let interface = AppInterface::new(&tauri_config, Some(target_triple), dirs.tauri)?;
let interface = AppInterface::new(&tauri_config, Some(target_triple), dirs.tauri)?;
let app = get_app(MobileTarget::Ios, &tauri_config, &interface, dirs.tauri); let app = get_app(MobileTarget::Ios, &tauri_config, &interface, dirs.tauri);
let (config, _metadata) = get_config( let (config, _) = get_config(
&app, &app,
&tauri_config, &tauri_config,
&dev_options.features, &dev_options.features,
&Default::default(), &Default::default(),
dirs.tauri, dirs.tauri,
)?; )?;
(interface, config)
};
set_current_dir(dirs.tauri).context("failed to set current directory to Tauri directory")?; set_current_dir(dirs.tauri).context("failed to set current directory to Tauri directory")?;

View File

@@ -95,40 +95,33 @@ pub fn command(options: Options) -> Result<()> {
let macos = macos_from_platform(&options.platform); let macos = macos_from_platform(&options.platform);
let mut tauri_config = get_tauri_config(tauri_utils::platform::Target::Ios, &[], dirs.tauri)?; let mut tauri_config = get_tauri_config(tauri_utils::platform::Target::Ios, &[], dirs.tauri)?;
let cli_options = { let cli_options = read_options(&tauri_config);
let cli_options = { read_options(&tauri_config) }; if !cli_options.config.is_empty() {
if !cli_options.config.is_empty() { // reload config with merges from the ios dev|build script
// reload config with merges from the ios dev|build script reload_tauri_config(
reload_tauri_config( &mut tauri_config,
&mut tauri_config, &cli_options
&cli_options .config
.config .iter()
.iter() .map(|conf| &conf.0)
.map(|conf| &conf.0) .collect::<Vec<_>>(),
.collect::<Vec<_>>(),
dirs.tauri,
)?
};
cli_options
};
let (config, metadata) = {
let cli_options = read_options(&tauri_config);
let (config, metadata) = get_config(
&get_app(
MobileTarget::Ios,
&tauri_config,
&AppInterface::new(&tauri_config, None, dirs.tauri)?,
dirs.tauri,
),
&tauri_config,
&[],
&cli_options,
dirs.tauri, dirs.tauri,
)?; )?
(config, metadata)
}; };
let (config, metadata) = get_config(
&get_app(
MobileTarget::Ios,
&tauri_config,
&AppInterface::new(&tauri_config, None, dirs.tauri)?,
dirs.tauri,
),
&tauri_config,
&[],
&cli_options,
dirs.tauri,
)?;
ensure_init( ensure_init(
&tauri_config, &tauri_config,
config.app(), config.app(),

View File

@@ -67,14 +67,9 @@ impl DevChild {
impl DevProcess for DevChild { impl DevProcess for DevChild {
fn kill(&self) -> std::io::Result<()> { fn kill(&self) -> std::io::Result<()> {
self.manually_killed_process.store(true, Ordering::Relaxed); self.child.kill()?;
match self.child.kill() { self.manually_killed_process.store(true, Ordering::SeqCst);
Ok(_) => Ok(()), Ok(())
Err(e) => {
self.manually_killed_process.store(false, Ordering::Relaxed);
Err(e)
}
}
} }
fn try_wait(&self) -> std::io::Result<Option<ExitStatus>> { fn try_wait(&self) -> std::io::Result<Option<ExitStatus>> {
@@ -86,7 +81,7 @@ impl DevProcess for DevChild {
} }
fn manually_killed_process(&self) -> bool { fn manually_killed_process(&self) -> bool {
self.manually_killed_process.load(Ordering::Relaxed) self.manually_killed_process.load(Ordering::SeqCst)
} }
} }