From cd9a395c76cc9401314f990547f0178f874ad018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emil=20Gardstr=C3=B6m?= Date: Thu, 7 Apr 2022 17:34:24 +0200 Subject: [PATCH] make manifest path work with metadata contains a minor workaround for when logic is hard --- src/cargo.rs | 80 +++++++++++++++++++++++++++------------------------ src/docker.rs | 34 +++++++++++++++------- src/main.rs | 17 +++++------ src/tests.rs | 4 +-- 4 files changed, 76 insertions(+), 59 deletions(-) diff --git a/src/cargo.rs b/src/cargo.rs index f49cf1a58..f62c61d62 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -2,6 +2,8 @@ use serde::Deserialize; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; +use crate::cli::Args; +use crate::config::Config; use crate::errors::*; use crate::extensions::CommandExt; @@ -50,52 +52,54 @@ impl<'a> From<&'a str> for Subcommand { } } -#[derive(Debug)] -pub struct Root { - pub path: PathBuf, +#[derive(Debug, Deserialize)] +pub struct CargoMetadata { + pub workspace_root: PathBuf, } -impl Root { - pub fn path(&self) -> &Path { - &self.path +impl CargoMetadata { + pub fn workspace_root(&self) -> &Path { + &self.workspace_root } } -/// Cargo project root -pub fn root(cd: Option<&Path>, manifest_path: Option) -> Result> { - if let Some(manifest_path) = manifest_path { - if !manifest_path.is_file() { - eyre::bail!("No manifest found at \"{}\"", manifest_path.display()); +/// Cargo metadata with specific invocation +pub fn cargo_metadata_with_args( + cd: Option<&Path>, + args: Option<&Args>, +) -> Result> { + let mut command = std::process::Command::new( + std::env::var("CARGO") + .ok() + .unwrap_or_else(|| "cargo".to_string()), + ); + command.arg("metadata").arg("--format-version=1"); + if let Some(cd) = cd { + command.current_dir(cd); + } + if let Some(config) = args { + if let Some(ref manifest_path) = config.manifest_path { + command.args(["--manifest-path".as_ref(), manifest_path.as_os_str()]); } - return Ok(Some(Root { - path: manifest_path - .parent() - .expect("File must have a parent") - .to_owned(), - })); } else { - #[derive(Deserialize)] - struct Manifest { - workspace_root: PathBuf, - } - let mut command = std::process::Command::new( - std::env::var("CARGO") - .ok() - .unwrap_or_else(|| "cargo".to_string()), - ); - command - .arg("metadata") - .arg("--format-version=1") - .arg("--no-deps"); - if let Some(cd) = cd { - command.current_dir(cd); - } - let output = command.output()?; - let manifest: Option = serde_json::from_slice(&output.stdout)?; - Ok(manifest.map(|m| Root { - path: m.workspace_root, - })) + command.arg("--no-deps"); } + let output = command.output()?; + let manifest: Option = + serde_json::from_slice(&output.stdout).wrap_err_with(|| { + format!( + "{command:?} returned nothing. {:?}", + String::from_utf8(output.stderr) + ) + })?; + Ok(manifest.map(|m| CargoMetadata { + workspace_root: m.workspace_root, + })) +} + +/// Cargo metadata +pub fn cargo_metadata(cd: Option<&Path>) -> Result> { + cargo_metadata_with_args(cd, None) } /// Pass-through mode diff --git a/src/docker.rs b/src/docker.rs index cac40162b..8e8ab3044 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; use std::{env, fs}; -use crate::cargo::Root; +use crate::cargo::CargoMetadata; use crate::errors::*; use crate::extensions::{CommandExt, SafeCommand}; use crate::id; @@ -57,13 +57,13 @@ pub fn run( target: &Target, args: &[String], target_dir: &Option, - root: &Root, - docker_root: &Path, + metadata: &CargoMetadata, config: &Config, uses_xargo: bool, sysroot: &Path, verbose: bool, docker_in_docker: bool, + cwd: &Path, ) -> Result { let mount_finder = if docker_in_docker { MountFinder::new(docker_read_mount_paths()?) @@ -71,14 +71,15 @@ pub fn run( MountFinder::default() }; - let root = root.path(); let home_dir = home::home_dir().ok_or_else(|| eyre::eyre!("could not find home directory"))?; let cargo_dir = home::cargo_home()?; let xargo_dir = env::var_os("XARGO_HOME") .map(PathBuf::from) .unwrap_or_else(|| home_dir.join(".xargo")); let nix_store_dir = env::var_os("NIX_STORE").map(PathBuf::from); - let target_dir = target_dir.clone().unwrap_or_else(|| root.join("target")); + let target_dir = target_dir + .clone() + .unwrap_or_else(|| metadata.workspace_root().join("target")); // create the directories we are going to mount before we mount them, // otherwise `docker` will create them but they will be owned by `root` @@ -90,7 +91,12 @@ pub fn run( let cargo_dir = mount_finder.find_mount_path(cargo_dir); let xargo_dir = mount_finder.find_mount_path(xargo_dir); let target_dir = mount_finder.find_mount_path(target_dir); - let host_root = mount_finder.find_mount_path(docker_root); + // root is either workspace_root, or, if we're outside the workspace root, the current directory + let host_root = mount_finder.find_mount_path(if metadata.workspace_root().starts_with(cwd) { + cwd + } else { + metadata.workspace_root() + }); let mount_root: PathBuf; #[cfg(target_os = "windows")] { @@ -105,11 +111,11 @@ pub fn run( #[cfg(target_os = "windows")] { // On Windows, we can not mount the directory name directly. Instead, we use wslpath to convert the path to a linux compatible path. - mount_cwd = wslpath(&std::env::current_dir()?, verbose)?; + mount_cwd = wslpath(&cwd, verbose)?; } #[cfg(not(target_os = "windows"))] { - mount_cwd = mount_finder.find_mount_path(std::env::current_dir()?); + mount_cwd = mount_finder.find_mount_path(cwd); } let sysroot = mount_finder.find_mount_path(sysroot); @@ -145,6 +151,12 @@ pub fn run( docker.args(&["-e", var]); } let mut env_volumes = false; + // FIXME(emilgardis 2022-04-07): This is a fallback so that if it's hard for use to do mounting logic, make it simple(r) + // Preferably we would not have to do this. + if cwd.strip_prefix(metadata.workspace_root()).is_err() { + env_volumes = true; + } + for ref var in config.env_volumes(target)? { validate_env_var(var)?; @@ -243,12 +255,12 @@ pub fn run( if env_volumes { docker.args(&["-w".as_ref(), mount_cwd.as_os_str()]); - } else if mount_cwd == root { + } else if mount_cwd == metadata.workspace_root() { docker.args(&["-w", "/project"]); } else { // We do this to avoid clashes with path separators. Windows uses `\` as a path separator on Path::join - let cwd = &std::env::current_dir()?; - let working_dir = Path::new("project").join(cwd.strip_prefix(docker_root)?); + let cwd = &cwd; + let working_dir = Path::new("project").join(cwd.strip_prefix(metadata.workspace_root())?); // No [T].join for OsStr let mut mount_wd = std::ffi::OsString::new(); for part in working_dir.iter() { diff --git a/src/main.rs b/src/main.rs index 3fb75ccd3..dceab030b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,7 @@ use std::process::ExitStatus; use config::Config; use serde::Deserialize; -use self::cargo::{Root, Subcommand}; +use self::cargo::{CargoMetadata, Subcommand}; use self::cross_toml::CrossToml; use self::errors::*; use self::rustc::{TargetList, VersionMetaExt}; @@ -279,9 +279,10 @@ fn run() -> Result { let host_version_meta = rustc_version::version_meta().wrap_err("couldn't fetch the `rustc` version")?; - if let Some(root) = cargo::root(None, args.manifest_path)? { + let cwd = std::env::current_dir()?; + if let Some(metadata) = cargo::cargo_metadata_with_args(Some(&cwd), Some(&args))? { let host = host_version_meta.host(); - let toml = toml(&root)?; + let toml = toml(&metadata)?; let config = Config::new(toml); let target = args .target @@ -399,13 +400,13 @@ fn run() -> Result { &target, &filtered_args, &args.target_dir, - &root, - &docker_root, + &metadata, &config, uses_xargo, &sysroot, verbose, args.docker_in_docker, + &cwd, ); } } @@ -464,10 +465,10 @@ pub(crate) fn warn_host_version_mismatch( /// Parses the `Cross.toml` at the root of the Cargo project or from the /// `CROSS_CONFIG` environment variable (if any exist in either location). -fn toml(root: &Root) -> Result> { +fn toml(root: &CargoMetadata) -> Result> { let path = match env::var("CROSS_CONFIG") { Ok(var) => PathBuf::from(var), - Err(_) => root.path().join("Cross.toml"), + Err(_) => root.workspace_root().join("Cross.toml"), }; if path.exists() { @@ -480,7 +481,7 @@ fn toml(root: &Root) -> Result> { Ok(Some(config)) } else { // Checks if there is a lowercase version of this file - if root.path().join("cross.toml").exists() { + if root.workspace_root().join("cross.toml").exists() { eprintln!("There's a file named cross.toml, instead of Cross.toml. You may want to rename it, or it won't be considered."); } Ok(None) diff --git a/src/tests.rs b/src/tests.rs index 6970ff077..3217f3ede 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -15,10 +15,10 @@ static WORKSPACE: OnceCell = OnceCell::new(); pub fn get_cargo_workspace() -> &'static Path { let manifest_dir = env!("CARGO_MANIFEST_DIR"); WORKSPACE.get_or_init(|| { - crate::cargo::root(Some(manifest_dir.as_ref())) + crate::cargo::cargo_metadata(Some(manifest_dir.as_ref())) .unwrap() .unwrap() - .path + .workspace_root }) }