File: MSBuild\SolutionFileReader.cs
Web Access
Project: src\src\Workspaces\MSBuild\Core\Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj (Microsoft.CodeAnalysis.Workspaces.MSBuild)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Immutable;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.SolutionPersistence.Serializer;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.MSBuild;
 
internal partial class SolutionFileReader
{
    public static Task<(string AbsoluteSolutionPath, ImmutableArray<(string ProjectPath, string ProjectGuid)> Projects)> ReadSolutionFileAsync(string solutionFilePath, CancellationToken cancellationToken)
    {
        return ReadSolutionFileAsync(solutionFilePath, new PathResolver(diagnosticReporter: null), cancellationToken);
    }
 
    public static async Task<(string AbsoluteSolutionPath, ImmutableArray<(string ProjectPath, string ProjectGuid)> Projects)> ReadSolutionFileAsync(string solutionFilePath, PathResolver pathResolver, CancellationToken cancellationToken)
    {
        Contract.ThrowIfFalse(pathResolver.TryGetAbsoluteSolutionPath(solutionFilePath, baseDirectory: Directory.GetCurrentDirectory(), DiagnosticReportingMode.Throw, out var absoluteSolutionPath));
 
        // When passed a solution filter, we need to read the filter file to get the solution path and included project paths.
        var projectFilter = ImmutableHashSet<string>.Empty;
        if (SolutionFilterReader.IsSolutionFilterFilename(absoluteSolutionPath) &&
            !SolutionFilterReader.TryRead(absoluteSolutionPath, pathResolver, out absoluteSolutionPath, out projectFilter))
        {
            throw new Exception(string.Format(WorkspaceMSBuildResources.Failed_to_load_solution_filter_0, solutionFilePath));
        }
 
        var projects = await TryReadSolutionFileAsync(absoluteSolutionPath, pathResolver, projectFilter, cancellationToken).ConfigureAwait(false);
        if (!projects.HasValue)
        {
            throw new Exception(string.Format(WorkspaceMSBuildResources.Failed_to_load_solution_0, absoluteSolutionPath));
        }
 
        return (absoluteSolutionPath, projects.Value);
    }
 
    private static async Task<ImmutableArray<(string ProjectPath, string ProjectGuid)>?> TryReadSolutionFileAsync(string solutionFilePath, PathResolver pathResolver, ImmutableHashSet<string> projectFilter, CancellationToken cancellationToken)
    {
        var serializer = SolutionSerializers.GetSerializerByMoniker(solutionFilePath);
        if (serializer == null)
        {
            return null;
        }
 
        // The solution folder is the base directory for project paths.
        var baseDirectory = Path.GetDirectoryName(solutionFilePath);
        RoslynDebug.AssertNotNull(baseDirectory);
 
        var solutionModel = await serializer.OpenAsync(solutionFilePath, cancellationToken).ConfigureAwait(false);
 
        var builder = ImmutableArray.CreateBuilder<(string ProjectPath, string ProjectGuid)>();
        foreach (var projectModel in solutionModel.SolutionProjects)
        {
            // If we are filtering based on a solution filter, then we need to verify the project is included.
            if (!projectFilter.IsEmpty)
            {
                Contract.ThrowIfFalse(pathResolver.TryGetAbsoluteProjectPath(projectModel.FilePath, baseDirectory, DiagnosticReportingMode.Throw, out var absoluteProjectPath));
                if (!projectFilter.Contains(absoluteProjectPath))
                {
                    continue;
                }
            }
 
            builder.Add((projectModel.FilePath, projectModel.Id.ToString()));
        }
 
        return builder.ToImmutable();
    }
}