A single-binary Go tool for cross compiling with the real MSVC toolchain on Linux via Wine. Behaves as cl/link/lib/rc/midl/mt/dumpbin/msbuild/ nmake/ml/ml64/armasm/armasm64/cmd/findstr depending on the name it's invoked as, plus download/install/env/version management subcommands. - download: fetches the MSVC/WinSDK installer manifest, resolves package selection and dependencies, downloads and verifies payloads, unpacks VSIX/MSI packages, and applies a handful of compatibility patches so VsDevCmd.bat and MSBuild's SDK detection work without a Windows Registry (which doesn't exist under Wine). - install: locates the installed toolchain/SDK versions, normalizes header/library name casing, lays out per-architecture tool symlinks with an env.json config each, and compiles a small native launcher (toolrelay.exe) that lets mt.exe's CMake-compatibility exit code survive Wine's own exit-code truncation. - The wrapper runtime rewrites absolute unix paths in tool arguments into Wine's z:\... form, runs the real .exe under wine, and rewrites the tool's output back to plain unix paths. Verified end-to-end against a real MSVC/WinSDK download: cl, link, mt and the resulting hello.exe all work under Wine, including through the toolrelay.exe relay path and with paths containing non-ASCII characters. Offline unit tests cover the wrapper's path-rewrite/output-filter logic, install-time header lowercasing, and download package-selection/ dependency-resolution.
msvc-go-wine
Cross compile with MSVC on Linux, using Wine — a single-binary Go tool inspired by mstorsjo/msvc-wine's approach (download the real MSVC/WinSDK, wrap the compiler under Wine), implemented independently.
Once installed, you invoke the real Microsoft toolchain exactly like on
Windows: cl, link, lib, rc, midl, mt, dumpbin, msbuild,
nmake, ml, ml64, armasm, armasm64 all just work from your PATH.
How it works
msvc-go-wine is one Go binary that behaves differently depending on the
name it's invoked as (a "multi-call binary", like busybox):
- Invoked as
cl,link,lib, ... → it loads a small per-architectureenv.json, builds theINCLUDE/LIB/WINEPATHenvironment Wine needs, rewrites absolute unix paths in the arguments into Wine'sz:\...form (working around a Wine/cl.exe include-path bug), runs the real.exeunderwine/wine64, and rewrites the tool's output back fromz:\...paths to plain unix paths so your build system's error parsing keeps working. - Invoked as
msvc-go-wine→ it exposes thedownload,install,envandversionmanagement subcommands described below.
Quick start
# 1. Download and unpack MSVC + Windows SDK (requires accepting Microsoft's
# Visual Studio Build Tools license, and msitools for unpacking .msi payloads)
msvc-go-wine download --accept-license --dest ~/my_msvc
# 2. Wire up the tool wrappers
msvc-go-wine install ~/my_msvc
# 3. Add the toolchain to PATH and build
export PATH=~/my_msvc/bin/x64:$PATH
cl /nologo /EHsc hello.cpp
Prerequisites
wine(orwine64) — runs the realcl.exe/link.exe/etc.msitools(msiextract) — unpacks the.msipayloads MSVC/WinSDK ship as.git— used to apply the small compatibility patches bundled withdownload(see Compatibility patches below).
On ALT Linux:
pkcon install wine msitools
Commands
msvc-go-wine download --dest <dir> [options] fetch and unpack MSVC/WinSDK
msvc-go-wine install <dir> wire up wrappers for a downloaded MSVC
msvc-go-wine env --bin <dir>/bin/<arch> print INCLUDE/LIB for native clang-cl/lld-link use
msvc-go-wine version print the version
download supports --msvc-version, --sdk-version, --architecture,
--host-arch, --with-* component toggles, --ignore, --only-download,
--only-unpack, --keep-unpack, --cache, --language,
--include-optional, --skip-recommended, --major, --preview,
--manifest. Run msvc-go-wine download -h for the full list.
Using clang-cl/lld-link instead of Wine
You don't need Wine at all if you drive the (nonredistributable) MSVC/WinSDK headers and libraries with Clang/LLD in MSVC-compatible mode:
eval "$(msvc-go-wine env --bin ~/my_msvc/bin/x64)"
clang-cl -c hello.c
lld-link hello.obj -out:hello.exe
Building from source
go build -o msvc-go-wine ./cmd/msvc-go-wine
Go 1.23+ is all you need to build it; wine/msitools are only needed at
run time (install/tool invocation and download respectively).
toolrelay.exe
install compiles assets/vendor/toolrelay.cpp (a small native Windows
launcher, original to this project) with the freshly-installed host-arch
cl.exe (best-effort: if wine isn't present yet, or the compile fails,
install still succeeds and the wrapper runtime just falls back to invoking
tools directly through wine). When present, every non-MSBuild tool
invocation is routed through it via two named FIFOs. This is what lets
mt.exe's CMake-compatibility exit-code translation (0x41020001 → 0xbb)
survive Wine's own exit-code truncation: only a native Windows process
observing the untranslated code via GetExitCodeProcess() can catch it
before Wine marshals the process exit back to Unix and drops everything but
the low byte.
Compatibility patches
download applies a handful of small patches (assets/patches) to the
downloaded MSVC/WinSDK tree - independently written for this project - that
make VsDevCmd.bat and MSBuild's SDK-detection props work without a
Windows Registry (which doesn't exist under Wine): they check the SDK
directly under the VS install root instead of querying the registry, skip
telemetry, and don't hard-fail devcmd setup when an optional component
(ConnectionManagerExe, bundled CMake/Ninja) wasn't downloaded.
Known gaps
downloaddoesn't yet support printing the dependency/reverse-dependency tree, listing available workloads/components/packages, or installing the Windows Driver Kit via--with-wdk-installers; the core selection/ download/unpack/install pipeline is fully implemented.
License
MIT, see LICENSE.txt - covers msvc-go-wine's own source only.
The MSVC Build Tools / Windows SDK that download fetches remain governed
by Microsoft's own license (accepted via --accept-license), same as with
any other way of obtaining them.