/// <summary> /// Gathers toolset data from the registry and configuration file, if any. /// </summary> private static string ReadAllToolsetsNative ( Dictionary <string, Toolset> toolsets, #if FEATURE_WIN32_REGISTRY ToolsetRegistryReader registryReader, #endif #if FEATURE_SYSTEM_CONFIGURATION ToolsetConfigurationReader configurationReader, #endif PropertyDictionary <ProjectPropertyInstance> environmentProperties, PropertyDictionary <ProjectPropertyInstance> globalProperties, ToolsetDefinitionLocations locations ) { var initialProperties = new PropertyDictionary <ProjectPropertyInstance>(environmentProperties); initialProperties.ImportProperties(globalProperties); // The ordering here is important because the configuration file should have greater precedence // than the registry, and we do a check and don't read in the new toolset if there's already one. string defaultToolsVersionFromConfiguration = null; string overrideTasksPathFromConfiguration = null; string defaultOverrideToolsVersionFromConfiguration = null; #if FEATURE_SYSTEM_CONFIGURATION if ((locations & ToolsetDefinitionLocations.ConfigurationFile) == ToolsetDefinitionLocations.ConfigurationFile) { if (configurationReader == null) { configurationReader = new ToolsetConfigurationReader(environmentProperties, globalProperties); } // Accumulation of properties is okay in the config file because it's deterministically ordered defaultToolsVersionFromConfiguration = configurationReader.ReadToolsets(toolsets, globalProperties, initialProperties, true /* accumulate properties */, out overrideTasksPathFromConfiguration, out defaultOverrideToolsVersionFromConfiguration); } #endif string defaultToolsVersionFromRegistry = null; string overrideTasksPathFromRegistry = null; string defaultOverrideToolsVersionFromRegistry = null; if ((locations & ToolsetDefinitionLocations.Registry) == ToolsetDefinitionLocations.Registry) { #if FEATURE_WIN32_REGISTRY if (NativeMethodsShared.IsWindows || registryReader != null) { // If we haven't been provided a registry reader (i.e. unit tests), create one registryReader = registryReader ?? new ToolsetRegistryReader(environmentProperties, globalProperties); // We do not accumulate properties when reading them from the registry, because the order // in which values are returned to us is essentially random: so we disallow one property // in the registry to refer to another also in the registry defaultToolsVersionFromRegistry = registryReader.ReadToolsets(toolsets, globalProperties, initialProperties, false /* do not accumulate properties */, out overrideTasksPathFromRegistry, out defaultOverrideToolsVersionFromRegistry); } else #endif { var currentDir = BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory.TrimEnd(Path.DirectorySeparatorChar); var props = new PropertyDictionary <ProjectPropertyInstance>(); var libraryPath = NativeMethodsShared.FrameworkBasePath; if (!string.IsNullOrEmpty(libraryPath)) { // The 4.0 toolset is installed in the framework directory var v4Dir = FrameworkLocationHelper.GetPathToDotNetFrameworkV40(DotNetFrameworkArchitecture.Current); if (v4Dir != null && !toolsets.ContainsKey("4.0")) { // Create standard properties. On Mono they are well known var buildProperties = CreateStandardProperties(globalProperties, "4.0", libraryPath, v4Dir); toolsets.Add( "4.0", new Toolset( "4.0", v4Dir, buildProperties, environmentProperties, globalProperties, null, currentDir, string.Empty)); } // Other toolsets are installed in the xbuild directory var xbuildToolsetsDir = Path.Combine(libraryPath, $"xbuild{Path.DirectorySeparatorChar}"); if (FileSystems.Default.DirectoryExists(xbuildToolsetsDir)) { var r = new Regex(Regex.Escape(xbuildToolsetsDir) + @"\d+\.\d+"); foreach (var d in Directory.GetDirectories(xbuildToolsetsDir).Where(d => r.IsMatch(d))) { var version = Path.GetFileName(d); var binPath = Path.Combine(d, "bin"); if (toolsets.ContainsKey(version)) { continue; } if (NativeMethodsShared.IsMono && Version.TryParse(version, out Version parsedVersion) && parsedVersion.Major > 14) { continue; } // Create standard properties. On Mono they are well known var buildProperties = CreateStandardProperties(globalProperties, version, xbuildToolsetsDir, binPath); toolsets.Add( version, new Toolset( version, binPath, buildProperties, environmentProperties, globalProperties, null, currentDir, string.Empty)); } } } if (!toolsets.ContainsKey(MSBuildConstants.CurrentToolsVersion)) { toolsets.Add( MSBuildConstants.CurrentToolsVersion, new Toolset( MSBuildConstants.CurrentToolsVersion, currentDir, props, new PropertyDictionary <ProjectPropertyInstance>(), currentDir, string.Empty)); } } } // The 2.0 .NET Framework installer did not write a ToolsVersion key for itself in the registry. // The 3.5 installer writes one for 2.0, but 3.5 might not be installed. // The 4.0 and subsequent installers can't keep writing the 2.0 one, because (a) it causes SxS issues and (b) we // don't want it unless 2.0 is installed. // So if the 2.0 framework is actually installed, we're reading the registry, and either the registry or the config // file have not already created the 2.0 toolset, mock up a fake one. if (((locations & ToolsetDefinitionLocations.Registry) != 0) && !toolsets.ContainsKey("2.0") && FrameworkLocationHelper.PathToDotNetFrameworkV20 != null) { var synthetic20Toolset = new Toolset( "2.0", FrameworkLocationHelper.PathToDotNetFrameworkV20, environmentProperties, globalProperties, null /* 2.0 did not have override tasks */, null /* 2.0 did not have a default override toolsversion */); toolsets.Add("2.0", synthetic20Toolset); } string defaultToolsVersionFromLocal = null; string overrideTasksPathFromLocal = null; string defaultOverrideToolsVersionFromLocal = null; if ((locations & ToolsetDefinitionLocations.Local) == ToolsetDefinitionLocations.Local) { var localReader = new ToolsetLocalReader(environmentProperties, globalProperties); defaultToolsVersionFromLocal = localReader.ReadToolsets( toolsets, globalProperties, initialProperties, false /* accumulate properties */, out overrideTasksPathFromLocal, out defaultOverrideToolsVersionFromLocal); } // We'll use the path from the configuration file if it was specified, otherwise we'll try // the one from the registry. It's possible (and valid) that neither the configuration file // nor the registry specify a override in which case we'll just return null. var overrideTasksPath = overrideTasksPathFromConfiguration ?? overrideTasksPathFromRegistry ?? overrideTasksPathFromLocal; // We'll use the path from the configuration file if it was specified, otherwise we'll try // the one from the registry. It's possible (and valid) that neither the configuration file // nor the registry specify a override in which case we'll just return null. var defaultOverrideToolsVersion = defaultOverrideToolsVersionFromConfiguration ?? defaultOverrideToolsVersionFromRegistry ?? defaultOverrideToolsVersionFromLocal; // We'll use the default from the configuration file if it was specified, otherwise we'll try // the one from the registry. It's possible (and valid) that neither the configuration file // nor the registry specify a default, in which case we'll just return null. var defaultToolsVersion = defaultToolsVersionFromConfiguration ?? defaultToolsVersionFromRegistry ?? defaultToolsVersionFromLocal; // If we got a default version from the registry or config file, and it // actually exists, fine. // Otherwise we have to come up with one. if (defaultToolsVersion != null && toolsets.ContainsKey(defaultToolsVersion)) { return(defaultToolsVersion); } // We're going to choose a hard coded default tools version of 2.0. defaultToolsVersion = Constants.defaultToolsVersion; // But don't overwrite any existing tools path for this default we're choosing. if (toolsets.ContainsKey(Constants.defaultToolsVersion)) { return(defaultToolsVersion); } // There's no tools path already for 2.0, so use the path to the v2.0 .NET Framework. // If an old-fashioned caller sets BinPath property, or passed a BinPath to the constructor, // that will overwrite what we're setting here. ErrorUtilities.VerifyThrow( Constants.defaultToolsVersion == "2.0", "Getting 2.0 FX path so default should be 2.0"); var pathToFramework = FrameworkLocationHelper.PathToDotNetFrameworkV20; // We could not find the default toolsversion because it was not installed on the machine. Fallback to the // one we expect to always be there when running msbuild 4.0. if (pathToFramework == null) { pathToFramework = FrameworkLocationHelper.PathToDotNetFrameworkV40; defaultToolsVersion = Constants.defaultFallbackToolsVersion; } // Again don't overwrite any existing tools path for this default we're choosing. if (toolsets.ContainsKey(defaultToolsVersion)) { return(defaultToolsVersion); } var defaultToolset = new Toolset( defaultToolsVersion, pathToFramework, environmentProperties, globalProperties, overrideTasksPath, defaultOverrideToolsVersion); toolsets.Add(defaultToolsVersion, defaultToolset); return(defaultToolsVersion); }
/// <summary> /// Gathers toolset data from the registry + configuration file + VS setup API, if any. /// NOTE: this method is internal for unit testing purposes only. /// </summary> internal static string ReadAllToolsets ( Dictionary <string, Toolset> toolsets, #if FEATURE_WIN32_REGISTRY ToolsetRegistryReader registryReader, #endif #if FEATURE_SYSTEM_CONFIGURATION ToolsetConfigurationReader configurationReader, #endif PropertyDictionary <ProjectPropertyInstance> environmentProperties, PropertyDictionary <ProjectPropertyInstance> globalProperties, ToolsetDefinitionLocations locations ) { var defaultVer = ReadAllToolsetsNative ( toolsets, #if FEATURE_WIN32_REGISTRY registryReader, #endif #if FEATURE_SYSTEM_CONFIGURATION configurationReader, #endif environmentProperties, globalProperties, locations ); const string _CUR = MSBuildConstants.CurrentToolsVersion; string currentDir = BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory.TrimEnd(Path.DirectorySeparatorChar); foreach (var vs in VisualStudioLocationHelper.GetInstances()) { string vstr = $"{vs.Version.Major}.0"; string toolbin = FileUtilities.CombinePaths ( vs.Path, "MSBuild", vs.Version.Major >= 16 ? _CUR : vstr, "Bin" ); if (IntPtr.Size == 8) { string toolbin64 = FileUtilities.CombinePaths(toolbin, "amd64"); if (Directory.Exists(toolbin64)) { toolbin = toolbin64; } } if (!toolsets.ContainsKey(vstr)) { // For Mono var buildProperties = CreateStandardProperties(globalProperties, vstr, NativeMethodsShared.FrameworkBasePath, toolbin); toolsets.Add ( vstr, new Toolset ( vstr, toolbin, buildProperties, environmentProperties, globalProperties, null, currentDir, string.Empty ) ); } } if (!toolsets.ContainsKey(_CUR)) { var actual = toolsets.GetMostActual(); if (actual != null) { toolsets.Add(_CUR, actual); } else { toolsets.Add ( _CUR, new Toolset ( _CUR, currentDir, new PropertyDictionary <ProjectPropertyInstance>(), new PropertyDictionary <ProjectPropertyInstance>(), currentDir, string.Empty ) ); } } return(defaultVer); }