Skip to content

Commit

Permalink
allow specifying a dockerfile to use for a target
Browse files Browse the repository at this point in the history
  • Loading branch information
Emilgardis committed Jun 15, 2022
1 parent f333a1b commit 7c0fe6d
Show file tree
Hide file tree
Showing 18 changed files with 635 additions and 72 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).

- #792 - added `CROSS_CONTAINER_IN_CONTAINER` environment variable to replace `CROSS_DOCKER_IN_DOCKER`.
- #782 - added `build-std` config option, which builds the rust standard library from source if enabled.
- #775 - forward Cargo exit code to host
- #678 - Add optional `target.{target}.dockerfile[.file]`, `target.{target}.dockerfile.context` and `target.{target}.dockerfile.build-args` to invoke docker/podman build before using an image.
- #678 - Add `target.{target}.pre-build` config for running commands before building the image.
- #772 - added `CROSS_CONTAINER_OPTS` environment variable to replace `DOCKER_OPTS`.
- #767, #788 - added the `cross-util` and `xtask` commands.
- #745 - added `thumbv7neon-*` targets.
Expand All @@ -26,6 +27,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Changed

- #775 - forward Cargo exit code to host
- #762 - re-enabled `x86_64-unknown-dragonfly` target.
- #747 - reduced android image sizes.
- #746 - limit image permissions for android images.
Expand Down
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_ignored = "0.1.2"
shell-words = "1.1.0"
sha1_smol = "1.0.0"

[target.'cfg(not(windows))'.dependencies]
nix = { version = "0.24", default-features = false, features = ["user"] }
Expand Down
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,22 @@ the default one. Normal Docker behavior applies, so:

- If only `tag` is omitted, then Docker will use the `latest` tag.

#### Dockerfiles

If you're using a custom Dockerfile, you can use `target.{{TARGET}}.dockerfile` to automatically build it

``` toml
[target.aarch64-unknown-linux-gnu.dockerfile]
dockerfile = "./path/to/where/the/Dockerfile/resides"
```

`cross` will build and use the image that was built instead of the default image.

It's recommended to base your custom image on the default Docker image that
cross uses: `ghcr.io/cross-rs/{{TARGET}}:{{VERSION}}` (where `{{VERSION}}` is cross's version).
This way you won't have to figure out how to install a cross C toolchain in your
custom image. Example below:
custom image.


``` Dockerfile
FROM ghcr.io/cross-rs/aarch64-unknown-linux-gnu:latest
Expand All @@ -125,8 +137,23 @@ RUN dpkg --add-architecture arm64 && \
apt-get install --assume-yes libfoo:arm64
```

If you want cross to provide the `FROM` instruction, you can do the following

``` Dockerfile
ARG CROSS_BASE_IMAGE
FROM $CROSS_BASE_IMAGE

RUN ...
```
$ docker build -t my/image:tag path/to/where/the/Dockerfile/resides

#### Pre-build hook

`cross` enables you to add dependencies and run other necessary commands in the image before using it.
This action will be added to the used image, so it won't be ran/built every time you use `cross`.

``` toml
[target.x86_64-unknown-linux-gnu]
pre-build = ["dpkg --add-architecture arm64 && apt-get update && apt-get install --assume-yes libfoo:arm64"]
```

### Docker in Docker
Expand Down
21 changes: 21 additions & 0 deletions docs/cross_toml.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
The `cross` configuration in the `Cross.toml` file, can contain the following elements:

# `build`

The `build` key allows you to set global variables, e.g.:

```toml
Expand All @@ -11,6 +12,7 @@ default-target = "x86_64-unknown-linux-gnu"
```

# `build.env`

With the `build.env` key you can globally set volumes that should be mounted
in the Docker container or environment variables that should be passed through.
For example:
Expand All @@ -22,17 +24,20 @@ passthrough = ["IMPORTANT_ENV_VARIABLES"]
```

# `target.TARGET`

The `target` key allows you to specify parameters for specific compilation targets.

```toml
[target.aarch64-unknown-linux-gnu]
xargo = false
build-std = false
image = "test-image"
pre-build = ["echo 'Hello world!'"]
runner = "custom-runner"
```

# `target.TARGET.env`

The `target` key allows you to specify environment variables that should be used for a specific compilation target.
This is similar to `build.env`, but allows you to be more specific per target.

Expand All @@ -41,3 +46,19 @@ This is similar to `build.env`, but allows you to be more specific per target.
volumes = ["VOL1_ARG", "VOL2_ARG"]
passthrough = ["IMPORTANT_ENV_VARIABLES"]
```

# `target.TARGET.dockerfile`

```toml
[target.x86_64-unknown-linux-gnu.dockerfile]
file = "./Dockerfile"
context = "."
build-args = { ARG1 = "foo" }
```

also supports

```toml
[target.x86_64-unknown-linux-gnu]
dockerfile = "./Dockerfile"
```
2 changes: 1 addition & 1 deletion src/bin/commands/images.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ fn remove_images(
}
command.args(images);
if execute {
command.run(verbose).map_err(Into::into)
command.run(verbose, false).map_err(Into::into)
} else {
println!("{:?}", command);
Ok(())
Expand Down
4 changes: 3 additions & 1 deletion src/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ pub fn cargo_metadata_with_args(

/// Pass-through mode
pub fn run(args: &[String], verbose: bool) -> Result<ExitStatus, CommandError> {
Command::new("cargo").args(args).run_and_get_status(verbose)
Command::new("cargo")
.args(args)
.run_and_get_status(verbose, false)
}

/// run cargo and get the output, does not check the exit status
Expand Down
93 changes: 78 additions & 15 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ impl Environment {
self.get_target_var(target, "IMAGE")
}

fn dockerfile(&self, target: &Target) -> Option<String> {
self.get_target_var(target, "DOCKERFILE")
}

fn dockerfile_context(&self, target: &Target) -> Option<String> {
self.get_target_var(target, "DOCKERFILE_CONTEXT")
}

fn pre_build(&self, target: &Target) -> Option<Vec<String>> {
self.get_target_var(target, "PRE_BUILD")
.map(|v| v.split('\n').map(String::from).collect())
}

fn runner(&self, target: &Target) -> Option<String> {
self.get_target_var(target, "RUNNER")
}
Expand Down Expand Up @@ -179,15 +192,37 @@ impl Config {
&self,
target: &Target,
env: impl Fn(&Environment, &Target) -> (Option<Vec<String>>, Option<Vec<String>>),
config_build: impl for<'a> Fn(&'a CrossToml) -> &'a [String],
config_target: impl for<'a> Fn(&'a CrossToml, &Target) -> &'a [String],
config: impl for<'a> Fn(&'a CrossToml, &Target) -> (&'a [String], &'a [String]),
sum: bool,
) -> Result<Vec<String>> {
let mut collected = vec![];
let (env_build, env_target) = env(&self.env, target);

let mut collected = self.sum_of_env_toml_values(env_build, config_build)?;
collected.extend(self.sum_of_env_toml_values(env_target, |t| config_target(t, target))?);
if sum {
collected.extend(self.sum_of_env_toml_values(env_target, |t| config(t, target))?);
return Ok(collected);
} else if let Some(env_target) = env_target {
return Ok(env_target);
}

let (build, target) = self
.toml
.as_ref()
.map(|t| config(t, target))
.unwrap_or((&[], &[]));

Ok(collected)
// FIXME: let expression
if target.is_empty() {
if let Some(env_build) = env_build {
return Ok(env_build);
}
}

if target.is_empty() {
Ok(build.to_vec())
} else {
Ok(target.to_vec())
}
}

#[cfg(test)]
Expand Down Expand Up @@ -215,18 +250,13 @@ impl Config {
self.vec_from_config(
target,
Environment::passthrough,
CrossToml::env_passthrough_build,
CrossToml::env_passthrough_target,
CrossToml::env_passthrough,
true,
)
}

pub fn env_volumes(&self, target: &Target) -> Result<Vec<String>> {
self.vec_from_config(
target,
Environment::volumes,
CrossToml::env_volumes_build,
CrossToml::env_volumes_target,
)
self.vec_from_config(target, Environment::volumes, CrossToml::env_volumes, false)
}

pub fn target(&self, target_list: &TargetList) -> Option<Target> {
Expand All @@ -238,16 +268,48 @@ impl Config {
.and_then(|t| t.default_target(target_list))
}

pub fn dockerfile(&self, target: &Target) -> Result<Option<String>> {
self.string_from_config(target, Environment::dockerfile, CrossToml::dockerfile)
}

pub fn dockerfile_context(&self, target: &Target) -> Result<Option<String>> {
self.string_from_config(
target,
Environment::dockerfile_context,
CrossToml::dockerfile_context,
)
}

pub fn dockerfile_build_args(
&self,
target: &Target,
) -> Result<Option<HashMap<String, String>>> {
// This value does not support env variables
self.toml
.as_ref()
.map_or(Ok(None), |t| Ok(t.dockerfile_build_args(target)))
}

pub fn pre_build(&self, target: &Target) -> Result<Vec<String>> {
self.vec_from_config(
target,
|e, t| (None, e.pre_build(t)),
CrossToml::pre_build,
false,
)
}

fn sum_of_env_toml_values<'a>(
&'a self,
env_values: Option<Vec<String>>,
toml_getter: impl FnOnce(&'a CrossToml) -> &'a [String],
toml_getter: impl FnOnce(&'a CrossToml) -> (&'a [String], &'a [String]),
) -> Result<Vec<String>> {
let mut collect = vec![];
if let Some(mut vars) = env_values {
collect.append(&mut vars);
} else if let Some(toml_values) = self.toml.as_ref().map(toml_getter) {
collect.extend(toml_values.iter().cloned());
collect.extend(toml_values.0.iter().cloned());
collect.extend(toml_values.1.iter().cloned());
}

Ok(collect)
Expand Down Expand Up @@ -390,6 +452,7 @@ mod tests {
let expected = vec!["VOLUME1".to_string(), "VOLUME2".into()];

let result = config.env_volumes(&target()).unwrap();
dbg!(&result);
assert!(result.len() == 2);
assert!(result.contains(&expected[0]));
assert!(result.contains(&expected[1]));
Expand Down
Loading

0 comments on commit 7c0fe6d

Please sign in to comment.