Skip to content

Commit edf1e17

Browse files
committed
always use buildx build and set output for custom dockerfile builds
bumps minimal versions
1 parent e729e88 commit edf1e17

File tree

5 files changed

+104
-67
lines changed

5 files changed

+104
-67
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ One of these container engines is required. If both are installed, `cross` will
4646
default to `docker`.
4747

4848
- [Docker]. Note that on Linux non-sudo users need to be in the `docker` group.
49-
Read the official [post-installation steps][post]. Requires version 1.24 or later.
49+
Read the official [post-installation steps][post]. Requires version 20.10 (API 1.40) or later.
5050

5151
[post]: https://docs.docker.com/install/linux/linux-postinstall/
5252

53-
- [Podman]. Requires version 1.6.3 or later.
53+
- [Podman]. Requires version 3.4.0 or later.
5454

5555
## Installation
5656

src/docker/custom.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use std::io::Write;
22
use std::path::PathBuf;
33
use std::str::FromStr;
44

5-
use crate::docker::{DockerOptions, DockerPaths};
5+
use crate::docker::{self, DockerOptions, DockerPaths};
66
use crate::shell::MessageInfo;
7-
use crate::{docker, CargoMetadata, TargetTriple};
87
use crate::{errors::*, file, CommandExt, ToUtf8};
8+
use crate::{CargoMetadata, TargetTriple};
99

1010
use super::{get_image_name, parse_docker_opts, path_hash, ImagePlatform};
1111

@@ -70,7 +70,8 @@ impl<'a> Dockerfile<'a> {
7070
build_args: impl IntoIterator<Item = (impl AsRef<str>, impl AsRef<str>)>,
7171
msg_info: &mut MessageInfo,
7272
) -> Result<String> {
73-
let mut docker_build = docker::subcommand(&options.engine, "build");
73+
let mut docker_build = docker::subcommand(&options.engine, "buildx");
74+
docker_build.arg("build");
7475
docker_build.env("DOCKER_SCAN_SUGGEST", "false");
7576
self.runs_with()
7677
.specify_platform(&options.engine, &mut docker_build);
@@ -140,9 +141,16 @@ impl<'a> Dockerfile<'a> {
140141
docker_build.args(["--file".into(), path]);
141142

142143
if let Some(build_opts) = options.config.build_opts() {
143-
// FIXME: Use shellwords
144144
docker_build.args(parse_docker_opts(&build_opts)?);
145145
}
146+
147+
let has_output = options.config.build_opts().map_or(false, |opts| {
148+
opts.contains("--load") || opts.contains("--output")
149+
});
150+
if options.engine.kind.is_docker() && !has_output {
151+
docker_build.args(&["--output", "type=docker"]);
152+
};
153+
146154
if let Some(context) = self.context() {
147155
docker_build.arg(&context);
148156
} else {

src/docker/engine.rs

Lines changed: 88 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ pub enum EngineType {
2020
Other,
2121
}
2222

23+
impl EngineType {
24+
/// Returns `true` if the engine type is [`Podman`](Self::Podman) or [`PodmanRemote`](Self::PodmanRemote).
25+
#[must_use]
26+
pub fn is_podman(&self) -> bool {
27+
matches!(self, Self::Podman | Self::PodmanRemote)
28+
}
29+
30+
/// Returns `true` if the engine type is [`Docker`](EngineType::Docker).
31+
#[must_use]
32+
pub fn is_docker(&self) -> bool {
33+
matches!(self, Self::Docker)
34+
}
35+
}
36+
2337
#[derive(Clone, Debug, PartialEq, Eq)]
2438
pub struct Engine {
2539
pub kind: EngineType,
@@ -117,82 +131,97 @@ fn get_engine_info(
117131
EngineType::Other
118132
};
119133

120-
let mut cmd = Command::new(ce);
121-
cmd.args(&["version", "-f", "{{ .Server.Os }},,,{{ .Server.Arch }}"]);
122-
123-
let out = cmd.run_and_get_output(msg_info)?;
124-
125-
let stdout = out.stdout()?.to_lowercase();
126-
127-
let osarch = stdout
128-
.trim()
129-
.split_once(",,,")
130-
.map(|(os, arch)| -> Result<_> { Ok((ContainerOs::new(os)?, Architecture::new(arch)?)) })
131-
.transpose();
132-
133-
let osarch = match (kind, osarch) {
134-
(_, Ok(Some(osarch))) => Some(osarch),
135-
(EngineType::PodmanRemote | EngineType::Podman, Ok(None)) => get_podman_info(ce, msg_info)?,
136-
(_, Err(e)) => {
137-
return Err(e.wrap_err(format!(
138-
"command `{}` returned unexpected data",
139-
cmd.command_pretty(msg_info, |_| false)
140-
)));
134+
// this can fail: podman can give partial output
135+
// linux,,,Error: template: version:1:15: executing "version" at <.Arch>:
136+
// can't evaluate field Arch in type *define.Version
137+
let os_arch_server = engine_info(
138+
ce,
139+
&["version", "-f", "{{ .Server.Os }},,,{{ .Server.Arch }}"],
140+
",,,",
141+
msg_info,
142+
);
143+
144+
let (os_arch_other, os_arch_server_result) = match os_arch_server {
145+
Ok(Some(os_arch)) => (Ok(Some(os_arch)), None),
146+
result => {
147+
if kind.is_podman() {
148+
(get_podman_info(ce, msg_info), result.err())
149+
} else {
150+
(get_custom_info(ce, msg_info), result.err())
151+
}
141152
}
142-
(EngineType::Docker | EngineType::Other, Ok(None)) => None,
143153
};
144154

145-
let osarch = if osarch.is_some() {
146-
osarch
147-
} else if !out.status.success() {
148-
get_custom_info(ce, msg_info).with_error(|| {
149-
cmd.status_result(msg_info, out.status, Some(&out))
150-
.expect_err("status_result should error")
151-
})?
152-
} else {
153-
get_custom_info(ce, msg_info)?
155+
let os_arch = match (os_arch_other, os_arch_server_result) {
156+
(Ok(os_arch), _) => os_arch,
157+
(Err(e), Some(server_err)) => return Err(server_err.to_section_report().with_error(|| e)),
158+
(Err(e), None) => return Err(e.to_section_report()),
154159
};
155160

156-
let (os, arch) = osarch.map_or(<_>::default(), |(os, arch)| (Some(os), Some(arch)));
161+
let (os, arch) = os_arch.map_or(<_>::default(), |(os, arch)| (Some(os), Some(arch)));
157162
Ok((kind, arch, os))
158163
}
159164

160-
fn get_podman_info(
165+
#[derive(Debug, thiserror::Error)]
166+
pub enum EngineInfoError {
167+
#[error(transparent)]
168+
Eyre(eyre::Report),
169+
#[error("could not get os and arch")]
170+
CommandError(#[from] CommandError),
171+
}
172+
173+
impl EngineInfoError {
174+
pub fn to_section_report(self) -> eyre::Report {
175+
match self {
176+
EngineInfoError::Eyre(e) => e,
177+
EngineInfoError::CommandError(e) => {
178+
e.to_section_report().wrap_err("could not get os and arch")
179+
}
180+
}
181+
}
182+
}
183+
184+
/// Get engine info
185+
fn engine_info(
161186
ce: &Path,
187+
args: &[&str],
188+
sep: &str,
162189
msg_info: &mut MessageInfo,
163-
) -> Result<Option<(ContainerOs, Architecture)>> {
190+
) -> Result<Option<(ContainerOs, Architecture)>, EngineInfoError> {
164191
let mut cmd = Command::new(ce);
165-
cmd.args(&["info", "-f", "{{ .Version.OsArch }}"]);
166-
cmd.run_and_get_stdout(msg_info)
167-
.map(|s| {
168-
s.to_lowercase()
169-
.trim()
170-
.split_once('/')
171-
.map(|(os, arch)| -> Result<_> {
172-
Ok((ContainerOs::new(os)?, Architecture::new(arch)?))
173-
})
174-
})
175-
.wrap_err("could not determine os and architecture of vm")?
192+
cmd.args(args);
193+
let out = cmd
194+
.run_and_get_output(msg_info)
195+
.map_err(EngineInfoError::Eyre)?;
196+
197+
cmd.status_result(msg_info, out.status, Some(&out))?;
198+
199+
out.stdout()?
200+
.to_lowercase()
201+
.trim()
202+
.split_once(sep)
203+
.map(|(os, arch)| -> Result<_> { Ok((ContainerOs::new(os)?, Architecture::new(arch)?)) })
176204
.transpose()
205+
.map_err(EngineInfoError::Eyre)
206+
}
207+
208+
fn get_podman_info(
209+
ce: &Path,
210+
msg_info: &mut MessageInfo,
211+
) -> Result<Option<(ContainerOs, Architecture)>, EngineInfoError> {
212+
engine_info(ce, &["info", "-f", "{{ .Version.OsArch }}"], "/", msg_info)
177213
}
178214

179215
fn get_custom_info(
180216
ce: &Path,
181217
msg_info: &mut MessageInfo,
182-
) -> Result<Option<(ContainerOs, Architecture)>> {
183-
let mut cmd = Command::new(ce);
184-
cmd.args(&["info", "-f", "{{ .Client.Os }},,,{{ .Client.Arch }}"]);
185-
cmd.run_and_get_stdout(msg_info)
186-
.map(|s| {
187-
s.to_lowercase()
188-
.trim()
189-
.split_once(",,,")
190-
.map(|(os, arch)| -> Result<_> {
191-
Ok((ContainerOs::new(os)?, Architecture::new(arch)?))
192-
})
193-
})
194-
.unwrap_or_default()
195-
.transpose()
218+
) -> Result<Option<(ContainerOs, Architecture)>, EngineInfoError> {
219+
engine_info(
220+
ce,
221+
&["version", "-f", "{{ .Client.Os }},,,{{ .Client.Arch }}"],
222+
",,,",
223+
msg_info,
224+
)
196225
}
197226

198227
pub fn get_container_engine() -> Result<PathBuf, which::Error> {

src/docker/shared.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ pub(crate) fn docker_seccomp(
612612
) -> Result<()> {
613613
// docker uses seccomp now on all installations
614614
if target.needs_docker_seccomp() {
615-
let seccomp = if engine_type == EngineType::Docker && cfg!(target_os = "windows") {
615+
let seccomp = if engine_type.is_docker() && cfg!(target_os = "windows") {
616616
// docker on windows fails due to a bug in reading the profile
617617
// https://github.com/docker/for-win/issues/12760
618618
"unconfined".to_owned()

xtask/src/build_docker_image.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ pub fn build_docker_image(
183183

184184
if push {
185185
docker_build.arg("--push");
186-
} else if no_output {
186+
} else if engine.kind.is_docker() && no_output {
187187
docker_build.args(&["--output", "type=tar,dest=/dev/null"]);
188188
} else {
189189
docker_build.arg("--load");

0 commit comments

Comments
 (0)