Skip to content

Commit fea70ef

Browse files
authored
refactor(cli): Use cargo metadata to detect the workspace root and target directory, closes #4632, #4928. (#4932)
1 parent e16b366 commit fea70ef

File tree

3 files changed

+49
-103
lines changed

3 files changed

+49
-103
lines changed

.changes/cli-detect-target-dir.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"cli.rs": "patch"
3+
"cli.js": "patch"
4+
---
5+
6+
Use `cargo metadata` to detect the workspace root and target directory.

tooling/cli/src/info.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -794,13 +794,10 @@ pub fn command(_options: Options) -> Result<()> {
794794
} else {
795795
None
796796
};
797-
let lock: Option<CargoLock> = if let Ok(lock_contents) =
798-
read_to_string(get_workspace_dir(&tauri_dir).join("Cargo.lock"))
799-
{
800-
toml::from_str(&lock_contents).ok()
801-
} else {
802-
None
803-
};
797+
let lock: Option<CargoLock> = get_workspace_dir()
798+
.ok()
799+
.and_then(|p| read_to_string(p.join("Cargo.lock")).ok())
800+
.and_then(|s| toml::from_str(&s).ok());
804801

805802
for (dep, label) in [
806803
("tauri", format!("{} {}", "tauri", "[RUST]".dimmed())),

tooling/cli/src/interface/rust.rs

Lines changed: 39 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{
77
fs::{File, FileType},
88
io::{Read, Write},
99
path::{Path, PathBuf},
10-
process::ExitStatus,
10+
process::{Command, ExitStatus},
1111
str::FromStr,
1212
sync::{
1313
atomic::{AtomicBool, Ordering},
@@ -20,7 +20,6 @@ use std::{
2020
use anyhow::Context;
2121
#[cfg(target_os = "linux")]
2222
use heck::ToKebabCase;
23-
use log::warn;
2423
use log::{debug, info};
2524
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
2625
use serde::Deserialize;
@@ -287,7 +286,7 @@ impl Rust {
287286
let process = Arc::new(Mutex::new(child));
288287
let (tx, rx) = channel();
289288
let tauri_path = tauri_dir();
290-
let workspace_path = get_workspace_dir(&tauri_path);
289+
let workspace_path = get_workspace_dir()?;
291290

292291
let watch_folders = if tauri_path == workspace_path {
293292
vec![tauri_path]
@@ -423,17 +422,6 @@ impl CargoSettings {
423422
}
424423
}
425424

426-
#[derive(Deserialize)]
427-
struct CargoBuildConfig {
428-
#[serde(rename = "target-dir")]
429-
target_dir: Option<String>,
430-
}
431-
432-
#[derive(Deserialize)]
433-
struct CargoConfig {
434-
build: Option<CargoBuildConfig>,
435-
}
436-
437425
pub struct RustAppSettings {
438426
manifest: Manifest,
439427
cargo_settings: CargoSettings,
@@ -639,100 +627,55 @@ impl RustAppSettings {
639627
}
640628

641629
pub fn out_dir(&self, target: Option<String>, debug: bool) -> crate::Result<PathBuf> {
642-
let tauri_dir = tauri_dir();
643-
let workspace_dir = get_workspace_dir(&tauri_dir);
644-
get_target_dir(&workspace_dir, target, !debug)
630+
get_target_dir(target, !debug)
631+
}
632+
}
633+
634+
#[derive(Deserialize)]
635+
struct CargoMetadata {
636+
target_directory: PathBuf,
637+
workspace_root: PathBuf,
638+
}
639+
640+
fn get_cargo_metadata() -> crate::Result<CargoMetadata> {
641+
let output = Command::new("cargo")
642+
.args(["metadata", "--no-deps", "--format-version", "1"])
643+
.current_dir(tauri_dir())
644+
.output()?;
645+
646+
if !output.status.success() {
647+
return Err(anyhow::anyhow!(
648+
"cargo metadata command exited with a non zero exit code: {}",
649+
String::from_utf8(output.stderr)?
650+
));
645651
}
652+
653+
Ok(serde_json::from_slice(&output.stdout)?)
646654
}
647655

648-
/// This function determines where 'target' dir is and suffixes it with 'release' or 'debug'
656+
/// This function determines the 'target' directory and suffixes it with 'release' or 'debug'
649657
/// to determine where the compiled binary will be located.
650-
fn get_target_dir(
651-
project_root_dir: &Path,
652-
target: Option<String>,
653-
is_release: bool,
654-
) -> crate::Result<PathBuf> {
655-
let mut path: PathBuf = match std::env::var_os("CARGO_TARGET_DIR") {
656-
Some(target_dir) => target_dir.into(),
657-
None => {
658-
let mut root_dir = project_root_dir.to_path_buf();
659-
let target_path: Option<PathBuf> = loop {
660-
// cargo reads configs under .cargo/config.toml or .cargo/config
661-
let mut cargo_config_path = root_dir.join(".cargo/config");
662-
if !cargo_config_path.exists() {
663-
cargo_config_path = root_dir.join(".cargo/config.toml");
664-
}
665-
// if the path exists, parse it
666-
if cargo_config_path.exists() {
667-
let mut config_str = String::new();
668-
let mut config_file = File::open(&cargo_config_path)
669-
.with_context(|| format!("failed to open {:?}", cargo_config_path))?;
670-
config_file
671-
.read_to_string(&mut config_str)
672-
.with_context(|| "failed to read cargo config file")?;
673-
let config: CargoConfig =
674-
toml::from_str(&config_str).with_context(|| "failed to parse cargo config file")?;
675-
if let Some(build) = config.build {
676-
if let Some(target_dir) = build.target_dir {
677-
break Some(target_dir.into());
678-
}
679-
}
680-
}
681-
if !root_dir.pop() {
682-
break None;
683-
}
684-
};
685-
target_path.unwrap_or_else(|| project_root_dir.join("target"))
686-
}
687-
};
658+
fn get_target_dir(target: Option<String>, is_release: bool) -> crate::Result<PathBuf> {
659+
let mut path = get_cargo_metadata()
660+
.with_context(|| "failed to get cargo metadata")?
661+
.target_directory;
688662

689663
if let Some(ref triple) = target {
690664
path.push(triple);
691665
}
666+
692667
path.push(if is_release { "release" } else { "debug" });
668+
693669
Ok(path)
694670
}
695671

696-
/// Walks up the file system, looking for a Cargo.toml file
697-
/// 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].
698-
///
699-
/// If this package is part of a workspace, returns the path to the workspace directory
700-
/// Otherwise returns the current directory.
701-
pub fn get_workspace_dir(current_dir: &Path) -> PathBuf {
702-
let mut dir = current_dir.to_path_buf();
703-
let project_path = dir.clone();
704-
705-
while dir.pop() {
706-
if dir.join("Cargo.toml").exists() {
707-
match CargoSettings::load(&dir) {
708-
Ok(cargo_settings) => {
709-
if let Some(workspace_settings) = cargo_settings.workspace {
710-
if let Some(members) = workspace_settings.members {
711-
if members.iter().any(|member| {
712-
glob::glob(&dir.join(member).to_string_lossy())
713-
.unwrap()
714-
.any(|p| p.unwrap() == project_path)
715-
}) {
716-
return dir;
717-
}
718-
}
719-
}
720-
}
721-
Err(e) => {
722-
warn!(
723-
"Found `{}`, which may define a parent workspace, but \
724-
failed to parse it. If this is indeed a parent workspace, undefined behavior may occur: \
725-
\n {:#}",
726-
dir.display(),
727-
e
728-
);
729-
}
730-
}
731-
}
732-
}
733-
734-
// Nothing found walking up the file system, return the starting directory
735-
current_dir.to_path_buf()
672+
/// Executes `cargo metadata` to get the workspace directory.
673+
pub fn get_workspace_dir() -> crate::Result<PathBuf> {
674+
Ok(
675+
get_cargo_metadata()
676+
.with_context(|| "failed to get cargo metadata")?
677+
.workspace_root,
678+
)
736679
}
737680

738681
#[allow(unused_variables)]

0 commit comments

Comments
 (0)