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 download
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/Cheviiot/msvc-go-wine/assets"
|
||||
)
|
||||
|
||||
// ApplyCompatibilityFixes applies the embedded Wine compatibility patches
|
||||
// (assets/patches) against a freshly unpacked+moved dest tree: a
|
||||
// "foo.props.patch" is git-applied over dest/foo.props (skipped if already
|
||||
// applied), a "foo.patch"-less file is copied verbatim, and a "foo.remove"
|
||||
// marker deletes dest/foo.
|
||||
func ApplyCompatibilityFixes(dest string) error {
|
||||
return fs.WalkDir(assets.Patches, "patches", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
rel := strings.TrimPrefix(path, "patches/")
|
||||
ext := filepath.Ext(rel)
|
||||
target := strings.TrimSuffix(rel, ext)
|
||||
|
||||
switch ext {
|
||||
case ".patch":
|
||||
return applyGitPatch(dest, path, target)
|
||||
case ".remove":
|
||||
full := filepath.Join(dest, target)
|
||||
if isFile(full) {
|
||||
fmt.Println("Removing", target)
|
||||
return os.Remove(full)
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
fmt.Println("Copying", rel)
|
||||
data, err := assets.Patches.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
full := filepath.Join(dest, rel)
|
||||
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(full, data, 0o644)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func applyGitPatch(dest, embeddedPath, target string) error {
|
||||
full := filepath.Join(dest, target)
|
||||
if !isFile(full) {
|
||||
return nil
|
||||
}
|
||||
data, err := assets.Patches.ReadFile(embeddedPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmp, err := os.CreateTemp("", "msvc-go-wine-*.patch")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(tmp.Name())
|
||||
if _, err := tmp.Write(data); err != nil {
|
||||
tmp.Close()
|
||||
return err
|
||||
}
|
||||
tmp.Close()
|
||||
|
||||
// Skip if the patch has already been applied (reverse-apply check),
|
||||
// so re-running download/install stays idempotent.
|
||||
check := exec.Command("git", "--work-tree=.", "apply", "--quiet", "--reverse", "--check", tmp.Name())
|
||||
check.Dir = dest
|
||||
if check.Run() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println("Patching", target)
|
||||
apply := exec.Command("git", "--work-tree=.", "apply", tmp.Name())
|
||||
apply.Dir = dest
|
||||
apply.Stderr = os.Stderr
|
||||
return apply.Run()
|
||||
}
|
||||
Reference in New Issue
Block a user