Skip to content

Commit d113ac1

Browse files
committed
allow specifying a dockerfile to use for a target
1 parent d6d9e66 commit d113ac1

18 files changed

+684
-93
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

+103-28
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
}
@@ -178,16 +195,34 @@ impl Config {
178195
fn vec_from_config(
179196
&self,
180197
target: &Target,
181-
env: impl Fn(&Environment, &Target) -> (Option<Vec<String>>, Option<Vec<String>>),
182-
config_build: impl for<'a> Fn(&'a CrossToml) -> Option<&'a [String]>,
183-
config_target: impl for<'a> Fn(&'a CrossToml, &Target) -> Option<&'a [String]>,
184-
) -> Result<Vec<String>> {
198+
env: impl for<'a> Fn(&'a Environment, &Target) -> (Option<Vec<String>>, Option<Vec<String>>),
199+
config: impl for<'a> Fn(&'a CrossToml, &Target) -> (Option<&'a [String]>, Option<&'a [String]>),
200+
sum: bool,
201+
) -> Result<Option<Vec<String>>> {
185202
let (env_build, env_target) = env(&self.env, target);
186203

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))?);
204+
if sum {
205+
return self.sum_of_env_toml_values(env_target, |t| config(t, target));
206+
} else if let Some(env_target) = env_target {
207+
return Ok(Some(env_target));
208+
}
189209

190-
Ok(collected)
210+
let (build, target) = self
211+
.toml
212+
.as_ref()
213+
.map(|t| config(t, target))
214+
.unwrap_or_default();
215+
216+
// FIXME: let expression
217+
if target.is_none() && env_build.is_some() {
218+
return Ok(env_build);
219+
}
220+
221+
if target.is_none() {
222+
Ok(build.map(ToOwned::to_owned))
223+
} else {
224+
Ok(target.map(ToOwned::to_owned))
225+
}
191226
}
192227

193228
#[cfg(test)]
@@ -211,22 +246,17 @@ impl Config {
211246
self.string_from_config(target, Environment::runner, CrossToml::runner)
212247
}
213248

214-
pub fn env_passthrough(&self, target: &Target) -> Result<Vec<String>> {
249+
pub fn env_passthrough(&self, target: &Target) -> Result<Option<Vec<String>>> {
215250
self.vec_from_config(
216251
target,
217252
Environment::passthrough,
218-
CrossToml::env_passthrough_build,
219-
CrossToml::env_passthrough_target,
253+
CrossToml::env_passthrough,
254+
true,
220255
)
221256
}
222257

223-
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-
)
258+
pub fn env_volumes(&self, target: &Target) -> Result<Option<Vec<String>>> {
259+
self.vec_from_config(target, Environment::volumes, CrossToml::env_volumes, false)
230260
}
231261

232262
pub fn target(&self, target_list: &TargetList) -> Option<Target> {
@@ -238,19 +268,62 @@ impl Config {
238268
.and_then(|t| t.default_target(target_list))
239269
}
240270

271+
pub fn dockerfile(&self, target: &Target) -> Result<Option<String>> {
272+
self.string_from_config(target, Environment::dockerfile, CrossToml::dockerfile)
273+
}
274+
275+
pub fn dockerfile_context(&self, target: &Target) -> Result<Option<String>> {
276+
self.string_from_config(
277+
target,
278+
Environment::dockerfile_context,
279+
CrossToml::dockerfile_context,
280+
)
281+
}
282+
283+
pub fn dockerfile_build_args(
284+
&self,
285+
target: &Target,
286+
) -> Result<Option<HashMap<String, String>>> {
287+
// This value does not support env variables
288+
self.toml
289+
.as_ref()
290+
.map_or(Ok(None), |t| Ok(t.dockerfile_build_args(target)))
291+
}
292+
293+
pub fn pre_build(&self, target: &Target) -> Result<Option<Vec<String>>> {
294+
self.vec_from_config(
295+
target,
296+
|e, t| (None, e.pre_build(t)),
297+
CrossToml::pre_build,
298+
false,
299+
)
300+
}
301+
241302
fn sum_of_env_toml_values<'a>(
242303
&'a self,
243-
env_values: Option<Vec<String>>,
244-
toml_getter: impl FnOnce(&'a CrossToml) -> Option<&'a [String]>,
245-
) -> Result<Vec<String>> {
304+
env_values: Option<impl AsRef<[String]>>,
305+
toml_getter: impl FnOnce(&'a CrossToml) -> (Option<&'a [String]>, Option<&'a [String]>),
306+
) -> Result<Option<Vec<String>>> {
307+
let mut defined = false;
246308
let mut collect = vec![];
247-
if let Some(mut vars) = env_values {
248-
collect.append(&mut vars);
249-
} else if let Some(toml_values) = self.toml.as_ref().and_then(toml_getter) {
250-
collect.extend(toml_values.iter().cloned());
309+
if let Some(vars) = env_values {
310+
collect.extend(vars.as_ref().iter().cloned());
311+
defined = true;
312+
} else if let Some((build, target)) = self.toml.as_ref().map(toml_getter) {
313+
if let Some(build) = build {
314+
collect.extend(build.iter().cloned());
315+
defined = true;
316+
}
317+
if let Some(target) = target {
318+
collect.extend(target.iter().cloned());
319+
defined = true;
320+
}
321+
}
322+
if !defined {
323+
Ok(None)
324+
} else {
325+
Ok(Some(collect))
251326
}
252-
253-
Ok(collect)
254327
}
255328
}
256329

@@ -389,7 +462,8 @@ mod tests {
389462
let config = Config::new_with(Some(toml(TOML_BUILD_VOLUMES)?), env);
390463
let expected = vec!["VOLUME1".to_string(), "VOLUME2".into()];
391464

392-
let result = config.env_volumes(&target()).unwrap();
465+
let result = config.env_volumes(&target()).unwrap().unwrap_or_default();
466+
dbg!(&result);
393467
assert!(result.len() == 2);
394468
assert!(result.contains(&expected[0]));
395469
assert!(result.contains(&expected[1]));
@@ -404,7 +478,8 @@ mod tests {
404478
let config = Config::new_with(Some(toml(TOML_BUILD_VOLUMES)?), env);
405479
let expected = vec!["VOLUME3".to_string(), "VOLUME4".into()];
406480

407-
let result = config.env_volumes(&target()).unwrap();
481+
let result = config.env_volumes(&target()).unwrap().unwrap_or_default();
482+
dbg!(&result);
408483
assert!(result.len() == 2);
409484
assert!(result.contains(&expected[0]));
410485
assert!(result.contains(&expected[1]));

0 commit comments

Comments
 (0)