Files
netris-nestri/build/Containerfile.proton
T
Wanjohi 6770684e5c fix(build): let Proton's Wine create non-ASCII file names under Steam
Steam starts games with LC_ALL=C, and Wine reads the C locale as ASCII
for every Unix file name it makes, so a name like '• drum loop.webm'
could not be created at all. Sheepy, an NW.js game, unpacks its app from
a zip at start, stopped at that file, and showed NW.js's empty window.

Proton is now patched before it builds (patches/proton/<submodule>/), and
the first patch reads an ASCII locale as UTF-8. The image name gains -nN
for our patch level, so a patched build never takes the plain tag's name.
2026-10-15 10:56:29 +03:00

135 lines
7.4 KiB
Docker

# ═══════════════════════════════════════════════════════════
# proton-cachyos, built wow64-only
#
# Separate from the guest Containerfile because it is a separate cadence:
# hours to build, and only when PROTON_TAG moves. A box is given the result
# at run time instead of carrying it, which is why this file is not part of
# the guest image build and is not reached by `make build`.
#
# The final stage is FROM scratch, so the image *is* the Proton tree — nothing
# to run, only something to COPY --from.
#
# Build and publish with `make proton-image` / `make proton-push`. **Its
# context is this directory**, not the repository root the guest build uses:
# all it needs is the two scripts beside it, and a context of the whole tree
# would hand it a multi-gigabyte `output/` for no reason.
#
# `PROTON_TAG` is the one thing to change, and the Makefile derives the
# published image's version from it. They are the same number in two
# spellings, and an image whose name does not say which Proton is inside it
# is worse than no image.
# ═══════════════════════════════════════════════════════════
FROM docker.io/archlinux:base-devel AS builder
# proton-cachyos-native's makedepends, minus every lib32-* (that is the whole
# point of --enable-wow64), with two substitutions Arch requires: ocl-icd
# provides opencl-icd-loader, and mesa-libgl is folded into libglvnd.
#
# glib2-devel is the one that is easy to miss — it carries glib-mkenums, which
# libsoup's meson looks up through glib-2.0's pkg-config variables and fails on
# obscurely. unzip and zip are for the xalia dist step. afdko is not packaged at
# all, and the fonts submodule needs it, so it comes from PyPI.
RUN pacman -Syu --noconfirm --needed \
alsa-lib clang cmake curl ffmpeg fontforge giflib git glib2-devel \
glslang gnutls gtk3 libgphoto2 libglvnd libpulse libva libxcomposite \
libxinerama libxxf86vm lld mesa meson ninja nasm \
opencl-headers ocl-icd pcsclite perl perl-json python python-pip \
python-pefile python-setuptools-scm rsync rust samba unixodbc \
unzip zip v4l-utils vulkan-headers vulkan-icd-loader wayland \
wayland-protocols wget xorg-util-macros \
mingw-w64-gcc mingw-w64-binutils mingw-w64-crt mingw-w64-headers \
mingw-w64-winpthreads \
&& pip install --break-system-packages --no-cache-dir afdko \
&& pacman -Scc --noconfirm
# widl generates vkd3d's public headers. Without it autoconf sets HAVE_WIDL to
# false, the headers are silently not generated, and the build dies an hour
# later on a missing vkd3d_d3dx9shader.h. Arch ships widl only inside `wine`,
# which requires multilib; the AUR's mingw-w64-tools builds it standalone, so do
# the same.
#
# The digest is not a formality. What is extracted here has its `configure` and
# its makefiles run as root in this builder, and what they produce is copied
# into the image a box runs -- so whoever can change these bytes can change what
# runs on every host. SourceForge hands the request to whichever mirror it
# likes, over a connection this builder does not pin, and the project publishes
# no signature. The digest is the only thing that makes the mirror not matter.
#
# Taken 2026-09-14 from two different mirrors of v14.0.0, which agreed. A
# mismatch here is not a thing to paper over by taking the new digest: it means
# the bytes behind this exact version string changed, and that wants looking at
# before it wants fixing.
ARG MINGW_W64_VER=14.0.0
ARG MINGW_W64_SHA256=6eaf921d9eb987d3820b364ea9775bc19b965ec81490b6fdd716526c28e1995c
RUN curl -fL --retry 3 -o /tmp/mingw-w64.tar.bz2 \
"https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/mingw-w64-v${MINGW_W64_VER}.tar.bz2/download" \
&& echo "${MINGW_W64_SHA256} /tmp/mingw-w64.tar.bz2" | sha256sum -c - \
&& tar xf /tmp/mingw-w64.tar.bz2 -C /tmp \
&& for arch in i686-w64-mingw32 x86_64-w64-mingw32; do \
mkdir -p "/tmp/widl-${arch}" && cd "/tmp/widl-${arch}" \
&& "/tmp/mingw-w64-v${MINGW_W64_VER}/mingw-w64-tools/widl/configure" \
--prefix=/usr --target="${arch}" --program-prefix="${arch}-" \
&& make -j"$(nproc)" && make install; \
done \
&& rm -rf /tmp/mingw-w64* /tmp/widl-*
WORKDIR /build
ARG PROTON_GIT=https://github.com/CachyOS/proton-cachyos.git
ARG PROTON_TAG=cachyos-11.0-20261005-native
ARG GECKO_VER=2.47.4
ARG MONO_VER=11.3.0
ARG XALIA_VER=0.5.0
ARG JOBS=
# Fetch and build are separate layers on purpose: the submodule checkout runs
# well past ten minutes, and a build that fails on a flag or a missing tool must
# not pay for it again.
COPY scripts/proton-fetch.sh /build/proton-fetch.sh
RUN PROTON_GIT="${PROTON_GIT}" PROTON_TAG="${PROTON_TAG}" \
GECKO_VER="${GECKO_VER}" MONO_VER="${MONO_VER}" XALIA_VER="${XALIA_VER}" \
bash /build/proton-fetch.sh
# What proton-cachyos-native lists as run-time dependencies, installed here too.
# Its own packaging builds with them present, and a newer tag can turn a plugin
# that used to be skipped when its library is absent into a required one --
# gst-plugins-bad's uvch264 needs libgudev as of cachyos-11.0-20261005, and
# configure stops an hour in on "gudev-1.0 not found". The set is the
# PKGBUILD's `depends`, minus what is installed above, the lib32 and virtual
# entries, and `atk`, which Arch no longer ships.
#
# Its own layer, after the fetch, so that a dependency a tag bump turns up
# costs a package install and not the submodule checkout again.
RUN pacman -Syu --noconfirm --needed \
attr cabextract desktop-file-utils fontconfig flac freetype2 gettext \
libgudev libnsl libpcap libunwind libvpx libwebp libx11 libxcursor \
libxext libxkbcommon libxml2 libxi libxrandr mpg123 pipewire python-six \
speex speexdsp cairo dbus-glib freeglut gdk-pixbuf2 glu lcms2 libcaca \
libcanberra dbus libdrm libice libibus libnm libusb libvdpau libvorbis \
libxft libxmu libxrender libxtst nspr openal pango sdl2-compat librsvg \
libsm libtheora xz \
&& pacman -Scc --noconfirm
# Our own patches, one directory per submodule they apply to: patches/proton/
# wine/ applies in wine/. Its own layer, after the fetch and the dependencies,
# so a patch change costs the build and nothing before it. A patch that no
# longer applies to a new tag stops here, before the hours of building.
COPY patches/proton /build/patches/proton
RUN set -e; cd /build/patches/proton; \
for dir in */; do \
for p in "${dir}"*.patch; do \
echo "applying ${p}"; \
patch -d "/build/proton-cachyos/${dir%/}" -p1 --forward < "${p}"; \
done; \
done
COPY scripts/proton-build.sh /build/proton-build.sh
RUN GECKO_VER="${GECKO_VER}" MONO_VER="${MONO_VER}" JOBS="${JOBS}" \
bash /build/proton-build.sh
# ═══════════════════════════════════════════════════════════
# The publishable artifact: the Proton tree and nothing else
# ═══════════════════════════════════════════════════════════
FROM scratch
COPY --from=builder /artifacts/proton/ /