mirror of
https://github.com/Cheviiot/Vintner.git
synced 2026-08-03 15:57:24 +00:00
Renamed the GitHub repo, Go module path, binary, and default install directory from msvc-go-wine to vintner. Updated every user-facing string (usage text, error prefixes, README, LICENSE, CI/release workflow) and the embedded compatibility patches' own header text to match; the VINTNER_LANG env var replaces VSMC_GO_WINE_LANG. Also fixes a real bug found while re-verifying the rename end-to-end: Microsoft.Cpp.WindowsSDK.props.patch had LF-only line endings in its hunk body while the real Microsoft-shipped file it targets is CRLF, so `git apply` silently failed on every real install and the SDK detection fix it's meant to provide was never actually taking effect. Restored matching CRLF endings in the hunk (checked against the other five patches, which already had this right). Left the patch's internal MsvcGoWine_ExtraSdkRoots MSBuild property name alone rather than renaming it too - changing hunk content, even just an identifier, breaks reverse-apply idempotency for anyone re-running download against an already-patched tree, which the fix above depends on. Added a .remove marker so an existing install's old-named props file gets cleaned up on the next download. Re-verified end-to-end after the rename: general MSBuild/cl/link still work, and a real KMDF driver build (compile, link, INF stamping, Inf2Cat signability check) still succeeds under the renamed binary. README also gets an accuracy pass: WDK support and the download/env CLI flags it lists were out of date (WDK was previously listed under "Known gaps" despite being implemented and verified), the full tool list was missing mc/cmd/findstr, and the new command aliases and VINTNER_LANG option are now documented.
450 lines
14 KiB
Go
450 lines
14 KiB
Go
package download
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
var reSDKVersion = regexp.MustCompile(`^\d+\.\d+\.\d+`)
|
|
|
|
// TriState represents a `--with-*` style flag: nil means "unset, use the
|
|
// applicable default"; a set value means the user (or a higher-level
|
|
// default) explicitly chose to include/exclude the component.
|
|
type TriState = *bool
|
|
|
|
func on() TriState { v := true; return &v }
|
|
func off() TriState { v := false; return &v }
|
|
|
|
// Options holds every flag that feeds package selection and download.
|
|
type Options struct {
|
|
Package []string
|
|
Ignore []string
|
|
Architecture []string // subset of x86,x64,arm,arm64,host
|
|
HostArch string // x86, x64 or arm64
|
|
OnlyHost bool
|
|
|
|
MSVCVersion string // "", "preview", "16.0".."18.0", "15.4".."15.9"
|
|
SDKVersion string
|
|
|
|
WithDefault TriState
|
|
WithWorkload TriState
|
|
WithMSVC TriState
|
|
WithASAN TriState
|
|
WithSDK TriState
|
|
WithATL TriState
|
|
WithDIA TriState
|
|
WithMSBuild TriState
|
|
WithDevCmd TriState
|
|
|
|
IncludeOptional bool
|
|
SkipRecommended bool
|
|
Language string
|
|
|
|
// WithWDK selects the PlatformToolset registration
|
|
// (Component.Microsoft.Windows.DriverKit.BuildTools) that lets MSBuild
|
|
// recognize WindowsKernelModeDriver10.0/WindowsUserModeDriver10.0
|
|
// PlatformToolsets. It's not part of the manifest-driven default
|
|
// selection - unlike everything else it depends on, it's opt-in via
|
|
// --with-wdk, since the actual driver headers/libs come from a separate
|
|
// NuGet package download handled outside ExpandSelection entirely (see
|
|
// wdk.go and cmd/vintner's runDownload).
|
|
WithWDK bool
|
|
}
|
|
|
|
func addIfWanted(opts *Options, flag TriState, pkg string) {
|
|
if flag == nil {
|
|
return
|
|
}
|
|
if *flag {
|
|
opts.Package = append(opts.Package, pkg)
|
|
} else {
|
|
opts.Ignore = append(opts.Ignore, pkg)
|
|
}
|
|
}
|
|
|
|
type msvcVersionEntry struct {
|
|
gen string // "15" or "16" - which package-name scheme applies
|
|
sdk string
|
|
toolVersion string
|
|
}
|
|
|
|
// msvcVersionTable maps a --msvc-version value to the SDK it pulls in by
|
|
// default and the toolset version fragment used in package ids.
|
|
var msvcVersionTable = map[string]msvcVersionEntry{
|
|
"preview": {"16", "", "Preview"},
|
|
|
|
"16.0": {"16", "10.0.17763", "14.20"},
|
|
"16.1": {"16", "10.0.18362", "14.21"},
|
|
"16.2": {"16", "10.0.18362", "14.22"},
|
|
"16.3": {"16", "10.0.18362", "14.23"},
|
|
"16.4": {"16", "10.0.18362", "14.24"},
|
|
"16.5": {"16", "10.0.18362", "14.25"},
|
|
"16.6": {"16", "10.0.18362", "14.26"},
|
|
"16.7": {"16", "10.0.18362", "14.27"},
|
|
"16.8": {"16", "10.0.18362", "14.28"},
|
|
"16.9": {"16", "10.0.19041", "14.28.16.9"},
|
|
"16.10": {"16", "10.0.19041", "14.29.16.10"},
|
|
"16.11": {"16", "10.0.19041", "14.29.16.11"},
|
|
"17.0": {"16", "10.0.19041", "14.30.17.0"},
|
|
"17.1": {"16", "10.0.19041", "14.31.17.1"},
|
|
"17.2": {"16", "10.0.19041", "14.32.17.2"},
|
|
"17.3": {"16", "10.0.19041", "14.33.17.3"},
|
|
"17.4": {"16", "10.0.22621", "14.34.17.4"},
|
|
"17.5": {"16", "10.0.22621", "14.35.17.5"},
|
|
"17.6": {"16", "10.0.22621", "14.36.17.6"},
|
|
"17.7": {"16", "10.0.22621", "14.37.17.7"},
|
|
"17.8": {"16", "10.0.22621", "14.38.17.8"},
|
|
"17.9": {"16", "10.0.22621", "14.39.17.9"},
|
|
"17.10": {"16", "10.0.22621", "14.40.17.10"},
|
|
"17.11": {"16", "10.0.22621", "14.41.17.11"},
|
|
"17.12": {"16", "10.0.22621", "14.42.17.12"},
|
|
"17.13": {"16", "10.0.22621", "14.43.17.13"},
|
|
"17.14": {"16", "10.0.26100", "14.44.17.14"},
|
|
"18.0": {"16", "10.0.26100", "14.50.18.0"},
|
|
|
|
"15.4": {"15", "10.0.16299", "14.11"},
|
|
"15.5": {"15", "10.0.16299", "14.12"},
|
|
"15.6": {"15", "10.0.16299", "14.13"},
|
|
"15.7": {"15", "10.0.17134", "14.14"},
|
|
"15.8": {"15", "10.0.17134", "14.15"},
|
|
"15.9": {"15", "10.0.17763", "14.16"},
|
|
}
|
|
|
|
func selectToolsetV16(opts *Options, idx Index, userVersion, sdk, toolVersion string, defaultPkgs, defaultIgnores []string) {
|
|
ext := ""
|
|
if toolVersion == "Preview" {
|
|
ext = ".Tools"
|
|
}
|
|
base := "Microsoft.VisualStudio.Component.VC." + toolVersion + ext
|
|
if idx.Find(base+".x86.x64", nil) != nil {
|
|
if contains(opts.Architecture, "x86") || contains(opts.Architecture, "x64") {
|
|
addIfWanted(opts, opts.WithMSVC, base+".x86.x64")
|
|
addIfWanted(opts, opts.WithASAN, "Microsoft.VC."+toolVersion+".ASAN.X86")
|
|
addIfWanted(opts, opts.WithATL, "Microsoft.VisualStudio.Component.VC."+toolVersion+".ATL")
|
|
}
|
|
if contains(opts.Architecture, "arm") {
|
|
addIfWanted(opts, opts.WithMSVC, "Microsoft.VisualStudio.Component.VC."+toolVersion+".ARM")
|
|
addIfWanted(opts, opts.WithATL, "Microsoft.VisualStudio.Component.VC."+toolVersion+".ATL.ARM")
|
|
}
|
|
if contains(opts.Architecture, "arm64") {
|
|
addIfWanted(opts, opts.WithMSVC, "Microsoft.VisualStudio.Component.VC."+toolVersion+".ARM64")
|
|
addIfWanted(opts, opts.WithATL, "Microsoft.VisualStudio.Component.VC."+toolVersion+".ATL.ARM64")
|
|
}
|
|
if opts.SDKVersion == "" {
|
|
opts.SDKVersion = sdk
|
|
}
|
|
} else {
|
|
fmt.Printf("Didn't find exact version packages for %s, assuming this is provided by the default/latest version\n", userVersion)
|
|
opts.Package = append(opts.Package, defaultPkgs...)
|
|
opts.Ignore = append(opts.Ignore, defaultIgnores...)
|
|
}
|
|
}
|
|
|
|
func selectToolsetV15(opts *Options, idx Index, userVersion, sdk, toolVersion string, defaultPkgs, defaultIgnores []string) {
|
|
id := "Microsoft.VisualStudio.Component.VC.Tools." + toolVersion
|
|
if idx.Find(id, nil) != nil {
|
|
addIfWanted(opts, opts.WithMSVC, id)
|
|
if opts.SDKVersion == "" {
|
|
opts.SDKVersion = sdk
|
|
}
|
|
} else {
|
|
fmt.Printf("Didn't find exact version packages for %s, assuming this is provided by the default/latest version\n", userVersion)
|
|
opts.Package = append(opts.Package, defaultPkgs...)
|
|
opts.Ignore = append(opts.Ignore, defaultIgnores...)
|
|
}
|
|
}
|
|
|
|
// ResolveSelection turns Options' high-level flags (--with-*,
|
|
// --msvc-version, --sdk-version, explicit packages) into the final
|
|
// opts.Package/opts.Ignore lists ready for ExpandSelection.
|
|
func ResolveSelection(opts *Options, idx Index) error {
|
|
if len(opts.Architecture) == 0 {
|
|
opts.Architecture = []string{"host", "x86", "x64", "arm", "arm64"}
|
|
}
|
|
if opts.HostArch != "" && contains(opts.Architecture, "host") {
|
|
opts.Architecture = append(opts.Architecture, opts.HostArch)
|
|
}
|
|
|
|
if opts.MSVCVersion != "" {
|
|
if opts.WithMSVC == nil {
|
|
opts.WithMSVC = on()
|
|
}
|
|
if opts.WithASAN == nil {
|
|
opts.WithASAN = on()
|
|
}
|
|
if opts.WithATL == nil {
|
|
opts.WithATL = on()
|
|
}
|
|
if opts.WithSDK == nil {
|
|
opts.WithSDK = on()
|
|
}
|
|
}
|
|
if opts.SDKVersion != "" && opts.WithSDK == nil {
|
|
opts.WithSDK = on()
|
|
}
|
|
|
|
if opts.WithDefault == nil && opts.MSVCVersion == "" && len(opts.Package) == 0 {
|
|
opts.WithDefault = on()
|
|
}
|
|
if opts.WithDefault != nil {
|
|
for _, pair := range []struct {
|
|
flag *TriState
|
|
}{
|
|
{&opts.WithWorkload}, {&opts.WithMSVC}, {&opts.WithASAN}, {&opts.WithSDK},
|
|
{&opts.WithATL}, {&opts.WithDIA}, {&opts.WithMSBuild}, {&opts.WithDevCmd},
|
|
} {
|
|
if *pair.flag == nil {
|
|
v := *opts.WithDefault
|
|
*pair.flag = &v
|
|
}
|
|
}
|
|
}
|
|
|
|
defaultPkgs, defaultIgnores := opts.Package, opts.Ignore
|
|
opts.Package, opts.Ignore = nil, nil
|
|
|
|
addIfWanted(opts, opts.WithWorkload, "Microsoft.VisualStudio.Workload.VCTools")
|
|
if contains(opts.Architecture, "x86") || contains(opts.Architecture, "x64") {
|
|
addIfWanted(opts, opts.WithMSVC, "Microsoft.VisualStudio.Component.VC.Tools.x86.x64")
|
|
addIfWanted(opts, opts.WithASAN, "Microsoft.VisualCpp.ASAN.X86")
|
|
addIfWanted(opts, opts.WithATL, "Microsoft.VisualStudio.Component.VC.ATL")
|
|
}
|
|
if contains(opts.Architecture, "arm") {
|
|
addIfWanted(opts, opts.WithMSVC, "Microsoft.VisualStudio.Component.VC.Tools.ARM")
|
|
addIfWanted(opts, opts.WithATL, "Microsoft.VisualStudio.Component.VC.ATL.ARM")
|
|
}
|
|
if contains(opts.Architecture, "arm64") {
|
|
addIfWanted(opts, opts.WithMSVC, "Microsoft.VisualStudio.Component.VC.Tools.ARM64")
|
|
addIfWanted(opts, opts.WithATL, "Microsoft.VisualStudio.Component.VC.ATL.ARM64")
|
|
}
|
|
|
|
defaultPkgs, opts.Package = opts.Package, defaultPkgs
|
|
defaultIgnores, opts.Ignore = opts.Ignore, defaultIgnores
|
|
|
|
switch {
|
|
case opts.MSVCVersion == "":
|
|
opts.Package = append(opts.Package, defaultPkgs...)
|
|
opts.Ignore = append(opts.Ignore, defaultIgnores...)
|
|
default:
|
|
entry, ok := msvcVersionTable[opts.MSVCVersion]
|
|
if !ok {
|
|
return fmt.Errorf("unsupported MSVC toolchain version %s", opts.MSVCVersion)
|
|
}
|
|
if entry.gen == "15" {
|
|
selectToolsetV15(opts, idx, opts.MSVCVersion, entry.sdk, entry.toolVersion, defaultPkgs, defaultIgnores)
|
|
} else {
|
|
selectToolsetV16(opts, idx, opts.MSVCVersion, entry.sdk, entry.toolVersion, defaultPkgs, defaultIgnores)
|
|
}
|
|
}
|
|
|
|
if err := selectSDK(opts, idx); err != nil {
|
|
return err
|
|
}
|
|
|
|
addIfWanted(opts, opts.WithDIA, "Microsoft.VisualCpp.DIA.SDK")
|
|
addIfWanted(opts, opts.WithMSBuild, "Microsoft.Build")
|
|
addIfWanted(opts, opts.WithMSBuild, "Microsoft.Build.Dependencies")
|
|
addIfWanted(opts, opts.WithDevCmd, "Microsoft.VisualStudio.VC.vcvars")
|
|
addIfWanted(opts, opts.WithDevCmd, "Microsoft.VisualStudio.PackageGroup.VsDevCmd")
|
|
|
|
if opts.WithWDK {
|
|
opts.Package = append(opts.Package, "Component.Microsoft.Windows.DriverKit.BuildTools")
|
|
}
|
|
|
|
normalizeIgnoreCase(opts)
|
|
return nil
|
|
}
|
|
|
|
func selectSDK(opts *Options, idx Index) error {
|
|
switch {
|
|
case opts.WithSDK == nil:
|
|
return nil
|
|
case !*opts.WithSDK:
|
|
for key := range idx {
|
|
if strings.HasPrefix(key, "win10sdk") || strings.HasPrefix(key, "win11sdk") {
|
|
opts.Ignore = append(opts.Ignore, key)
|
|
}
|
|
}
|
|
case opts.SDKVersion == "":
|
|
saved := *opts
|
|
opts.Package = []string{"Microsoft.VisualStudio.Workload.VCTools"}
|
|
opts.IncludeOptional = false
|
|
opts.SkipRecommended = false
|
|
recommended, err := ExpandSelection(idx, opts)
|
|
*opts = saved
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, p := range recommended {
|
|
key := strings.ToLower(p.ID)
|
|
if strings.HasPrefix(key, "win10sdk") || strings.HasPrefix(key, "win11sdk") {
|
|
opts.Package = append(opts.Package, key)
|
|
}
|
|
}
|
|
default:
|
|
found := false
|
|
var versions []string
|
|
for key := range idx {
|
|
if !strings.HasPrefix(key, "win10sdk") && !strings.HasPrefix(key, "win11sdk") {
|
|
continue
|
|
}
|
|
version := key[9:]
|
|
if reSDKVersion.MatchString(version) {
|
|
versions = append(versions, version)
|
|
}
|
|
if key == key[:8]+"_"+opts.SDKVersion {
|
|
found = true
|
|
opts.Package = append(opts.Package, key)
|
|
} else {
|
|
opts.Ignore = append(opts.Ignore, key)
|
|
}
|
|
}
|
|
if !found {
|
|
return fmt.Errorf("WinSDK version %s not found (available: %s)", opts.SDKVersion, strings.Join(versions, ", "))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func normalizeIgnoreCase(opts *Options) {
|
|
for i, s := range opts.Ignore {
|
|
opts.Ignore[i] = strings.ToLower(s)
|
|
}
|
|
}
|
|
|
|
func contains(list []string, v string) bool {
|
|
for _, s := range list {
|
|
if s == v {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// collectDependencyClosure recursively resolves target's dependency tree,
|
|
// honoring arch matching, --ignore, and Optional/Recommended filtering.
|
|
func collectDependencyClosure(idx Index, included map[string]bool, target string, constraints map[string]string, opts *Options) []*Package {
|
|
if contains(opts.Ignore, strings.ToLower(target)) {
|
|
return nil
|
|
}
|
|
p := idx.Find(target, constraints)
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
if opts.OnlyHost && !HostArchCompatible(p, opts.HostArch) {
|
|
return nil
|
|
}
|
|
if !TargetArchCompatible(p, opts.Architecture) {
|
|
return nil
|
|
}
|
|
key := p.Key()
|
|
if included[key] {
|
|
return nil
|
|
}
|
|
included[key] = true
|
|
|
|
ret := []*Package{p}
|
|
for target, dep := range p.Dependencies() {
|
|
id := target
|
|
if dep.TargetID != "" {
|
|
id = dep.TargetID
|
|
}
|
|
if dep.Type == "Optional" && !opts.IncludeOptional {
|
|
continue
|
|
}
|
|
if dep.Type == "Recommended" && opts.SkipRecommended {
|
|
continue
|
|
}
|
|
c := map[string]string{}
|
|
if dep.Version != "" {
|
|
c["version"] = dep.Version
|
|
}
|
|
ret = append(ret, collectDependencyClosure(idx, included, id, c, opts)...)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// ExpandSelection resolves opts.Package (and everything they transitively
|
|
// depend on) into a flat, deduped package list.
|
|
func ExpandSelection(idx Index, opts *Options) ([]*Package, error) {
|
|
included := map[string]bool{}
|
|
var ret []*Package
|
|
for _, id := range opts.Package {
|
|
ret = append(ret, collectDependencyClosure(idx, included, id, nil, opts)...)
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
// PrintDependencyTree writes an indented tree of opts.Package and everything
|
|
// they transitively depend on to w, applying the exact same
|
|
// arch/--ignore/Optional/Recommended filtering collectDependencyClosure
|
|
// (used by ExpandSelection) does, so what's printed matches what an actual
|
|
// download would select. A package already printed once elsewhere in the
|
|
// tree is shown again as a leaf ("(see above)") rather than re-expanded, to
|
|
// keep the output finite for packages multiple components depend on.
|
|
func PrintDependencyTree(w io.Writer, idx Index, opts *Options) {
|
|
printed := map[string]bool{}
|
|
for _, id := range opts.Package {
|
|
printDepNode(w, idx, id, nil, "", opts, 0, printed)
|
|
}
|
|
}
|
|
|
|
func printDepNode(w io.Writer, idx Index, target string, constraints map[string]string, depType string, opts *Options, depth int, printed map[string]bool) {
|
|
if contains(opts.Ignore, strings.ToLower(target)) {
|
|
return
|
|
}
|
|
indent := strings.Repeat(" ", depth)
|
|
annotation := ""
|
|
if depType != "" {
|
|
annotation = " [" + depType + "]"
|
|
}
|
|
|
|
p := idx.Find(target, constraints)
|
|
if p == nil {
|
|
fmt.Fprintf(w, "%s%s (not found)%s\n", indent, target, annotation)
|
|
return
|
|
}
|
|
if opts.OnlyHost && !HostArchCompatible(p, opts.HostArch) {
|
|
return
|
|
}
|
|
if !TargetArchCompatible(p, opts.Architecture) {
|
|
return
|
|
}
|
|
|
|
key := p.Key()
|
|
if printed[key] {
|
|
fmt.Fprintf(w, "%s%s%s (see above)\n", indent, p.ID, annotation)
|
|
return
|
|
}
|
|
printed[key] = true
|
|
fmt.Fprintf(w, "%s%s@%s%s\n", indent, p.ID, p.Version, annotation)
|
|
|
|
deps := p.Dependencies()
|
|
targets := make([]string, 0, len(deps))
|
|
for t := range deps {
|
|
targets = append(targets, t)
|
|
}
|
|
sort.Strings(targets)
|
|
for _, depTarget := range targets {
|
|
dep := deps[depTarget]
|
|
id := depTarget
|
|
if dep.TargetID != "" {
|
|
id = dep.TargetID
|
|
}
|
|
if dep.Type == "Optional" && !opts.IncludeOptional {
|
|
continue
|
|
}
|
|
if dep.Type == "Recommended" && opts.SkipRecommended {
|
|
continue
|
|
}
|
|
c := map[string]string{}
|
|
if dep.Version != "" {
|
|
c["version"] = dep.Version
|
|
}
|
|
printDepNode(w, idx, id, c, dep.Type, opts, depth+1, printed)
|
|
}
|
|
}
|