|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System;
using System.Text.Json;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.VisualStudio.SolutionPersistence;
using Microsoft.VisualStudio.SolutionPersistence.Model;
using Microsoft.VisualStudio.SolutionPersistence.Serializer;
namespace Microsoft.DotNet.Cli;
public static class SlnFileFactory
{
public static readonly string[] DefaultPlatforms = new[] { "Any CPU", "x64", "x86" };
public static readonly string[] DefaultBuildTypes = new[] { "Debug", "Release" };
public static string GetSolutionFileFullPath(string slnFileOrDirectory, bool includeSolutionFilterFiles = false, bool includeSolutionXmlFiles = true)
{
// Throw error if slnFileOrDirectory is an invalid path
if (string.IsNullOrWhiteSpace(slnFileOrDirectory) || slnFileOrDirectory.IndexOfAny(Path.GetInvalidPathChars()) != -1)
{
throw new GracefulException(CliStrings.CouldNotFindSolutionOrDirectory);
}
if (File.Exists(slnFileOrDirectory))
{
return Path.GetFullPath(slnFileOrDirectory);
}
if (Directory.Exists(slnFileOrDirectory))
{
string[] files = ListSolutionFilesInDirectory(slnFileOrDirectory, includeSolutionFilterFiles, includeSolutionXmlFiles);
if (files.Length == 0)
{
throw new GracefulException(
CliStrings.CouldNotFindSolutionIn,
slnFileOrDirectory);
}
if (files.Length > 1)
{
throw new GracefulException(
CliStrings.MoreThanOneSolutionInDirectory,
slnFileOrDirectory);
}
return Path.GetFullPath(files.Single());
}
throw new GracefulException(
CliStrings.CouldNotFindSolutionOrDirectory,
slnFileOrDirectory);
}
public static string[] ListSolutionFilesInDirectory(string directory, bool includeSolutionFilterFiles = false, bool includeSolutionXmlFiles = true)
{
return
[
.. Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly),
.. includeSolutionXmlFiles ? Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly) : [],
.. includeSolutionFilterFiles ? Directory.GetFiles(directory, "*.slnf", SearchOption.TopDirectoryOnly) : []
];
}
public static SolutionModel CreateFromFileOrDirectory(string fileOrDirectory, bool includeSolutionFilterFiles = false, bool includeSolutionXmlFiles = true)
{
string solutionPath = GetSolutionFileFullPath(fileOrDirectory, includeSolutionFilterFiles, includeSolutionXmlFiles);
if (solutionPath.HasExtension(".slnf"))
{
return CreateFromFilteredSolutionFile(solutionPath);
}
try
{
ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath)!;
return serializer.OpenAsync(solutionPath, CancellationToken.None).Result;
}
catch (Exception ex)
{
throw new GracefulException(
CliStrings.InvalidSolutionFormatString,
solutionPath, ex.Message);
}
}
public static SolutionModel CreateFromFilteredSolutionFile(string filteredSolutionPath)
{
string originalSolutionPath;
string originalSolutionPathAbsolute;
IEnumerable<string> filteredSolutionProjectPaths;
try
{
JsonElement root = JsonDocument.Parse(File.ReadAllText(filteredSolutionPath)).RootElement;
originalSolutionPath = Uri.UnescapeDataString(root.GetProperty("solution").GetProperty("path").GetString());
filteredSolutionProjectPaths = [.. root.GetProperty("solution").GetProperty("projects").EnumerateArray().Select(p => p.GetString())];
originalSolutionPathAbsolute = Path.GetFullPath(originalSolutionPath, Path.GetDirectoryName(filteredSolutionPath));
}
catch (Exception ex)
{
throw new GracefulException(
CliStrings.InvalidSolutionFormatString,
filteredSolutionPath, ex.Message);
}
SolutionModel filteredSolution = new();
SolutionModel originalSolution = CreateFromFileOrDirectory(originalSolutionPathAbsolute);
// Store the original solution path in the description field of the filtered solution
filteredSolution.Description = originalSolutionPathAbsolute;
foreach (var platform in originalSolution.Platforms)
{
filteredSolution.AddPlatform(platform);
}
foreach (var buildType in originalSolution.BuildTypes)
{
filteredSolution.AddBuildType(buildType);
}
IEnumerable<SolutionProjectModel> projects = filteredSolutionProjectPaths
.Select(path => path.Replace('\\', Path.DirectorySeparatorChar))
.Select(path => Uri.UnescapeDataString(path))
.Select(path => originalSolution.FindProject(path) ?? throw new GracefulException(
CliStrings.ProjectNotFoundInTheSolution,
path,
originalSolutionPath));
foreach (var project in projects)
{
_ = filteredSolution.AddProject(
project.FilePath,
project.Type,
project.Parent is null ? null : filteredSolution.AddFolder(project.Parent.Path));
}
return filteredSolution;
}
}
|