From 72ffc4e1fa9f998944af0120cf120e5740a88315 Mon Sep 17 00:00:00 2001 From: wngr Date: Sat, 19 Mar 2022 20:02:00 +0100 Subject: [PATCH] feat: Parse Cargo's --manifest-path option to determine mounted docker root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commits adds support for parsing the `--manifest-path` option to cross. So far, the `Cargo.toml` manifest of the crate (or its Cargo workspace) to compile has been assumed to be in the current working directory. This means, that relative crate dependencies were not supported, because those paths were not mapped into the docker container. Take the following example structure, where `my-bin` depends on `my-lib`: . ├── my-bin │ ├── Cargo.toml │ └── src │ └── main.rs └── my-lib ├── Cargo.toml └── src └── lib.rs This commits enables such scenarios, by running cross from `.` like so: `cross build --manifest-path=my-lib/Cargo.toml --target x86_64-pc-windows-gnu`, as `.` is mapped as the container's root, and the options passed through to Cargo. Related cross-rs#388 cross-rs#139 cross-rs#277 cross-rs#78 Co-authored-by: Kviring Alexey --- CHANGELOG.md | 1 + README.md | 3 +++ src/cargo.rs | 54 +++++++++++++++++++++++++++++++-------------------- src/cli.rs | 19 +++++++++++++++++- src/docker.rs | 5 +++-- src/main.rs | 4 +++- 6 files changed, 61 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb9c0a3fd..b4673447a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- #494 - Parse Cargo's --manifest-path option to determine mounted docker root - #718 - remove deb subcommand. - #714 - use host target directory when falling back to host cargo. - #713 - convert relative target directories to absolute paths. diff --git a/README.md b/README.md index 80f6f437a..4b0b04c32 100644 --- a/README.md +++ b/README.md @@ -356,6 +356,9 @@ $ QEMU_STRACE=1 cross run --target aarch64-unknown-linux-gnu - path dependencies (in Cargo.toml) that point outside the Cargo project won't work because `cross` use docker containers only mounts the Cargo project so the container doesn't have access to the rest of the filesystem. + However, you may use Cargo's `--manifest-path` option to reference your + target crate, executed from a common root directory from which all your + dependencies are available. ## Minimum Supported Rust Version (MSRV) diff --git a/src/cargo.rs b/src/cargo.rs index ce07484eb..f49cf1a58 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -62,28 +62,40 @@ impl Root { } /// Cargo project root -pub fn root(cd: Option<&Path>) -> Result> { - #[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); +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()); + } + 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, + })) } - let output = command.output()?; - let manifest: Option = serde_json::from_slice(&output.stdout)?; - Ok(manifest.map(|m| Root { - path: m.workspace_root, - })) } /// Pass-through mode diff --git a/src/cli.rs b/src/cli.rs index df9abd2a4..736f7b5c4 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -14,6 +14,7 @@ pub struct Args { pub target: Option, pub target_dir: Option, pub docker_in_docker: bool, + pub manifest_path: Option, } // Fix for issue #581. target_dir must be absolute. @@ -28,6 +29,7 @@ fn absolute_path(path: PathBuf) -> Result { pub fn parse(target_list: &TargetList) -> Result { let mut channel = None; let mut target = None; + let mut manifest_path: Option = None; let mut target_dir = None; let mut sc = None; let mut all: Vec = Vec::new(); @@ -38,7 +40,21 @@ pub fn parse(target_list: &TargetList) -> Result { if arg.is_empty() { continue; } - if let ("+", ch) = arg.split_at(1) { + if arg == "--manifest-path" { + all.push(arg); + if let Some(m) = args.next() { + let p = PathBuf::from(&m); + all.push(m); + manifest_path = env::current_dir().ok().map(|cwd| cwd.join(p)); + } + } else if arg.starts_with("--manifest-path=") { + manifest_path = arg + .split_once('=') + .map(|x| x.1) + .map(PathBuf::from) + .and_then(|p| env::current_dir().ok().map(|cwd| cwd.join(p))); + all.push(arg); + } else if let ("+", ch) = arg.split_at(1) { channel = Some(ch.to_string()); } else if arg == "--target" { all.push(arg); @@ -83,5 +99,6 @@ pub fn parse(target_list: &TargetList) -> Result { target, target_dir, docker_in_docker, + manifest_path, }) } diff --git a/src/docker.rs b/src/docker.rs index aa2da7348..cac40162b 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -58,6 +58,7 @@ pub fn run( args: &[String], target_dir: &Option, root: &Root, + docker_root: &Path, config: &Config, uses_xargo: bool, sysroot: &Path, @@ -89,7 +90,7 @@ 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(root); + let host_root = mount_finder.find_mount_path(docker_root); let mount_root: PathBuf; #[cfg(target_os = "windows")] { @@ -247,7 +248,7 @@ pub fn run( } 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(root)?); + let working_dir = Path::new("project").join(cwd.strip_prefix(docker_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 3e11248c2..3fb75ccd3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -279,7 +279,7 @@ 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)? { + if let Some(root) = cargo::root(None, args.manifest_path)? { let host = host_version_meta.host(); let toml = toml(&root)?; let config = Config::new(toml); @@ -394,11 +394,13 @@ fn run() -> Result { docker::register(&target, verbose)? } + let docker_root = env::current_dir()?; return docker::run( &target, &filtered_args, &args.target_dir, &root, + &docker_root, &config, uses_xargo, &sysroot,