mirror of
https://github.com/Cheviiot/Vintner.git
synced 2026-08-03 15:57:24 +00:00
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:
@@ -0,0 +1,72 @@
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// lineFilter rewrites a single line of tool output. nil means "no extra
|
||||
// rewriting" (CR-stripping still always happens, see stripCR).
|
||||
type lineFilter func(line string) string
|
||||
|
||||
// reZDrive matches the wine "z:" drive prefix followed by a path separator,
|
||||
// case-insensitively, anywhere in the line - mirroring sed's `s/z:([\\/])/\1/i`
|
||||
// (no /g flag: only the first occurrence is touched).
|
||||
var reZDrive = regexp.MustCompile(`(?i)z:([\\/])`)
|
||||
|
||||
// stripFirstZDrive removes the first "z:" preceding a path separator,
|
||||
// keeping the separator itself, matching the original's un-global sed rule.
|
||||
func stripFirstZDrive(line string) string {
|
||||
loc := reZDrive.FindStringSubmatchIndex(line)
|
||||
if loc == nil {
|
||||
return line
|
||||
}
|
||||
sep := line[loc[2]:loc[3]]
|
||||
return line[:loc[0]] + sep + line[loc[1]:]
|
||||
}
|
||||
|
||||
var (
|
||||
reNoteIncluding = regexp.MustCompile(`^Note: including file: `)
|
||||
reLineDirective = regexp.MustCompile(`^[ \t]*#[ \t]*line[ \t]`)
|
||||
reNoteErrorWarning = regexp.MustCompile(`(?i)^z:.*\([0-9]+\): (note|error c[0-9]{4}|warning c[0-9]{4}): `)
|
||||
reDumpbinPath = regexp.MustCompile(`^(Dump of file | PDB file found at )`)
|
||||
)
|
||||
|
||||
// clStdoutFilter rewrites cl's "Note: including file:", "#line", and
|
||||
// note/warning/error diagnostic lines from wine's z:\... notation back to
|
||||
// plain unix paths.
|
||||
func clStdoutFilter(line string) string {
|
||||
switch {
|
||||
case reNoteIncluding.MatchString(line):
|
||||
return strings.ReplaceAll(stripFirstZDrive(line), `\`, `/`)
|
||||
case reLineDirective.MatchString(line):
|
||||
return strings.ReplaceAll(stripFirstZDrive(line), `\\`, `/`)
|
||||
case reNoteErrorWarning.MatchString(line):
|
||||
return strings.ReplaceAll(stripFirstZDrive(line), `\`, `/`)
|
||||
default:
|
||||
return line
|
||||
}
|
||||
}
|
||||
|
||||
// clStderrFilter only rewrites "Note: including file:" lines - cl's stderr
|
||||
// diagnostics don't carry the z:\... prefix the stdout ones do.
|
||||
func clStderrFilter(line string) string {
|
||||
if reNoteIncluding.MatchString(line) {
|
||||
return strings.ReplaceAll(stripFirstZDrive(line), `\`, `/`)
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
// dumpbinStdoutFilter mirrors dumpbin's unixify_path variant.
|
||||
func dumpbinStdoutFilter(line string) string {
|
||||
if reDumpbinPath.MatchString(line) {
|
||||
return strings.ReplaceAll(stripFirstZDrive(line), `\`, `/`)
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
// stripCR mirrors the base `s/\r//` rule every wrapper prepends: removes the
|
||||
// first carriage return in the line (CRLF line endings only ever carry one).
|
||||
func stripCR(line string) string {
|
||||
return strings.Replace(line, "\r", "", 1)
|
||||
}
|
||||
Reference in New Issue
Block a user