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,90 @@
|
||||
package install
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FixIncludeOptions controls how FixInclude rewrites include directives.
|
||||
type FixIncludeOptions struct {
|
||||
// MapWinSDK restores the canonical "GL/" casing after lowercasing,
|
||||
// since that's the cross-platform spelling for that header directory.
|
||||
MapWinSDK bool
|
||||
}
|
||||
|
||||
// reIncludeLine matches `#include <foo/bar.h>` or `#include "foo.h"`, but
|
||||
// not `#include IDENTIFIER` (macro-expanded includes).
|
||||
var reIncludeLine = regexp.MustCompile(`^\s*#\s*include\s+["<][\w.\\/]+[">]`)
|
||||
|
||||
// FixInclude rewrites #include directives under root to reference the
|
||||
// lowercase header names produced by Lowercase, since MSVC/WinSDK headers
|
||||
// reference each other with casing that's internally inconsistent (but
|
||||
// self-consistent once lowercased). Every text file under root is
|
||||
// rewritten with normalized (LF) line endings as a side effect.
|
||||
func FixInclude(root string, opts FixIncludeOptions) error {
|
||||
var doDir func(dir string) error
|
||||
doDir = func(dir string) error {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, e := range entries {
|
||||
path := filepath.Join(dir, e.Name())
|
||||
if e.Type()&os.ModeSymlink != 0 {
|
||||
continue
|
||||
}
|
||||
if e.IsDir() {
|
||||
if err := doDir(path); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := fixFile(path, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return doDir(root)
|
||||
}
|
||||
|
||||
func fixFile(path string, opts FixIncludeOptions) error {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for i, line := range lines {
|
||||
if reIncludeLine.MatchString(line) {
|
||||
code, comment := line, ""
|
||||
if idx := strings.Index(line, "//"); idx >= 0 {
|
||||
code, comment = line[:idx], line[idx:]
|
||||
}
|
||||
code = lowercaseAndSlash(code)
|
||||
if opts.MapWinSDK {
|
||||
code = strings.Replace(code, "gl/", "GL/", 1)
|
||||
}
|
||||
line = code + comment
|
||||
}
|
||||
lines[i] = strings.TrimRight(line, "\r\n")
|
||||
}
|
||||
return os.WriteFile(path, []byte(strings.Join(lines, "\n")), 0o644)
|
||||
}
|
||||
|
||||
// lowercaseAndSlash lowercases ASCII letters and turns backslashes into
|
||||
// forward slashes, leaving everything else (including non-ASCII bytes)
|
||||
// untouched.
|
||||
func lowercaseAndSlash(s string) string {
|
||||
b := []byte(s)
|
||||
for i, c := range b {
|
||||
switch {
|
||||
case c >= 'A' && c <= 'Z':
|
||||
b[i] = c - 'A' + 'a'
|
||||
case c == '\\':
|
||||
b[i] = '/'
|
||||
}
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
Reference in New Issue
Block a user