/// <summary> /// Gathers toolset data from the registry and configuration file, 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 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 and configuration file, if any. /// NOTE: this method is internal for unit testing purposes only. /// </summary> internal static string ReadAllToolsets ( Dictionary<string, Toolset> toolsets, ToolsetRegistryReader registryReader, ToolsetConfigurationReader configurationReader, PropertyDictionary<ProjectPropertyInstance> environmentProperties, PropertyDictionary<ProjectPropertyInstance> globalProperties, ToolsetDefinitionLocations locations ) { PropertyDictionary<ProjectPropertyInstance> 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 ((locations & ToolsetDefinitionLocations.ConfigurationFile) == ToolsetDefinitionLocations.ConfigurationFile) { if (configurationReader == null && ToolsetConfigurationReaderHelpers.ConfigurationFileMayHaveToolsets()) { // We haven't been passed in a fake configuration reader by a unit test, // and it looks like we have a .config file to read, so create a real // configuration reader configurationReader = new ToolsetConfigurationReader(environmentProperties, globalProperties); } if (configurationReader != null) { // 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); } } string defaultToolsVersionFromRegistry = null; string overrideTasksPathFromRegistry = null; string defaultOverrideToolsVersionFromRegistry = null; if ((locations & ToolsetDefinitionLocations.Registry) == ToolsetDefinitionLocations.Registry) { // 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); } // 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 ) { Toolset 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); } // 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. string overrideTasksPath = overrideTasksPathFromConfiguration ?? overrideTasksPathFromRegistry; // 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. string defaultOverrideToolsVersion = defaultOverrideToolsVersionFromConfiguration ?? defaultOverrideToolsVersionFromRegistry; // 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. string defaultToolsVersion = defaultToolsVersionFromConfiguration ?? defaultToolsVersionFromRegistry; // 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)) { // 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)) { // 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"); string 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)) { Toolset defaultToolset = new Toolset(defaultToolsVersion, pathToFramework, environmentProperties, globalProperties, overrideTasksPath, defaultOverrideToolsVersion); toolsets.Add(defaultToolsVersion, defaultToolset); } } } return defaultToolsVersion; }
public void DefaultValueInRegistryDoesNotExist() { ToolsetReader reader = new ToolsetRegistryReader(new ProjectCollection().EnvironmentProperties, new PropertyDictionary<ProjectPropertyInstance>(), new MockRegistryKey(testRegistryPath, "3.5" /* fail to find subkey 3.5 */)); Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase); // Should not throw string msbuildOverrideTasksPath = null; string defaultOverrideToolsVersion = null; string defaultToolsVersion = reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), false, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion); Assert.Equal(null, defaultToolsVersion); }
/// <summary> /// Gathers toolset data from the registry and configuration file, if any. /// NOTE: this method is internal for unit testing purposes only. /// </summary> internal static string ReadAllToolsets ( Dictionary <string, Toolset> toolsets, ToolsetRegistryReader registryReader, ToolsetConfigurationReader configurationReader, PropertyDictionary <ProjectPropertyInstance> environmentProperties, PropertyDictionary <ProjectPropertyInstance> globalProperties, ToolsetDefinitionLocations locations ) { PropertyDictionary <ProjectPropertyInstance> 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 ((locations & ToolsetDefinitionLocations.ConfigurationFile) == ToolsetDefinitionLocations.ConfigurationFile) { if (configurationReader == null && ToolsetConfigurationReaderHelpers.ConfigurationFileMayHaveToolsets()) { // We haven't been passed in a fake configuration reader by a unit test, // and it looks like we have a .config file to read, so create a real // configuration reader configurationReader = new ToolsetConfigurationReader(environmentProperties, globalProperties); } if (configurationReader != null) { // 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); } } string defaultToolsVersionFromRegistry = null; string overrideTasksPathFromRegistry = null; string defaultOverrideToolsVersionFromRegistry = null; if ((locations & ToolsetDefinitionLocations.Registry) == ToolsetDefinitionLocations.Registry) { // 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); } // 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 ) { Toolset 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); } // 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. string overrideTasksPath = overrideTasksPathFromConfiguration ?? overrideTasksPathFromRegistry; // 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. string defaultOverrideToolsVersion = defaultOverrideToolsVersionFromConfiguration ?? defaultOverrideToolsVersionFromRegistry; // 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. string defaultToolsVersion = defaultToolsVersionFromConfiguration ?? defaultToolsVersionFromRegistry; // 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)) { // 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)) { // 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"); string 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)) { Toolset defaultToolset = new Toolset(defaultToolsVersion, pathToFramework, environmentProperties, globalProperties, overrideTasksPath, defaultOverrideToolsVersion); toolsets.Add(defaultToolsVersion, defaultToolset); } } } return(defaultToolsVersion); }
// Ignore: Test requires installed toolset. public void DefaultValuesInRegistryCreatedBySetup() { ToolsetReader reader = new ToolsetRegistryReader(new ProjectCollection().EnvironmentProperties, new PropertyDictionary<ProjectPropertyInstance>()); //we don't use the test registry key because we want to verify the install Dictionary<string, Toolset> values = new Dictionary<string, Toolset>(StringComparer.OrdinalIgnoreCase); string msbuildOverrideTasksPath = null; string defaultOverrideToolsVersion = null; string defaultToolsVersion = reader.ReadToolsets(values, new PropertyDictionary<ProjectPropertyInstance>(), new PropertyDictionary<ProjectPropertyInstance>(), false, out msbuildOverrideTasksPath, out defaultOverrideToolsVersion); // Check the values in the data Assert.True(values.ContainsKey("4.0")); // "Tools version 4.0 should be defined by default" Assert.True(values.ContainsKey(ObjectModelHelpers.MSBuildDefaultToolsVersion), String.Format("Tools version {0} should be defined by default", ObjectModelHelpers.MSBuildDefaultToolsVersion)); Assert.Equal("2.0", defaultToolsVersion); // "Default tools version should be 2.0" }