Files
Vintner/internal/wrapper/rewrite_test.go
T
Cheviiot 6464da7847 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.
2026-07-25 00:57:41 +10:00

50 lines
1.3 KiB
Go

package wrapper
import (
"os"
"path/filepath"
"testing"
)
func TestRewriteArg(t *testing.T) {
dir := t.TempDir()
sub := filepath.Join(dir, "inc")
if err := os.Mkdir(sub, 0o755); err != nil {
t.Fatal(err)
}
file := filepath.Join(sub, "foo.h")
if err := os.WriteFile(file, nil, 0o644); err != nil {
t.Fatal(err)
}
cases := []struct {
name string
in string
want string
}{
{"single-letter option + abs dir", "-I" + sub, "-Iz:" + sub},
{"two-letter option + abs file", "-Fo" + file, "-Foz:" + file},
{"long colon option + abs file", "-MANIFESTINPUT:" + file, "-MANIFESTINPUT:z:" + file},
{"bare absolute path", file, "z:" + file},
{"plain flag untouched", "-nologo", "-nologo"},
{"nonexistent dir untouched", "-I/does/not/exist/at/all", "-I/does/not/exist/at/all"},
{"root-level bare path untouched", "/nologo", "/nologo"},
{"relative path untouched", "test.c", "test.c"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := rewriteArg(tc.in); got != tc.want {
t.Errorf("rewriteArg(%q) = %q, want %q", tc.in, got, tc.want)
}
})
}
}
func TestRewriteArgsPreservesOrderAndLength(t *testing.T) {
in := []string{"/nologo", "-c", "test.c"}
out := RewriteArgs(in)
if len(out) != len(in) {
t.Fatalf("length changed: %v -> %v", in, out)
}
}