Эх сурвалжийг харах

refactor(cli): reuse Rust build logic for dev and build commands (#4578)

Lucas Fernandes Nogueira 3 жил өмнө
parent
commit
64416100c6

+ 1 - 1
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");
 

+ 127 - 144
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<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
     &mut self,
-    options: Options,
+    mut options: Options,
     on_exit: F,
   ) -> crate::Result<DevChild> {
     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<String> = 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<F: FnOnce(ExitStatus, ExitReason) + Send + 'static>(
+    &mut self,
+    options: Options,
+    on_exit: F,
+  ) -> crate::Result<Arc<SharedChild>> {
     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<PathBuf> {
+fn rename_app(bin_path: &Path, product_name: Option<&str>) -> crate::Result<PathBuf> {
   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<Pa
     })?;
     Ok(product_path)
   } else {
-    Ok(bin_path)
+    Ok(bin_path.to_path_buf())
   }
 }