Forráskód Böngészése

refactor(cli): Use `cargo metadata` to detect the workspace root and target directory, closes #4632, #4928. (#4932)

Fabian-Lars 3 éve
szülő
commit
fea70effad

+ 6 - 0
.changes/cli-detect-target-dir.md

@@ -0,0 +1,6 @@
+---
+"cli.rs": "patch"
+"cli.js": "patch"
+---
+
+Use `cargo metadata` to detect the workspace root and target directory.

+ 4 - 7
tooling/cli/src/info.rs

@@ -794,13 +794,10 @@ pub fn command(_options: Options) -> Result<()> {
         } else {
           None
         };
-      let lock: Option<CargoLock> = if let Ok(lock_contents) =
-        read_to_string(get_workspace_dir(&tauri_dir).join("Cargo.lock"))
-      {
-        toml::from_str(&lock_contents).ok()
-      } else {
-        None
-      };
+      let lock: Option<CargoLock> = get_workspace_dir()
+        .ok()
+        .and_then(|p| read_to_string(p.join("Cargo.lock")).ok())
+        .and_then(|s| toml::from_str(&s).ok());
 
       for (dep, label) in [
         ("tauri", format!("{} {}", "tauri", "[RUST]".dimmed())),

+ 39 - 96
tooling/cli/src/interface/rust.rs

@@ -7,7 +7,7 @@ use std::{
   fs::{File, FileType},
   io::{Read, Write},
   path::{Path, PathBuf},
-  process::ExitStatus,
+  process::{Command, ExitStatus},
   str::FromStr,
   sync::{
     atomic::{AtomicBool, Ordering},
@@ -20,7 +20,6 @@ use std::{
 use anyhow::Context;
 #[cfg(target_os = "linux")]
 use heck::ToKebabCase;
-use log::warn;
 use log::{debug, info};
 use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
 use serde::Deserialize;
@@ -287,7 +286,7 @@ impl Rust {
     let process = Arc::new(Mutex::new(child));
     let (tx, rx) = channel();
     let tauri_path = tauri_dir();
-    let workspace_path = get_workspace_dir(&tauri_path);
+    let workspace_path = get_workspace_dir()?;
 
     let watch_folders = if tauri_path == workspace_path {
       vec![tauri_path]
@@ -423,17 +422,6 @@ impl CargoSettings {
   }
 }
 
-#[derive(Deserialize)]
-struct CargoBuildConfig {
-  #[serde(rename = "target-dir")]
-  target_dir: Option<String>,
-}
-
-#[derive(Deserialize)]
-struct CargoConfig {
-  build: Option<CargoBuildConfig>,
-}
-
 pub struct RustAppSettings {
   manifest: Manifest,
   cargo_settings: CargoSettings,
@@ -639,100 +627,55 @@ impl RustAppSettings {
   }
 
   pub fn out_dir(&self, target: Option<String>, debug: bool) -> crate::Result<PathBuf> {
-    let tauri_dir = tauri_dir();
-    let workspace_dir = get_workspace_dir(&tauri_dir);
-    get_target_dir(&workspace_dir, target, !debug)
+    get_target_dir(target, !debug)
+  }
+}
+
+#[derive(Deserialize)]
+struct CargoMetadata {
+  target_directory: PathBuf,
+  workspace_root: PathBuf,
+}
+
+fn get_cargo_metadata() -> crate::Result<CargoMetadata> {
+  let output = Command::new("cargo")
+    .args(["metadata", "--no-deps", "--format-version", "1"])
+    .current_dir(tauri_dir())
+    .output()?;
+
+  if !output.status.success() {
+    return Err(anyhow::anyhow!(
+      "cargo metadata command exited with a non zero exit code: {}",
+      String::from_utf8(output.stderr)?
+    ));
   }
+
+  Ok(serde_json::from_slice(&output.stdout)?)
 }
 
-/// This function determines where 'target' dir is and suffixes it with 'release' or 'debug'
+/// This function determines the 'target' directory and suffixes it with 'release' or 'debug'
 /// to determine where the compiled binary will be located.
-fn get_target_dir(
-  project_root_dir: &Path,
-  target: Option<String>,
-  is_release: bool,
-) -> crate::Result<PathBuf> {
-  let mut path: PathBuf = match std::env::var_os("CARGO_TARGET_DIR") {
-    Some(target_dir) => target_dir.into(),
-    None => {
-      let mut root_dir = project_root_dir.to_path_buf();
-      let target_path: Option<PathBuf> = loop {
-        // cargo reads configs under .cargo/config.toml or .cargo/config
-        let mut cargo_config_path = root_dir.join(".cargo/config");
-        if !cargo_config_path.exists() {
-          cargo_config_path = root_dir.join(".cargo/config.toml");
-        }
-        // if the path exists, parse it
-        if cargo_config_path.exists() {
-          let mut config_str = String::new();
-          let mut config_file = File::open(&cargo_config_path)
-            .with_context(|| format!("failed to open {:?}", cargo_config_path))?;
-          config_file
-            .read_to_string(&mut config_str)
-            .with_context(|| "failed to read cargo config file")?;
-          let config: CargoConfig =
-            toml::from_str(&config_str).with_context(|| "failed to parse cargo config file")?;
-          if let Some(build) = config.build {
-            if let Some(target_dir) = build.target_dir {
-              break Some(target_dir.into());
-            }
-          }
-        }
-        if !root_dir.pop() {
-          break None;
-        }
-      };
-      target_path.unwrap_or_else(|| project_root_dir.join("target"))
-    }
-  };
+fn get_target_dir(target: Option<String>, is_release: bool) -> crate::Result<PathBuf> {
+  let mut path = get_cargo_metadata()
+    .with_context(|| "failed to get cargo metadata")?
+    .target_directory;
 
   if let Some(ref triple) = target {
     path.push(triple);
   }
+
   path.push(if is_release { "release" } else { "debug" });
+
   Ok(path)
 }
 
-/// Walks up the file system, looking for a Cargo.toml file
-/// If one is found before reaching the root, then the current_dir's package belongs to that parent workspace if it's listed on [workspace.members].
-///
-/// If this package is part of a workspace, returns the path to the workspace directory
-/// Otherwise returns the current directory.
-pub fn get_workspace_dir(current_dir: &Path) -> PathBuf {
-  let mut dir = current_dir.to_path_buf();
-  let project_path = dir.clone();
-
-  while dir.pop() {
-    if dir.join("Cargo.toml").exists() {
-      match CargoSettings::load(&dir) {
-        Ok(cargo_settings) => {
-          if let Some(workspace_settings) = cargo_settings.workspace {
-            if let Some(members) = workspace_settings.members {
-              if members.iter().any(|member| {
-                glob::glob(&dir.join(member).to_string_lossy())
-                  .unwrap()
-                  .any(|p| p.unwrap() == project_path)
-              }) {
-                return dir;
-              }
-            }
-          }
-        }
-        Err(e) => {
-          warn!(
-              "Found `{}`, which may define a parent workspace, but \
-            failed to parse it. If this is indeed a parent workspace, undefined behavior may occur: \
-            \n    {:#}",
-              dir.display(),
-              e
-            );
-        }
-      }
-    }
-  }
-
-  // Nothing found walking up the file system, return the starting directory
-  current_dir.to_path_buf()
+/// Executes `cargo metadata` to get the workspace directory.
+pub fn get_workspace_dir() -> crate::Result<PathBuf> {
+  Ok(
+    get_cargo_metadata()
+      .with_context(|| "failed to get cargo metadata")?
+      .workspace_root,
+  )
 }
 
 #[allow(unused_variables)]