Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow specifying volumes in Cross.toml. #443

Merged
merged 1 commit into from
Jul 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 21 additions & 3 deletions src/docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,15 @@ pub fn run(target: &Target,
} else {
SafeCommand::new("cargo")
};

cmd.args(args);

let runner = None;

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");
}
Expand All @@ -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"]);
Expand Down Expand Up @@ -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())])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change will make it impossible to use a rustc wrapper with Cargo, which is the only way to pass custom flags due to #463, and the wrapper path needs to be absolute.

Setting it to /project/my-wrapper.sh works just fine, but if the mount point is changed, there is no easy way to pass the absolute path to the wrapper.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, actually I already have to do something different for cross builds vs. native builds, so maybe this won't be that big of a deal? It's certainly a breaking change though.

.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");
Expand Down
47 changes: 26 additions & 21 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<Vec<&str>> {
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<Vec<&str>> {
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<Vec<&str>> {
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<Vec<&str>> {
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.<triple>.env.passthrough` part of `Cross.toml` for `target`.
fn target_env_passthrough(&self, target: &Target) -> Result<Vec<&str>> {
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<Vec<&str>> {
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()),
}
Expand Down