| File: SolutionConfiguration.cs | Web Access |
| Project: src\msbuild\src\Framework\Microsoft.Build.Framework.csproj (Microsoft.Build.Framework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Xml; using Microsoft.Build.Framework; namespace Microsoft.Build.Shared { internal sealed class SolutionConfiguration { public const string ProjectAttribute = "Project"; public const string AbsolutePathAttribute = "AbsolutePath"; public const string BuildProjectInSolutionAttribute = "BuildProjectInSolution"; public static readonly char[] ConfigPlatformSeparator = { '|' }; // This field stores pre-cached project elements for project guids for quicker access by project guid private readonly Dictionary<string, XmlElement> _cachedProjectElements; // This field stores pre-cached project elements for project guids for quicker access by project absolute path private readonly Dictionary<string, XmlElement> _cachedProjectElementsByAbsolutePath; // This field stores the project absolute path for quicker access by project guid private readonly Dictionary<string, string> _cachedProjectAbsolutePathsByGuid; // This field stores the project guid for quicker access by project absolute path private readonly Dictionary<string, string> _cachedProjectGuidsByAbsolutePath; // This field stores the list of dependency project guids by depending project guid private readonly Dictionary<string, List<string>> _cachedDependencyProjectGuidsByDependingProjectGuid; public SolutionConfiguration(string xmlString) { // Example: // // <SolutionConfiguration> // <ProjectConfiguration Project="{786E302A-96CE-43DC-B640-D6B6CC9BF6C0}" AbsolutePath="c:foo\Project1\A.csproj" BuildProjectInSolution="True">Debug|AnyCPU</ProjectConfiguration> // <ProjectConfiguration Project="{881C1674-4ECA-451D-85B6-D7C59B7F16FA}" AbsolutePath="c:foo\Project2\B.csproj" BuildProjectInSolution="True">Debug|AnyCPU<ProjectDependency Project="{4A727FF8-65F2-401E-95AD-7C8BBFBE3167}" /></ProjectConfiguration> // <ProjectConfiguration Project="{4A727FF8-65F2-401E-95AD-7C8BBFBE3167}" AbsolutePath="c:foo\Project3\C.csproj" BuildProjectInSolution="True">Debug|AnyCPU</ProjectConfiguration> // </SolutionConfiguration> // XmlNodeList? projectConfigurationElements = GetProjectConfigurations(xmlString); int projectConfigurationCount = projectConfigurationElements?.Count ?? 0; _cachedProjectElements = new Dictionary<string, XmlElement>(projectConfigurationCount, StringComparer.OrdinalIgnoreCase); _cachedProjectElementsByAbsolutePath = new Dictionary<string, XmlElement>(projectConfigurationCount, StringComparer.OrdinalIgnoreCase); _cachedProjectAbsolutePathsByGuid = new Dictionary<string, string>(projectConfigurationCount, StringComparer.OrdinalIgnoreCase); _cachedProjectGuidsByAbsolutePath = new Dictionary<string, string>(projectConfigurationCount, StringComparer.OrdinalIgnoreCase); _cachedDependencyProjectGuidsByDependingProjectGuid = new Dictionary<string, List<string>>(projectConfigurationCount, StringComparer.OrdinalIgnoreCase); if (projectConfigurationElements != null) { foreach (XmlElement xmlElement in projectConfigurationElements) { string projectGuid = xmlElement.GetAttribute(ProjectAttribute); string projectAbsolutePath = xmlElement.GetAttribute(AbsolutePathAttribute); // What we really want here is the normalized path, like we'd get with an item's "FullPath" metadata. However, // if there's some bogus full path in the solution configuration (e.g. a website with a "full path" of c:\solutiondirectory\http://localhost) // we do NOT want to throw -- chances are extremely high that that's information that will never actually be used. So resolve the full path // but just swallow any IO-related exceptions that result. If the path is bogus, the method will return null, so we'll just quietly fail // to cache it below. projectAbsolutePath = FileUtilities.GetFullPathNoThrow(projectAbsolutePath); if (!string.IsNullOrEmpty(projectGuid)) { _cachedProjectElements[projectGuid] = xmlElement; if (!string.IsNullOrEmpty(projectAbsolutePath)) { _cachedProjectElementsByAbsolutePath[projectAbsolutePath] = xmlElement; _cachedProjectAbsolutePathsByGuid[projectGuid] = projectAbsolutePath; _cachedProjectGuidsByAbsolutePath[projectAbsolutePath] = projectGuid; } foreach (XmlNode dependencyNode in xmlElement.ChildNodes) { if (dependencyNode.NodeType != XmlNodeType.Element) { continue; } XmlElement dependencyElement = ((XmlElement)dependencyNode); if (!String.Equals(dependencyElement.Name, "ProjectDependency", StringComparison.Ordinal)) { continue; } string dependencyGuid = dependencyElement.GetAttribute("Project"); if (dependencyGuid.Length == 0) { continue; } if (!_cachedDependencyProjectGuidsByDependingProjectGuid.TryGetValue(projectGuid, out List<string>? list)) { list = new List<string>(); _cachedDependencyProjectGuidsByDependingProjectGuid.Add(projectGuid, list); } list.Add(dependencyGuid); } } } } } public static SolutionConfiguration Empty { get; } = new SolutionConfiguration(string.Empty); public ICollection<XmlElement> ProjectConfigurations => _cachedProjectElements.Values; public static XmlNodeList? GetProjectConfigurations(string xmlString) { XmlDocument? doc = null; if (!string.IsNullOrEmpty(xmlString)) { doc = new XmlDocument(); var settings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }; using (XmlReader reader = XmlReader.Create(new StringReader(xmlString), settings)) { doc.Load(reader); } } return doc?.DocumentElement?.ChildNodes; } public bool TryGetProjectByGuid(string projectGuid, [NotNullWhen(true)] out XmlElement? projectElement) => _cachedProjectElements.TryGetValue(projectGuid, out projectElement); public bool TryGetProjectByAbsolutePath(string projectFullPath, [NotNullWhen(true)] out XmlElement? projectElement) => _cachedProjectElementsByAbsolutePath.TryGetValue(projectFullPath, out projectElement); public bool TryGetProjectGuidByAbsolutePath(string projectFullPath, [NotNullWhen(true)] out string? projectGuid) => _cachedProjectGuidsByAbsolutePath.TryGetValue(projectFullPath, out projectGuid); public bool TryGetProjectDependencies(string projectGuid, [NotNullWhen(true)] out List<string>? dependencyProjectGuids) => _cachedDependencyProjectGuidsByDependingProjectGuid.TryGetValue(projectGuid, out dependencyProjectGuids); public bool TryGetProjectPathByGuid(string projectGuid, [NotNullWhen(true)] out string? projectPath) => _cachedProjectAbsolutePathsByGuid.TryGetValue(projectGuid, out projectPath); } }