On 10/23/2026 9:06 AM, Fabiano Rosas wrote: > Pierrick Bouvier <[email protected]> writes: > >> This new dockerfile allows to build a distroless image containing only >> qemu stripped release binaries, their lib dependencies, and installed >> artifacts (roms, keymap, ...). There is no shell, no python, nothing >> else. This is achieved thanks to a multi-stage build, where we add qemu >> binaries, artifacts and libraries in a new scratch image. Image has >> the minimal size possible. >> >> This is based on Ubuntu, which has the advantage to have working >> containers available up to 12.04. Debian old old stable have broken apt >> sources for instance. Ubuntu 10.04 binaries segfault due to missing >> vsyscall emulation on modern kernels, so 12.04 is the lowest possible. >> >> This unique dockerfile works for all qemu versions, with 0.12.0 being the >> oldest we can build today (based on ubuntu 12.04). Previous ones need an >> older compiler. >> >> To build the image, you need to provide a qemu and ubuntu version: >> $ podman build -f release.Dockerfile --build-arg QEMU_VERSION=v11.1.0 >> --build-arg UBUNTU_VERSION=24.04 -t qemu/v11.1.0 >> >> To run it, as there is no shell, and no defined entrypoint, you need to >> choose the binary you want explicitely: >> $ podman run -it --rm qemu/v11.1.0 qemu-system-aarch64 --version >> >> Notes: We could optimize a bit further, by creating a base container for >> each ubuntu version, that is reused between all qemu versions built with >> it. Given that recent QEMU have around 800MB of binaries + artifacts, >> and only 100MB of library dependency, I don't think it's worth the extra >> complexity. >> >> Notes: Also, I considered create one image per binary. Problem is >> that qemu depends on other artifacts (rom, keymap, etc) and it's not >> easy to identify what is needed for one binary. Thus, I think it's >> better to stick to one container with all binaries per release. >> > > Yeah, it's fine this way. > >> Signed-off-by: Pierrick Bouvier <[email protected]> >> --- >> tests/docker/dockerfiles/release.docker | 76 +++++++++++++++++++++++++ >> 1 file changed, 76 insertions(+) >> create mode 100644 tests/docker/dockerfiles/release.docker >> >> diff --git a/tests/docker/dockerfiles/release.docker >> b/tests/docker/dockerfiles/release.docker >> new file mode 100644 >> index 00000000000..611ecca9859 >> --- /dev/null >> +++ b/tests/docker/dockerfiles/release.docker >> @@ -0,0 +1,76 @@ >> +ARG UBUNTU_VERSION="UBUNTU_VERSION_build_arg_must_be_set" >> +FROM ubuntu:$UBUNTU_VERSION as build > > I believe FROM should be capitalized the same as AS. >
Dockerfile instructions are not case sensitive, but yes, AS would be more consistent, I'll change it. >> +ARG UBUNTU_VERSION > > Is this duplicated? > ARG have to be duplicated for each stage of the build. In this case: - pre-stage (to be used in first FROM) - first-stage (to be used inside this stage) >> + >> +# add deb-src for apt build-dep >> +RUN sed -e 's/Types: deb/Types: deb deb-src/' -i /etc/apt/sources.list.d/* >> || true >> +RUN sed -e 's/# deb-src/deb-src/' -i /etc/apt/sources.list >> +# archive.canonical.com is not reachable anymore, and cause a long timeout >> +RUN sed -e '/archive.canonical.com/d' -i /etc/apt/sources.list >> +# LTS < 14.04 are hosted on https://old-releases.ubuntu.com/releases/ >> +RUN if [ $(echo $UBUNTU_VERSION | cut -f 1 -d '.') -lt 14 ]; then \ >> + sed -e 's/archive/old-releases/g' -i /etc/apt/sources.list; \ >> +fi > > Hm, so I imagine these containers have reached end-of-life? I wonder > whether we should cap how further back we go based on this. It's one > thing to workaround a few installation bugs we list/install our > dependencies, another thing is the distro image poiting to dead URLs. > If we want to cap it, then 14.04 is the lowest limit with fixed url. Note that archive.canonical.com still has to be removed, it's present in some LTS > 14.04 and create a long timeout for apt update. >> + >> +# use apt-get since apt was missing -y option in some ancient distro > > Ok, this is a reasonable issue to encounter and fix in the dockerfile. I > pointed at this on the cover letter, but we don't gain much in knowing > exactly which apt version has -y or not. > >> +RUN apt-get update && \ >> +env DEBIAN_FRONTEND=noninteractive apt-get install -y git build-essential >> +RUN apt-get update && \ >> +env DEBIAN_FRONTEND=noninteractive apt-get build-dep -y --arch-only qemu >> +# additional dependencies that are missing at some point in history >> +RUN apt-get update && \ >> +env DEBIAN_FRONTEND=noninteractive apt-get install -y ninja-build flex >> bison libpixman-1-dev > > So a discrepancy on which QEMU version and how the distro packaged it at > the time vs. which dependencies we had. Is this something that would > have been solved by lcitool? (Hm, did we even use it back then?) > It's new dependencies added by QEMU over time, that distro didn't reflect yet at a past date. (ninja introduction, or recently hexagon target which requires flex/bison for idef tool). >> +# python3-venv didn't exist in ancient ubuntu distro >> +RUN apt-get update && \ >> +if apt-cache show python3-venv; then \ >> + env DEBIAN_FRONTEND=noninteractive apt-get install -y python3-venv; \ >> +fi > > Maybe good to know which version so we know when to drop this block? > > I see some of these check the ubuntu version, some not, I wonder how > easy it will be to break an older image's build when trying to fix a new > one. I find it a bit confusing. > Good point, I think I had this written *before* trying to go before 14.04, thus the inconsistency. >> + >> +# patch cc and c++ to inject warning suppression options >> +RUN mv /usr/bin/cc /usr/bin/cc.real >> +RUN echo "#!/usr/bin/env bash" > /usr/bin/cc && \ >> +echo "exec /usr/bin/cc.real \"\$@\" -Wno-error -Wno-int-conversion" >> >> /usr/bin/cc && \ >> +chmod +x /usr/bin/cc >> +RUN mv /usr/bin/c++ /usr/bin/c++.real >> +RUN echo "#!/usr/bin/env bash" > /usr/bin/c++ && \ >> +echo "exec /usr/bin/c++.real \"\$@\" -Wno-error -Wno-int-conversion" >> >> /usr/bin/c++ && \ >> +chmod +x /usr/bin/c++ >> + > > This assumes this dockerfile will not be used to test a current > build. Probably ok. > Why is it a problem? It just disables werror in a systematic way, without relying on configure --disable-werror that was not always there. >> +# 2.9.0 => 3.1.0: clone git submodules from expired url, replace them with >> gitlab. >> +# 2.9.0 => 2.11.0: issue with libfdt version, solved with dtc submodule, >> which >> +# was removed later. >> +# Ubuntu < 14.04: git has no --single-branch, so perform manual fetch after >> clone. > > See how unstructured this has become? A comment here, the code down > there, nothing actually checks the version, the sed happens for all > versions, etc. Dockerfiles are bad at this kind of thing. > This one is just a comment, and the code below has a single implementation that works over all versions. We could have ifs with multiple code paths, just a question of personal taste I suppose. Out of the if on ubuntu version at the beginning, the general approach was to have a single code path for all versions of QEMU/Ubuntu, without ifs per version. >> +ARG QEMU_VERSION >> +RUN echo "QEMU_VERSION=${QEMU_VERSION:?QEMU_VERSION build arg must be set}" > > In order to build local trees I added at this point: > > RUN --mount=type=bind,from=qemu-src,target=/tmp/qemu-src,rw \ > cd /tmp && \ > if echo "${QEMU_VERSION}" | grep -q "\."; then \ > git clone https://gitlab.com/qemu-project/qemu.git --depth 1 && cd qemu > && \ > git fetch origin "${QEMU_VERSION}" --depth 1 && \ > git checkout FETCH_HEAD; \ > else \ > git clone qemu-src qemu && cd qemu && \ > git checkout "${QEMU_VERSION}"; \ > fi && \ > We can extend to support any sha-1 (it's just a matter of doing full vs shallow clone). For the local build, I don't like the idea to build from local state, it has zero guarantee for reproducibility, which goes against what dockerfiles are IMHO. Regards, Pierrick
