From 64416100c6bb138e92acea1928ea3281d9a53281 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Tue, 5 Jul 2022 10:21:20 -0300 Subject: [PATCH] refactor(cli): reuse Rust build logic for dev and build commands (#4578) --- tooling/cli/src/dev.rs | 2 +- tooling/cli/src/interface/rust.rs | 271 ++++++++++++++---------------- 2 files changed, 128 insertions(+), 145 deletions(-) diff --git a/tooling/cli/src/dev.rs b/tooling/cli/src/dev.rs index e2e9b84a4..79e4ce558 100644 --- a/tooling/cli/src/dev.rs +++ b/tooling/cli/src/dev.rs @@ -20,7 +20,6 @@ use shared_child::SharedChild; use std::{ env::set_current_dir, - io::Write, process::{exit, Command, ExitStatus, Stdio}, sync::{ atomic::{AtomicBool, Ordering}, @@ -284,6 +283,7 @@ fn kill_before_dev_process() { .status(); #[cfg(unix)] { + use std::io::Write; let mut kill_children_script_path = std::env::temp_dir(); kill_children_script_path.push("kill-children.sh"); diff --git a/tooling/cli/src/interface/rust.rs b/tooling/cli/src/interface/rust.rs index 0326e93d5..1191c34b6 100644 --- a/tooling/cli/src/interface/rust.rs +++ b/tooling/cli/src/interface/rust.rs @@ -145,12 +145,18 @@ impl Interface for Rust { &self.app_settings } - fn build(&mut self, options: Options) -> crate::Result<()> { + fn build(&mut self, mut options: Options) -> crate::Result<()> { let bin_path = self.app_settings.app_binary_path(&options)?; let out_dir = bin_path.parent().unwrap(); let bin_name = bin_path.file_stem().unwrap(); + options + .features + .get_or_insert(Vec::new()) + .push("custom-protocol".into()); + std::env::set_var("STATIC_VCRUNTIME", "true"); + if options.target == Some("universal-apple-darwin".into()) { std::fs::create_dir_all(&out_dir) .with_context(|| "failed to create project out directory")?; @@ -169,7 +175,7 @@ impl Interface for Rust { .out_dir(Some(triple.into()), options.debug) .with_context(|| format!("failed to get {} out dir", triple))?; self - .build_app(options) + .build_app_blocking(options) .with_context(|| format!("failed to build {} binary", triple))?; lipo_cmd.arg(triple_out_dir.join(&bin_name)); @@ -184,11 +190,11 @@ impl Interface for Rust { } } else { self - .build_app(options) + .build_app_blocking(options) .with_context(|| "failed to build app")?; } - rename_app(bin_path, self.product_name.as_deref())?; + rename_app(&bin_path, self.product_name.as_deref())?; Ok(()) } @@ -271,37 +277,12 @@ impl Rust { fn run_dev( &mut self, - options: Options, + mut options: Options, on_exit: F, ) -> crate::Result { let bin_path = self.app_settings.app_binary_path(&options)?; let product_name = self.product_name.clone(); - let runner = options.runner.unwrap_or_else(|| "cargo".into()); - - if let Some(target) = &options.target { - self.fetch_available_targets(); - self.validate_target(target)?; - } - - let mut build_cmd = Command::new(&runner); - build_cmd - .env( - "CARGO_TERM_PROGRESS_WIDTH", - terminal::stderr_width() - .map(|width| { - if cfg!(windows) { - std::cmp::min(60, width) - } else { - width - } - }) - .unwrap_or(if cfg!(windows) { 60 } else { 80 }) - .to_string(), - ) - .env("CARGO_TERM_PROGRESS_WHEN", "always"); - build_cmd.arg("build").arg("--color").arg("always"); - if !options.args.contains(&"--no-default-features".into()) { let manifest_features = self.app_settings.manifest.features(); let enable_features: Vec = manifest_features @@ -317,28 +298,16 @@ impl Rust { } }) .collect(); - build_cmd.arg("--no-default-features"); + options.args.push("--no-default-features".into()); if !enable_features.is_empty() { - build_cmd.args(&["--features", &enable_features.join(",")]); + options + .features + .get_or_insert(Vec::new()) + .extend(enable_features); } } - if !options.debug { - build_cmd.args(&["--release"]); - } - - if let Some(target) = &options.target { - build_cmd.args(&["--target", target]); - } - - let mut features = self.config_features.clone(); - if let Some(f) = options.features { - features.extend(f); - } - if !features.is_empty() { - build_cmd.args(&["--features", &features.join(",")]); - } - + let mut args = Vec::new(); let mut run_args = Vec::new(); let mut reached_run_args = false; for arg in options.args.clone() { @@ -347,66 +316,20 @@ impl Rust { } else if arg == "--" { reached_run_args = true; } else { - build_cmd.arg(arg); + args.push(arg); } } - - build_cmd.stdout(os_pipe::dup_stdout()?); - build_cmd.stderr(Stdio::piped()); + options.args = args; let manually_killed_app = Arc::new(AtomicBool::default()); let manually_killed_app_ = manually_killed_app.clone(); - - let build_child = match SharedChild::spawn(&mut build_cmd) { - Ok(c) => c, - Err(e) => { - if e.kind() == ErrorKind::NotFound { - return Err(anyhow::anyhow!( - "`{}` command not found.{}", - runner, - if runner == "cargo" { - " Please follow the Tauri setup guide: https://tauri.app/v1/guides/getting-started/prerequisites" - } else { - "" - } - )); - } else { - return Err(e.into()); - } - } - }; - let build_child = Arc::new(build_child); - let build_child_stderr = build_child.take_stderr().unwrap(); - let mut stderr = BufReader::new(build_child_stderr); - let stderr_lines = Arc::new(Mutex::new(Vec::new())); - let stderr_lines_ = stderr_lines.clone(); - std::thread::spawn(move || { - let mut buf = Vec::new(); - let mut lines = stderr_lines_.lock().unwrap(); - let mut io_stderr = std::io::stderr(); - loop { - buf.clear(); - match tauri_utils::io::read_line(&mut stderr, &mut buf) { - Ok(s) if s == 0 => break, - _ => (), - } - let _ = io_stderr.write_all(&buf); - if !buf.ends_with(&[b'\r']) { - let _ = io_stderr.write_all(b"\n"); - } - lines.push(String::from_utf8_lossy(&buf).into_owned()); - } - }); - - let build_child_ = build_child.clone(); let app_child = Arc::new(Mutex::new(None)); let app_child_ = app_child.clone(); - std::thread::spawn(move || { - let status = build_child_.wait().expect("failed to wait on build"); + let build_child = self.build_app(options, move |status, reason| { if status.success() { - let bin_path = rename_app(bin_path, product_name.as_deref()).expect("failed to rename app"); - + let bin_path = + rename_app(&bin_path, product_name.as_deref()).expect("failed to rename app"); let mut app = Command::new(bin_path); app.stdout(os_pipe::dup_stdout().unwrap()); app.stderr(os_pipe::dup_stderr().unwrap()); @@ -427,24 +350,9 @@ impl Rust { app_child_.lock().unwrap().replace(app_child); } else { - let is_cargo_compile_error = stderr_lines - .lock() - .unwrap() - .last() - .map(|l| l.contains("could not compile")) - .unwrap_or_default(); - stderr_lines.lock().unwrap().clear(); - - on_exit( - status, - if status.code() == Some(101) && is_cargo_compile_error { - ExitReason::CompilationFailed - } else { - ExitReason::NormalExit - }, - ); + on_exit(status, reason); } - }); + })?; Ok(DevChild { manually_killed_app, @@ -537,7 +445,22 @@ impl Rust { } } - fn build_app(&mut self, options: Options) -> crate::Result<()> { + fn build_app_blocking(&mut self, options: Options) -> crate::Result<()> { + let (tx, rx) = channel(); + self.build_app(options, move |status, _| tx.send(status).unwrap())?; + let status = rx.recv().unwrap(); + if status.success() { + Ok(()) + } else { + Err(anyhow::anyhow!("failed to build app")) + } + } + + fn build_app( + &mut self, + options: Options, + on_exit: F, + ) -> crate::Result> { let runner = options.runner.unwrap_or_else(|| "cargo".into()); if let Some(target) = &options.target { @@ -552,11 +475,13 @@ impl Rust { args.extend(options.args); } - if let Some(features) = options.features { - if !features.is_empty() { - args.push("--features".into()); - args.push(features.join(",")); - } + let mut features = self.config_features.clone(); + if let Some(f) = options.features { + features.extend(f); + } + if !features.is_empty() { + args.push("--features".into()); + args.push(features.join(",")); } if !options.debug { @@ -568,25 +493,33 @@ impl Rust { args.push(target); } - match Command::new(&runner) - .args(&["build", "--features=custom-protocol"]) - .args(args) - .env("STATIC_VCRUNTIME", "true") - .piped() - { - Ok(status) => { - if status.success() { - Ok(()) - } else { - Err(anyhow::anyhow!( - "Result of `{} build` operation was unsuccessful", - runner - )) - } - } + let mut build_cmd = Command::new(&runner); + build_cmd + .env( + "CARGO_TERM_PROGRESS_WIDTH", + terminal::stderr_width() + .map(|width| { + if cfg!(windows) { + std::cmp::min(60, width) + } else { + width + } + }) + .unwrap_or(if cfg!(windows) { 60 } else { 80 }) + .to_string(), + ) + .env("CARGO_TERM_PROGRESS_WHEN", "always"); + build_cmd.arg("build").arg("--color").arg("always"); + build_cmd.args(args); + + build_cmd.stdout(os_pipe::dup_stdout()?); + build_cmd.stderr(Stdio::piped()); + + let build_child = match SharedChild::spawn(&mut build_cmd) { + Ok(c) => c, Err(e) => { if e.kind() == ErrorKind::NotFound { - Err(anyhow::anyhow!( + return Err(anyhow::anyhow!( "`{}` command not found.{}", runner, if runner == "cargo" { @@ -594,12 +527,62 @@ impl Rust { } else { "" } - )) + )); } else { - Err(e.into()) + return Err(e.into()); } } - } + }; + let build_child = Arc::new(build_child); + let build_child_stderr = build_child.take_stderr().unwrap(); + let mut stderr = BufReader::new(build_child_stderr); + let stderr_lines = Arc::new(Mutex::new(Vec::new())); + let stderr_lines_ = stderr_lines.clone(); + std::thread::spawn(move || { + let mut buf = Vec::new(); + let mut lines = stderr_lines_.lock().unwrap(); + let mut io_stderr = std::io::stderr(); + loop { + buf.clear(); + match tauri_utils::io::read_line(&mut stderr, &mut buf) { + Ok(s) if s == 0 => break, + _ => (), + } + let _ = io_stderr.write_all(&buf); + if !buf.ends_with(&[b'\r']) { + let _ = io_stderr.write_all(b"\n"); + } + lines.push(String::from_utf8_lossy(&buf).into_owned()); + } + }); + + let build_child_ = build_child.clone(); + std::thread::spawn(move || { + let status = build_child_.wait().expect("failed to wait on build"); + + if status.success() { + on_exit(status, ExitReason::NormalExit); + } else { + let is_cargo_compile_error = stderr_lines + .lock() + .unwrap() + .last() + .map(|l| l.contains("could not compile")) + .unwrap_or_default(); + stderr_lines.lock().unwrap().clear(); + + on_exit( + status, + if status.code() == Some(101) && is_cargo_compile_error { + ExitReason::CompilationFailed + } else { + ExitReason::NormalExit + }, + ); + } + }); + + Ok(build_child) } } @@ -1123,7 +1106,7 @@ fn tauri_config_to_bundle_settings( }) } -fn rename_app(bin_path: PathBuf, product_name: Option<&str>) -> crate::Result { +fn rename_app(bin_path: &Path, product_name: Option<&str>) -> crate::Result { if let Some(product_name) = product_name { #[cfg(target_os = "linux")] let product_name = product_name.to_kebab_case(); @@ -1143,7 +1126,7 @@ fn rename_app(bin_path: PathBuf, product_name: Option<&str>) -> crate::Result