diff --git a/CHANGELOG.md b/CHANGELOG.md index 8006fe20f..d1a5d7dc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Added support for mounting volumes. + ## [v0.2.1] - 2020-06-30 - Disabled `powerpc64-unknown-linux-gnu` image. diff --git a/README.md b/README.md index dded073d4..8aca55b23 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,18 @@ passthrough = [ ] ``` +### Mounting volumes into the build environment + +In addition to passing environment variables, you can also specify environment +variables pointing to paths which should be mounted into the container: + +```toml +[target.aarch64-unknown-linux-gnu.env] +volumes = [ + "BUILD_DIR", +] +``` + ### Use Xargo instead of Cargo By default, `cross` uses `xargo` to build your Cargo project only for all diff --git a/src/docker.rs b/src/docker.rs index 008447f9f..95d991f06 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -92,6 +92,7 @@ pub fn run(target: &Target, } else { SafeCommand::new("cargo") }; + cmd.args(args); let runner = None; @@ -99,7 +100,7 @@ pub fn run(target: &Target, let mut docker = docker_command("run")?; if let Some(toml) = toml { - for var in toml.env_passthrough(target)? { + let validate_env_var = |var: &str| -> Result<()> { if var.contains('=') { bail!("environment variable names must not contain the '=' character"); } @@ -110,10 +111,27 @@ pub fn run(target: &Target, ); } + Ok(()) + }; + + for var in toml.env_passthrough(target)? { + validate_env_var(var)?; + // Only specifying the environment variable name in the "-e" // flag forwards the value from the parent shell docker.args(&["-e", var]); } + + for var in toml.env_volumes(target)? { + validate_env_var(var)?; + + if let Ok(val) = env::var(var) { + let host_path = Path::new(&val).canonicalize()?; + let mount_path = &host_path; + docker.args(&["-v", &format!("{}:{}", host_path.display(), mount_path.display())]); + docker.args(&["-e", &format!("{}={}", var, mount_path.display())]); + } + } } docker.args(&["-e", "PKG_CONFIG_ALLOW_CROSS=1"]); @@ -152,10 +170,10 @@ pub fn run(target: &Target, .args(&["-v", &format!("{}:/cargo:Z", cargo_dir.display())]) // Prevent `bin` from being mounted inside the Docker container. .args(&["-v", "/cargo/bin"]) - .args(&["-v", &format!("{}:/project:Z", mount_root.display())]) + .args(&["-v", &format!("{}:/{}:Z", mount_root.display(), mount_root.display())]) .args(&["-v", &format!("{}:/rust:Z,ro", sysroot.display())]) .args(&["-v", &format!("{}:/target:Z", target_dir.display())]) - .args(&["-w", "/project"]); + .args(&["-w", &mount_root.display().to_string()]); if atty::is(Stream::Stdin) { docker.arg("-i"); diff --git a/src/main.rs b/src/main.rs index 421388fbd..f0c40b9ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,6 @@ use std::io::Write; use std::process::ExitStatus; use std::{env, io, process}; -use error_chain::bail; use toml::{Value, value::Table}; use self::cargo::{Root, Subcommand}; @@ -363,38 +362,44 @@ impl Toml { /// Returns the list of environment variables to pass through for `target`, /// including variables specified under `build` and under `target`. pub fn env_passthrough(&self, target: &Target) -> Result> { - let mut bwl = self.build_env_passthrough()?; - let mut twl = self.target_env_passthrough(target)?; + let mut bwl = self.build_env("passthrough")?; + let mut twl = self.target_env(target, "passthrough")?; bwl.extend(twl.drain(..)); Ok(bwl) } - /// Returns the `build.env.passthrough` part of `Cross.toml` - fn build_env_passthrough(&self) -> Result> { - match self.table.get("build").and_then(|b| b.get("env")).and_then(|e| e.get("passthrough")) { + /// Returns the list of volumes to pass through for `target`, + /// including volumes specified under `build` and under `target`. + pub fn env_volumes(&self, target: &Target) -> Result> { + let mut bwl = self.build_env("volumes")?; + let mut twl = self.target_env(target, "volumes")?; + bwl.extend(twl.drain(..)); + + Ok(bwl) + } + + fn target_env(&self, target: &Target, key: &str) -> Result> { + let triple = target.triple(); + + match self.table.get("target").and_then(|t| t.get(triple)).and_then(|t| t.get("env")).and_then(|e| e.get(key)) { Some(&Value::Array(ref vec)) => { - if vec.iter().any(|val| val.as_str().is_none()) { - bail!("every build.env.passthrough element must be a string"); - } - Ok(vec.iter().map(|val| val.as_str().unwrap()).collect()) + vec.iter().map(|val| { + val.as_str().ok_or_else(|| { + format!("every target.{}.env.{} element must be a string", triple, key).into() + }) + }).collect() }, _ => Ok(Vec::new()), } } - /// Returns the `target..env.passthrough` part of `Cross.toml` for `target`. - fn target_env_passthrough(&self, target: &Target) -> Result> { - let triple = target.triple(); - - let key = format!("target.{}.env.passthrough", triple); - - match self.table.get("target").and_then(|t| t.get(triple)).and_then(|t| t.get("env")).and_then(|e| e.get("passthrough")) { + fn build_env(&self, key: &str) -> Result> { + match self.table.get("build").and_then(|b| b.get("env")).and_then(|e| e.get(key)) { Some(&Value::Array(ref vec)) => { - if vec.iter().any(|val| val.as_str().is_none()) { - bail!("every {} element must be a string", key); - } - Ok(vec.iter().map(|val| val.as_str().unwrap()).collect()) + vec.iter().map(|val| { + val.as_str().ok_or_else(|| format!("every build.env.{} element must be a string", key).into()) + }).collect() }, _ => Ok(Vec::new()), }