mirror of
https://github.com/Cheviiot/Vintner.git
synced 2026-08-03 15:57:24 +00:00
vintner only ever downloads one compiler generation, but real-world .vcxproj files are pinned to whichever PlatformToolset they were last saved under - v142 (VS2019) for anything not actively maintained is extremely common. MSBuild checks toolset "installed-ness" (MSB8020) by testing whether MSBuild/Microsoft/VC/v<schema>/Platforms/<arch>/ PlatformToolsets/<toolset>/ exists on disk - a plain file lookup our downloaded MSBuild package only satisfies for the exact generation it shipped. `install` now symlinks every historical numeric PlatformToolset name (v90 through v143) onto whichever real toolset directory is actually present, so any of them resolves transparently; Toolset.props/ .targets don't hardcode a version number, so aliasing is correct, not just a workaround. Three more MSBuild property/environment issues came with it, all found building a real years-old project against the one modern toolchain vintner installs: - VCInstallDir_<N>/VCToolsInstallDir_<N> needed a third numbering source (PlatformToolset short names from Microsoft.VCToolsVersion. v<N>.default.props) alongside the existing MSBuild schema-version and known-toolset lists, so the env-var-driven half of toolset resolution covers the same names the on-disk alias does. - VCToolsVersion must be a real version string: left unset, it falls back to a literal placeholder that then hits an unconditional version-string comparison elsewhere in Microsoft.CppBuild.targets (MSB4184). Setting it to the real installed version in turn requires CheckMSVCComponents=false, since CheckVCToolsetVersion (MSB8052) otherwise rejects an aliased PlatformToolset whenever its numeric generation doesn't match VCToolsVersion's - exactly the case aliasing creates on purpose. Everything else CheckMSVCComponents gates is diagnostic-only (MFC/ATL/Spectre presence warnings), so disabling it costs nothing else. - WindowsTargetPlatformVersion needed to become an explicit /p: global property on the msbuild command line, not just an env var: legacy .vcxproj files commonly hardcode this in a PropertyGroup, and an explicit project assignment always wins over an inherited environment variable of the same name. A command-line global property is the one thing a project file can't override. Only injected when the caller hasn't already pinned it themselves.
93 lines
2.8 KiB
Go
93 lines
2.8 KiB
Go
package install
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
|
|
"github.com/Cheviiot/vintner/internal/wineenv"
|
|
)
|
|
|
|
var rePlatformToolsetDir = regexp.MustCompile(`^v(\d+)$`)
|
|
|
|
// aliasPlatformToolsets makes every historical PlatformToolset name in
|
|
// wineenv.KnownPlatformToolsets resolve to the one compiler `download`
|
|
// actually fetched.
|
|
//
|
|
// MSBuild decides whether a PlatformToolset is "installed" at all - the
|
|
// check behind MSB8020 - by testing whether
|
|
// MSBuild/Microsoft/VC/v<schema>/Platforms/<arch>/PlatformToolsets/<toolset>/
|
|
// exists on disk (Microsoft.Cpp.props, via
|
|
// ToolLocationHelper.FindRootFolderWhereAllFilesExist). That's a plain file
|
|
// lookup, not influenced by any environment variable - unlike the later,
|
|
// env-var-driven VCInstallDir_<N> checks internal/wrapper's msbuildEnv
|
|
// covers, this one needs the actual directory to exist under dest.
|
|
// Microsoft's own downloaded MSBuild package only ships a PlatformToolsets
|
|
// entry for the exact generation matching the fetched compiler, so any
|
|
// project pinned to an older PlatformToolset (v142 for a project last saved
|
|
// under VS2019, say) fails this check outright even though the one real
|
|
// toolchain installed could easily build it.
|
|
//
|
|
// Toolset.props/Toolset.targets don't hardcode a version number (they just
|
|
// import version-agnostic files like Microsoft.Cpp.MSVC.Toolset.<arch>.props),
|
|
// so a symlink under any other historical name is a correct, transparent
|
|
// alias rather than a divergent copy.
|
|
func aliasPlatformToolsets(dest string) error {
|
|
schemaDirs, err := filepath.Glob(filepath.Join(dest, "MSBuild", "Microsoft", "VC", "v*"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, schemaDir := range schemaDirs {
|
|
archDirs, err := filepath.Glob(filepath.Join(schemaDir, "Platforms", "*", "PlatformToolsets"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, toolsetsDir := range archDirs {
|
|
if err := aliasOneDir(toolsetsDir); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// aliasOneDir symlinks every name in wineenv.KnownPlatformToolsets that
|
|
// doesn't already exist in toolsetsDir onto whichever real v<N> toolset
|
|
// subdirectory is actually present there.
|
|
func aliasOneDir(toolsetsDir string) error {
|
|
entries, err := os.ReadDir(toolsetsDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var real string
|
|
for _, e := range entries {
|
|
if !e.IsDir() {
|
|
continue
|
|
}
|
|
if rePlatformToolsetDir.MatchString(e.Name()) {
|
|
real = e.Name()
|
|
break
|
|
}
|
|
}
|
|
if real == "" {
|
|
// Nothing numeric here (e.g. only the WindowsKernelModeDriver10.0-style
|
|
// WDK toolsets) - nothing to alias.
|
|
return nil
|
|
}
|
|
|
|
for _, n := range wineenv.KnownPlatformToolsets {
|
|
alias := "v" + n
|
|
if alias == real {
|
|
continue
|
|
}
|
|
aliasPath := filepath.Join(toolsetsDir, alias)
|
|
if exists(aliasPath) {
|
|
continue
|
|
}
|
|
if err := os.Symlink(real, aliasPath); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|