Skip to content

Commit 3c7f900

Browse files
committed
allow specifying a dockerfile to use for a target
1 parent d90b016 commit 3c7f900

18 files changed

+639
-72
lines changed

Diff for: CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1010
- #795 - added images for additional toolchains maintained by cross-rs.
1111
- #792 - added `CROSS_CONTAINER_IN_CONTAINER` environment variable to replace `CROSS_DOCKER_IN_DOCKER`.
1212
- #782 - added `build-std` config option, which builds the rust standard library from source if enabled.
13-
- #775 - forward Cargo exit code to host
13+
- #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.
14+
- #678 - Add `target.{target}.pre-build` config for running commands before building the image.
1415
- #772 - added `CROSS_CONTAINER_OPTS` environment variable to replace `DOCKER_OPTS`.
1516
- #767, #788 - added the `cross-util` and `xtask` commands.
1617
- #745 - added `thumbv7neon-*` targets.
@@ -27,6 +28,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
2728

2829
### Changed
2930

31+
- #775 - forward Cargo exit code to host
3032
- #762 - re-enabled `x86_64-unknown-dragonfly` target.
3133
- #747 - reduced android image sizes.
3234
- #746 - limit image permissions for android images.

Diff for: Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ serde = { version = "1", features = ["derive"] }
3131
serde_json = "1"
3232
serde_ignored = "0.1.2"
3333
shell-words = "1.1.0"
34+
sha1_smol = "1.0.0"
3435

3536
[target.'cfg(not(windows))'.dependencies]
3637
nix = { version = "0.24", default-features = false, features = ["user"] }

Diff for: README.md

+29-2
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,22 @@ the default one. Normal Docker behavior applies, so:
112112

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

115+
#### Dockerfiles
116+
117+
If you're using a custom Dockerfile, you can use `target.{{TARGET}}.dockerfile` to automatically build it
118+
119+
``` toml
120+
[target.aarch64-unknown-linux-gnu.dockerfile]
121+
dockerfile = "./path/to/where/the/Dockerfile/resides"
122+
```
123+
124+
`cross` will build and use the image that was built instead of the default image.
125+
115126
It's recommended to base your custom image on the default Docker image that
116127
cross uses: `ghcr.io/cross-rs/{{TARGET}}:{{VERSION}}` (where `{{VERSION}}` is cross's version).
117128
This way you won't have to figure out how to install a cross C toolchain in your
118-
custom image. Example below:
129+
custom image.
130+
119131

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

140+
If you want cross to provide the `FROM` instruction, you can do the following
141+
142+
``` Dockerfile
143+
ARG CROSS_BASE_IMAGE
144+
FROM $CROSS_BASE_IMAGE
145+
146+
RUN ...
128147
```
129-
$ docker build -t my/image:tag path/to/where/the/Dockerfile/resides
148+
149+
#### Pre-build hook
150+
151+
`cross` enables you to add dependencies and run other necessary commands in the image before using it.
152+
This action will be added to the used image, so it won't be ran/built every time you use `cross`.
153+
154+
``` toml
155+
[target.x86_64-unknown-linux-gnu]
156+
pre-build = ["dpkg --add-architecture arm64 && apt-get update && apt-get install --assume-yes libfoo:arm64"]
130157
```
131158

132159
### Docker in Docker

Diff for: docs/cross_toml.md

+21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
The `cross` configuration in the `Cross.toml` file, can contain the following elements:
22

33
# `build`
4+
45
The `build` key allows you to set global variables, e.g.:
56

67
```toml
@@ -11,6 +12,7 @@ default-target = "x86_64-unknown-linux-gnu"
1112
```
1213

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

2426
# `target.TARGET`
27+
2528
The `target` key allows you to specify parameters for specific compilation targets.
2629

2730
```toml
2831
[target.aarch64-unknown-linux-gnu]
2932
xargo = false
3033
build-std = false
3134
image = "test-image"
35+
pre-build = ["echo 'Hello world!'"]
3236
runner = "custom-runner"
3337
```
3438

3539
# `target.TARGET.env`
40+
3641
The `target` key allows you to specify environment variables that should be used for a specific compilation target.
3742
This is similar to `build.env`, but allows you to be more specific per target.
3843

@@ -41,3 +46,19 @@ This is similar to `build.env`, but allows you to be more specific per target.
4146
volumes = ["VOL1_ARG", "VOL2_ARG"]
4247
passthrough = ["IMPORTANT_ENV_VARIABLES"]
4348
```
49+
50+
# `target.TARGET.dockerfile`
51+
52+
```toml
53+
[target.x86_64-unknown-linux-gnu.dockerfile]
54+
file = "./Dockerfile"
55+
context = "."
56+
build-args = { ARG1 = "foo" }
57+
```
58+
59+
also supports
60+
61+
```toml
62+
[target.x86_64-unknown-linux-gnu]
63+
dockerfile = "./Dockerfile"
64+
```

Diff for: src/bin/commands/images.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ fn remove_images(
186186
}
187187
command.args(images);
188188
if execute {
189-
command.run(verbose).map_err(Into::into)
189+
command.run(verbose, false).map_err(Into::into)
190190
} else {
191191
println!("{:?}", command);
192192
Ok(())

Diff for: src/cargo.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ pub fn cargo_metadata_with_args(
148148

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

154156
/// run cargo and get the output, does not check the exit status

Diff for: src/config.rs

+82-15
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,23 @@ impl Environment {
6868
self.get_target_var(target, "IMAGE")
6969
}
7070

71+
fn dockerfile(&self, target: &Target) -> Option<String> {
72+
let res = self.get_values_for("DOCKERFILE", target, |s| s.to_string());
73+
res.0.or(res.1)
74+
}
75+
76+
fn dockerfile_context(&self, target: &Target) -> Option<String> {
77+
let res = self.get_values_for("DOCKERFILE_CONTEXT", target, |s| s.to_string());
78+
res.0.or(res.1)
79+
}
80+
81+
fn pre_build(&self, target: &Target) -> Option<Vec<String>> {
82+
let res = self.get_values_for("PRE_BUILD", target, |v| {
83+
v.split('\n').map(String::from).collect()
84+
});
85+
res.0.or(res.1)
86+
}
87+
7188
fn runner(&self, target: &Target) -> Option<String> {
7289
self.get_target_var(target, "RUNNER")
7390
}
@@ -179,15 +196,37 @@ impl Config {
179196
&self,
180197
target: &Target,
181198
env: impl Fn(&Environment, &Target) -> (Option<Vec<String>>, Option<Vec<String>>),
182-
config_build: impl for<'a> Fn(&'a CrossToml) -> &'a [String],
183-
config_target: impl for<'a> Fn(&'a CrossToml, &Target) -> &'a [String],
199+
config: impl for<'a> Fn(&'a CrossToml, &Target) -> (&'a [String], &'a [String]),
200+
sum: bool,
184201
) -> Result<Vec<String>> {
202+
let mut collected = vec![];
185203
let (env_build, env_target) = env(&self.env, target);
186204

187-
let mut collected = self.sum_of_env_toml_values(env_build, config_build)?;
188-
collected.extend(self.sum_of_env_toml_values(env_target, |t| config_target(t, target))?);
205+
if sum {
206+
collected.extend(self.sum_of_env_toml_values(env_target, |t| config(t, target))?);
207+
return Ok(collected);
208+
} else if let Some(env_target) = env_target {
209+
return Ok(env_target);
210+
}
211+
212+
let (build, target) = self
213+
.toml
214+
.as_ref()
215+
.map(|t| config(t, target))
216+
.unwrap_or((&[], &[]));
189217

190-
Ok(collected)
218+
// FIXME: let expression
219+
if target.is_empty() {
220+
if let Some(env_build) = env_build {
221+
return Ok(env_build);
222+
}
223+
}
224+
225+
if target.is_empty() {
226+
Ok(build.to_vec())
227+
} else {
228+
Ok(target.to_vec())
229+
}
191230
}
192231

193232
#[cfg(test)]
@@ -215,18 +254,13 @@ impl Config {
215254
self.vec_from_config(
216255
target,
217256
Environment::passthrough,
218-
CrossToml::env_passthrough_build,
219-
CrossToml::env_passthrough_target,
257+
CrossToml::env_passthrough,
258+
true,
220259
)
221260
}
222261

223262
pub fn env_volumes(&self, target: &Target) -> Result<Vec<String>> {
224-
self.vec_from_config(
225-
target,
226-
Environment::volumes,
227-
CrossToml::env_volumes_build,
228-
CrossToml::env_volumes_target,
229-
)
263+
self.vec_from_config(target, Environment::volumes, CrossToml::env_volumes, false)
230264
}
231265

232266
pub fn target(&self, target_list: &TargetList) -> Option<Target> {
@@ -238,16 +272,48 @@ impl Config {
238272
.and_then(|t| t.default_target(target_list))
239273
}
240274

275+
pub fn dockerfile(&self, target: &Target) -> Result<Option<String>> {
276+
self.string_from_config(target, Environment::dockerfile, CrossToml::dockerfile)
277+
}
278+
279+
pub fn dockerfile_context(&self, target: &Target) -> Result<Option<String>> {
280+
self.string_from_config(
281+
target,
282+
Environment::dockerfile_context,
283+
CrossToml::dockerfile_context,
284+
)
285+
}
286+
287+
pub fn dockerfile_build_args(
288+
&self,
289+
target: &Target,
290+
) -> Result<Option<HashMap<String, String>>> {
291+
// This value does not support env variables
292+
self.toml
293+
.as_ref()
294+
.map_or(Ok(None), |t| Ok(t.dockerfile_build_args(target)))
295+
}
296+
297+
pub fn pre_build(&self, target: &Target) -> Result<Vec<String>> {
298+
self.vec_from_config(
299+
target,
300+
|e, t| (None, e.pre_build(t)),
301+
CrossToml::pre_build,
302+
false,
303+
)
304+
}
305+
241306
fn sum_of_env_toml_values<'a>(
242307
&'a self,
243308
env_values: Option<Vec<String>>,
244-
toml_getter: impl FnOnce(&'a CrossToml) -> &'a [String],
309+
toml_getter: impl FnOnce(&'a CrossToml) -> (&'a [String], &'a [String]),
245310
) -> Result<Vec<String>> {
246311
let mut collect = vec![];
247312
if let Some(mut vars) = env_values {
248313
collect.append(&mut vars);
249314
} else if let Some(toml_values) = self.toml.as_ref().map(toml_getter) {
250-
collect.extend(toml_values.iter().cloned());
315+
collect.extend(toml_values.0.iter().cloned());
316+
collect.extend(toml_values.1.iter().cloned());
251317
}
252318

253319
Ok(collect)
@@ -390,6 +456,7 @@ mod tests {
390456
let expected = vec!["VOLUME1".to_string(), "VOLUME2".into()];
391457

392458
let result = config.env_volumes(&target()).unwrap();
459+
dbg!(&result);
393460
assert!(result.len() == 2);
394461
assert!(result.contains(&expected[0]));
395462
assert!(result.contains(&expected[1]));

0 commit comments

Comments
 (0)