diff --git a/.changes/982.json b/.changes/982.json new file mode 100644 index 000000000..f6979d803 --- /dev/null +++ b/.changes/982.json @@ -0,0 +1,4 @@ +{ + "type": "internal", + "description": "use generic dockerfiles for when the toolchain and image platfom match." +} diff --git a/docker/Dockerfile.native b/docker/Dockerfile.native new file mode 100644 index 000000000..291848f5c --- /dev/null +++ b/docker/Dockerfile.native @@ -0,0 +1,31 @@ +# This dockerfile is used when the target matches the images platform in `build-docker-image` +FROM ubuntu:20.04 +ARG DEBIAN_FRONTEND=noninteractive + +COPY common.sh lib.sh / +RUN /common.sh + +COPY cmake.sh / +RUN /cmake.sh + +COPY xargo.sh / +RUN /xargo.sh + +ARG TARGETARCH +ARG TARGETVARIANT +ARG CROSS_TARGET_TRIPLE + +COPY qemu.sh native-qemu.sh / +RUN /native-qemu.sh + +COPY dropbear.sh / +RUN /dropbear.sh + +COPY linux-image.sh native-linux-image.sh / +RUN /native-linux-image.sh + +COPY linux-runner native-linux-runner base-runner.sh / + +ENV CROSS_TARGETARCH=$TARGETARCH +ENV CROSS_TARGETVARIANT=$TARGETVARIANT +ENV CARGO_TARGET_${CROSS_TARGET_TRIPLE}_RUNNER="/native-linux-runner" diff --git a/docker/Dockerfile.native.centos b/docker/Dockerfile.native.centos new file mode 100644 index 000000000..c92b56571 --- /dev/null +++ b/docker/Dockerfile.native.centos @@ -0,0 +1,40 @@ +FROM ubuntu:20.04 +ARG DEBIAN_FRONTEND=noninteractive + +ARG TARGETARCH +ARG TARGETVARIANT +ARG CROSS_TARGET_TRIPLE + +COPY lib.sh / +COPY linux-image.sh native-linux-image.sh / +RUN /native-linux-image.sh + +FROM centos:7 + +COPY common.sh lib.sh / +RUN /common.sh + +COPY cmake.sh / +RUN /cmake.sh + +COPY xargo.sh / +RUN /xargo.sh + +# these need to be present in **both** FROM sections +ARG TARGETARCH +ARG TARGETVARIANT +ARG CROSS_TARGET_TRIPLE + +COPY qemu.sh native-qemu.sh / +RUN /native-qemu.sh + +COPY dropbear.sh / +RUN /dropbear.sh + +COPY --from=0 /qemu /qemu + +COPY linux-runner native-linux-runner base-runner.sh / + +ENV CROSS_TARGETARCH=$TARGETARCH +ENV CROSS_TARGETVARIANT=$TARGETVARIANT +ENV CARGO_TARGET_${CROSS_TARGET_TRIPLE}_RUNNER="/native-linux-runner" diff --git a/docker/Dockerfile.x86_64-unknown-linux-gnu b/docker/Dockerfile.x86_64-unknown-linux-gnu index 5e1105ef8..d0fa70e30 100644 --- a/docker/Dockerfile.x86_64-unknown-linux-gnu +++ b/docker/Dockerfile.x86_64-unknown-linux-gnu @@ -10,6 +10,15 @@ RUN /cmake.sh COPY xargo.sh / RUN /xargo.sh +RUN apt-get update && apt-get install --assume-yes --no-install-recommends \ + g++-x86-64-linux-gnu \ + libc6-dev-amd64-cross + +COPY deny-debian-packages.sh / +RUN TARGET_ARCH=amd64 /deny-debian-packages.sh \ + binutils \ + binutils-x86-64-linux-gnu + COPY qemu.sh / RUN /qemu.sh x86_64 softmmu @@ -21,4 +30,11 @@ RUN /linux-image.sh x86_64 COPY linux-runner base-runner.sh / -ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="/linux-runner x86_64" +ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc \ + CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="/linux-runner x86_64" \ + CC_x86_64_unknown_linux_gnu=x86_64-linux-gnu-gcc \ + CXX_x86_64_unknown_linux_gnu=x86_64-linux-gnu-g++ \ + BINDGEN_EXTRA_CLANG_ARGS_x86_64_unknown_linux_gnu="--sysroot=/usr/x86_64-linux-gnu" \ + QEMU_LD_PREFIX=/usr/x86_64-linux-gnu \ + RUST_TEST_THREADS=1 \ + PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig/:${PKG_CONFIG_PATH}" diff --git a/docker/lib.sh b/docker/lib.sh index 412500b80..4d47e309c 100644 --- a/docker/lib.sh +++ b/docker/lib.sh @@ -86,3 +86,72 @@ download_gcc() { download_mirrors "gcc/gcc-${version}" "${filename}" "${GNU_MIRRORS[@]}" } + +docker_to_qemu_arch() { + local arch="${1}" + case "${arch}" in + arm64) + echo "aarch64" + ;; + 386) + echo "i386" + ;; + amd64) + echo "x86_64" + ;; + arm|ppc64le|riscv64|s390x) + echo "${arch}" + ;; + *) + echo "Unknown Docker image architecture, got \"${arch}\"." >&2 + exit 1 + ;; + esac +} + +docker_to_linux_arch() { + # variant may not be provided + local oldstate + oldstate="$(set +o)" + set +u + + local arch="${1}" + local variant="${2}" + case "${arch}" in + arm64) + echo "aarch64" + ;; + 386) + echo "i686" + ;; + amd64) + echo "x86_64" + ;; + ppc64le) + echo "powerpc64le" + ;; + arm) + case "${variant}" in + v6) + echo "arm" + ;; + ""|v7) + echo "armv7" + ;; + *) + echo "Unknown Docker image variant, got \"${variant}\"." >&2 + exit 1 + ;; + esac + ;; + riscv64|s390x) + echo "${arch}" + ;; + *) + echo "Unknown Docker image architecture, got \"${arch}\"." >&2 + exit 1 + ;; + esac + + eval "${oldstate}" +} diff --git a/docker/linux-image.sh b/docker/linux-image.sh index 976a43a2e..907d8628f 100755 --- a/docker/linux-image.sh +++ b/docker/linux-image.sh @@ -171,12 +171,14 @@ main() { sharutils \ gnupg - # amd64 has conflicting versions of the packages installed, so + # conflicting versions of some packages will be installed already for the host platform, # we need to remove the system installs later. since apt relies # on these packages, we need to download them and reinstall # using dpkg later, since we cannot redownload via apt. + local dpkg_arch + dpkg_arch=$(dpkg --print-architecture) local libgcc_packages=("${libgcc}:${arch}" "libstdc++6:${arch}") - if [[ "${arch}" == "amd64" ]]; then + if [[ "${arch}" == "${dpkg_arch}" ]]; then local libgcc_root=/qemu/libgcc mkdir -p "${libgcc_root}" pushd "${libgcc_root}" @@ -186,6 +188,7 @@ main() { # Download packages mv /etc/apt/sources.list /etc/apt/sources.list.bak + mv /etc/apt/sources.list.d /etc/apt/sources.list.d.bak echo -e "${debsource}" > /etc/apt/sources.list # Old ubuntu does not support --add-architecture, so we directly change multiarch file @@ -240,10 +243,10 @@ main() { ncurses-base"${ncurses}" \ "zlib1g:${arch}" - if [[ "${arch}" != "amd64" ]]; then + if [[ "${arch}" != "${dpkg_arch}" ]]; then apt-get -d --no-install-recommends download "${libgcc_packages[@]}" else - # amd64 has conflicting versions of the packages installed + # host arch has conflicting versions of the packages installed # this prevents us from downloading them, so we need to # simply grab the last version from the debian sources. # we're search for a paragraph with: @@ -380,7 +383,7 @@ EOF find . | cpio --create --format='newc' --quiet | gzip > ../initrd.gz cd - - if [[ "${arch}" == "amd64" ]]; then + if [[ "${arch}" == "${dpkg_arch}" ]]; then # need to reinstall these packages, since basic utilities rely on them. pushd "${libgcc_root}" dpkg -i --force-depends "${libgcc_root}"/*.deb @@ -391,15 +394,16 @@ EOF # Clean up rm -rf "/qemu/${root}" "/qemu/${arch}" mv -f /etc/apt/sources.list.bak /etc/apt/sources.list + mv -f /etc/apt/sources.list.d.bak /etc/apt/sources.list.d if [ -f /etc/dpkg/dpkg.cfg.d/multiarch.bak ]; then mv /etc/dpkg/dpkg.cfg.d/multiarch.bak /etc/dpkg/dpkg.cfg.d/multiarch fi - # can fail if arch is used (amd64 and/or i386) + # can fail if arch is used (image arch, such as amd64 and/or i386) dpkg --remove-architecture "${arch}" || true apt-get update # need to reinstall the removed libgcc packages, which are required for apt - if [[ "${arch}" == "amd64" ]]; then + if [[ "${arch}" == "${dpkg_arch}" ]]; then apt-get install --no-install-recommends --assume-yes "${packages[@]}" fi diff --git a/docker/linux-runner b/docker/linux-runner index ade8b6cd0..90e174f1c 100755 --- a/docker/linux-runner +++ b/docker/linux-runner @@ -16,7 +16,7 @@ fi arch="${1}" shift -if [ "${CROSS_RUNNER}" = "" ]; then +if [[ -z "${CROSS_RUNNER}" ]]; then if is_native_binary "${arch}"; then CROSS_RUNNER=native else diff --git a/docker/native-linux-image.sh b/docker/native-linux-image.sh new file mode 100755 index 000000000..e8c988e0a --- /dev/null +++ b/docker/native-linux-image.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -x +set -eo pipefail + +# shellcheck disable=SC1091 +. lib.sh + +main() { + local arch + arch=$(docker_to_linux_arch "${TARGETARCH}" "${TARGETVARIANT}") + /linux-image.sh "${arch}" + rm "${0}" +} + +main "${@}" diff --git a/docker/native-linux-runner b/docker/native-linux-runner new file mode 100755 index 000000000..ae1523eab --- /dev/null +++ b/docker/native-linux-runner @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -eo pipefail + +# shellcheck disable=SC1091 +. /lib.sh + +main() { + local arch + arch=$(docker_to_linux_arch "${CROSS_TARGETARCH}" "${CROSS_TARGETVARIANT}") + + if [[ -z "${CROSS_RUNNER}" ]]; then + export CROSS_RUNNER=native + fi + + exec /linux-runner "${arch}" "${@}" +} + +main "${@}" diff --git a/docker/native-qemu.sh b/docker/native-qemu.sh new file mode 100755 index 000000000..f975489f3 --- /dev/null +++ b/docker/native-qemu.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -x +set -euo pipefail + +# shellcheck disable=SC1091 +. lib.sh + +main() { + local arch + arch=$(docker_to_qemu_arch "${TARGETARCH}") + /qemu.sh "${arch}" softmmu + rm "${0}" +} + +main "${@}" diff --git a/docker/qemu-runner b/docker/qemu-runner index 698763278..1d5b38935 100755 --- a/docker/qemu-runner +++ b/docker/qemu-runner @@ -16,7 +16,7 @@ fi arch="${1}" shift -if [ "${CROSS_RUNNER}" = "" ]; then +if [[ -z "${CROSS_RUNNER}" ]]; then if is_native_binary "${arch}"; then CROSS_RUNNER=native else diff --git a/src/bin/commands/images.rs b/src/bin/commands/images.rs index cd02f2077..d8fb0ac5e 100644 --- a/src/bin/commands/images.rs +++ b/src/bin/commands/images.rs @@ -276,7 +276,7 @@ pub fn list_images( map.get_mut(&target).expect("map must have key").push(image); } } - let mut keys: Vec<&str> = map.iter().map(|(k, _)| k.as_ref()).collect(); + let mut keys: Vec<&str> = map.keys().map(|k| k.as_ref()).collect(); keys.sort_unstable(); let print_string = diff --git a/xtask/src/build_docker_image.rs b/xtask/src/build_docker_image.rs index c9d27d125..365656a23 100644 --- a/xtask/src/build_docker_image.rs +++ b/xtask/src/build_docker_image.rs @@ -1,7 +1,10 @@ use std::fmt::Write; use std::path::Path; -use crate::util::{cargo_metadata, gha_error, gha_output, gha_print}; +use crate::util::{ + cargo_metadata, get_matrix, gha_error, gha_output, gha_print, DEFAULT_PLATFORMS, +}; +use crate::ImageTarget; use clap::Args; use cross::docker::ImagePlatform; use cross::shell::MessageInfo; @@ -72,14 +75,14 @@ pub struct BuildDockerImage { pub platform: Vec, /// Targets to build for #[clap()] - pub targets: Vec, + pub targets: Vec, } fn locate_dockerfile( - target: crate::ImageTarget, + target: ImageTarget, docker_root: &Path, cross_toolchain_root: &Path, -) -> cross::Result<(crate::ImageTarget, String)> { +) -> cross::Result<(ImageTarget, String)> { let dockerfile_name = format!("Dockerfile.{target}"); let dockerfile_root = if cross_toolchain_root.join(&dockerfile_name).exists() { &cross_toolchain_root @@ -129,7 +132,7 @@ pub fn build_docker_image( .clone(); if targets.is_empty() { if from_ci { - targets = crate::util::get_matrix() + targets = get_matrix() .iter() .filter(|m| m.os.starts_with("ubuntu")) .map(|m| m.to_image_target()) @@ -160,7 +163,7 @@ pub fn build_docker_image( .collect::>>()?; let platforms = if platform.is_empty() { - crate::util::DEFAULT_PLATFORMS.to_vec() + DEFAULT_PLATFORMS.to_vec() } else { platform }; @@ -183,7 +186,28 @@ pub fn build_docker_image( }; docker_build.current_dir(&docker_root); - docker_build.args(["--platform", &platform.docker_platform()]); + let docker_platform = platform.docker_platform(); + let mut dockerfile = dockerfile.clone(); + docker_build.args(["--platform", &docker_platform]); + let uppercase_triple = target.name.to_ascii_uppercase().replace('-', "_"); + docker_build.args([ + "--build-arg", + &format!("CROSS_TARGET_TRIPLE={}", uppercase_triple), + ]); + // add our platform, and determine if we need to use a native docker image + if has_native_image(docker_platform.as_str(), target, msg_info)? { + let dockerfile_name = match target.sub.as_deref() { + Some(sub) => format!("Dockerfile.native.{sub}"), + None => "Dockerfile.native".to_owned(), + }; + let dockerfile_path = docker_root.join(&dockerfile_name); + if !dockerfile_path.exists() { + eyre::bail!( + "unable to find native dockerfile named {dockerfile_name} for target {target}." + ); + } + dockerfile = dockerfile_path.to_utf8()?.to_string(); + } if push { docker_build.arg("--push"); @@ -276,7 +300,7 @@ pub fn build_docker_image( ), ]); - docker_build.args(["-f", dockerfile]); + docker_build.args(["-f", &dockerfile]); if gha || progress == "plain" { docker_build.args(["--progress", "plain"]); @@ -338,8 +362,42 @@ pub fn build_docker_image( Ok(()) } +fn has_native_image( + platform: &str, + target: &ImageTarget, + msg_info: &mut MessageInfo, +) -> cross::Result { + let note_host_target_detection = |msg_info: &mut MessageInfo| -> cross::Result<()> { + msg_info.note("using the rust target triple to determine the host triple to determine if the docker platform is native. this may fail if cross-compiling xtask.") + }; + + Ok(match target.sub.as_deref() { + // FIXME: add additional subs for new Linux distros, such as alpine. + None | Some("centos") => match (platform, target.name.as_str()) { + ("linux/386", "i686-unknown-linux-gnu") + | ("linux/amd64", "x86_64-unknown-linux-gnu") + | ("linux/arm64" | "linux/arm64/v8", "aarch64-unknown-linux-gnu") + | ("linux/ppc64le", "powerpc64le-unknown-linux-gnu") + | ("linux/riscv64", "riscv64gc-unknown-linux-gnu") + | ("linux/s390x", "s390x-unknown-linux-gnu") => true, + ("linux/arm/v6", "arm-unknown-linux-gnueabi") if target.is_armv6() => { + note_host_target_detection(msg_info)?; + true + } + ("linux/arm" | "linux/arm/v7", "armv7-unknown-linux-gnueabihf") + if target.is_armv7() => + { + note_host_target_detection(msg_info)?; + true + } + _ => false, + }, + Some(_) => false, + }) +} + pub fn determine_image_name( - target: &crate::ImageTarget, + target: &ImageTarget, repository: &str, ref_type: &str, ref_name: &str, @@ -377,7 +435,7 @@ pub fn determine_image_name( } pub fn job_summary( - results: &[Result], + results: &[Result], ) -> cross::Result { let mut summary = "# SUMMARY\n\n".to_string(); let success: Vec<_> = results.iter().filter_map(|r| r.as_ref().ok()).collect(); diff --git a/xtask/src/util.rs b/xtask/src/util.rs index 05aa0cfaa..78b30f8e4 100644 --- a/xtask/src/util.rs +++ b/xtask/src/util.rs @@ -188,6 +188,20 @@ impl ImageTarget { pub fn needs_workspace_root_context(&self) -> bool { self.name == "cross" } + + pub fn is_armv6(&self) -> bool { + matches!( + self.name.as_str(), + "arm-unknown-linux-gnueabi" | "arm-unknown-linux-musleabi" + ) + } + + pub fn is_armv7(&self) -> bool { + matches!( + self.name.as_str(), + "armv7-unknown-linux-gnueabihf" | "armv7-unknown-linux-musleabihf" + ) + } } impl std::str::FromStr for ImageTarget {