/// <summary> /// Internal constructor /// </summary> /// <param name="engine"></param> /// <param name="toolset"></param> internal ToolsetState(Engine engine, Toolset toolset) : this(engine, toolset, new GetFiles(Directory.GetFiles), new LoadXmlFromPath(ToolsetState.LoadXmlDocumentFromPath) ) { }
/// <summary> /// Additional constructor to make unit testing the TaskRegistry support easier /// </summary> /// <remarks> /// Internal for unit test purposes only. /// </remarks> /// <param name="engine"></param> /// <param name="toolset"></param> /// <param name="getFiles"></param> /// <param name="loadXmlFromPath"></param> internal ToolsetState(Engine engine, Toolset toolset, GetFiles getFiles, LoadXmlFromPath loadXmlFromPath ) { this.parentEngine = engine; this.loggingServices = engine.LoggingServices; ErrorUtilities.VerifyThrowArgumentNull(toolset, "toolset"); this.toolset = toolset; this.getFiles = getFiles; this.loadXmlFromPath = loadXmlFromPath; }
/// <summary> /// Reads all the toolsets and populates the given ToolsetCollection with them /// </summary> /// <param name="toolsets"></param> /// <param name="globalProperties"></param> /// <param name="initialProperties"></param> /// <param name="accumulateProperties"></param> private void ReadEachToolset(ToolsetCollection toolsets, BuildPropertyGroup globalProperties, BuildPropertyGroup initialProperties, bool accumulateProperties) { foreach (PropertyDefinition toolsVersion in ToolsVersions) { // We clone here because we don't want to interfere with the evaluation // of subsequent Toolsets; otherwise, properties found during the evaluation // of this Toolset would be persisted in initialProperties and appear // to later Toolsets as Global or Environment properties from the Engine. BuildPropertyGroup initialPropertiesClone = initialProperties.Clone(true /* deep clone */); Toolset toolset = ReadToolset(toolsVersion, globalProperties, initialPropertiesClone, accumulateProperties); if (toolset != null) { toolsets.Add(toolset); } } }
/// <summary> /// Reads the settings for a specified tools version /// </summary> /// <param name="toolsVersion"></param> /// <param name="globalProperties"></param> /// <param name="initialProperties"></param> /// <param name="accumulateProperties"></param> /// <returns></returns> private Toolset ReadToolset(PropertyDefinition toolsVersion, BuildPropertyGroup globalProperties, BuildPropertyGroup initialProperties, bool accumulateProperties) { // Initial properties is the set of properties we're going to use to expand property expressions like $(foo) // in the values we read out of the registry or config file. We'll add to it as we pick up properties (including binpath) // from the registry or config file, so that properties there can be referenced in values below them. // After processing all the properties, we don't need initialProperties anymore. string toolsPath = null; string binPath = null; BuildPropertyGroup properties = new BuildPropertyGroup(); IEnumerable <PropertyDefinition> rawProperties = GetPropertyDefinitions(toolsVersion.Name); Expander expander = new Expander(initialProperties); foreach (PropertyDefinition property in rawProperties) { if (0 == String.Compare(property.Name, ReservedPropertyNames.toolsPath, StringComparison.OrdinalIgnoreCase)) { toolsPath = ExpandProperty(property, expander); toolsPath = ExpandRelativePathsRelativeToExeLocation(toolsPath); if (accumulateProperties) { SetProperty ( new PropertyDefinition(ReservedPropertyNames.toolsPath, toolsPath, property.Source), initialProperties, globalProperties ); } } else if (0 == String.Compare(property.Name, ReservedPropertyNames.binPath, StringComparison.OrdinalIgnoreCase)) { binPath = ExpandProperty(property, expander); binPath = ExpandRelativePathsRelativeToExeLocation(binPath); if (accumulateProperties) { SetProperty ( new PropertyDefinition(ReservedPropertyNames.binPath, binPath, property.Source), initialProperties, globalProperties ); } } else if (ReservedPropertyNames.IsReservedProperty(property.Name)) { // We don't allow toolsets to define reserved properties string baseMessage = ResourceUtilities.FormatResourceString("CannotModifyReservedProperty", property.Name); InvalidToolsetDefinitionException.Throw("InvalidPropertyNameInToolset", property.Name, property.Source, baseMessage); } else { // It's an arbitrary property string propertyValue = ExpandProperty(property, expander); PropertyDefinition expandedProperty = new PropertyDefinition(property.Name, propertyValue, property.Source); SetProperty(expandedProperty, properties, globalProperties); if (accumulateProperties) { SetProperty(expandedProperty, initialProperties, globalProperties); } } if (accumulateProperties) { expander = new Expander(initialProperties); } } // All tools versions must specify a value for MSBuildToolsPath (or MSBuildBinPath) if (String.IsNullOrEmpty(toolsPath) && String.IsNullOrEmpty(binPath)) { InvalidToolsetDefinitionException.Throw("MSBuildToolsPathIsNotSpecified", toolsVersion.Name, toolsVersion.Source); } // If both MSBuildBinPath and MSBuildToolsPath are present, they must be the same if (toolsPath != null && binPath != null && !toolsPath.Equals(binPath, StringComparison.OrdinalIgnoreCase)) { return(null); } Toolset toolset = null; try { toolset = new Toolset(toolsVersion.Name, toolsPath == null ? binPath : toolsPath, properties); } catch (ArgumentException e) { InvalidToolsetDefinitionException.Throw("ErrorCreatingToolset", toolsVersion.Name, e.Message); } return(toolset); }
/// <summary> /// Gathers toolset data from the registry and configuration file, if any. /// NOTE: this method is internal for unit testing purposes only. /// </summary> /// <param name="toolsets"></param> /// <param name="registryReader"></param> /// <param name="configurationReader"></param> /// <param name="globalProperties"></param> /// <param name="initialProperties"></param> /// <param name="locations"></param> /// <returns></returns> internal static string ReadAllToolsets(ToolsetCollection toolsets, ToolsetRegistryReader registryReader, ToolsetConfigurationReader configurationReader, BuildPropertyGroup globalProperties, BuildPropertyGroup initialProperties, ToolsetDefinitionLocations locations) { // 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, and we're reading the registry, create a toolset for it. // The registry and config file can overwrite it. if ( ((locations & ToolsetDefinitionLocations.Registry) != 0) && !toolsets.Contains("2.0") && FrameworkLocationHelper.PathToDotNetFrameworkV20 != null ) { Toolset synthetic20Toolset = new Toolset("2.0", FrameworkLocationHelper.PathToDotNetFrameworkV20, initialProperties); toolsets.Add(synthetic20Toolset); } // The ordering here is important because the configuration file should have greater precedence // than the registry string defaultToolsVersionFromRegistry = null; ToolsetRegistryReader registryReaderToUse = null; if ((locations & ToolsetDefinitionLocations.Registry) == ToolsetDefinitionLocations.Registry) { registryReaderToUse = registryReader == null ? new ToolsetRegistryReader() : registryReader; // 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 = registryReaderToUse.ReadToolsets(toolsets, globalProperties, initialProperties, false /* do not accumulate properties */); } string defaultToolsVersionFromConfiguration = null; ToolsetConfigurationReader configurationReaderToUse = null; if ((locations & ToolsetDefinitionLocations.ConfigurationFile) == ToolsetDefinitionLocations.ConfigurationFile) { if (configurationReader == null && 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(); } if (configurationReader != null) { configurationReaderToUse = configurationReader == null ? new ToolsetConfigurationReader() : configurationReader; // Accumulation of properties is okay in the config file because it's deterministically ordered defaultToolsVersionFromConfiguration = configurationReaderToUse.ReadToolsets(toolsets, globalProperties, initialProperties, true /* accumulate properties */); } } // 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.Contains(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.Contains(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.Contains(defaultToolsVersion)) { Toolset defaultToolset = new Toolset(defaultToolsVersion, pathToFramework, initialProperties); toolsets.Add(defaultToolset); } } } return(defaultToolsVersion); }