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

Reply via email to