Builds a native arm64 Wine for macOS from
athei/wine, upstream Wine with our own
series, and bundles it into a self-contained, relocatable wine/ tree. x86
programs, i386 and x86_64, run under FEX
in the same process. Direct3D goes through per-process selectable
implementations (DXMT, mtld3d, wined3d), chosen by compatdb.so, a library
built from compatdb/. Direct3D 12 is vkd3d-proton on Vulkan.
- docs/arm64.md: how the build works: the sources, FEX, DXMT, vkd3d-proton, Vulkan, the signed loader app, what the bundle step checks.
- docs/compatdb.md: rules,
WINE_COMPATDB, recipes, logging. - docs/maintenance.md: internals, floors, pins, upgrade checklist.
The arm64-* releases
carry the bundle and, in a second archive, its PE files before their
symbols were stripped. The release workflow
builds them (releases). The loader is
signed by a certificate of the team that the provisioning profile in
signing/ belongs to (loader app).
To build the bundle locally, see Building.
The cx-* releases are the x86_64 Wine this repository built before, run
under Rosetta. They are frozen and kept for comparison.
The minimum macOS is MACOS_FLOOR in profile.sh.
| Path | Contents |
|---|---|
bin/ |
What make install creates; bin/wine is a symlink to the loader in wine.app |
lib/wine/aarch64-windows/, lib/wine/i386-windows/ |
Wine's PE modules, FEX's emulator DLLs (also as xtajit.dll and xtajit64.dll, the names Wine loads), and fake-module markers for the Direct3D DLLs |
lib/wine/aarch64-unix/ |
Wine's unix modules, compatdb.so, DXMT's winemetal.so and FEX's unix halves |
lib/wine/aarch64-unix/wine.app/ |
The loader, its Info.plist and the embedded provisioning profile (loader app) |
lib/wine/dxgi/<impl>/, lib/wine/d3d9/<impl>/ |
One tree per Direct3D implementation (see Direct3D backends) |
lib/wine/x86_64-windows/ |
Full bundle only: the x86_64 link libraries of the Wine SDK. Nothing reads the directory at run time |
lib/wine/tests/i386-windows/, lib/wine/tests/x86_64-windows/ |
Full bundle only: Wine's graphics conformance tests as <name>_test.exe, for testing the bundle's Direct3D, OpenGL and Vulkan stack (graphics tests) |
lib/wine/tests/VERSION |
Full bundle only: one line, the wine --version string of the Wine the tests were built from, the same as the bundle's own bin/wine --version |
lib/external/ |
Bundled dylibs, the Vulkan loader, KosmicKrisp, and Mesa's EGL with Zink (OpenGL) |
share/vulkan/icd.d/ |
KosmicKrisp's manifest, naming the driver by a path relative to the manifest |
An x64 process runs as ARM64EC, and Wine looks up its builtins in
aarch64-windows, so the 64-bit modules of every Direct3D tree live under
<impl>/aarch64-windows/. They are all ARM64X, so the default-dir markers
of their names are hybrid ones, which x64 and native ARM64 processes both
accept, and the bundle step fails on any file in an aarch64-windows
directory that an x64 process would load but not run natively
(Direct3D trees).
| Variable | Effect |
|---|---|
WINE_COMPATDB |
Extra or changed compatdb rules for the whole process tree. Format: docs/compatdb.md |
WINEDLLOVERRIDES |
Works as in Wine, but cannot undo an override compatdb adds; use a rule (recipes) |
WINEMSYNC |
Unset or a non-zero number: msync is on. Any other value (0, empty, not a number): off, and Wine synchronizes through wineserver requests as upstream Wine does. Only the wineserver reads it (runtime) |
WINENCPU |
Limits the number of processors Wine reports to programs, for those that misbehave with many. A value of 0, one that is not a number, or one above the real count is ignored (runtime) |
FEX_HARDWARE_TSO |
Unset or 1: FEX runs x86 code with the hardware x86 memory ordering (TSO) instead of emulating it with ordered loads and stores. The ordering is a per-thread mode: FEX switches a thread to it whenever the thread enters x86 code and back at the syscalls and unix calls of x86 code, since native code runs slower in it. 0: FEX emulates the ordering. Any other value logs an error and FEX emulates the ordering |
FEX_HOSTFEATURES |
FEX's host feature overrides. x86_64 programs, which run as ARM64EC, run without AFP (the Arm floating-point behaviour FEX uses to match x86) by default, because the floating-point mode would have to change on every call between x86 code and native code, which costs more than AFP saves. enableafp turns it back on. i386 programs keep AFP |
VK_DRIVER_FILES |
Unset: ntdll sets it to the bundle's share/vulkan/icd.d, so the Vulkan loader uses the bundled KosmicKrisp and no driver installed elsewhere on the Mac. Set: the loader uses the manifests it names instead. ntdll also leaves it unset when the caller sets VK_ICD_FILENAMES or VK_ADD_DRIVER_FILES; the loader then uses those, and the bundled driver is not added to them (Vulkan) |
MESA_KK_EXPERIMENTAL |
Which of Mesa's own experimental features KosmicKrisp advertises. The features Direct3D 12 and OpenGL through Zink need are always on in this build, whatever it says (Vulkan) |
VKD3D_CONFIG, VKD3D_DEBUG and vkd3d-proton's other variables |
vkd3d-proton's settings, as upstream describes them. A compatdb env rule sets one for one game (recipes) |
WINE_D3D_CONFIG |
wined3d's settings, as in Wine. renderer=vulkan puts wined3d on KosmicKrisp. compatdb's wined3d=vulkan sets it for one game, keeping the variable's other options; the programs that game starts inherit it unless a rule for them sets wined3d (wined3d on Vulkan) |
| Family | APIs | Rule value | Tree | Chosen by |
|---|---|---|---|---|
dxgi |
D3D10, D3D11, D3D12 | dxmt (DXMT, D3D12 from vkd3d-proton) |
lib/wine/dxgi/dxmt/ |
A built-in rule for every process (builtin.rs) |
dxgi |
D3D10, D3D10.1, D3D11 | wined3d |
lib/wine/dxgi/wined3d/ |
A rule that asks for it, or no rule at all, as in upstream Wine |
d3d9 |
D3D9 | mtld3d (mtld3d) |
lib/wine/d3d9/mtld3d/ |
A built-in rule for every process (builtin.rs) |
d3d9 |
D3D9 | wined3d |
lib/wine/d3d9/wined3d/ |
A rule that asks for it, or no rule at all, as in upstream Wine |
- compatdb.so prepends one tree per family to every process's builtin search path, so a process sees one coherent set of modules. To pick a tree per game, see recipes.
- The default directories hold only fake-module markers for the Direct3D
DLLs, so a process gets Direct3D only through the tree compatdb.so
prepends. The bundle always ships
compatdb.so; a process without it has no Direct3D. - D3D10, D3D11 and D3D12 form one family because they create their device
or swapchain through one
dxgi.dlland only work with their own implementation's copy. - D3D12 is vkd3d-proton, which runs on Vulkan, on KosmicKrisp. Its DLLs sit
in the
dxmttree, and DXMT'sdxgi.dllpresents its swapchains (vkd3d-proton). Thewined3dtree has no D3D12: a game that a rule givesdxgi=wined3dcannot loadd3d12.dll. Wine's own D3D12 is left out of the bundle. - A game that can use both D3D12 and D3D11 and prefers D3D12 gets vkd3d-proton instead of DXMT's D3D11. A rule can hide D3D12 from one game and keep DXMT (recipes).
- DXMT's, vkd3d-proton's and mtld3d's 64-bit modules are ARM64X: an x64 game loads their ARM64EC half and a native ARM64 program their ARM64 half, so they run natively in either, and only their i386 modules run under FEX with the game (DXMT, vkd3d-proton, mtld3d).
- A native ARM64 program gets the same trees, DXMT for
dxgiand mtld3d ford3d9, and a rule can give it either tree of each family (native ARM64 programs). It can load a Direct3D name only where its marker is a hybrid one, which every name has while every tree is ARM64X (limitations). - D3D8 and DirectDraw stay on wined3d. Which modules each tree holds is in
Step 4 of
bundle-wine.sh. - wined3d draws through OpenGL. The compatdb setting
wined3dcan put it on Vulkan for one game; it applies to every wined3d user in the process (wined3d on Vulkan).
Each row decided by a rule is a built-in compatdb rule that matches every process and can be changed per game or globally. The other rows are not compatdb rules, on purpose: msync is the wineserver's, which starts before compatdb.so loads in any process; FEX's memory ordering and AFP are emulator settings, not compatibility settings; KosmicKrisp's features are what Direct3D 12 and OpenGL through Zink need, so nothing turns them off; D3D12's frame latency handles are signalled about when Windows signals them, a fix rather than a setting to choose per game; and the Vulkan driver choice keeps the bundle self-contained, so it is ntdll's, before any rule.
| Behavior | Default in this bundle | Decided by |
|---|---|---|
| D3D10, D3D11 and D3D12 | DXMT, and vkd3d-proton for D3D12, instead of wined3d, which has no D3D12 here | A built-in rule for every process (builtin.rs, Direct3D backends) |
| D3D9 | mtld3d instead of wined3d | A built-in rule for every process (builtin.rs, Direct3D backends) |
| DPI awareness | Every process is system-aware | A built-in rule for every process (builtin.rs, recipes) |
| Raw mouse input | Raw input and DirectInput take mouse movement from GameController, in the device's own counts, instead of from the cursor, which macOS accelerates and scales for retina: mouselook is linear | A built-in rule for every process (builtin.rs, GameController mice) |
| x87 precision under FEX | FEX emulates every program's x87 code with reduced precision | A built-in rule for every process (builtin.rs, built-in environment rules) |
| No-execute | Permanently on in every 32-bit process, even when its executable or a DLL lacks the NX_COMPAT flag, for which Wine would turn it off |
A built-in rule for every process (builtin.rs, no-execute) |
| Synchronization | CrossOver's msync is on: synchronization objects use Mach semaphores and shared memory | The wineserver of our Wine at wine-src.ref; WINEMSYNC=0 where the session starts turns it off (runtime) |
| x86 memory ordering | FEX runs a thread's x86 code with the hardware x86 memory ordering (TSO), switching per thread | Our FEX at fex-src.ref; FEX_HARDWARE_TSO (environment variables) |
| AFP in x86_64 programs | FEX runs x86_64 programs, which run as ARM64EC, without AFP | Our FEX at fex-src.ref; FEX_HOSTFEATURES (environment variables) |
| KosmicKrisp's experimental features | Advertised whatever MESA_KK_EXPERIMENTAL says |
Our Mesa at mesa-src.ref (Vulkan) |
| Vulkan driver | Only the bundled KosmicKrisp: ntdll points VK_DRIVER_FILES at the bundle's manifest unless the caller sets VK_DRIVER_FILES, VK_ICD_FILENAMES or VK_ADD_DRIVER_FILES |
ntdll of our Wine at wine-src.ref (environment variables, Vulkan) |
| OpenGL | Mesa's EGL with Zink on KosmicKrisp instead of Apple's OpenGL; no OpenGL on a Mac without a Metal 4 GPU unless a rule gives the program opengl=cgl |
A built-in rule for every process (builtin.rs, OpenGL, recipes) |
| D3D12 frame latency handles | Signalled once the GPU has finished a frame instead of once it is on screen, so a game that waits on one keeps the GPU busy; how far it gets ahead of the display stays bounded | Our vkd3d-proton at vkd3d-proton-src.ref (vkd3d-proton) |
| Mono and Gecko | Disabled (mscoree,mshtml=): no install prompt, and an installed wine-mono, Gecko or native .NET stays disabled too |
A built-in rule for every process (builtin.rs, recipes) |
What the build needs is in docs/arm64.md: an
Apple Silicon Mac, the arm64 Homebrew with the formulae in
pins/brew-formulae, Xcode's Metal toolchain,
llvm-mingw, Rust with the aarch64-apple-darwin target, for
compatdb.so, and the signing identity of the loader's
provisioning profile.
bin/build-wine.sh # Mesa and the Vulkan loader, Wine, the SDK, then FEX, DXMT and vkd3d-proton
bin/bundle-wine.sh # assemble the bundle (see Layout)| Script | Flag | Effect |
|---|---|---|
build-wine.sh |
none | Incremental build; configure runs only when the build directory has no Makefile, and in the x86 tree of the Wine SDK also when its configure arguments or WINE_SRC (the source path) changed. Mesa, FEX, DXMT and vkd3d-proton are not built at all while what they staged is current (commands) |
build-wine.sh |
--clean |
Wipes the build directories and what they staged first, and is passed on to build-mesa.sh, build-fex.sh, build-dxmt.sh and build-vkd3d-proton.sh; the only way to pick up changed configure flags or deployment target, except in the x86 tree of the Wine SDK, which is configured again whenever its flags or WINE_SRC change |
bundle-wine.sh |
--dest <dir> |
Writes <dir>/wine instead of the bundle in the layout. Either way it asks before it deletes an existing one, unless --yes |
bundle-wine.sh |
--yes |
Deletes an existing bundle without asking, for an unattended run. A directory that is not empty and has no bin/wine is still refused |
bundle-wine.sh |
--runtime-only |
No development files; the flavor to put into an application |
bundle-wine.sh |
--strict-floor |
A Mach-O that needs a newer macOS than the floor is an error instead of a warning (Homebrew libraries and the floor) |
bundle-wine.sh |
--symbols |
Also keeps the PE files as they were before their symbols were stripped, in wine-symbols next to the bundle |
usage() and the argument parsing in
build-wine.sh and
bundle-wine.sh are authoritative.
build-mesa.sh, build-fex.sh, build-dxmt.sh and
build-vkd3d-proton.sh also run alone
(commands).
| Path | What it holds |
|---|---|
bin/ |
The build and bundle scripts; bin/lib/profile.sh holds the settings and helpers they share, bin/ci/ what the workflows run besides them |
pins/ |
The pinned inputs: redist.env, the refs of the sources the build checks out, and the Homebrew formulae a CI build installs |
.github/workflows/ |
The CI workflows (releases) |
compatdb/ |
The Cargo workspace of compatdb.so and the compatdb-table crate (docs/compatdb.md); run cargo there |
smoke/ |
The programs the bundle step builds and runs to check the bundle |
signing/ |
The loader's provisioning profile |
docs/ |
The documentation this README links to |
Everything lives beside this repository, in the directory that holds it
(the root; WINE_BUILD_ROOT names another, for a checkout kept elsewhere):
| Path | What it is |
|---|---|
scripts/ |
The checkout of this repository, wine-build, named scripts locally |
src/ |
The Wine source, a checkout of athei/wine |
src-fex/ |
The FEX checkout |
src-mesa/ |
The Mesa checkout, for the Vulkan driver |
cache/ |
Downloaded redistributables |
build/<component>/ |
One tree per intermediate: the Wine build, its SDK and the x86 tree for its link libraries and tests, FEX, DXMT, vkd3d-proton and Mesa with the Vulkan loader. DXMT's and vkd3d-proton's trees hold their own clones of the source |
dist/wine |
The bundle |
dist/wine-symbols |
With --symbols, the bundle's PE files before their symbols were stripped (Step 4e of bundle-wine.sh); not part of the bundle |
The name of each tree is set in profile.sh,
with the variable that overrides it (paths). The
scripts mark each build tree they make with a .wine-build-tree file and
refuse to fill or delete a directory that is not empty and has no such
marker, so an override cannot point them at something else (claim_tree
in profile.sh). The exception is an unmarked
tree from before markers existed, which is adopted when its parent is
build/ or another tree and it is not a git checkout.
- Every optional dependency is
--without-*unless re-enabled with--with-*inbuild-wine.sh, so a missing package fails configure instead of silently changing the feature set. --with-openglmakes configure require EGL, the one OpenGL library it looks for without X11. It finds Mesa's, whichbuild-mesa.shbuilds with Zink, through theegl.pcthat script stages; the Mac driver links Apple's OpenGL framework on its own. Zink is only used through the Mac driver's EGL driver, which our Wine series adds (OpenGL).- Wine's Vulkan is on, on the loader and KosmicKrisp
build-mesa.shbuilds (Vulkan). The bundle does not ship Wine's own D3D12 modules; D3D12 is vkd3d-proton's.