mirror of
https://github.com/Cheviiot/Vintner.git
synced 2026-08-03 15:57:24 +00:00
Build old (pre-v145) PlatformToolset-pinned .vcxproj files
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.
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user