Initial implementation of msvc-go-wine

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.
This commit is contained in:
Cheviiot
2026-07-25 00:57:41 +10:00
commit 6464da7847
41 changed files with 4207 additions and 0 deletions
@@ -0,0 +1,20 @@
msvc-go-wine: always record the starting directory.
vsdevcmd_start.bat only saved __VSCMD_CURRENT_DIR when the caller passed
-startdir=none, which doesn't hold up under Wine's cmd.exe (see
https://bugs.winehq.org/show_bug.cgi?id=57137). Just save it
unconditionally instead of gating on the flag.
diff --git a/Common7/Tools/vsdevcmd/core/vsdevcmd_start.bat b/Common7/Tools/vsdevcmd/core/vsdevcmd_start.bat
index 21caae9..fc18228 100644
--- a/Common7/Tools/vsdevcmd/core/vsdevcmd_start.bat
+++ b/Common7/Tools/vsdevcmd/core/vsdevcmd_start.bat
@@ -8,7 +8,7 @@ call "%~dp0parse_cmd.bat" %*
if "%ERRORLEVEL%" NEQ "0" set /A __vscmd_vsdevcmd_errcount=__vscmd_vsdevcmd_errcount+1
@REM Save the current directory, if -startdir=none was specified by the user.
-if /I "%VSCMD_ARG_STARTDIR%" == "none" set "__VSCMD_CURRENT_DIR=%CD%"
+set "__VSCMD_CURRENT_DIR=%CD%"
if "%VSCMD_DEBUG%" GEQ "2" (
@echo [DEBUG:%~n0] Parsing results...
@@ -0,0 +1,66 @@
msvc-go-wine: find the Windows SDK/UCRT dirs on disk before trying the registry.
winsdk.bat locates the Windows 10/11 SDK and Universal CRT SDK by querying
the Windows Registry, which doesn't have real SDK installation entries
under Wine. msvc-wine's own install layout always puts the SDK directly
under "%VSINSTALLDIR%Windows Kits\10", so check that location first and
only fall back to the registry-based lookups when it's not there.
diff --git a/Common7/Tools/vsdevcmd/core/winsdk.bat b/Common7/Tools/vsdevcmd/core/winsdk.bat
index af09494..c303775 100644
--- a/Common7/Tools/vsdevcmd/core/winsdk.bat
+++ b/Common7/Tools/vsdevcmd/core/winsdk.bat
@@ -64,6 +64,11 @@ exit /B 0
if "%VSCMD_DEBUG%" GEQ "3" goto :GetWin10SdkDirVerbose
+if exist "%VSINSTALLDIR%Windows Kits\10\include\" (
+ SET "WindowsSdkDir=%VSINSTALLDIR%Windows Kits\10\"
+ exit /B 0
+)
+
call :GetWin10SdkDirHelper HKLM\SOFTWARE\Wow6432Node > nul 2>&1
if errorlevel 1 call :GetWin10SdkDirHelper HKCU\SOFTWARE\Wow6432Node > nul 2>&1
if errorlevel 1 call :GetWin10SdkDirHelper HKLM\SOFTWARE > nul 2>&1
@@ -73,6 +78,11 @@ exit /B 0
:GetWin10SdkDirVerbose
+if exist "%VSINSTALLDIR%Windows Kits\10\include\" (
+ SET "WindowsSdkDir=%VSINSTALLDIR%Windows Kits\10\"
+ exit /B 0
+)
+
call :GetWin10SdkDirHelper HKLM\SOFTWARE\Wow6432Node
if errorlevel 1 call :GetWin10SdkDirHelper HKCU\SOFTWARE\Wow6432Node
if errorlevel 1 call :GetWin10SdkDirHelper HKLM\SOFTWARE
@@ -231,6 +241,14 @@ set UniversalCRTSdkDir=
if "%VSCMD_DEBUG%" GEQ "3" goto :GetUniversalCRTSdkDirVerbose
+if exist "%WindowsSdkDir%Lib\%WindowsSDKVersion%ucrt\" (
+ SET "UniversalCRTSdkDir=%WindowsSdkDir%"
+ exit /B 0
+) else if exist "%VSINSTALLDIR%Windows Kits\10\Lib\" (
+ SET "UniversalCRTSdkDir=%VSINSTALLDIR%Windows Kits\10\"
+ exit /B 0
+)
+
call :GetUniversalCRTSdkDirHelper HKLM\SOFTWARE\Wow6432Node > nul 2>&1
if errorlevel 1 call :GetUniversalCRTSdkDirHelper HKCU\SOFTWARE\Wow6432Node > nul 2>&1
if errorlevel 1 call :GetUniversalCRTSdkDirHelper HKLM\SOFTWARE > nul 2>&1
@@ -240,6 +258,14 @@ exit /B 0
:GetUniversalCRTSdkDirVerbose
+if exist "%WindowsSdkDir%Lib\%WindowsSDKVersion%ucrt\" (
+ SET "UniversalCRTSdkDir=%WindowsSdkDir%"
+ exit /B 0
+) else if exist "%VSINSTALLDIR%Windows Kits\10\Lib\" (
+ SET "UniversalCRTSdkDir=%VSINSTALLDIR%Windows Kits\10\"
+ exit /B 0
+)
+
call :GetUniversalCRTSdkDirHelper HKLM\SOFTWARE\Wow6432Node
if errorlevel 1 call :GetUniversalCRTSdkDirHelper HKCU\SOFTWARE\Wow6432Node
if errorlevel 1 call :GetUniversalCRTSdkDirHelper HKLM\SOFTWARE