Skip to content

Repository files navigation

Wine Build Tools

This project provides a modern, containerized environment for building and running a wide range of Wine releases in a Fedora 42-based Docker environment. Unlike many other Wine build projects, this one focuses on rebuilding as many old Wine releases as possible using the same reproducible setup. It mostly avoids out-of-tree patches and instead applies existing commits to fix or work around build issues with legacy Wine releases.

All builds and runs are performed inside a Docker container.

Key points of this build setup

  • oldest Wine release supported: Wine 1.3.34 (Dec 2, 2011)
  • all builds are shared WoW64 (32‑bit + 64‑bit)
  • since Wine 5.2, llvm‑mingw cross‑toolchain is preferred (to produce PE binaries when supported)
  • since Wine 10.0, builds are pure WoW64 (no 32‑bit multilib required anymore)

Quickstart (TL;DR)

# source env script to use helpers
source ./wine_docker.env

# build the docker image
wine_docker_image_build

# enter the docker container (for building/running Wine)
wine_docker_run

# register llvm-mingw toolchain (see later)
export PATH="$PWD/llvm-mingw-20251104-ucrt-ubuntu-22.04-x86_64/bin:$PATH"

# build a Wine release inside the container
./buildwine.py --version=10.0 --clean

# register Wine bindir path for particular build
export PATH="$PWD/mainline-install-10.0-x86_64/bin:$PATH"

# install/run Wine/app
wineboot

Docker setup using helper aliases

Source the environment helpers

The file wine_docker.env defines shell aliases for building and running the Docker environment. It must be sourced, not executed:

source ./wine_docker.env

This registers two aliases:

wine_docker_image_build - builds the Docker image wine-build-env using your host user and group IDs wine_docker_run - creates a Docker container from wine-build-env image and runs it (home directory and the current working directory are mapped)

Build the Docker image

wine_docker_image_build

This creates the wine-build-env Docker image and configures user mappings so files created inside the container are owned by your host user.

Run the Docker container

wine_docker_run

This starts an interactive container session with:

  • DISPLAY forwarding via X11 for GUI apps.
  • PulseAudio mounting for sound.
  • home directory and current directory mounted for seamless development.

The alias internally runs xhost +local:docker to permit local X11 connections.

Building Wine inside the container

It is recommended to use buildwine.py inside the Docker container. If you run buildwine.py without the provided Docker-based build environment, you are on your own.

Cross-compiling with LLVM MinGW

This is the preferred build mode for this Wine builder project (supported since Wine 6.0). See project home page LLVM/Clang/LLD based mingw-w64 toolchain for overview.

Download and install

Download and unpack llvm-mingw release from LLVM/Clang/LLD mingw-w64 release downloads.

wget https://github.com/mstorsjo/llvm-mingw/releases/download/20251104/llvm-mingw-20251104-ucrt-ubuntu-22.04-x86_64.tar.xz

tar axf llvm-mingw-20251104-ucrt-ubuntu-22.04-x86_64.tar.xz

Register toolchain

Inside the container, prepend the bin directory to PATH:

export PATH=$PWD/llvm-mingw-20251104-ucrt-ubuntu-22.04-x86_64/bin:$PATH
clang --version

Now run buildwine.py as usual. The llvm-mingw toolchain will be automatically detected.

Common build commands

Build Wine from the current branch HEAD:

./buildwine.py

Build a specific Wine release:

./buildwine.py --version=8.5

Build Wine-Staging variant of a release (applies the Wine-Staging patchset on top of mainline):

./buildwine.py --variant=staging --version=9.15

Build a custom variant (clones mainline into a separate custom-src tree so you can apply your own patches without touching mainline — useful for Git bisect):

./buildwine.py --variant=custom

Behavior and useful buildwine.py flags

  • shared WoW64 build by default: 32-bit + 64-bit builds are produced
  • tests are disabled by default: enable with --enable-tests
  • Wine-Mono is disabled on non-release builds (no exact Wine release tag on git checkout): enable with --enable-mscoree
  • debugging: to diagnose build failures deterministically, use --jobs=1

Run ./buildwine.py --help for the full list of options.

Building ranges of Wine releases

# Build Wine 9.[0..22]
for i in 9.{0..22}; do ./buildwine.py --version="$i" --clean || break; done

# Build Wine 1.7.[40..49] using available source tags
for i in $(git -C mainline-src tag | sed -n 's/^wine-\(1\.7\.4[0-9]\)/\1/p' | sort -V); do
  ./buildwine.py --version="$i" --clean || break
done

Running Wine inside the container

After building, applications can be run from inside the same container. The build script prints the exact export PATH=... command to run at the end of each build.

# Example: run Wine's notepad
wine notepad.exe

# Run a Windows program from your mounted home directory
wine ~/Downloads/SomeSetup.exe

If you are doing regression testing be careful with automatic downgrades and upgrades of existing WINEPREFIXes ("prefix recycling"). It may work for a consecutive runs of different Wine releases but may get broken beyond repair. Restart with fresh WINEPREFIX in such cases. Reuse prefixes at own risk.

Directory layout

The script maintains a separation of sources, build and install artifacts across variants and architectures:

Directory pattern Contents
mainline-src-*, staging-src-*, custom-src Source trees + git reference mirrors
mainline-build-*, staging-build-*, custom-build-* Build artifacts per arch and version
mainline-install-*, staging-install-*, custom-install-* Install trees

Links

Linting before a push

lint.sh runs the same checks as CI, on the same pinned image:

./lint.sh

It reads both the image tag and the list of checks out of .gitea/workflows/lint.yml, so there is nothing to keep in sync -- adding a check to the workflow makes it run locally too, with no edit here.

Every check runs even after one fails, matching the workflow. Being unable to run them (no docker, unreachable registry) exits non-zero rather than passing: a check that did not run is not a check that passed.

To have it block a bad push automatically, install it as a hook. Hooks are not carried in the repository, so this is done once per clone:

printf '#!/usr/bin/env bash\nexec "$(git rev-parse --show-toplevel)/lint.sh"\n' \
  > .git/hooks/pre-push
chmod +x .git/hooks/pre-push

Override deliberately with git push --no-verify.



License

This project is licensed under the MIT License. See the LICENSE file for details.

SPDX-License-Identifier: MIT

About

Collection of small scripts for working with Wine

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages