From eea2d7fbf006d574d115928313f93dff255c11d8 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Tue, 26 Nov 2024 14:41:18 -0800 Subject: [PATCH 01/27] sln-remove: Support for slnx --- .../commands/dotnet-sln/remove/Program.cs | 92 +++---------------- 1 file changed, 12 insertions(+), 80 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs index 766ce64d9b7c..9f801851cf08 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs @@ -5,12 +5,11 @@ using Microsoft.Build.Construction; using Microsoft.Build.Execution; using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools.Common; -using Microsoft.Extensions.EnvironmentAbstractions; using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; -using Microsoft.VisualStudio.SolutionPersistence.Serializer.SlnV12; namespace Microsoft.DotNet.Tools.Sln.Remove { @@ -19,28 +18,6 @@ internal class RemoveProjectFromSolutionCommand : CommandBase private readonly string _fileOrDirectory; private readonly IReadOnlyCollection _projects; - private int CountNonFolderDescendants( - SolutionModel solution, - SolutionFolderModel item, - Dictionary solutionItemsGroupedByParent, - Dictionary cached) - { - if (cached.ContainsKey(item)) - { - return cached[item]; - } - int count = item.Files?.Count ?? 0; - var children = solutionItemsGroupedByParent.TryGetValue(item, out var items) ? items : Array.Empty(); - foreach (var child in children) - { - count += child is SolutionFolderModel folderModel - ? CountNonFolderDescendants(solution, folderModel, solutionItemsGroupedByParent, cached) - : 1; - } - cached.Add(item, count); - return count; - } - public RemoveProjectFromSolutionCommand(ParseResult parseResult) : base(parseResult) { _fileOrDirectory = parseResult.GetValue(SlnCommandParser.SlnArgument); @@ -58,30 +35,19 @@ public override int Execute() throw new GracefulException(CommonLocalizableStrings.SpecifyAtLeastOneProjectToRemove); } + IEnumerable fullProjectPaths = _projects.Select(project => + { + var fullPath = Path.GetFullPath(project); + return Directory.Exists(fullPath) ? MsbuildProject.GetProjectFileFromDirectory(fullPath).FullName : fullPath; + }); + try { - var relativeProjectPaths = _projects.Select(p => - { - var fullPath = Path.GetFullPath(p); - return Path.GetRelativePath( - Path.GetDirectoryName(solutionFileFullPath), - Directory.Exists(fullPath) - ? MsbuildProject.GetProjectFileFromDirectory(fullPath).FullName - : fullPath); - }); - RemoveProjectsAsync(solutionFileFullPath, relativeProjectPaths, CancellationToken.None).Wait(); + RemoveProjectsAsync(solutionFileFullPath, fullProjectPaths, CancellationToken.None).Wait(); return 0; } - catch (Exception ex) when (ex is not GracefulException) + catch (Exception ex) { - if (ex is SolutionException || ex.InnerException is SolutionException) - { - throw new GracefulException(CommonLocalizableStrings.InvalidSolutionFormatString, solutionFileFullPath, ex.Message); - } - if (ex.InnerException is GracefulException) - { - throw ex.InnerException; - } throw new GracefulException(ex.Message, ex); } } @@ -91,44 +57,10 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable< ISolutionSerializer serializer = SlnCommandParser.GetSolutionSerializer(solutionFileFullPath); SolutionModel solution = await serializer.OpenAsync(solutionFileFullPath, cancellationToken); - // set UTF8 BOM encoding for .sln - if (serializer is ISolutionSerializer v12Serializer) - { - solution.SerializerExtension = v12Serializer.CreateModelExtension(new() - { - Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true) - }); - } - - foreach (var projectPath in projectPaths) - { - var project = solution.FindProject(projectPath); - if (project != null) - { - solution.RemoveProject(project); - Reporter.Output.WriteLine(CommonLocalizableStrings.ProjectRemovedFromTheSolution, projectPath); - } - else - { - Reporter.Output.WriteLine(CommonLocalizableStrings.ProjectNotFoundInTheSolution, projectPath); - } - } - - Dictionary solutionItemsGroupedByParent = solution.SolutionItems - .Where(i => i.Parent != null) - .GroupBy(i => i.Parent) - .ToDictionary(g => g.Key, g => g.ToArray()); - - Dictionary nonFolderDescendantsCount = new(); - foreach (var item in solution.SolutionFolders) - { - CountNonFolderDescendants(solution, item, solutionItemsGroupedByParent, nonFolderDescendantsCount); - } - - var emptyFolders = nonFolderDescendantsCount.Where(i => i.Value == 0).Select(i => i.Key); - foreach (var folder in emptyFolders) + foreach (var project in projectPaths) { - solution.RemoveFolder(folder); + SolutionProjectModel projectModel = solution.FindProject(project); + solution.RemoveProject(projectModel); } await serializer.SaveAsync(solutionFileFullPath, solution, cancellationToken); From 5209ec1e5ce044ec64e14077234ebadb399434bf Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 2 Dec 2024 14:54:04 -0800 Subject: [PATCH 02/27] WIP --- src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs index 9f801851cf08..30735728740f 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs @@ -8,6 +8,7 @@ using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools.Common; +using Microsoft.Extensions.EnvironmentAbstractions; using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; @@ -46,7 +47,7 @@ public override int Execute() RemoveProjectsAsync(solutionFileFullPath, fullProjectPaths, CancellationToken.None).Wait(); return 0; } - catch (Exception ex) + catch (Exception ex) when (ex is not GracefulException) { throw new GracefulException(ex.Message, ex); } @@ -57,9 +58,14 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable< ISolutionSerializer serializer = SlnCommandParser.GetSolutionSerializer(solutionFileFullPath); SolutionModel solution = await serializer.OpenAsync(solutionFileFullPath, cancellationToken); - foreach (var project in projectPaths) + foreach (var projectPath in projectPaths) { - SolutionProjectModel projectModel = solution.FindProject(project); + // Open project instance to see if it is a valid project + ProjectRootElement projectRootElement = ProjectRootElement.Open(projectPath); + ProjectInstance projectInstance = new ProjectInstance(projectRootElement); + string projectInstanceId = projectInstance.GetProjectId(); + + SolutionProjectModel? projectModel = (SolutionProjectModel?) solution.FindItemById(new Guid(projectInstanceId)); solution.RemoveProject(projectModel); } From 28a2e776c6899148765e0d3eb9aace07bb121238 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Thu, 5 Dec 2024 13:59:54 -0800 Subject: [PATCH 03/27] Added edge cases --- .../commands/dotnet-sln/remove/Program.cs | 49 +++- test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs | 250 ++++++++++++++++-- 2 files changed, 269 insertions(+), 30 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs index 30735728740f..8f09bfc134d9 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.EnvironmentAbstractions; using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; +using Microsoft.VisualStudio.SolutionPersistence.Serializer.SlnV12; namespace Microsoft.DotNet.Tools.Sln.Remove { @@ -36,19 +37,26 @@ public override int Execute() throw new GracefulException(CommonLocalizableStrings.SpecifyAtLeastOneProjectToRemove); } - IEnumerable fullProjectPaths = _projects.Select(project => - { - var fullPath = Path.GetFullPath(project); - return Directory.Exists(fullPath) ? MsbuildProject.GetProjectFileFromDirectory(fullPath).FullName : fullPath; - }); - try { - RemoveProjectsAsync(solutionFileFullPath, fullProjectPaths, CancellationToken.None).Wait(); + var relativeProjectPaths = _projects.Select(p => + { + var fullPath = Path.GetFullPath(p); + return Path.GetRelativePath( + Path.GetDirectoryName(solutionFileFullPath), + Directory.Exists(fullPath) + ? MsbuildProject.GetProjectFileFromDirectory(fullPath).FullName + : fullPath); + }); + RemoveProjectsAsync(solutionFileFullPath, relativeProjectPaths, CancellationToken.None).Wait(); return 0; } catch (Exception ex) when (ex is not GracefulException) { + if (ex is SolutionException || ex.InnerException is SolutionException) + { + throw new GracefulException(CommonLocalizableStrings.InvalidSolutionFormatString, solutionFileFullPath, ex.Message); + } throw new GracefulException(ex.Message, ex); } } @@ -58,17 +66,32 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable< ISolutionSerializer serializer = SlnCommandParser.GetSolutionSerializer(solutionFileFullPath); SolutionModel solution = await serializer.OpenAsync(solutionFileFullPath, cancellationToken); + // set UTF8 BOM encoding for .sln + if (serializer is ISolutionSerializer v12Serializer) + { + solution.SerializerExtension = v12Serializer.CreateModelExtension(new() + { + Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true) + }); + } + foreach (var projectPath in projectPaths) { - // Open project instance to see if it is a valid project - ProjectRootElement projectRootElement = ProjectRootElement.Open(projectPath); - ProjectInstance projectInstance = new ProjectInstance(projectRootElement); - string projectInstanceId = projectInstance.GetProjectId(); + var project = solution.FindProject(projectPath); + if (project != null) + { + solution.RemoveProject(project); - SolutionProjectModel? projectModel = (SolutionProjectModel?) solution.FindItemById(new Guid(projectInstanceId)); - solution.RemoveProject(projectModel); + Reporter.Output.WriteLine(CommonLocalizableStrings.ProjectRemovedFromTheSolution, projectPath); + } + else + { + Reporter.Output.WriteLine(CommonLocalizableStrings.ProjectNotFoundInTheSolution, projectPath); + } } + // TODO: Remove empty solution folders + await serializer.SaveAsync(solutionFileFullPath, solution, cancellationToken); } } diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs index 42f51f4c31d3..c829a7031c14 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs @@ -24,6 +24,224 @@ dotnet solution remove [...] [options] Options: -?, -h, --help Show command line help."; + private const string ExpectedSlnContentsAfterRemove = @" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""App"", ""App\App.csproj"", ""{7072A694-548F-4CAE-A58F-12D257D5F486}"" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.ActiveCfg = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.Build.0 = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.ActiveCfg = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.Build.0 = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.Build.0 = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.ActiveCfg = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.Build.0 = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal +"; + + private const string ExpectedSlnContentsAfterRemoveAllProjects = @" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Global +EndGlobal +"; + + private const string ExpectedSlnContentsAfterRemoveNestedProj = @" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""App"", ""App.csproj"", ""{7072A694-548F-4CAE-A58F-12D257D5F486}"" +EndProject +Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""src"", ""src"", ""{7B86CE74-F620-4B32-99FE-82D40F8D6BF2}"" +EndProject +Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""Lib"", ""Lib"", ""{EAB71280-AF32-4531-8703-43CDBA261AA3}"" +EndProject +Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""Lib"", ""src\Lib\Lib.csproj"", ""{84A45D44-B677-492D-A6DA-B3A71135AB8E}"" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.ActiveCfg = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.Build.0 = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.ActiveCfg = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.Build.0 = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.Build.0 = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.ActiveCfg = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.Build.0 = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|x64.ActiveCfg = Debug|x64 + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|x64.Build.0 = Debug|x64 + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|x86.ActiveCfg = Debug|x86 + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|x86.Build.0 = Debug|x86 + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|Any CPU.Build.0 = Release|Any CPU + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|x64.ActiveCfg = Release|x64 + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|x64.Build.0 = Release|x64 + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|x86.ActiveCfg = Release|x86 + {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EAB71280-AF32-4531-8703-43CDBA261AA3} = {7B86CE74-F620-4B32-99FE-82D40F8D6BF2} + {84A45D44-B677-492D-A6DA-B3A71135AB8E} = {EAB71280-AF32-4531-8703-43CDBA261AA3} + EndGlobalSection +EndGlobal +"; + + private const string ExpectedSlnContentsAfterRemoveLastNestedProj = @" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""App"", ""App.csproj"", ""{7072A694-548F-4CAE-A58F-12D257D5F486}"" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.ActiveCfg = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.Build.0 = Debug|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.ActiveCfg = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.Build.0 = Debug|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.Build.0 = Release|Any CPU + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.ActiveCfg = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.Build.0 = Release|x64 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 + {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 + EndGlobalSection +EndGlobal +"; + + private const string ExpectedSlnContentsAfterRemoveProjectWithDependencies = @" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27110.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""App"", ""App\App.csproj"", ""{BB02B949-F6BD-4872-95CB-96A05B1FE026}"" + ProjectSection(ProjectDependencies) = postProject + {D53E177A-8ECF-43D5-A01E-98B884D53CA6} = {D53E177A-8ECF-43D5-A01E-98B884D53CA6} + EndProjectSection +EndProject +Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""First"", ""First\First.csproj"", ""{D53E177A-8ECF-43D5-A01E-98B884D53CA6}"" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Release|Any CPU.Build.0 = Release|Any CPU + {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F6D9A973-1CFD-41C9-84F2-1471C0FE67DF} + EndGlobalSection +EndGlobal +"; + + private const string ExpectedSlnContentsAfterRemoveProjectInSolutionWithNestedSolutionItems = @" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.705 +MinimumVisualStudioVersion = 10.0.40219.1 +Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""NewFolder1"", ""NewFolder1"", ""{F39E429A-F91C-44F9-9025-EA6E41C99637}"" +EndProject +Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""NewFolder2"", ""NewFolder2"", ""{1B19AA22-7DB3-4A0F-8B89-2B80040B2BF0}"" + ProjectSection(SolutionItems) = preProject + TextFile1.txt = TextFile1.txt + EndProjectSection +EndProject +Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""NestedSolution"", ""NestedSolution"", ""{2128FC1C-7B60-4BC4-9010-D7A97E1805EA}"" +EndProject +Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""NestedFolder"", ""NestedFolder"", ""{7BC6A99C-7321-47A2-8CA5-B394195BD2B8}"" +EndProject +Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""NestedFolder"", ""NestedFolder"", ""{F6B0D958-42FF-4123-9668-A77A4ABFE5BA}"" +EndProject +Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""ConsoleApp2"", ""ConsoleApp2\ConsoleApp2.csproj"", ""{A264F7DB-E1EF-4F99-96BE-C08F29CA494F}"" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A264F7DB-E1EF-4F99-96BE-C08F29CA494F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A264F7DB-E1EF-4F99-96BE-C08F29CA494F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A264F7DB-E1EF-4F99-96BE-C08F29CA494F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A264F7DB-E1EF-4F99-96BE-C08F29CA494F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1B19AA22-7DB3-4A0F-8B89-2B80040B2BF0} = {F39E429A-F91C-44F9-9025-EA6E41C99637} + {7BC6A99C-7321-47A2-8CA5-B394195BD2B8} = {2128FC1C-7B60-4BC4-9010-D7A97E1805EA} + {F6B0D958-42FF-4123-9668-A77A4ABFE5BA} = {7BC6A99C-7321-47A2-8CA5-B394195BD2B8} + {A264F7DB-E1EF-4F99-96BE-C08F29CA494F} = {F6B0D958-42FF-4123-9668-A77A4ABFE5BA} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D36A0DD6-BD9C-4B57-9441-693B33DB7C2D} + EndGlobalSection +EndGlobal +"; + public GivenDotnetSlnRemove(ITestOutputHelper log) : base(log) { } @@ -105,15 +323,13 @@ public void WhenInvalidSolutionIsPassedItPrintsErrorAndUsage(string solutionComm .WithWorkingDirectory(projectDirectory) .Execute(solutionCommand, $"InvalidSolution{solutionExtension}", "remove", projectToRemove); cmd.Should().Fail(); - cmd.StdErr.Should().Match(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, Path.Combine(projectDirectory, $"InvalidSolution{solutionExtension}"), "*")); + cmd.StdErr.Should().Match(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, "InvalidSolution.sln", "*")); cmd.StdOut.Should().BeVisuallyEquivalentToIfNotLocalized(""); } [Theory] [InlineData("sln", ".sln")] [InlineData("solution", ".sln")] - [InlineData("sln", ".slnx")] - [InlineData("solution", ".slnx")] public void WhenInvalidSolutionIsFoundRemovePrintsErrorAndUsage(string solutionCommand, string solutionExtension) { var projectDirectoryRoot = _testAssetsManager @@ -125,7 +341,7 @@ public void WhenInvalidSolutionIsFoundRemovePrintsErrorAndUsage(string solutionC ? Path.Join(projectDirectoryRoot, "Sln") : Path.Join(projectDirectoryRoot, "Slnx"); - var solutionPath = Path.Combine(projectDirectory, $"InvalidSolution{solutionExtension}"); + var solutionPath = Path.Combine(projectDirectory, "InvalidSolution.sln"); var projectToRemove = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) @@ -209,7 +425,7 @@ public void WhenPassedAReferenceNotInSlnItPrintsStatus(string solutionCommand, s var contentBefore = File.ReadAllText(solutionPath); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", "referenceDoesNotExistInSln.csproj"); + .Execute(solutionCommand, "App.sln", "remove", "referenceDoesNotExistInSln.csproj"); cmd.Should().Pass(); cmd.StdOut.Should().Be(string.Format(CommonLocalizableStrings.ProjectNotFoundInTheSolution, "referenceDoesNotExistInSln.csproj")); File.ReadAllText(solutionPath) @@ -238,7 +454,7 @@ public async Task WhenPassedAReferenceItRemovesTheReferenceButNotOtherReferences var projectToRemove = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); + .Execute(solutionCommand, "App.sln", "remove", projectToRemove); cmd.Should().Pass(); cmd.StdOut.Should().Be(string.Format(CommonLocalizableStrings.ProjectRemovedFromTheSolution, projectToRemove)); @@ -264,7 +480,7 @@ public void WhenSolutionItemsExistInFolderParentFoldersAreNotRemoved(string solu var projectToRemove = Path.Combine("ConsoleApp1", "ConsoleApp1.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); + .Execute(solutionCommand, "App.sln", "remove", projectToRemove); cmd.Should().Pass(); cmd.StdOut.Should().Be(string.Format(CommonLocalizableStrings.ProjectRemovedFromTheSolution, projectToRemove)); @@ -326,7 +542,7 @@ public async Task WhenPassedMultipleReferencesAndOneOfThemDoesNotExistItRemovesT var projectToRemove = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", "idontexist.csproj", projectToRemove, "idontexisteither.csproj"); + .Execute(solutionCommand, "App.sln", "remove", "idontexist.csproj", projectToRemove, "idontexisteither.csproj"); cmd.Should().Pass(); string outputText = $@"{string.Format(CommonLocalizableStrings.ProjectNotFoundInTheSolution, "idontexist.csproj")} @@ -362,7 +578,7 @@ public async Task WhenReferenceIsRemovedBuildConfigsAreAlsoRemoved(string soluti var projectToRemove = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); + .Execute(solutionCommand, "App.sln", "remove", projectToRemove); cmd.Should().Pass(); var templateContents = GetSolutionFileTemplateContents($"ExpectedSlnContentsAfterRemove{solutionExtension}"); @@ -391,7 +607,7 @@ public async Task WhenDirectoryContainingProjectIsGivenProjectIsRemoved(string s var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", "Lib"); + .Execute(solutionCommand, "App.sln", "remove", "Lib"); cmd.Should().Pass(); var templateContents = GetSolutionFileTemplateContents($"ExpectedSlnContentsAfterRemove{solutionExtension}"); @@ -414,7 +630,7 @@ public void WhenDirectoryContainsNoProjectsItCancelsWholeOperation(string soluti var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", directoryToRemove); + .Execute(solutionCommand, "App.sln", "remove", directoryToRemove); cmd.Should().Fail(); cmd.StdErr.Should().Be( string.Format( @@ -438,7 +654,7 @@ public void WhenDirectoryContainsMultipleProjectsItCancelsWholeOperation(string var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", directoryToRemove); + .Execute(solutionCommand, "App.sln", "remove", directoryToRemove); cmd.Should().Fail(); cmd.StdErr.Should().Be( string.Format( @@ -469,7 +685,7 @@ public async Task WhenReferenceIsRemovedSlnBuilds(string solutionCommand, string var projectToRemove = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); + .Execute(solutionCommand, "App.sln", "remove", projectToRemove); cmd.Should().Pass(); new DotnetCommand(Log) @@ -543,7 +759,7 @@ public async Task WhenFinalReferenceIsRemovedEmptySectionsAreRemoved(string solu var libPath = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", libPath, appPath); + .Execute(solutionCommand, "App.sln", "remove", libPath, appPath); cmd.Should().Pass(); var solutionContents = File.ReadAllText(solutionPath); @@ -568,7 +784,7 @@ public void WhenNestedProjectIsRemovedItsSolutionFoldersAreRemoved(string soluti var projectToRemove = Path.Combine("src", "NotLastProjInSrc", "NotLastProjInSrc.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); + .Execute(solutionCommand, "App.sln", "remove", projectToRemove); cmd.Should().Pass(); var templateContents = GetSolutionFileTemplateContents($"ExpectedSlnContentsAfterRemoveNestedProj{solutionExtension}"); @@ -593,7 +809,7 @@ public void WhenFinalNestedProjectIsRemovedSolutionFoldersAreRemoved(string solu var projectToRemove = Path.Combine("src", "Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); + .Execute(solutionCommand, "App.sln", "remove", projectToRemove); cmd.Should().Pass(); var templateContents = GetSolutionFileTemplateContents($"ExpectedSlnContentsAfterRemoveLastNestedProj{solutionExtension}"); @@ -618,7 +834,7 @@ public void WhenProjectIsRemovedThenDependenciesOnProjectAreAlsoRemoved(string s var projectToRemove = Path.Combine("Second", "Second.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); + .Execute(solutionCommand, "App.sln", "remove", projectToRemove); cmd.Should().Pass(); var templateContents = GetSolutionFileTemplateContents($"ExpectedSlnContentsAfterRemoveProjectWithDependencies{solutionExtension}"); From 6db4b1743887d4a1f873f3e445b344866155c465 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Thu, 5 Dec 2024 15:20:16 -0800 Subject: [PATCH 04/27] Fix tests' edge cases --- .../commands/dotnet-sln/remove/Program.cs | 18 +++++++++++++++++- test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs | 13 ++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs index 8f09bfc134d9..2b6f6e64d2e5 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs @@ -57,6 +57,11 @@ public override int Execute() { throw new GracefulException(CommonLocalizableStrings.InvalidSolutionFormatString, solutionFileFullPath, ex.Message); } + // TODO: Check + if (ex.InnerException is GracefulException) + { + throw ex.InnerException; + } throw new GracefulException(ex.Message, ex); } } @@ -81,7 +86,6 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable< if (project != null) { solution.RemoveProject(project); - Reporter.Output.WriteLine(CommonLocalizableStrings.ProjectRemovedFromTheSolution, projectPath); } else @@ -91,6 +95,18 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable< } // TODO: Remove empty solution folders + HashSet emptySolutionFolders = solution.SolutionFolders.ToHashSet(); + foreach (var item in solution.SolutionItems) + { + if (item.Parent != null) + { + emptySolutionFolders.Remove(item.Parent); + } + } + foreach (var emptySolutionFolder in emptySolutionFolders) + { + solution.RemoveFolder(emptySolutionFolder); + } await serializer.SaveAsync(solutionFileFullPath, solution, cancellationToken); } diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs index c829a7031c14..e59a012adc2e 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs @@ -66,6 +66,17 @@ dotnet solution remove [...] [options] VisualStudioVersion = 15.0.26006.2 MinimumVisualStudioVersion = 10.0.40219.1 Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection EndGlobal "; @@ -323,7 +334,7 @@ public void WhenInvalidSolutionIsPassedItPrintsErrorAndUsage(string solutionComm .WithWorkingDirectory(projectDirectory) .Execute(solutionCommand, $"InvalidSolution{solutionExtension}", "remove", projectToRemove); cmd.Should().Fail(); - cmd.StdErr.Should().Match(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, "InvalidSolution.sln", "*")); + cmd.StdErr.Should().Match(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, Path.Combine(projectDirectory, "InvalidSolution.sln"), "*")); cmd.StdOut.Should().BeVisuallyEquivalentToIfNotLocalized(""); } From 9e03453960220c3a0eacdc9712de71f3d16d1461 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 9 Dec 2024 15:18:19 -0800 Subject: [PATCH 05/27] Fix all tests --- .../commands/dotnet-sln/remove/Program.cs | 40 ++++++++++++++----- test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs | 3 ++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs index 2b6f6e64d2e5..c7166b4241f5 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs @@ -20,6 +20,30 @@ internal class RemoveProjectFromSolutionCommand : CommandBase private readonly string _fileOrDirectory; private readonly IReadOnlyCollection _projects; + private int CountNonFolderDescendants(SolutionModel solution, SolutionFolderModel item, Dictionary cached) + { + if (cached.ContainsKey(item)) + { + return cached[item]; + } + int count = 0; + var children = solution.SolutionItems.Where(i => i.Parent == item); + foreach (var child in children) + { + if (child is SolutionFolderModel folderModel) + { + count += CountNonFolderDescendants(solution, folderModel, cached); + } + else + { + count++; + } + } + count += (item.Files?.Count ?? 0); + cached.Add(item, count); + return count; + } + public RemoveProjectFromSolutionCommand(ParseResult parseResult) : base(parseResult) { _fileOrDirectory = parseResult.GetValue(SlnCommandParser.SlnArgument); @@ -94,18 +118,16 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable< } } - // TODO: Remove empty solution folders - HashSet emptySolutionFolders = solution.SolutionFolders.ToHashSet(); - foreach (var item in solution.SolutionItems) + Dictionary nonFolderDescendantsCount = new(); + foreach (var item in solution.SolutionFolders) { - if (item.Parent != null) - { - emptySolutionFolders.Remove(item.Parent); - } + CountNonFolderDescendants(solution, item, nonFolderDescendantsCount); } - foreach (var emptySolutionFolder in emptySolutionFolders) + + var emptyFolders = nonFolderDescendantsCount.Where(i => i.Value == 0); + foreach (var folder in emptyFolders) { - solution.RemoveFolder(emptySolutionFolder); + solution.RemoveFolder(folder.Key); } await serializer.SaveAsync(solutionFileFullPath, solution, cancellationToken); diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs index e59a012adc2e..2bf9c07da40a 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs @@ -168,6 +168,9 @@ dotnet solution remove [...] [options] {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection EndGlobal "; From 8c1044a67c524c9a701c1bec9f521b7e06b3838d Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Tue, 10 Dec 2024 09:49:33 -0800 Subject: [PATCH 06/27] sln-remove: Update tests --- .../commands/dotnet-sln/remove/Program.cs | 18 +- test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs | 279 ++---------------- 2 files changed, 31 insertions(+), 266 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs index c7166b4241f5..a96295141c39 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs @@ -26,20 +26,14 @@ private int CountNonFolderDescendants(SolutionModel solution, SolutionFolderMode { return cached[item]; } - int count = 0; + int count = item.Files?.Count ?? 0; var children = solution.SolutionItems.Where(i => i.Parent == item); foreach (var child in children) { - if (child is SolutionFolderModel folderModel) - { - count += CountNonFolderDescendants(solution, folderModel, cached); - } - else - { - count++; - } + count += child is SolutionFolderModel folderModel + ? CountNonFolderDescendants(solution, folderModel, cached) + : 1; } - count += (item.Files?.Count ?? 0); cached.Add(item, count); return count; } @@ -124,10 +118,10 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable< CountNonFolderDescendants(solution, item, nonFolderDescendantsCount); } - var emptyFolders = nonFolderDescendantsCount.Where(i => i.Value == 0); + var emptyFolders = nonFolderDescendantsCount.Where(i => i.Value == 0).Select(i => i.Key); foreach (var folder in emptyFolders) { - solution.RemoveFolder(folder.Key); + solution.RemoveFolder(folder); } await serializer.SaveAsync(solutionFileFullPath, solution, cancellationToken); diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs index 2bf9c07da40a..799348f55852 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs @@ -24,238 +24,6 @@ dotnet solution remove [...] [options] Options: -?, -h, --help Show command line help."; - private const string ExpectedSlnContentsAfterRemove = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26006.2 -MinimumVisualStudioVersion = 10.0.40219.1 -Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""App"", ""App\App.csproj"", ""{7072A694-548F-4CAE-A58F-12D257D5F486}"" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.ActiveCfg = Debug|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.Build.0 = Debug|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.ActiveCfg = Debug|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.Build.0 = Debug|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.Build.0 = Release|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.ActiveCfg = Release|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.Build.0 = Release|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal -"; - - private const string ExpectedSlnContentsAfterRemoveAllProjects = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26006.2 -MinimumVisualStudioVersion = 10.0.40219.1 -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal -"; - - private const string ExpectedSlnContentsAfterRemoveNestedProj = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26006.2 -MinimumVisualStudioVersion = 10.0.40219.1 -Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""App"", ""App.csproj"", ""{7072A694-548F-4CAE-A58F-12D257D5F486}"" -EndProject -Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""src"", ""src"", ""{7B86CE74-F620-4B32-99FE-82D40F8D6BF2}"" -EndProject -Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""Lib"", ""Lib"", ""{EAB71280-AF32-4531-8703-43CDBA261AA3}"" -EndProject -Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""Lib"", ""src\Lib\Lib.csproj"", ""{84A45D44-B677-492D-A6DA-B3A71135AB8E}"" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.ActiveCfg = Debug|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.Build.0 = Debug|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.ActiveCfg = Debug|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.Build.0 = Debug|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.Build.0 = Release|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.ActiveCfg = Release|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.Build.0 = Release|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|x64.ActiveCfg = Debug|x64 - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|x64.Build.0 = Debug|x64 - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|x86.ActiveCfg = Debug|x86 - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Debug|x86.Build.0 = Debug|x86 - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|Any CPU.Build.0 = Release|Any CPU - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|x64.ActiveCfg = Release|x64 - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|x64.Build.0 = Release|x64 - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|x86.ActiveCfg = Release|x86 - {84A45D44-B677-492D-A6DA-B3A71135AB8E}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EAB71280-AF32-4531-8703-43CDBA261AA3} = {7B86CE74-F620-4B32-99FE-82D40F8D6BF2} - {84A45D44-B677-492D-A6DA-B3A71135AB8E} = {EAB71280-AF32-4531-8703-43CDBA261AA3} - EndGlobalSection -EndGlobal -"; - - private const string ExpectedSlnContentsAfterRemoveLastNestedProj = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26006.2 -MinimumVisualStudioVersion = 10.0.40219.1 -Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""App"", ""App.csproj"", ""{7072A694-548F-4CAE-A58F-12D257D5F486}"" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.ActiveCfg = Debug|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.Build.0 = Debug|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.ActiveCfg = Debug|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.Build.0 = Debug|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.Build.0 = Release|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.ActiveCfg = Release|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.Build.0 = Release|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal -"; - - private const string ExpectedSlnContentsAfterRemoveProjectWithDependencies = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27110.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""App"", ""App\App.csproj"", ""{BB02B949-F6BD-4872-95CB-96A05B1FE026}"" - ProjectSection(ProjectDependencies) = postProject - {D53E177A-8ECF-43D5-A01E-98B884D53CA6} = {D53E177A-8ECF-43D5-A01E-98B884D53CA6} - EndProjectSection -EndProject -Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""First"", ""First\First.csproj"", ""{D53E177A-8ECF-43D5-A01E-98B884D53CA6}"" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BB02B949-F6BD-4872-95CB-96A05B1FE026}.Release|Any CPU.Build.0 = Release|Any CPU - {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D53E177A-8ECF-43D5-A01E-98B884D53CA6}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {F6D9A973-1CFD-41C9-84F2-1471C0FE67DF} - EndGlobalSection -EndGlobal -"; - - private const string ExpectedSlnContentsAfterRemoveProjectInSolutionWithNestedSolutionItems = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.705 -MinimumVisualStudioVersion = 10.0.40219.1 -Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""NewFolder1"", ""NewFolder1"", ""{F39E429A-F91C-44F9-9025-EA6E41C99637}"" -EndProject -Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""NewFolder2"", ""NewFolder2"", ""{1B19AA22-7DB3-4A0F-8B89-2B80040B2BF0}"" - ProjectSection(SolutionItems) = preProject - TextFile1.txt = TextFile1.txt - EndProjectSection -EndProject -Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""NestedSolution"", ""NestedSolution"", ""{2128FC1C-7B60-4BC4-9010-D7A97E1805EA}"" -EndProject -Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""NestedFolder"", ""NestedFolder"", ""{7BC6A99C-7321-47A2-8CA5-B394195BD2B8}"" -EndProject -Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""NestedFolder"", ""NestedFolder"", ""{F6B0D958-42FF-4123-9668-A77A4ABFE5BA}"" -EndProject -Project(""{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"") = ""ConsoleApp2"", ""ConsoleApp2\ConsoleApp2.csproj"", ""{A264F7DB-E1EF-4F99-96BE-C08F29CA494F}"" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A264F7DB-E1EF-4F99-96BE-C08F29CA494F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A264F7DB-E1EF-4F99-96BE-C08F29CA494F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A264F7DB-E1EF-4F99-96BE-C08F29CA494F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A264F7DB-E1EF-4F99-96BE-C08F29CA494F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {1B19AA22-7DB3-4A0F-8B89-2B80040B2BF0} = {F39E429A-F91C-44F9-9025-EA6E41C99637} - {7BC6A99C-7321-47A2-8CA5-B394195BD2B8} = {2128FC1C-7B60-4BC4-9010-D7A97E1805EA} - {F6B0D958-42FF-4123-9668-A77A4ABFE5BA} = {7BC6A99C-7321-47A2-8CA5-B394195BD2B8} - {A264F7DB-E1EF-4F99-96BE-C08F29CA494F} = {F6B0D958-42FF-4123-9668-A77A4ABFE5BA} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {D36A0DD6-BD9C-4B57-9441-693B33DB7C2D} - EndGlobalSection -EndGlobal -"; - public GivenDotnetSlnRemove(ITestOutputHelper log) : base(log) { } @@ -337,13 +105,15 @@ public void WhenInvalidSolutionIsPassedItPrintsErrorAndUsage(string solutionComm .WithWorkingDirectory(projectDirectory) .Execute(solutionCommand, $"InvalidSolution{solutionExtension}", "remove", projectToRemove); cmd.Should().Fail(); - cmd.StdErr.Should().Match(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, Path.Combine(projectDirectory, "InvalidSolution.sln"), "*")); + cmd.StdErr.Should().Match(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, Path.Combine(projectDirectory, $"InvalidSolution{solutionExtension}"), "*")); cmd.StdOut.Should().BeVisuallyEquivalentToIfNotLocalized(""); } [Theory] [InlineData("sln", ".sln")] [InlineData("solution", ".sln")] + [InlineData("sln", ".slnx")] + [InlineData("solution", ".slnx")] public void WhenInvalidSolutionIsFoundRemovePrintsErrorAndUsage(string solutionCommand, string solutionExtension) { var projectDirectoryRoot = _testAssetsManager @@ -355,7 +125,7 @@ public void WhenInvalidSolutionIsFoundRemovePrintsErrorAndUsage(string solutionC ? Path.Join(projectDirectoryRoot, "Sln") : Path.Join(projectDirectoryRoot, "Slnx"); - var solutionPath = Path.Combine(projectDirectory, "InvalidSolution.sln"); + var solutionPath = Path.Combine(projectDirectory, $"InvalidSolution{solutionExtension}"); var projectToRemove = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) @@ -439,7 +209,7 @@ public void WhenPassedAReferenceNotInSlnItPrintsStatus(string solutionCommand, s var contentBefore = File.ReadAllText(solutionPath); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", "referenceDoesNotExistInSln.csproj"); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", "referenceDoesNotExistInSln.csproj"); cmd.Should().Pass(); cmd.StdOut.Should().Be(string.Format(CommonLocalizableStrings.ProjectNotFoundInTheSolution, "referenceDoesNotExistInSln.csproj")); File.ReadAllText(solutionPath) @@ -460,7 +230,7 @@ public async Task WhenPassedAReferenceItRemovesTheReferenceButNotOtherReferences var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); @@ -468,7 +238,7 @@ public async Task WhenPassedAReferenceItRemovesTheReferenceButNotOtherReferences var projectToRemove = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", projectToRemove); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); cmd.Should().Pass(); cmd.StdOut.Should().Be(string.Format(CommonLocalizableStrings.ProjectRemovedFromTheSolution, projectToRemove)); @@ -494,7 +264,7 @@ public void WhenSolutionItemsExistInFolderParentFoldersAreNotRemoved(string solu var projectToRemove = Path.Combine("ConsoleApp1", "ConsoleApp1.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", projectToRemove); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); cmd.Should().Pass(); cmd.StdOut.Should().Be(string.Format(CommonLocalizableStrings.ProjectRemovedFromTheSolution, projectToRemove)); @@ -529,7 +299,8 @@ public async Task WhenDuplicateReferencesArePresentItRemovesThemAll(string solut outputText += Environment.NewLine + outputText; cmd.StdOut.Should().BeVisuallyEquivalentTo(outputText); - solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); + SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(1); solution.SolutionProjects.Single().FilePath.Should().Be(Path.Combine("App", "App.csproj")); } @@ -548,7 +319,7 @@ public async Task WhenPassedMultipleReferencesAndOneOfThemDoesNotExistItRemovesT var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); @@ -556,7 +327,7 @@ public async Task WhenPassedMultipleReferencesAndOneOfThemDoesNotExistItRemovesT var projectToRemove = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", "idontexist.csproj", projectToRemove, "idontexisteither.csproj"); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", "idontexist.csproj", projectToRemove, "idontexisteither.csproj"); cmd.Should().Pass(); string outputText = $@"{string.Format(CommonLocalizableStrings.ProjectNotFoundInTheSolution, "idontexist.csproj")} @@ -584,7 +355,7 @@ public async Task WhenReferenceIsRemovedBuildConfigsAreAlsoRemoved(string soluti var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); @@ -592,7 +363,7 @@ public async Task WhenReferenceIsRemovedBuildConfigsAreAlsoRemoved(string soluti var projectToRemove = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", projectToRemove); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); cmd.Should().Pass(); var templateContents = GetSolutionFileTemplateContents($"ExpectedSlnContentsAfterRemove{solutionExtension}"); @@ -614,14 +385,14 @@ public async Task WhenDirectoryContainingProjectIsGivenProjectIsRemoved(string s var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", "Lib"); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", "Lib"); cmd.Should().Pass(); var templateContents = GetSolutionFileTemplateContents($"ExpectedSlnContentsAfterRemove{solutionExtension}"); @@ -644,7 +415,7 @@ public void WhenDirectoryContainsNoProjectsItCancelsWholeOperation(string soluti var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", directoryToRemove); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", directoryToRemove); cmd.Should().Fail(); cmd.StdErr.Should().Be( string.Format( @@ -668,7 +439,7 @@ public void WhenDirectoryContainsMultipleProjectsItCancelsWholeOperation(string var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", directoryToRemove); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", directoryToRemove); cmd.Should().Fail(); cmd.StdErr.Should().Be( string.Format( @@ -691,7 +462,7 @@ public async Task WhenReferenceIsRemovedSlnBuilds(string solutionCommand, string var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); @@ -699,7 +470,7 @@ public async Task WhenReferenceIsRemovedSlnBuilds(string solutionCommand, string var projectToRemove = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", projectToRemove); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); cmd.Should().Pass(); new DotnetCommand(Log) @@ -765,7 +536,7 @@ public async Task WhenFinalReferenceIsRemovedEmptySectionsAreRemoved(string solu var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); @@ -773,7 +544,7 @@ public async Task WhenFinalReferenceIsRemovedEmptySectionsAreRemoved(string solu var libPath = Path.Combine("Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", libPath, appPath); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", libPath, appPath); cmd.Should().Pass(); var solutionContents = File.ReadAllText(solutionPath); @@ -798,7 +569,7 @@ public void WhenNestedProjectIsRemovedItsSolutionFoldersAreRemoved(string soluti var projectToRemove = Path.Combine("src", "NotLastProjInSrc", "NotLastProjInSrc.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", projectToRemove); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); cmd.Should().Pass(); var templateContents = GetSolutionFileTemplateContents($"ExpectedSlnContentsAfterRemoveNestedProj{solutionExtension}"); @@ -823,7 +594,7 @@ public void WhenFinalNestedProjectIsRemovedSolutionFoldersAreRemoved(string solu var projectToRemove = Path.Combine("src", "Lib", "Lib.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", projectToRemove); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); cmd.Should().Pass(); var templateContents = GetSolutionFileTemplateContents($"ExpectedSlnContentsAfterRemoveLastNestedProj{solutionExtension}"); @@ -848,7 +619,7 @@ public void WhenProjectIsRemovedThenDependenciesOnProjectAreAlsoRemoved(string s var projectToRemove = Path.Combine("Second", "Second.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute(solutionCommand, "App.sln", "remove", projectToRemove); + .Execute(solutionCommand, $"App{solutionExtension}", "remove", projectToRemove); cmd.Should().Pass(); var templateContents = GetSolutionFileTemplateContents($"ExpectedSlnContentsAfterRemoveProjectWithDependencies{solutionExtension}"); From bca2475a11fc8f4744816263778a2e0e98f199f9 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Tue, 10 Dec 2024 16:32:50 -0800 Subject: [PATCH 07/27] Nit --- src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs | 1 - test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs index a96295141c39..37886d28ab20 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs @@ -5,7 +5,6 @@ using Microsoft.Build.Construction; using Microsoft.Build.Execution; using Microsoft.DotNet.Cli; -using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools.Common; using Microsoft.Extensions.EnvironmentAbstractions; diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs index 799348f55852..c59d5e6a9c39 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs @@ -285,7 +285,7 @@ public async Task WhenDuplicateReferencesArePresentItRemovesThemAll(string solut .Path; var solutionPath = Path.Combine(projectDirectory, "App.sln"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(3); @@ -299,8 +299,7 @@ public async Task WhenDuplicateReferencesArePresentItRemovesThemAll(string solut outputText += Environment.NewLine + outputText; cmd.StdOut.Should().BeVisuallyEquivalentTo(outputText); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); - SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); + solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(1); solution.SolutionProjects.Single().FilePath.Should().Be(Path.Combine("App", "App.csproj")); } From eb206beb1bd41646276e80dc6736ef3f90166547 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 23 Dec 2024 16:22:38 -0800 Subject: [PATCH 08/27] Apply suggestions from code review --- .../commands/dotnet-sln/remove/Program.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs index 37886d28ab20..f771a5c234b7 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs @@ -19,18 +19,22 @@ internal class RemoveProjectFromSolutionCommand : CommandBase private readonly string _fileOrDirectory; private readonly IReadOnlyCollection _projects; - private int CountNonFolderDescendants(SolutionModel solution, SolutionFolderModel item, Dictionary cached) + private int CountNonFolderDescendants( + SolutionModel solution, + SolutionFolderModel item, + Dictionary solutionItemsGroupedByParent, + Dictionary cached) { if (cached.ContainsKey(item)) { return cached[item]; } int count = item.Files?.Count ?? 0; - var children = solution.SolutionItems.Where(i => i.Parent == item); + var children = solutionItemsGroupedByParent.TryGetValue(item, out var items) ? items : Array.Empty(); foreach (var child in children) { count += child is SolutionFolderModel folderModel - ? CountNonFolderDescendants(solution, folderModel, cached) + ? CountNonFolderDescendants(solution, folderModel, solutionItemsGroupedByParent, cached) : 1; } cached.Add(item, count); @@ -111,10 +115,15 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable< } } + Dictionary solutionItemsGroupedByParent = solution.SolutionItems + .Where(i => i.Parent != null) + .GroupBy(i => i.Parent) + .ToDictionary(g => g.Key, g => g.ToArray()); + Dictionary nonFolderDescendantsCount = new(); foreach (var item in solution.SolutionFolders) { - CountNonFolderDescendants(solution, item, nonFolderDescendantsCount); + CountNonFolderDescendants(solution, item, solutionItemsGroupedByParent, nonFolderDescendantsCount); } var emptyFolders = nonFolderDescendantsCount.Where(i => i.Value == 0).Select(i => i.Key); From 8e1e34838df6e5f66c90a9ea7fbf49ae19dc1713 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Wed, 25 Dec 2024 14:56:17 -0800 Subject: [PATCH 09/27] Apply suggestions from code review Co-authored-by: kasperk81 <83082615+kasperk81@users.noreply.github.com> --- test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs index c59d5e6a9c39..42f51f4c31d3 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs @@ -230,7 +230,7 @@ public async Task WhenPassedAReferenceItRemovesTheReferenceButNotOtherReferences var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); @@ -285,7 +285,7 @@ public async Task WhenDuplicateReferencesArePresentItRemovesThemAll(string solut .Path; var solutionPath = Path.Combine(projectDirectory, "App.sln"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(3); @@ -318,7 +318,7 @@ public async Task WhenPassedMultipleReferencesAndOneOfThemDoesNotExistItRemovesT var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); @@ -354,7 +354,7 @@ public async Task WhenReferenceIsRemovedBuildConfigsAreAlsoRemoved(string soluti var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); @@ -384,7 +384,7 @@ public async Task WhenDirectoryContainingProjectIsGivenProjectIsRemoved(string s var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); @@ -461,7 +461,7 @@ public async Task WhenReferenceIsRemovedSlnBuilds(string solutionCommand, string var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); @@ -535,7 +535,7 @@ public async Task WhenFinalReferenceIsRemovedEmptySectionsAreRemoved(string solu var solutionPath = Path.Combine(projectDirectory, $"App{solutionExtension}"); - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new InvalidOperationException($"Unable to get solution serializer for {solutionPath}."); SolutionModel solution = await serializer.OpenAsync(solutionPath, CancellationToken.None); solution.SolutionProjects.Count.Should().Be(2); From 6e52aeaef128eac87ea7d6bc88688b3b47d1d182 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 6 Jan 2025 14:32:46 -0800 Subject: [PATCH 10/27] Remove SlnFile references --- src/Cli/dotnet/ProjectInstanceExtensions.cs | 4 +- src/Cli/dotnet/SlnFileExtensions.cs | 541 ------------------ .../dotnet/SlnProjectCollectionExtensions.cs | 24 - src/Cli/dotnet/SlnProjectExtensions.cs | 36 -- .../commands/dotnet-sln/list/Program.cs | 1 - test/dotnet-sln.Tests/GivenDotnetSlnList.cs | 1 - .../dotnet-sln.Tests/GivenDotnetSlnMigrate.cs | 1 - 7 files changed, 2 insertions(+), 606 deletions(-) delete mode 100644 src/Cli/dotnet/SlnFileExtensions.cs delete mode 100644 src/Cli/dotnet/SlnProjectCollectionExtensions.cs delete mode 100644 src/Cli/dotnet/SlnProjectExtensions.cs diff --git a/src/Cli/dotnet/ProjectInstanceExtensions.cs b/src/Cli/dotnet/ProjectInstanceExtensions.cs index 29337fa22c35..67fddac7f032 100644 --- a/src/Cli/dotnet/ProjectInstanceExtensions.cs +++ b/src/Cli/dotnet/ProjectInstanceExtensions.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Build.Execution; -using Microsoft.DotNet.Cli.Sln.Internal; namespace Microsoft.DotNet.Tools.Common { @@ -22,7 +21,8 @@ public static string GetDefaultProjectTypeGuid(this ProjectInstance projectInsta string projectTypeGuid = projectInstance.GetPropertyValue("DefaultProjectTypeGuid"); if (string.IsNullOrEmpty(projectTypeGuid) && projectInstance.FullPath.EndsWith(".shproj", StringComparison.OrdinalIgnoreCase)) { - projectTypeGuid = ProjectTypeGuids.SharedProjectGuid; + // TODO: Centralize project type guids + projectTypeGuid = "{D954291E-2A0B-460D-934E-DC6B0785DB48}"; } return projectTypeGuid; } diff --git a/src/Cli/dotnet/SlnFileExtensions.cs b/src/Cli/dotnet/SlnFileExtensions.cs deleted file mode 100644 index 0a981b9e2429..000000000000 --- a/src/Cli/dotnet/SlnFileExtensions.cs +++ /dev/null @@ -1,541 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Build.Construction; -using Microsoft.Build.Exceptions; -using Microsoft.Build.Execution; -using Microsoft.DotNet.Cli.Sln.Internal; -using Microsoft.DotNet.Cli.Utils; -using System.Collections.Generic; - -namespace Microsoft.DotNet.Tools.Common -{ - internal static class SlnFileExtensions - { - public static void AddProject(this SlnFile slnFile, string fullProjectPath, IList solutionFolders) - { - if (string.IsNullOrEmpty(fullProjectPath)) - { - throw new ArgumentException(); - } - - var relativeProjectPath = Path.GetRelativePath( - PathUtility.EnsureTrailingSlash(slnFile.BaseDirectory), - fullProjectPath); - - if (slnFile.Projects.Any((p) => - string.Equals(p.FilePath, relativeProjectPath, StringComparison.OrdinalIgnoreCase))) - { - Reporter.Output.WriteLine(string.Format( - CommonLocalizableStrings.SolutionAlreadyContainsProject, - slnFile.FullPath, - relativeProjectPath)); - } - else - { - ProjectRootElement rootElement = null; - ProjectInstance projectInstance = null; - try - { - rootElement = ProjectRootElement.Open(fullProjectPath); - projectInstance = new ProjectInstance(rootElement); - } - catch (InvalidProjectFileException e) - { - Reporter.Error.WriteLine(string.Format( - CommonLocalizableStrings.InvalidProjectWithExceptionMessage, - fullProjectPath, - e.Message)); - return; - } - - var slnProject = new SlnProject - { - Id = projectInstance.GetProjectId(), - TypeGuid = rootElement.GetProjectTypeGuid() ?? projectInstance.GetDefaultProjectTypeGuid(), - Name = Path.GetFileNameWithoutExtension(relativeProjectPath), - FilePath = relativeProjectPath - }; - - if (string.IsNullOrEmpty(slnProject.TypeGuid)) - { - Reporter.Error.WriteLine( - string.Format( - CommonLocalizableStrings.UnsupportedProjectType, - projectInstance.FullPath)); - return; - } - - // NOTE: The order you create the sections determines the order they are written to the sln - // file. In the case of an empty sln file, in order to make sure the solution configurations - // section comes first we need to add it first. This doesn't affect correctness but does - // stop VS from re-ordering things later on. Since we are keeping the SlnFile class low-level - // it shouldn't care about the VS implementation details. That's why we handle this here. - if (AreBuildConfigurationsApplicable(slnProject.TypeGuid)) - { - slnFile.AddDefaultBuildConfigurations(); - - slnFile.MapSolutionConfigurationsToProject( - projectInstance, - slnFile.ProjectConfigurationsSection.GetOrCreatePropertySet(slnProject.Id)); - } - - SetupSolutionFolders(slnFile, solutionFolders, relativeProjectPath, slnProject); - - slnFile.Projects.Add(slnProject); - - Reporter.Output.WriteLine( - string.Format(CommonLocalizableStrings.ProjectAddedToTheSolution, relativeProjectPath)); - } - } - - private static bool AreBuildConfigurationsApplicable(string projectTypeGuid) - { - return !projectTypeGuid.Equals(ProjectTypeGuids.SharedProjectGuid, StringComparison.OrdinalIgnoreCase); - } - - private static void SetupSolutionFolders(SlnFile slnFile, IList solutionFolders, string relativeProjectPath, SlnProject slnProject) - { - if (solutionFolders != null) - { - if (solutionFolders.Any()) - { - // Before adding a solution folder, check if the name conflicts with any existing projects in the solution - var duplicateProjects = slnFile.Projects.Where(p => solutionFolders.Contains(p.Name) - && p.TypeGuid != ProjectTypeGuids.SolutionFolderGuid).ToList(); - foreach (SlnProject duplicateProject in duplicateProjects) - { - slnFile.AddSolutionFolders(duplicateProject, new List() { Path.GetDirectoryName(duplicateProject.FilePath) }); - } - } - else - { - // If a project and solution folder have the same name, add it's own folder as a solution folder - // eg. foo\extensions.csproj and extensions\library\library.csproj would have a project and solution folder with conflicting names - var duplicateProject = slnFile.Projects.Where(p => string.Equals(p.Name, slnProject.Name, StringComparison.OrdinalIgnoreCase) - && p.TypeGuid == ProjectTypeGuids.SolutionFolderGuid).FirstOrDefault(); - if (duplicateProject != null) - { - // Try making a new folder for the project to put it under so we can still add it despite there being one with the same name already in the parent folder - slnFile.AddSolutionFolders(slnProject, new List() { Path.GetDirectoryName(relativeProjectPath) }); - } - } - // Even if we added a solution folder above for a duplicate, we still need to add the expected folder for the current project - slnFile.AddSolutionFolders(slnProject, solutionFolders); - } - } - - private static void AddDefaultBuildConfigurations(this SlnFile slnFile) - { - var configurationsSection = slnFile.SolutionConfigurationsSection; - - if (!configurationsSection.IsEmpty) - { - return; - } - - var defaultConfigurations = new List() - { - "Debug|Any CPU", - "Debug|x64", - "Debug|x86", - "Release|Any CPU", - "Release|x64", - "Release|x86", - }; - - foreach (var config in defaultConfigurations) - { - configurationsSection[config] = config; - } - } - - private static void MapSolutionConfigurationsToProject( - this SlnFile slnFile, - ProjectInstance projectInstance, - SlnPropertySet solutionProjectConfigs) - { - var (projectConfigurations, defaultProjectConfiguration) = GetKeysDictionary(projectInstance.GetConfigurations()); - var (projectPlatforms, defaultProjectPlatform) = GetKeysDictionary(projectInstance.GetPlatforms()); - - foreach (var solutionConfigKey in slnFile.SolutionConfigurationsSection.Keys) - { - var projectConfigKey = MapSolutionConfigKeyToProjectConfigKey( - solutionConfigKey, - projectConfigurations, - defaultProjectConfiguration, - projectPlatforms, - defaultProjectPlatform); - if (projectConfigKey == null) - { - continue; - } - - var activeConfigKey = $"{solutionConfigKey}.ActiveCfg"; - if (!solutionProjectConfigs.ContainsKey(activeConfigKey)) - { - solutionProjectConfigs[activeConfigKey] = projectConfigKey; - } - - var buildKey = $"{solutionConfigKey}.Build.0"; - if (!solutionProjectConfigs.ContainsKey(buildKey)) - { - solutionProjectConfigs[buildKey] = projectConfigKey; - } - } - } - - private static (Dictionary Keys, string DefaultKey) GetKeysDictionary(IEnumerable keys) - { - // A dictionary mapping key -> key is used instead of a HashSet so the original case of the key can be retrieved from the set - var dictionary = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - - foreach (var key in keys) - { - dictionary[key] = key; - } - - return (dictionary, keys.FirstOrDefault()); - } - - private static string GetMatchingProjectKey(IDictionary projectKeys, string solutionKey) - { - string projectKey; - if (projectKeys.TryGetValue(solutionKey, out projectKey)) - { - return projectKey; - } - - var keyWithoutWhitespace = string.Concat(solutionKey.Where(c => !char.IsWhiteSpace(c))); - if (projectKeys.TryGetValue(keyWithoutWhitespace, out projectKey)) - { - return projectKey; - } - - return null; - } - - private static string MapSolutionConfigKeyToProjectConfigKey( - string solutionConfigKey, - Dictionary projectConfigurations, - string defaultProjectConfiguration, - Dictionary projectPlatforms, - string defaultProjectPlatform) - { - var pair = solutionConfigKey.Split(new char[] { '|' }, 2); - if (pair.Length != 2) - { - return null; - } - - var projectConfiguration = GetMatchingProjectKey(projectConfigurations, pair[0]) ?? defaultProjectConfiguration; - if (projectConfiguration == null) - { - return null; - } - - var projectPlatform = GetMatchingProjectKey(projectPlatforms, pair[1]) ?? defaultProjectPlatform; - if (projectPlatform == null) - { - return null; - } - - // VS stores "Any CPU" platform in the solution regardless of how it is named at the project level - return $"{projectConfiguration}|{(projectPlatform == "AnyCPU" ? "Any CPU" : projectPlatform)}"; - } - - private static void AddSolutionFolders(this SlnFile slnFile, SlnProject slnProject, IList solutionFolders) - { - if (solutionFolders.Any()) - { - var nestedProjectsSection = slnFile.Sections.GetOrCreateSection( - "NestedProjects", - SlnSectionType.PreProcess); - - var pathToGuidMap = slnFile.GetSolutionFolderPaths(nestedProjectsSection.Properties); - - if (slnFile.HasSolutionFolder(nestedProjectsSection.Properties, slnProject)) - { - return; - } - - string parentDirGuid = null; - var solutionFolderHierarchy = string.Empty; - foreach (var dir in solutionFolders) - { - solutionFolderHierarchy = Path.Combine(solutionFolderHierarchy, dir); - if (pathToGuidMap.ContainsKey(solutionFolderHierarchy)) - { - parentDirGuid = pathToGuidMap[solutionFolderHierarchy]; - } - else - { - - if(HasDuplicateNameForSameValueOfNestedProjects(nestedProjectsSection, dir, parentDirGuid, slnFile.Projects)) - { - throw new GracefulException(CommonLocalizableStrings.SolutionFolderAlreadyContainsProject, slnFile.FullPath, slnProject.Name, slnFile.Projects.FirstOrDefault(p => p.Id == parentDirGuid).Name); - } - - var solutionFolder = new SlnProject - { - Id = Guid.NewGuid().ToString("B").ToUpper(), - TypeGuid = ProjectTypeGuids.SolutionFolderGuid, - Name = dir, - FilePath = dir - }; - - slnFile.Projects.Add(solutionFolder); - - if (parentDirGuid != null) - { - nestedProjectsSection.Properties[solutionFolder.Id] = parentDirGuid; - } - parentDirGuid = solutionFolder.Id; - } - } - if (HasDuplicateNameForSameValueOfNestedProjects(nestedProjectsSection, slnProject.Name, parentDirGuid, slnFile.Projects)) - { - throw new GracefulException(CommonLocalizableStrings.SolutionFolderAlreadyContainsProject, slnFile.FullPath, slnProject.Name, slnFile.Projects.FirstOrDefault(p => p.Id == parentDirGuid).Name); - } - nestedProjectsSection.Properties[slnProject.Id] = parentDirGuid; - } - } - - private static bool HasDuplicateNameForSameValueOfNestedProjects(SlnSection nestedProjectsSection, string name, string value, IList projects) - { - foreach (var property in nestedProjectsSection.Properties) - { - if (property.Value == value) - { - var existingProject = projects.FirstOrDefault(p => p.Id == property.Key); - - if (existingProject != null && existingProject.Name == name) - { - return true; - } - } - } - return false; - } - - private static IDictionary GetSolutionFolderPaths( - this SlnFile slnFile, - SlnPropertySet nestedProjects) - { - var solutionFolderPaths = new Dictionary(StringComparer.OrdinalIgnoreCase); - - var solutionFolderProjects = slnFile.Projects.GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid); - foreach (var slnProject in solutionFolderProjects) - { - var path = slnProject.FilePath; - var id = slnProject.Id; - while (nestedProjects.ContainsKey(id)) - { - id = nestedProjects[id]; - var parentSlnProject = solutionFolderProjects.Where(p => p.Id == id).SingleOrDefault(); - if (parentSlnProject == null) // see: https://github.com/dotnet/sdk/pull/28811 - throw new GracefulException(CommonLocalizableStrings.CorruptSolutionProjectFolderStructure, slnFile.FullPath, id); - path = Path.Combine(parentSlnProject.FilePath, path); - } - - solutionFolderPaths[path] = slnProject.Id; - } - - return solutionFolderPaths; - } - - private static bool HasSolutionFolder( - this SlnFile slnFile, - SlnPropertySet properties, - SlnProject slnProject) - { - return properties.ContainsKey(slnProject.Id); - } - - public static bool RemoveProject(this SlnFile slnFile, string projectPath) - { - if (string.IsNullOrEmpty(projectPath)) - { - throw new ArgumentException(); - } - - var projectPathNormalized = PathUtility.GetPathWithDirectorySeparator(projectPath); - - var projectsToRemove = slnFile.Projects.Where((p) => - string.Equals(p.FilePath, projectPathNormalized, StringComparison.OrdinalIgnoreCase)).ToList(); - - bool projectRemoved = false; - if (projectsToRemove.Count == 0) - { - Reporter.Output.WriteLine(string.Format( - CommonLocalizableStrings.ProjectNotFoundInTheSolution, - projectPath)); - } - else - { - foreach (var slnProject in projectsToRemove) - { - var buildConfigsToRemove = slnFile.ProjectConfigurationsSection.GetPropertySet(slnProject.Id); - if (buildConfigsToRemove != null) - { - slnFile.ProjectConfigurationsSection.Remove(buildConfigsToRemove); - } - - var nestedProjectsSection = slnFile.Sections.GetSection( - "NestedProjects", - SlnSectionType.PreProcess); - if (nestedProjectsSection != null && nestedProjectsSection.Properties.ContainsKey(slnProject.Id)) - { - nestedProjectsSection.Properties.Remove(slnProject.Id); - } - - slnFile.Projects.Remove(slnProject); - Reporter.Output.WriteLine( - string.Format(CommonLocalizableStrings.ProjectRemovedFromTheSolution, slnProject.FilePath)); - } - - foreach (var project in slnFile.Projects) - { - var dependencies = project.Dependencies; - if (dependencies == null) - { - continue; - } - - dependencies.SkipIfEmpty = true; - - foreach (var removed in projectsToRemove) - { - dependencies.Properties.Remove(removed.Id); - } - } - - projectRemoved = true; - } - - return projectRemoved; - } - - public static void RemoveEmptyConfigurationSections(this SlnFile slnFile) - { - if (slnFile.Projects.Count == 0) - { - var solutionConfigs = slnFile.Sections.GetSection("SolutionConfigurationPlatforms"); - if (solutionConfigs != null) - { - slnFile.Sections.Remove(solutionConfigs); - } - - var projectConfigs = slnFile.Sections.GetSection("ProjectConfigurationPlatforms"); - if (projectConfigs != null) - { - slnFile.Sections.Remove(projectConfigs); - } - } - } - - public static void RemoveEmptySolutionFolders(this SlnFile slnFile) - { - var solutionFolderProjects = slnFile.Projects - .GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid) - .ToList(); - - if (solutionFolderProjects.Any()) - { - var nestedProjectsSection = slnFile.Sections.GetSection( - "NestedProjects", - SlnSectionType.PreProcess); - - if (nestedProjectsSection == null) - { - foreach (var solutionFolderProject in solutionFolderProjects) - { - if (solutionFolderProject.Sections.Count() == 0) - { - slnFile.Projects.Remove(solutionFolderProject); - } - } - } - else - { - var solutionFoldersInUse = slnFile.GetSolutionFoldersThatContainProjectsInItsHierarchy( - nestedProjectsSection.Properties); - - solutionFoldersInUse.UnionWith(slnFile.GetSolutionFoldersThatContainSolutionItemsInItsHierarchy( - nestedProjectsSection.Properties)); - - foreach (var solutionFolderProject in solutionFolderProjects) - { - if (!solutionFoldersInUse.Contains(solutionFolderProject.Id)) - { - nestedProjectsSection.Properties.Remove(solutionFolderProject.Id); - if (solutionFolderProject.Sections.Count() == 0) - { - slnFile.Projects.Remove(solutionFolderProject); - } - } - } - - if (nestedProjectsSection.IsEmpty) - { - slnFile.Sections.Remove(nestedProjectsSection); - } - } - } - } - - private static HashSet GetSolutionFoldersThatContainProjectsInItsHierarchy( - this SlnFile slnFile, - SlnPropertySet nestedProjects) - { - var solutionFoldersInUse = new HashSet(); - - IEnumerable nonSolutionFolderProjects; - nonSolutionFolderProjects = slnFile.Projects.GetProjectsNotOfType( - ProjectTypeGuids.SolutionFolderGuid); - - foreach (var nonSolutionFolderProject in nonSolutionFolderProjects) - { - var id = nonSolutionFolderProject.Id; - while (nestedProjects.ContainsKey(id)) - { - id = nestedProjects[id]; - solutionFoldersInUse.Add(id); - } - } - - return solutionFoldersInUse; - } - - private static HashSet GetSolutionFoldersThatContainSolutionItemsInItsHierarchy( - this SlnFile slnFile, - SlnPropertySet nestedProjects) - { - var solutionFoldersInUse = new HashSet(); - - var solutionItemsFolderProjects = slnFile.Projects - .GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid) - .Where(ContainsSolutionItems); - - foreach (var solutionItemsFolderProject in solutionItemsFolderProjects) - { - var id = solutionItemsFolderProject.Id; - solutionFoldersInUse.Add(id); - - while (nestedProjects.ContainsKey(id)) - { - id = nestedProjects[id]; - solutionFoldersInUse.Add(id); - } - } - - return solutionFoldersInUse; - } - - private static bool ContainsSolutionItems(SlnProject project) - { - return project.Sections - .GetSection("SolutionItems", SlnSectionType.PreProcess) != null; - } - } -} diff --git a/src/Cli/dotnet/SlnProjectCollectionExtensions.cs b/src/Cli/dotnet/SlnProjectCollectionExtensions.cs deleted file mode 100644 index f0d70ae4e640..000000000000 --- a/src/Cli/dotnet/SlnProjectCollectionExtensions.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.DotNet.Cli.Sln.Internal; - -namespace Microsoft.DotNet.Tools.Common -{ - internal static class SlnProjectCollectionExtensions - { - public static IEnumerable GetProjectsByType( - this SlnProjectCollection projects, - string typeGuid) - { - return projects.Where(p => p.TypeGuid == typeGuid); - } - - public static IEnumerable GetProjectsNotOfType( - this SlnProjectCollection projects, - string typeGuid) - { - return projects.Where(p => p.TypeGuid != typeGuid); - } - } -} diff --git a/src/Cli/dotnet/SlnProjectExtensions.cs b/src/Cli/dotnet/SlnProjectExtensions.cs deleted file mode 100644 index 405c3b43cb88..000000000000 --- a/src/Cli/dotnet/SlnProjectExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.DotNet.Cli.Sln.Internal; - -namespace Microsoft.DotNet.Tools.Common -{ - internal static class SlnProjectExtensions - { - public static string GetFullSolutionFolderPath(this SlnProject slnProject) - { - var slnFile = slnProject.ParentFile; - var nestedProjects = slnFile.Sections - .GetOrCreateSection("NestedProjects", SlnSectionType.PreProcess) - .Properties; - var solutionFolders = slnFile.Projects - .GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid) - .ToArray(); - - string path = slnProject.Name; - string id = slnProject.Id; - - // If the nested projects contains this project's id then it has a parent - // Traverse from the project to each parent prepending the solution folder to the path - while (nestedProjects.ContainsKey(id)) - { - id = nestedProjects[id]; - - string solutionFolderPath = solutionFolders.Single(p => p.Id == id).FilePath; - path = Path.Combine(solutionFolderPath, path); - } - - return path; - } - } -} diff --git a/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs index 99b948207011..0a7522c46242 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs @@ -3,7 +3,6 @@ using System.CommandLine; using Microsoft.DotNet.Cli; -using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnList.cs b/test/dotnet-sln.Tests/GivenDotnetSlnList.cs index f462ff1e6042..7cd5200208bd 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnList.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnList.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Tools; using Microsoft.DotNet.Tools.Common; using CommandLocalizableStrings = Microsoft.DotNet.Tools.Sln.LocalizableStrings; diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnMigrate.cs b/test/dotnet-sln.Tests/GivenDotnetSlnMigrate.cs index 7af7267a2997..f18ac2d7fba5 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnMigrate.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnMigrate.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Tools; using Microsoft.DotNet.Tools.Common; using CommandLocalizableStrings = Microsoft.DotNet.Tools.Sln.LocalizableStrings; From 904637985b49087c3da5d4147f9de4ed1d8fede8 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 6 Jan 2025 15:16:33 -0800 Subject: [PATCH 11/27] Remove SlnFile references --- .../dotnet/ReleasePropertyProjectLocator.cs | 28 +++++++++++-------- src/Cli/dotnet/SlnFileFactory.cs | 20 ++++++++----- test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs | 10 ++++++- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs index 803a44b0e442..0670ce36db15 100644 --- a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs +++ b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs @@ -4,11 +4,13 @@ using System.CommandLine; using System.Diagnostics; using Microsoft.Build.Execution; -using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; using Microsoft.DotNet.Tools.Common; using Microsoft.NET.Build.Tasks; +using Microsoft.VisualStudio.SolutionPersistence; +using Microsoft.VisualStudio.SolutionPersistence.Model; +using Microsoft.VisualStudio.SolutionPersistence.Serializer; namespace Microsoft.DotNet.Cli { @@ -145,7 +147,11 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() /// Throws exception if two+ projects disagree in PublishRelease, PackRelease, or whatever _propertyToCheck is, and have it defined. public ProjectInstance? GetArbitraryProjectFromSolution(string slnPath, Dictionary globalProps) { - SlnFile sln; + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(slnPath) ?? throw new GracefulException( + CommonLocalizableStrings.CouldNotFindSolutionOrDirectory, + slnPath); + + SolutionModel sln = serializer.OpenAsync(slnPath, CancellationToken.None).Result; try { sln = SlnFileFactory.CreateFromFileOrDirectory(slnPath); @@ -163,13 +169,13 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() if (string.Equals(Environment.GetEnvironmentVariable(EnvironmentVariableNames.DOTNET_CLI_LAZY_PUBLISH_AND_PACK_RELEASE_FOR_SOLUTIONS), "true", StringComparison.OrdinalIgnoreCase)) { // Evaluate only one project for speed if this environment variable is used. Will break more customers if enabled (adding 8.0 project to SLN with other project TFMs with no Publish or PackRelease.) - return GetSingleProjectFromSolution(sln, globalProps); + return GetSingleProjectFromSolution(sln, slnPath, globalProps); } - Parallel.ForEach(sln.Projects.AsEnumerable(), (project, state) => + Parallel.ForEach(sln.SolutionProjects.AsEnumerable(), (project, state) => { #pragma warning disable CS8604 // Possible null reference argument. - string projectFullPath = Path.Combine(Path.GetDirectoryName(sln.FullPath), project.FilePath); + string projectFullPath = Path.Combine(Path.GetDirectoryName(slnPath), project.FilePath); #pragma warning restore CS8604 // Possible null reference argument. if (IsUnanalyzableProjectInSolution(project, projectFullPath)) return; @@ -208,12 +214,12 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() /// The solution to get an arbitrary project from. /// The global properties to load into the project. /// null if no project exists in the solution that can be evaluated properly. Else, the first project in the solution that can be. - private ProjectInstance? GetSingleProjectFromSolution(SlnFile sln, Dictionary globalProps) + private ProjectInstance? GetSingleProjectFromSolution(SolutionModel sln, string slnPath, Dictionary globalProps) { - foreach (var project in sln.Projects.AsEnumerable()) + foreach (var project in sln.SolutionProjects.AsEnumerable()) { #pragma warning disable CS8604 // Possible null reference argument. - string projectFullPath = Path.Combine(Path.GetDirectoryName(sln.FullPath), project.FilePath); + string projectFullPath = Path.Combine(Path.GetDirectoryName(slnPath), project.FilePath); #pragma warning restore CS8604 // Possible null reference argument. if (IsUnanalyzableProjectInSolution(project, projectFullPath)) continue; @@ -234,9 +240,9 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() /// The project under a solution to evaluate. /// The full hard-coded path of the project. /// True if the project is not supported by ProjectInstance class or appears to be invalid. - private bool IsUnanalyzableProjectInSolution(SlnProject project, string projectFullPath) + private bool IsUnanalyzableProjectInSolution(SolutionProjectModel project, string projectFullPath) { - return project.TypeGuid == solutionFolderGuid || project.TypeGuid == sharedProjectGuid || !IsValidProjectFilePath(projectFullPath); + return project.TypeId.ToString() == solutionFolderGuid || project.TypeId.ToString() == sharedProjectGuid || !IsValidProjectFilePath(projectFullPath); } /// Creates a ProjectInstance if the project is valid, elsewise, fails. @@ -262,7 +268,7 @@ private bool IsValidProjectFilePath(string path) /// Returns true if the path exists and is a sln file type. private bool IsValidSlnFilePath(string path) { - return File.Exists(path) && Path.GetExtension(path).EndsWith("sln"); + return File.Exists(path) && (Path.GetExtension(path) == ".sln" || Path.GetExtension(path) == ".slnx"); } /// A case-insensitive dictionary of any properties passed from the user and their values. diff --git a/src/Cli/dotnet/SlnFileFactory.cs b/src/Cli/dotnet/SlnFileFactory.cs index 49a33de4c782..80f02fe40f99 100644 --- a/src/Cli/dotnet/SlnFileFactory.cs +++ b/src/Cli/dotnet/SlnFileFactory.cs @@ -1,14 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; +using Microsoft.VisualStudio.SolutionPersistence; +using Microsoft.VisualStudio.SolutionPersistence.Model; +using Microsoft.VisualStudio.SolutionPersistence.Serializer; namespace Microsoft.DotNet.Tools.Common { public static class SlnFileFactory { - public static SlnFile CreateFromFileOrDirectory(string fileOrDirectory) + public static SolutionModel CreateFromFileOrDirectory(string fileOrDirectory) { if (File.Exists(fileOrDirectory)) { @@ -20,12 +22,16 @@ public static SlnFile CreateFromFileOrDirectory(string fileOrDirectory) } } - private static SlnFile FromFile(string solutionPath) + private static SolutionModel FromFile(string solutionPath) { - SlnFile slnFile = null; + SolutionModel slnFile = null; try { - slnFile = SlnFile.Read(solutionPath); + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new GracefulException( + CommonLocalizableStrings.CouldNotFindSolutionOrDirectory, + solutionPath); + + slnFile = serializer.OpenAsync(solutionPath, CancellationToken.None).Result; } catch (InvalidSolutionFormatException e) { @@ -37,7 +43,7 @@ private static SlnFile FromFile(string solutionPath) return slnFile; } - private static SlnFile FromDirectory(string solutionDirectory) + private static SolutionModel FromDirectory(string solutionDirectory) { DirectoryInfo dir; try @@ -57,7 +63,7 @@ private static SlnFile FromDirectory(string solutionDirectory) solutionDirectory); } - FileInfo[] files = dir.GetFiles("*.sln"); + FileInfo[] files = [..dir.GetFiles("*.sln"), ..dir.GetFiles("*.slnx")]; if (files.Length == 0) { throw new GracefulException( diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs b/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs index 42061903efaa..0231bd518eaf 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; using Microsoft.DotNet.Tools.Common; @@ -12,6 +11,15 @@ namespace Microsoft.DotNet.Cli.Sln.Add.Tests { + public static class ProjectTypeGuids + { + public const string CSharpProjectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"; + public const string FSharpProjectTypeGuid = "{F2A71F9B-5D33-465A-A702-920D77279786}"; + public const string VBProjectTypeGuid = "{F184B08F-C81C-45F6-A57F-5ABD9991F28F}"; + public const string SolutionFolderGuid = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}"; + public const string SharedProjectGuid = "{D954291E-2A0B-460D-934E-DC6B0785DB48}"; + } + public class GivenDotnetSlnAdd : SdkTest { private Func HelpText = (defaultVal) => $@"Description: From c8e0cb84213b7f76faf7b9e8411a5d2f693ddba6 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 6 Jan 2025 15:43:04 -0800 Subject: [PATCH 12/27] Fix typo --- src/Cli/dotnet/SlnFileFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/SlnFileFactory.cs b/src/Cli/dotnet/SlnFileFactory.cs index 80f02fe40f99..e84e965a8cae 100644 --- a/src/Cli/dotnet/SlnFileFactory.cs +++ b/src/Cli/dotnet/SlnFileFactory.cs @@ -33,7 +33,7 @@ private static SolutionModel FromFile(string solutionPath) slnFile = serializer.OpenAsync(solutionPath, CancellationToken.None).Result; } - catch (InvalidSolutionFormatException e) + catch (SolutionException e) { throw new GracefulException( CommonLocalizableStrings.InvalidSolutionFormatString, From cc555bc2b960c718de500da14a4188044cf00849 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 6 Jan 2025 16:06:07 -0800 Subject: [PATCH 13/27] Update solution and project files --- sdk.sln | 7 ------- src/Cli/dotnet/dotnet.csproj | 1 - .../Microsoft.DotNet.Cli.Sln.Internal.Tests.csproj | 1 - .../Msbuild.Tests.Utilities/Msbuild.Tests.Utilities.csproj | 1 - test/dotnet-sln.Tests/dotnet-sln.Tests.csproj | 1 - test/dotnet.Tests/dotnet.Tests.csproj | 1 - 6 files changed, 12 deletions(-) diff --git a/sdk.sln b/sdk.sln index 52840d8fce54..6bac5a188e1e 100644 --- a/sdk.sln +++ b/sdk.sln @@ -73,8 +73,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cli", "Cli", "{9CEF03F6-1A6 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Cli.Utils", "src\Cli\Microsoft.DotNet.Cli.Utils\Microsoft.DotNet.Cli.Utils.csproj", "{CC25E192-70AD-4D91-B288-3AD40065DBAC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Cli.Sln.Internal", "src\Cli\Microsoft.DotNet.Cli.Sln.Internal\Microsoft.DotNet.Cli.Sln.Internal.csproj", "{2119E6E5-2B75-402A-AFF3-3E7E7B671B9B}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.InternalAbstractions", "src\Cli\Microsoft.DotNet.InternalAbstractions\Microsoft.DotNet.InternalAbstractions.csproj", "{0FA6CEE9-7A97-4036-8D48-39BA4F85118E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Configurer", "src\Cli\Microsoft.DotNet.Configurer\Microsoft.DotNet.Configurer.csproj", "{B22C90C9-BA46-4F07-9613-253DD01E3EFF}" @@ -578,10 +576,6 @@ Global {CC25E192-70AD-4D91-B288-3AD40065DBAC}.Debug|Any CPU.Build.0 = Debug|Any CPU {CC25E192-70AD-4D91-B288-3AD40065DBAC}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC25E192-70AD-4D91-B288-3AD40065DBAC}.Release|Any CPU.Build.0 = Release|Any CPU - {2119E6E5-2B75-402A-AFF3-3E7E7B671B9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2119E6E5-2B75-402A-AFF3-3E7E7B671B9B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2119E6E5-2B75-402A-AFF3-3E7E7B671B9B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2119E6E5-2B75-402A-AFF3-3E7E7B671B9B}.Release|Any CPU.Build.0 = Release|Any CPU {0FA6CEE9-7A97-4036-8D48-39BA4F85118E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0FA6CEE9-7A97-4036-8D48-39BA4F85118E}.Debug|Any CPU.Build.0 = Debug|Any CPU {0FA6CEE9-7A97-4036-8D48-39BA4F85118E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -1005,7 +999,6 @@ Global {E740A596-2CAE-476C-8062-49705C3A9CF0} = {580D1AE7-AA8F-4912-8B76-105594E00B3B} {9CEF03F6-1A61-431A-8ABD-9095974629AE} = {22AB674F-ED91-4FBC-BFEE-8A1E82F9F05E} {CC25E192-70AD-4D91-B288-3AD40065DBAC} = {9CEF03F6-1A61-431A-8ABD-9095974629AE} - {2119E6E5-2B75-402A-AFF3-3E7E7B671B9B} = {9CEF03F6-1A61-431A-8ABD-9095974629AE} {0FA6CEE9-7A97-4036-8D48-39BA4F85118E} = {9CEF03F6-1A61-431A-8ABD-9095974629AE} {B22C90C9-BA46-4F07-9613-253DD01E3EFF} = {9CEF03F6-1A61-431A-8ABD-9095974629AE} {D62591F3-ED86-4014-B765-7CDA47F55EE2} = {9CEF03F6-1A61-431A-8ABD-9095974629AE} diff --git a/src/Cli/dotnet/dotnet.csproj b/src/Cli/dotnet/dotnet.csproj index 82db3621e7fc..da9f09bd96c8 100644 --- a/src/Cli/dotnet/dotnet.csproj +++ b/src/Cli/dotnet/dotnet.csproj @@ -91,7 +91,6 @@ - diff --git a/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.csproj b/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.csproj index 1c9566e18687..ac047650cc58 100644 --- a/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.csproj +++ b/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.csproj @@ -13,7 +13,6 @@ - diff --git a/test/Msbuild.Tests.Utilities/Msbuild.Tests.Utilities.csproj b/test/Msbuild.Tests.Utilities/Msbuild.Tests.Utilities.csproj index 94c82028582a..8ec880dcddaf 100644 --- a/test/Msbuild.Tests.Utilities/Msbuild.Tests.Utilities.csproj +++ b/test/Msbuild.Tests.Utilities/Msbuild.Tests.Utilities.csproj @@ -9,7 +9,6 @@ - diff --git a/test/dotnet-sln.Tests/dotnet-sln.Tests.csproj b/test/dotnet-sln.Tests/dotnet-sln.Tests.csproj index ad45aa7b7e6c..8b9e3844a891 100644 --- a/test/dotnet-sln.Tests/dotnet-sln.Tests.csproj +++ b/test/dotnet-sln.Tests/dotnet-sln.Tests.csproj @@ -12,7 +12,6 @@ - diff --git a/test/dotnet.Tests/dotnet.Tests.csproj b/test/dotnet.Tests/dotnet.Tests.csproj index 4bb8bfdc1df5..75da805f7e39 100644 --- a/test/dotnet.Tests/dotnet.Tests.csproj +++ b/test/dotnet.Tests/dotnet.Tests.csproj @@ -110,7 +110,6 @@ - From 21e5da7ae90e9d8af67053f6ce9bffa0ce54b906 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 6 Jan 2025 16:31:50 -0800 Subject: [PATCH 14/27] Remove tests --- sdk.sln | 7 - .../FileManipulation/FileUtil.cs | 152 --- .../LocalizableStrings.resx | 153 --- .../Microsoft.DotNet.Cli.Sln.Internal.csproj | 21 - .../ProjectTypeGuids.cs | 14 - .../Properties/AssemblyInfo.cs | 9 - .../SlnFile.cs | 1199 ----------------- .../xlf/LocalizableStrings.cs.xlf | 62 - .../xlf/LocalizableStrings.de.xlf | 62 - .../xlf/LocalizableStrings.es.xlf | 62 - .../xlf/LocalizableStrings.fr.xlf | 62 - .../xlf/LocalizableStrings.it.xlf | 62 - .../xlf/LocalizableStrings.ja.xlf | 62 - .../xlf/LocalizableStrings.ko.xlf | 62 - .../xlf/LocalizableStrings.pl.xlf | 62 - .../xlf/LocalizableStrings.pt-BR.xlf | 62 - .../xlf/LocalizableStrings.ru.xlf | 62 - .../xlf/LocalizableStrings.tr.xlf | 62 - .../xlf/LocalizableStrings.zh-Hans.xlf | 62 - .../xlf/LocalizableStrings.zh-Hant.xlf | 62 - ...Microsoft.DotNet.Cli.Sln.Internal.Tests.cs | 530 -------- ...osoft.DotNet.Cli.Sln.Internal.Tests.csproj | 21 - 22 files changed, 2912 deletions(-) delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FileUtil.cs delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/LocalizableStrings.resx delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Microsoft.DotNet.Cli.Sln.Internal.csproj delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/ProjectTypeGuids.cs delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Properties/AssemblyInfo.cs delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.cs.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.de.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.es.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.fr.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.it.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ja.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ko.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pl.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pt-BR.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ru.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.tr.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hans.xlf delete mode 100644 src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hant.xlf delete mode 100644 test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs delete mode 100644 test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.csproj diff --git a/sdk.sln b/sdk.sln index 6bac5a188e1e..f077fa6852a4 100644 --- a/sdk.sln +++ b/sdk.sln @@ -107,8 +107,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArgumentForwarding.Tests", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EndToEnd.Tests", "test\EndToEnd.Tests\EndToEnd.Tests.csproj", "{A0EFB2CB-517F-4746-902E-D0EBEFFBD89B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Cli.Sln.Internal.Tests", "test\Microsoft.DotNet.Cli.Sln.Internal.Tests\Microsoft.DotNet.Cli.Sln.Internal.Tests.csproj", "{82D910C3-A5E2-41F0-A142-F4F2D770CB59}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Cli.Utils.Tests", "test\Microsoft.DotNet.Cli.Utils.Tests\Microsoft.DotNet.Cli.Utils.Tests.csproj", "{C34FB893-2320-41A3-9D38-0061C22A6464}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.MSBuildSdkResolver.Tests", "test\Microsoft.DotNet.MSBuildSdkResolver.Tests\Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj", "{0AE2AB83-1F0D-4E04-9C8D-5C35CE97FDDC}" @@ -640,10 +638,6 @@ Global {A0EFB2CB-517F-4746-902E-D0EBEFFBD89B}.Debug|Any CPU.Build.0 = Debug|Any CPU {A0EFB2CB-517F-4746-902E-D0EBEFFBD89B}.Release|Any CPU.ActiveCfg = Release|Any CPU {A0EFB2CB-517F-4746-902E-D0EBEFFBD89B}.Release|Any CPU.Build.0 = Release|Any CPU - {82D910C3-A5E2-41F0-A142-F4F2D770CB59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {82D910C3-A5E2-41F0-A142-F4F2D770CB59}.Debug|Any CPU.Build.0 = Debug|Any CPU - {82D910C3-A5E2-41F0-A142-F4F2D770CB59}.Release|Any CPU.ActiveCfg = Release|Any CPU - {82D910C3-A5E2-41F0-A142-F4F2D770CB59}.Release|Any CPU.Build.0 = Release|Any CPU {C34FB893-2320-41A3-9D38-0061C22A6464}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C34FB893-2320-41A3-9D38-0061C22A6464}.Debug|Any CPU.Build.0 = Debug|Any CPU {C34FB893-2320-41A3-9D38-0061C22A6464}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -1016,7 +1010,6 @@ Global {587A6639-FA2A-498C-8FA2-F77925DB732B} = {580D1AE7-AA8F-4912-8B76-105594E00B3B} {2D8AA9EA-3013-47DE-B2DD-E074F67467ED} = {580D1AE7-AA8F-4912-8B76-105594E00B3B} {A0EFB2CB-517F-4746-902E-D0EBEFFBD89B} = {580D1AE7-AA8F-4912-8B76-105594E00B3B} - {82D910C3-A5E2-41F0-A142-F4F2D770CB59} = {580D1AE7-AA8F-4912-8B76-105594E00B3B} {C34FB893-2320-41A3-9D38-0061C22A6464} = {580D1AE7-AA8F-4912-8B76-105594E00B3B} {0AE2AB83-1F0D-4E04-9C8D-5C35CE97FDDC} = {580D1AE7-AA8F-4912-8B76-105594E00B3B} {3D20D19D-74FB-4A43-B78C-B0AF90A696A0} = {580D1AE7-AA8F-4912-8B76-105594E00B3B} diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FileUtil.cs b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FileUtil.cs deleted file mode 100644 index 504eba093dcc..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FileUtil.cs +++ /dev/null @@ -1,152 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// FileUtil.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -namespace Microsoft.DotNet.Cli.Sln.Internal.FileManipulation -{ - internal static class FileUtil - { - internal static TextFormatInfo GetTextFormatInfo(string file) - { - var info = new TextFormatInfo(); - - string newLine = null; - Encoding encoding; - - using (FileStream fs = File.OpenRead(file)) - { - byte[] buf = new byte[1024]; - int nread, i; - - if ((nread = fs.Read(buf, 0, buf.Length)) <= 0) - { - return info; - } - - if (TryParse(buf, nread, out encoding)) - { - i = encoding.GetPreamble().Length; - } - else - { - encoding = null; - i = 0; - } - - do - { - while (i < nread) - { - if (buf[i] == '\r') - { - newLine = "\r\n"; - break; - } - else if (buf[i] == '\n') - { - newLine = "\n"; - break; - } - - i++; - } - - if (newLine == null) - { - if ((nread = fs.Read(buf, 0, buf.Length)) <= 0) - { - newLine = "\n"; - break; - } - - i = 0; - } - } while (newLine == null); - - info.EndsWithEmptyLine = fs.Seek(-1, SeekOrigin.End) > 0 && fs.ReadByte() == (int)'\n'; - info.NewLine = newLine; - info.Encoding = encoding; - return info; - } - } - - private static bool TryParse(byte[] buffer, int available, out Encoding encoding) - { - if (buffer.Length >= 2) - { - for (int i = 0; i < table.Length; i++) - { - bool matched = true; - - if (available < table[i].GetPreamble().Length) - { - continue; - } - - for (int j = 0; j < table[i].GetPreamble().Length; j++) - { - if (buffer[j] != table[i].GetPreamble()[j]) - { - matched = false; - break; - } - } - - if (matched) - { - encoding = table[i]; - return true; - } - } - } - - encoding = null; - - return false; - } - - private static readonly Encoding[] table = new[] { - Encoding.UTF8, - Encoding.UTF32, - Encoding.ASCII, - }; - } - - internal class TextFormatInfo - { - public TextFormatInfo() - { - NewLine = Environment.NewLine; - Encoding = null; - EndsWithEmptyLine = true; - } - - public string NewLine { get; set; } - public Encoding Encoding { get; set; } - public bool EndsWithEmptyLine { get; set; } - } -} diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/LocalizableStrings.resx b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/LocalizableStrings.resx deleted file mode 100644 index 524b72423138..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/LocalizableStrings.resx +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Invalid format in line {0}: {1} - - - Project section is missing '{0}' when parsing the line starting at position {1} - - - Property set is missing '{0}' - - - Global section specified more than once - - - Global section not closed - - - File header is missing version - - - Expected file header not found - - - Project section not closed - - - Invalid section type: {0} - - - Section id missing - - - Closing section tag not found - - diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Microsoft.DotNet.Cli.Sln.Internal.csproj b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Microsoft.DotNet.Cli.Sln.Internal.csproj deleted file mode 100644 index 1618b780490d..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Microsoft.DotNet.Cli.Sln.Internal.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - SLN file reader/writer - $(SdkTargetFramework) - portable - Microsoft.DotNet.Cli.Sln.Internal - MicrosoftAspNetCore - true - true - true - - - - - - - - - - diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/ProjectTypeGuids.cs b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/ProjectTypeGuids.cs deleted file mode 100644 index 5db6beb30ed7..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/ProjectTypeGuids.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.DotNet.Cli.Sln.Internal -{ - public static class ProjectTypeGuids - { - public const string CSharpProjectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"; - public const string FSharpProjectTypeGuid = "{F2A71F9B-5D33-465A-A702-920D77279786}"; - public const string VBProjectTypeGuid = "{F184B08F-C81C-45F6-A57F-5ABD9991F28F}"; - public const string SolutionFolderGuid = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}"; - public const string SharedProjectGuid = "{D954291E-2A0B-460D-934E-DC6B0785DB48}"; - } -} diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Properties/AssemblyInfo.cs b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Properties/AssemblyInfo.cs deleted file mode 100644 index 98cdd1d19b73..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.DotNet.Cli.Sln.Internal.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] -[assembly: InternalsVisibleTo("dotnet-sln.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] - - diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs deleted file mode 100644 index e7e758d6f00e..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs +++ /dev/null @@ -1,1199 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// -// SlnFile.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (c) 2016 Xamarin, Inc (http://www.xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System.Collections; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Globalization; -using System.Reflection; -using Microsoft.DotNet.Cli.Sln.Internal.FileManipulation; -using Microsoft.DotNet.Tools.Common; - -namespace Microsoft.DotNet.Cli.Sln.Internal -{ - public class SlnFile - { - private SlnProjectCollection _projects = new(); - private SlnSectionCollection _sections = new(); - private SlnPropertySet _metadata = new(true); - private int _prefixBlankLines = 1; - private TextFormatInfo _format = new(); - - public string FormatVersion { get; set; } - public string ProductDescription { get; set; } - - public string VisualStudioVersion - { - get { return _metadata.GetValue("VisualStudioVersion"); } - set { _metadata.SetValue("VisualStudioVersion", value); } - } - - public string MinimumVisualStudioVersion - { - get { return _metadata.GetValue("MinimumVisualStudioVersion"); } - set { _metadata.SetValue("MinimumVisualStudioVersion", value); } - } - - public string BaseDirectory - { - get { return Path.GetDirectoryName(FullPath); } - } - - public string FullPath { get; set; } - - public SlnPropertySet SolutionConfigurationsSection - { - get - { - return _sections - .GetOrCreateSection("SolutionConfigurationPlatforms", SlnSectionType.PreProcess) - .Properties; - } - } - - public SlnPropertySetCollection ProjectConfigurationsSection - { - get - { - return _sections - .GetOrCreateSection("ProjectConfigurationPlatforms", SlnSectionType.PostProcess) - .NestedPropertySets; - } - } - - public SlnSectionCollection Sections - { - get { return _sections; } - } - - public SlnProjectCollection Projects - { - get { return _projects; } - } - - public SlnFile() - { - _projects.ParentFile = this; - _sections.ParentFile = this; - } - - public static SlnFile Read(string file) - { - SlnFile slnFile = new() - { - FullPath = Path.GetFullPath(file), - _format = FileUtil.GetTextFormatInfo(file) - }; - - using (var sr = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read))) - { - slnFile.Read(sr); - } - - return slnFile; - } - - private void Read(TextReader reader) - { - const string HeaderPrefix = "Microsoft Visual Studio Solution File, Format Version"; - - string line; - int curLineNum = 0; - bool globalFound = false; - bool productRead = false; - - while ((line = reader.ReadLine()) != null) - { - curLineNum++; - line = line.Trim(); - if (line.StartsWith(HeaderPrefix, StringComparison.Ordinal)) - { - if (line.Length <= HeaderPrefix.Length) - { - throw new InvalidSolutionFormatException( - curLineNum, - LocalizableStrings.FileHeaderMissingVersionError); - } - - FormatVersion = line.Substring(HeaderPrefix.Length).Trim(); - _prefixBlankLines = curLineNum - 1; - } - if (line.StartsWith("# ", StringComparison.Ordinal)) - { - if (!productRead) - { - productRead = true; - ProductDescription = line.Substring(2); - } - } - else if (line.StartsWith("Project", StringComparison.Ordinal)) - { - SlnProject p = new(); - p.Read(reader, line, ref curLineNum); - _projects.Add(p); - } - else if (line == "Global") - { - if (globalFound) - { - throw new InvalidSolutionFormatException( - curLineNum, - LocalizableStrings.GlobalSectionMoreThanOnceError); - } - globalFound = true; - while ((line = reader.ReadLine()) != null) - { - curLineNum++; - line = line.Trim(); - if (line == "EndGlobal") - { - break; - } - else if (line.StartsWith("GlobalSection", StringComparison.Ordinal)) - { - var sec = new SlnSection(); - sec.Read(reader, line, ref curLineNum); - _sections.Add(sec); - } - else // Ignore text that's out of place - { - continue; - } - } - if (line == null) - { - throw new InvalidSolutionFormatException( - curLineNum, - LocalizableStrings.GlobalSectionNotClosedError); - } - } - else if (line.IndexOf('=') != -1) - { - _metadata.ReadLine(line, curLineNum); - } - } - if (FormatVersion == null) - { - throw new InvalidSolutionFormatException(LocalizableStrings.FileHeaderMissingError); - } - } - - public void Write(string file = null) - { - if (!string.IsNullOrEmpty(file)) - { - FullPath = Path.GetFullPath(file); - } - var sw = new StringWriter(); - Write(sw); - File.WriteAllText(FullPath, sw.ToString(), Encoding.UTF8); - } - - private void Write(TextWriter writer) - { - writer.NewLine = _format.NewLine; - for (int n = 0; n < _prefixBlankLines; n++) - { - writer.WriteLine(); - } - writer.WriteLine("Microsoft Visual Studio Solution File, Format Version " + FormatVersion); - writer.WriteLine("# " + ProductDescription); - - _metadata.Write(writer); - - foreach (var p in _projects) - { - p.Write(writer); - } - - writer.WriteLine("Global"); - foreach (SlnSection s in _sections) - { - s.Write(writer, "GlobalSection"); - } - writer.WriteLine("EndGlobal"); - } - } - - public class SlnProject - { - private SlnSectionCollection _sections = new(); - - private SlnFile _parentFile; - - public SlnFile ParentFile - { - get - { - return _parentFile; - } - internal set - { - _parentFile = value; - _sections.ParentFile = _parentFile; - } - } - - public string Id { get; set; } - public string TypeGuid { get; set; } - public string Name { get; set; } - - private string _filePath; - public string FilePath - { - get - { - return _filePath; - } - set - { - _filePath = PathUtility.RemoveExtraPathSeparators( - PathUtility.GetPathWithDirectorySeparator(value)); - } - } - - public int Line { get; private set; } - internal bool Processed { get; set; } - - public SlnSectionCollection Sections - { - get { return _sections; } - } - - public SlnSection Dependencies - { - get - { - return _sections.GetSection("ProjectDependencies", SlnSectionType.PostProcess); - } - } - - internal void Read(TextReader reader, string line, ref int curLineNum) - { - Line = curLineNum; - - int n = 0; - FindNext(curLineNum, line, ref n, '('); - n++; - FindNext(curLineNum, line, ref n, '"'); - int n2 = n + 1; - FindNext(curLineNum, line, ref n2, '"'); - TypeGuid = line.Substring(n + 1, n2 - n - 1); - - n = n2 + 1; - FindNext(curLineNum, line, ref n, ')'); - FindNext(curLineNum, line, ref n, '='); - - FindNext(curLineNum, line, ref n, '"'); - n2 = n + 1; - FindNext(curLineNum, line, ref n2, '"'); - Name = line.Substring(n + 1, n2 - n - 1); - - n = n2 + 1; - FindNext(curLineNum, line, ref n, ','); - FindNext(curLineNum, line, ref n, '"'); - n2 = n + 1; - FindNext(curLineNum, line, ref n2, '"'); - FilePath = line.Substring(n + 1, n2 - n - 1); - - n = n2 + 1; - FindNext(curLineNum, line, ref n, ','); - FindNext(curLineNum, line, ref n, '"'); - n2 = n + 1; - FindNext(curLineNum, line, ref n2, '"'); - Id = line.Substring(n + 1, n2 - n - 1); - - while ((line = reader.ReadLine()) != null) - { - curLineNum++; - line = line.Trim(); - if (line == "EndProject") - { - return; - } - if (line.StartsWith("ProjectSection", StringComparison.Ordinal)) - { - if (_sections == null) - { - _sections = new SlnSectionCollection(); - } - var sec = new SlnSection(); - _sections.Add(sec); - sec.Read(reader, line, ref curLineNum); - } - } - - throw new InvalidSolutionFormatException( - curLineNum, - LocalizableStrings.ProjectSectionNotClosedError); - } - - private void FindNext(int ln, string line, ref int i, char c) - { - var inputIndex = i; - i = line.IndexOf(c, i); - if (i == -1) - { - throw new InvalidSolutionFormatException( - ln, - string.Format(LocalizableStrings.ProjectParsingErrorFormatString, c, inputIndex)); - } - } - - internal void Write(TextWriter writer) - { - writer.Write("Project(\""); - writer.Write(TypeGuid); - writer.Write("\") = \""); - writer.Write(Name); - writer.Write("\", \""); - writer.Write(PathUtility.GetPathWithBackSlashes(FilePath)); - writer.Write("\", \""); - writer.Write(Id); - writer.WriteLine("\""); - if (_sections != null) - { - foreach (SlnSection s in _sections) - { - s.Write(writer, "ProjectSection"); - } - } - writer.WriteLine("EndProject"); - } - } - - public class SlnSection - { - private SlnPropertySetCollection _nestedPropertySets; - private SlnPropertySet _properties; - private List _sectionLines; - private int _baseIndex; - - public string Id { get; set; } - public int Line { get; private set; } - - internal bool Processed { get; set; } - - public SlnFile ParentFile { get; internal set; } - - public bool IsEmpty - { - get - { - return (_properties == null || _properties.Count == 0) && - (_nestedPropertySets == null || _nestedPropertySets.All(t => t.IsEmpty)) && - (_sectionLines == null || _sectionLines.Count == 0); - } - } - - /// - /// If true, this section won't be written to the file if it is empty - /// - /// true if skip if empty; otherwise, false. - public bool SkipIfEmpty { get; set; } - - public void Clear() - { - _properties = null; - _nestedPropertySets = null; - _sectionLines = null; - } - - public SlnPropertySet Properties - { - get - { - if (_properties == null) - { - _properties = new SlnPropertySet - { - ParentSection = this - }; - if (_sectionLines != null) - { - foreach (var line in _sectionLines) - { - _properties.ReadLine(line, Line); - } - _sectionLines = null; - } - } - return _properties; - } - } - - public SlnPropertySetCollection NestedPropertySets - { - get - { - if (_nestedPropertySets == null) - { - _nestedPropertySets = new SlnPropertySetCollection(this); - if (_sectionLines != null) - { - LoadPropertySets(); - } - } - return _nestedPropertySets; - } - } - - public void SetContent(IEnumerable> lines) - { - _sectionLines = new List(lines.Select(p => p.Key + " = " + p.Value)); - _properties = null; - _nestedPropertySets = null; - } - - public IEnumerable> GetContent() - { - if (_sectionLines != null) - { - return _sectionLines.Select(li => - { - int i = li.IndexOf('='); - if (i != -1) - { - return new KeyValuePair(li.Substring(0, i).Trim(), li.Substring(i + 1).Trim()); - } - else - { - return new KeyValuePair(li.Trim(), ""); - } - }); - } - else - { - return new KeyValuePair[0]; - } - } - - public SlnSectionType SectionType { get; set; } - - private SlnSectionType ToSectionType(int curLineNum, string s) - { - if (s == "preSolution" || s == "preProject") - { - return SlnSectionType.PreProcess; - } - if (s == "postSolution" || s == "postProject") - { - return SlnSectionType.PostProcess; - } - throw new InvalidSolutionFormatException( - curLineNum, - string.Format(LocalizableStrings.InvalidSectionTypeError, s)); - } - - private string FromSectionType(bool isProjectSection, SlnSectionType type) - { - if (type == SlnSectionType.PreProcess) - { - return isProjectSection ? "preProject" : "preSolution"; - } - else - { - return isProjectSection ? "postProject" : "postSolution"; - } - } - - internal void Read(TextReader reader, string line, ref int curLineNum) - { - Line = curLineNum; - int k = line.IndexOf('('); - if (k == -1) - { - throw new InvalidSolutionFormatException( - curLineNum, - LocalizableStrings.SectionIdMissingError); - } - var tag = line.Substring(0, k).Trim(); - var k2 = line.IndexOf(')', k); - if (k2 == -1) - { - throw new InvalidSolutionFormatException( - curLineNum, - LocalizableStrings.SectionIdMissingError); - } - Id = line.Substring(k + 1, k2 - k - 1); - - k = line.IndexOf('=', k2); - SectionType = ToSectionType(curLineNum, line.Substring(k + 1).Trim()); - - var endTag = "End" + tag; - - _sectionLines = new List(); - _baseIndex = ++curLineNum; - while ((line = reader.ReadLine()) != null) - { - curLineNum++; - line = line.Trim(); - if (line == endTag) - { - break; - } - _sectionLines.Add(line); - } - if (line == null) - { - throw new InvalidSolutionFormatException( - curLineNum, - LocalizableStrings.ClosingSectionTagNotFoundError); - } - } - - private void LoadPropertySets() - { - if (_sectionLines != null) - { - SlnPropertySet curSet = null; - for (int n = 0; n < _sectionLines.Count; n++) - { - var line = _sectionLines[n]; - if (string.IsNullOrEmpty(line.Trim())) - { - continue; - } - var i = line.IndexOf('.'); - if (i == -1) - { - throw new InvalidSolutionFormatException( - _baseIndex + n, - string.Format(LocalizableStrings.InvalidPropertySetFormatString, '.')); - } - var id = line.Substring(0, i); - if (curSet == null || id != curSet.Id) - { - curSet = new SlnPropertySet(id); - _nestedPropertySets.Add(curSet); - } - curSet.ReadLine(line.Substring(i + 1), _baseIndex + n); - } - _sectionLines = null; - } - } - - internal void Write(TextWriter writer, string sectionTag) - { - if (SkipIfEmpty && IsEmpty) - { - return; - } - - writer.Write("\t"); - writer.Write(sectionTag); - writer.Write('('); - writer.Write(Id); - writer.Write(") = "); - writer.WriteLine(FromSectionType(sectionTag == "ProjectSection", SectionType)); - if (_sectionLines != null) - { - foreach (var l in _sectionLines) - { - writer.WriteLine("\t\t" + l); - } - } - else if (_properties != null) - { - _properties.Write(writer); - } - else if (_nestedPropertySets != null) - { - foreach (var ps in _nestedPropertySets) - { - ps.Write(writer); - } - } - writer.WriteLine("\tEnd" + sectionTag); - } - } - - /// - /// A collection of properties - /// - public class SlnPropertySet : IDictionary - { - private OrderedDictionary _values = new(); - private bool _isMetadata; - - internal bool Processed { get; set; } - - public SlnFile ParentFile - { - get { return ParentSection != null ? ParentSection.ParentFile : null; } - } - - public SlnSection ParentSection { get; set; } - - /// - /// Text file line of this section in the original file - /// - /// The line. - public int Line { get; private set; } - - internal SlnPropertySet() - { - } - - /// - /// Creates a new property set with the specified ID - /// - /// Identifier. - public SlnPropertySet(string id) - { - Id = id; - } - - internal SlnPropertySet(bool isMetadata) - { - _isMetadata = isMetadata; - } - - public bool IsEmpty - { - get - { - return _values.Count == 0; - } - } - - internal void ReadLine(string line, int currentLine) - { - if (Line == 0) - { - Line = currentLine; - } - int k = line.IndexOf('='); - if (k != -1) - { - var name = line.Substring(0, k).Trim(); - var val = line.Substring(k + 1).Trim(); - _values[name] = val; - } - else - { - line = line.Trim(); - if (!string.IsNullOrWhiteSpace(line)) - { - _values.Add(line, null); - } - } - } - - internal void Write(TextWriter writer) - { - foreach (DictionaryEntry e in _values) - { - if (!_isMetadata) - { - writer.Write("\t\t"); - } - if (Id != null) - { - writer.Write(Id + "."); - } - writer.WriteLine(e.Key + " = " + e.Value); - } - } - - public string Id { get; private set; } - - public string GetValue(string name, string defaultValue = null) - { - string res; - if (TryGetValue(name, out res)) - { - return res; - } - else - { - return defaultValue; - } - } - - public T GetValue(string name) - { - return (T)GetValue(name, typeof(T), default(T)); - } - - public T GetValue(string name, T defaultValue) - { - return (T)GetValue(name, typeof(T), defaultValue); - } - - public object GetValue(string name, Type t, object defaultValue) - { - string val; - if (TryGetValue(name, out val)) - { - if (t == typeof(bool)) - { - return (object)val.Equals("true", StringComparison.OrdinalIgnoreCase); - } - if (t.GetTypeInfo().IsEnum) - { - return Enum.Parse(t, val, true); - } - if (t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - var at = t.GetTypeInfo().GetGenericArguments()[0]; - if (string.IsNullOrEmpty(val)) - { - return null; - } - return Convert.ChangeType(val, at, CultureInfo.InvariantCulture); - - } - return Convert.ChangeType(val, t, CultureInfo.InvariantCulture); - } - else - { - return defaultValue; - } - } - - public void SetValue(string name, string value, string defaultValue = null, bool preserveExistingCase = false) - { - if (value == null && defaultValue == "") - { - value = ""; - } - if (value == defaultValue) - { - // if the value is default, only remove the property if it was not already the default - // to avoid unnecessary project file churn - string res; - if (TryGetValue(name, out res) && - !string.Equals(defaultValue ?? "", - res, preserveExistingCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) - { - Remove(name); - } - return; - } - string currentValue; - if (preserveExistingCase && TryGetValue(name, out currentValue) && - string.Equals(value, currentValue, StringComparison.OrdinalIgnoreCase)) - { - return; - } - _values[name] = value; - } - - public void SetValue(string name, object value, object defaultValue = null) - { - var isDefault = Equals(value, defaultValue); - if (isDefault) - { - // if the value is default, only remove the property if it was not already the default - // to avoid unnecessary project file churn - if (ContainsKey(name) && (defaultValue == null || - !Equals(defaultValue, GetValue(name, defaultValue.GetType(), null)))) - { - Remove(name); - } - return; - } - - if (value is bool) - { - _values[name] = (bool)value ? "TRUE" : "FALSE"; - } - else - { - _values[name] = Convert.ToString(value, CultureInfo.InvariantCulture); - } - } - - void IDictionary.Add(string key, string value) - { - SetValue(key, value); - } - - public bool ContainsKey(string key) - { - return _values.Contains(key); - } - - public bool Remove(string key) - { - var wasThere = _values.Contains(key); - _values.Remove(key); - return wasThere; - } - - public bool TryGetValue(string key, out string value) - { - value = (string)_values[key]; - return value != null; - } - - public string this[string index] - { - get - { - return (string)_values[index]; - } - set - { - _values[index] = value; - } - } - - public ICollection Values - { - get - { - return _values.Values.Cast().ToList(); - } - } - - public ICollection Keys - { - get { return _values.Keys.Cast().ToList(); } - } - - void ICollection>.Add(KeyValuePair item) - { - SetValue(item.Key, item.Value); - } - - public void Clear() - { - _values.Clear(); - } - - internal void ClearExcept(HashSet keys) - { - foreach (var k in _values.Keys.Cast().Except(keys).ToArray()) - { - _values.Remove(k); - } - } - - bool ICollection>.Contains(KeyValuePair item) - { - var val = GetValue(item.Key); - return val == item.Value; - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - foreach (DictionaryEntry de in _values) - { - array[arrayIndex++] = new KeyValuePair((string)de.Key, (string)de.Value); - } - } - - bool ICollection>.Remove(KeyValuePair item) - { - if (((ICollection>)this).Contains(item)) - { - Remove(item.Key); - return true; - } - else - { - return false; - } - } - - public int Count - { - get - { - return _values.Count; - } - } - - internal void SetLines(IEnumerable> lines) - { - _values.Clear(); - foreach (var line in lines) - { - _values[line.Key] = line.Value; - } - } - - bool ICollection>.IsReadOnly - { - get - { - return false; - } - } - - public IEnumerator> GetEnumerator() - { - foreach (DictionaryEntry de in _values) - { - yield return new KeyValuePair((string)de.Key, (string)de.Value); - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - foreach (DictionaryEntry de in _values) - { - yield return new KeyValuePair((string)de.Key, (string)de.Value); - } - } - } - - public class SlnProjectCollection : Collection - { - private SlnFile _parentFile; - - internal SlnFile ParentFile - { - get - { - return _parentFile; - } - set - { - _parentFile = value; - foreach (var it in this) - { - it.ParentFile = _parentFile; - } - } - } - - public SlnProject GetProject(string id) - { - return this.FirstOrDefault(s => s.Id == id); - } - - public SlnProject GetOrCreateProject(string id) - { - var p = this.FirstOrDefault(s => s.Id.Equals(id, StringComparison.OrdinalIgnoreCase)); - if (p == null) - { - p = new SlnProject { Id = id }; - Add(p); - } - return p; - } - - protected override void InsertItem(int index, SlnProject item) - { - base.InsertItem(index, item); - item.ParentFile = ParentFile; - } - - protected override void SetItem(int index, SlnProject item) - { - base.SetItem(index, item); - item.ParentFile = ParentFile; - } - - protected override void RemoveItem(int index) - { - var it = this[index]; - it.ParentFile = null; - base.RemoveItem(index); - } - - protected override void ClearItems() - { - foreach (var it in this) - { - it.ParentFile = null; - } - base.ClearItems(); - } - } - - public class SlnSectionCollection : Collection - { - private SlnFile _parentFile; - - internal SlnFile ParentFile - { - get - { - return _parentFile; - } - set - { - _parentFile = value; - foreach (var it in this) - { - it.ParentFile = _parentFile; - } - } - } - - public SlnSection GetSection(string id) - { - return this.FirstOrDefault(s => s.Id == id); - } - - public SlnSection GetSection(string id, SlnSectionType sectionType) - { - return this.FirstOrDefault(s => s.Id == id && s.SectionType == sectionType); - } - - public SlnSection GetOrCreateSection(string id, SlnSectionType sectionType) - { - if (id == null) - { - throw new ArgumentNullException("id"); - } - var sec = this.FirstOrDefault(s => s.Id == id); - if (sec == null) - { - sec = new SlnSection - { - Id = id, - SectionType = sectionType - }; - Add(sec); - } - return sec; - } - - public void RemoveSection(string id) - { - if (id == null) - { - throw new ArgumentNullException("id"); - } - var s = GetSection(id); - if (s != null) - { - Remove(s); - } - } - - protected override void InsertItem(int index, SlnSection item) - { - base.InsertItem(index, item); - item.ParentFile = ParentFile; - } - - protected override void SetItem(int index, SlnSection item) - { - base.SetItem(index, item); - item.ParentFile = ParentFile; - } - - protected override void RemoveItem(int index) - { - var it = this[index]; - it.ParentFile = null; - base.RemoveItem(index); - } - - protected override void ClearItems() - { - foreach (var it in this) - { - it.ParentFile = null; - } - base.ClearItems(); - } - } - - public class SlnPropertySetCollection : Collection - { - private SlnSection _parentSection; - - internal SlnPropertySetCollection(SlnSection parentSection) - { - _parentSection = parentSection; - } - - public SlnPropertySet GetPropertySet(string id, bool ignoreCase = false) - { - var sc = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - return this.FirstOrDefault(s => s.Id.Equals(id, sc)); - } - - public SlnPropertySet GetOrCreatePropertySet(string id, bool ignoreCase = false) - { - var ps = GetPropertySet(id, ignoreCase); - if (ps == null) - { - ps = new SlnPropertySet(id); - Add(ps); - } - return ps; - } - - protected override void InsertItem(int index, SlnPropertySet item) - { - base.InsertItem(index, item); - item.ParentSection = _parentSection; - } - - protected override void SetItem(int index, SlnPropertySet item) - { - base.SetItem(index, item); - item.ParentSection = _parentSection; - } - - protected override void RemoveItem(int index) - { - var it = this[index]; - it.ParentSection = null; - base.RemoveItem(index); - } - - protected override void ClearItems() - { - foreach (var it in this) - { - it.ParentSection = null; - } - base.ClearItems(); - } - } - - public class InvalidSolutionFormatException : Exception - { - public InvalidSolutionFormatException(string details) - : base(details) - { - } - - public InvalidSolutionFormatException(int line, string details) - : base(string.Format(LocalizableStrings.ErrorMessageFormatString, line, details)) - { - } - } - - public enum SlnSectionType - { - PreProcess, - PostProcess - } -} - diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.cs.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.cs.xlf deleted file mode 100644 index 5d973eb64247..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.cs.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - Několikrát zadaný globální oddíl - - - - Global section not closed - Neuzavřený globální oddíl - - - - Expected file header not found - Nenalezena očekávaná hlavička souboru - - - - Project section not closed - Neuzavřený oddíl projektu - - - - Invalid section type: {0} - Neplatný typ oddílu: {0} - - - - Section id missing - Chybí ID oddílu - - - - Closing section tag not found - Nenalezena koncová značka oddílu - - - - Invalid format in line {0}: {1} - Neplatný formát na řádku {0}: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - V oddíle projektu chybí {0}, když analýza řádku začíná na pozici {1}. - - - - Property set is missing '{0}' - V sadě vlastností chybí {0}. - - - - File header is missing version - Chybějící verze v hlavičce souboru - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.de.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.de.xlf deleted file mode 100644 index 5e6f488308ac..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.de.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - Globaler Abschnitt mehrmals angegeben - - - - Global section not closed - Globaler Abschnitt nicht geschlossen - - - - Expected file header not found - Erwarteter Dateiheader nicht gefunden. - - - - Project section not closed - Projektabschnitt nicht geschlossen - - - - Invalid section type: {0} - Ungültiger Abschnittstyp: {0} - - - - Section id missing - Abschnitts-ID fehlt - - - - Closing section tag not found - Schließendes Abschnittstag nicht gefunden - - - - Invalid format in line {0}: {1} - Ungültiges Format in Zeile {0}: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - Im Projektabschnitt fehlt "{0}" beim Analysieren des Zeilenbeginns in Position {1} - - - - Property set is missing '{0}' - Im Eigenschaftensatz fehlt "{0}" - - - - File header is missing version - Der Dateiheader enthält keine Version. - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.es.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.es.xlf deleted file mode 100644 index 365664c207aa..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.es.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - Sección global especificada más de una vez - - - - Global section not closed - Sección global no cerrada - - - - Expected file header not found - No se encontró el encabezado de archivo esperado - - - - Project section not closed - Sección de proyecto no cerrada - - - - Invalid section type: {0} - Tipo de sección no válido: {0} - - - - Section id missing - Id. de sección omitido - - - - Closing section tag not found - No se encuentra la etiqueta de cierre de la sección - - - - Invalid format in line {0}: {1} - Formato no válido en línea {0}: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - Falta la sección del proyecto "{0}" al analizar la línea que inicia en la posición {1} - - - - Property set is missing '{0}' - A la propiedad establecida le falta "{0}" - - - - File header is missing version - Falta la versión del encabezado de archivo - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.fr.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.fr.xlf deleted file mode 100644 index dd00afbe039e..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.fr.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - Section Global spécifiée plusieurs fois - - - - Global section not closed - Section Global non fermée - - - - Expected file header not found - En-tête de fichier attendu introuvable - - - - Project section not closed - Section Project non fermée - - - - Invalid section type: {0} - Type de section non valide : {0} - - - - Section id missing - ID de section manquant - - - - Closing section tag not found - Balise de fermeture de section introuvable - - - - Invalid format in line {0}: {1} - Format non valide dans la ligne {0} : {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - '{0}' est manquant dans la section de projet lors de l'analyse de la ligne à partir de la position {1} - - - - Property set is missing '{0}' - '{0}' est manquant dans le jeu de propriétés - - - - File header is missing version - Version manquante pour l'en-tête de fichier - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.it.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.it.xlf deleted file mode 100644 index a19ec0313e93..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.it.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - La sezione Global è stata specificata più di una volta - - - - Global section not closed - La sezione Global non è stata chiusa - - - - Expected file header not found - L'intestazione del file prevista non è stata trovata - - - - Project section not closed - La sezione Project non è stata chiusa - - - - Invalid section type: {0} - Tipo di sezione non valido: {0} - - - - Section id missing - Manca l'ID sezione - - - - Closing section tag not found - Il tag di chiusura sessione non è stato trovato - - - - Invalid format in line {0}: {1} - Formato non valido alla riga {0}: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - Nella sezione del progetto manca '{0}' quando si analizza la riga a partire dalla posizione {1} - - - - Property set is missing '{0}' - Nel set di proprietà manca '{0}' - - - - File header is missing version - Nell'intestazione del file manca la versione - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ja.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ja.xlf deleted file mode 100644 index 0f7670d89f28..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ja.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - グローバル セクションが 2 回以上指定されています - - - - Global section not closed - グローバル セクションが閉じられていません - - - - Expected file header not found - 予期されたファイル ヘッダーがありません - - - - Project section not closed - プロジェクト セクションが閉じられていません - - - - Invalid section type: {0} - 無効なセクションの種類: {0} - - - - Section id missing - セクション ID がありません - - - - Closing section tag not found - 終了セクション タグが見つかりません - - - - Invalid format in line {0}: {1} - 行 {0} の形式が無効です: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - 位置 {1} から始まる行の解析中にプロジェクト セクションが見つかりません '{0}' - - - - Property set is missing '{0}' - プロパティ セットが見つかりません '{0}' - - - - File header is missing version - ファイル ヘッダーにバージョンが見つかりません - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ko.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ko.xlf deleted file mode 100644 index 7d8766ad5dda..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ko.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - 전역 섹션이 두 번 이상 지정됨 - - - - Global section not closed - 전역 섹션이 닫히지 않음 - - - - Expected file header not found - 필요한 파일 헤더가 없음 - - - - Project section not closed - 프로젝트 섹션이 닫히지 않음 - - - - Invalid section type: {0} - 잘못된 섹션 유형: {0} - - - - Section id missing - 섹션 ID가 누락됨 - - - - Closing section tag not found - 닫기 섹션 태그를 찾을 수 없음 - - - - Invalid format in line {0}: {1} - {0} 줄에 잘못된 형식: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - {1} 위치에서 시작하는 줄을 구문 분석할 때 프로젝트 섹션에 '{0}'이(가) 없습니다. - - - - Property set is missing '{0}' - 속성 설정에 '{0}'이(가) 없습니다. - - - - File header is missing version - 파일 헤더에 버전이 없습니다. - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pl.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pl.xlf deleted file mode 100644 index 957ee2041b72..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pl.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - Sekcja globalna została określona więcej niż raz - - - - Global section not closed - Nie zamknięto sekcji globalnej - - - - Expected file header not found - Nie znaleziono oczekiwanego nagłówka pliku - - - - Project section not closed - Nie zamknięto sekcji projektu - - - - Invalid section type: {0} - Nieprawidłowy typ sekcji: {0} - - - - Section id missing - Brak identyfikatora sekcji - - - - Closing section tag not found - Nie odnaleziono tagu zamykającego sekcję - - - - Invalid format in line {0}: {1} - Nieprawidłowy format w wierszu {0}: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - W sekcji projektu brakuje elementu „{0}” podczas analizowania wiersza, począwszy od pozycji {1} - - - - Property set is missing '{0}' - W zestawie właściwości brakuje elementu „{0}” - - - - File header is missing version - Brak wersji nagłówka pliku - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pt-BR.xlf deleted file mode 100644 index 877faf844fb7..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pt-BR.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - Seção global especificada mais de uma vez - - - - Global section not closed - Seção global não fechada - - - - Expected file header not found - Cabeçalho do arquivo esperado não encontrado - - - - Project section not closed - Seção de projeto não fechada - - - - Invalid section type: {0} - Tipo de seção inválido: {0} - - - - Section id missing - ID de sessão ausente - - - - Closing section tag not found - Marca de fechamento de seção não encontrada - - - - Invalid format in line {0}: {1} - Formato inválido na linha {0}: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - A seção do projeto está sem o '{0}' ao analisar a linha na posição {1} - - - - Property set is missing '{0}' - O conjunto de propriedade está sem '{0}' - - - - File header is missing version - O cabeçalho do arquivo está sem a versão - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ru.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ru.xlf deleted file mode 100644 index 7caa01a43eef..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ru.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - Глобальный раздел указан более одного раза. - - - - Global section not closed - Глобальный раздел не закрыт. - - - - Expected file header not found - Отсутствует требуемый заголовок файла - - - - Project section not closed - Раздел проекта не закрыт. - - - - Invalid section type: {0} - Недопустимый тип раздела: {0} - - - - Section id missing - Отсутствует идентификатор раздела. - - - - Closing section tag not found - Закрывающий тег раздела не найден. - - - - Invalid format in line {0}: {1} - Формат в строке {0} недопустим: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - В разделе проекта отсутствует "{0}" при анализе строки с начальной позицией {1} - - - - Property set is missing '{0}' - В наборе свойств отсутствует "{0}" - - - - File header is missing version - В заголовке файла отсутствует версия - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.tr.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.tr.xlf deleted file mode 100644 index a7c5cba6b88b..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.tr.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - Genel bölüm birden çok kez belirtilmiş - - - - Global section not closed - Genel bölüm kapatılmadı - - - - Expected file header not found - Beklenen dosya üst bilgisi bulunamadı - - - - Project section not closed - Proje bölümü kapatılmadı - - - - Invalid section type: {0} - Geçersiz bölüm türü: {0} - - - - Section id missing - Bölüm kimliği eksik - - - - Closing section tag not found - Kapatma bölümü etiketi bulunamadı - - - - Invalid format in line {0}: {1} - {0}. satırda geçersiz biçim: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - {1}. konumdan başlayan satır ayrıştırılırken proje bölümünde '{0}' eksik - - - - Property set is missing '{0}' - Özellik kümesinde '{0}' eksik - - - - File header is missing version - Dosya üst bilgisinde sürüm eksik - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hans.xlf deleted file mode 100644 index b353d12475e4..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hans.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - 多次指定了全局节 - - - - Global section not closed - 未关闭全局节 - - - - Expected file header not found - 未找到必需的文件头 - - - - Project section not closed - 未关闭项目节 - - - - Invalid section type: {0} - 无效的节类型: {0} - - - - Section id missing - 缺少节 ID - - - - Closing section tag not found - 未找到结束节标记 - - - - Invalid format in line {0}: {1} - 行 {0} 存在无效格式: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - 分析从位置 {1} 开始的行时,项目节缺少“{0}” - - - - Property set is missing '{0}' - 属性集缺少“{0}” - - - - File header is missing version - 文件头缺少版本 - - - - - \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hant.xlf deleted file mode 100644 index 7fef8a4f8621..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hant.xlf +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Global section specified more than once - 全域區段指定了一次以上 - - - - Global section not closed - 全域區段未關閉 - - - - Expected file header not found - 找不到預期的檔案標頭 - - - - Project section not closed - 專案區段未關閉 - - - - Invalid section type: {0} - 區段類型無效: {0} - - - - Section id missing - 遺漏區段識別碼 - - - - Closing section tag not found - 找不到關閉區段標記 - - - - Invalid format in line {0}: {1} - 第 {0} 行的格式不正確: {1} - - - - Project section is missing '{0}' when parsing the line starting at position {1} - 從位置 {1} 開始剖析程式行時,專案區段缺少 '{0}' - - - - Property set is missing '{0}' - 屬性集缺少 '{0}' - - - - File header is missing version - 檔案標頭遺漏版本 - - - - - \ No newline at end of file diff --git a/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs b/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs deleted file mode 100644 index 910eb8685f28..000000000000 --- a/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.cs +++ /dev/null @@ -1,530 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace Microsoft.DotNet.Cli.Sln.Internal.Tests -{ - public class GivenAnSlnFile : SdkTest - { - private const string SolutionModified = @" -Microsoft Visual Studio Solution File, Format Version 14.00 -# Visual Studio 16 -VisualStudioVersion = 16.0.26006.2 -MinimumVisualStudioVersion = 11.0.40219.1 -Project(""{7072A694-548F-4CAE-A58F-12D257D5F486}"") = ""AppModified"", ""AppModified\AppModified.csproj"", ""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.ActiveCfg = Debug|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.Build.0 = Debug|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.ActiveCfg = Debug|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.Build.0 = Debug|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.Build.0 = Release|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.ActiveCfg = Release|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.Build.0 = Release|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = TRUE - EndGlobalSection -EndGlobal -"; - - private const string SolutionWithAppAndLibProjects = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26006.2 -MinimumVisualStudioVersion = 10.0.40219.1 -Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""App"", ""App\App.csproj"", ""{7072A694-548F-4CAE-A58F-12D257D5F486}"" -EndProject -Project(""{13B669BE-BB05-4DDF-9536-439F39A36129}"") = ""Lib"", ""..\Lib\Lib.csproj"", ""{21D9159F-60E6-4F65-BC6B-D01B71B15FFC}"" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.ActiveCfg = Debug|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x64.Build.0 = Debug|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.ActiveCfg = Debug|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Debug|x86.Build.0 = Debug|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|Any CPU.Build.0 = Release|Any CPU - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.ActiveCfg = Release|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x64.Build.0 = Release|x64 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.ActiveCfg = Release|x86 - {7072A694-548F-4CAE-A58F-12D257D5F486}.Release|x86.Build.0 = Release|x86 - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|x64.ActiveCfg = Debug|x64 - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|x64.Build.0 = Debug|x64 - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|x86.ActiveCfg = Debug|x86 - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Debug|x86.Build.0 = Debug|x86 - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|Any CPU.Build.0 = Release|Any CPU - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|x64.ActiveCfg = Release|x64 - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|x64.Build.0 = Release|x64 - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|x86.ActiveCfg = Release|x86 - {21D9159F-60E6-4F65-BC6B-D01B71B15FFC}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal -"; - - public GivenAnSlnFile(ITestOutputHelper log) : base(log) - { - } - - private string CreateFile([CallerMemberName] string callerName = null, string identifier = null) - { - var folder = _testAssetsManager.CreateTestDirectory(testName: callerName + identifier); - var filename = Path.Combine(folder.Path, Guid.NewGuid().ToString() + ".tmp"); - using (new FileStream(filename, FileMode.CreateNew)) { } - return filename; - } - - - [Fact] - public void WhenGivenAValidSlnFileItReadsAndVerifiesContents() - { - var tmpFile = CreateFile(); - File.WriteAllText(tmpFile, SolutionWithAppAndLibProjects); - - SlnFile slnFile = SlnFile.Read(tmpFile); - - Console.WriteLine(new - { - slnFile_FormatVersion = slnFile.FormatVersion, - slnFile_ProductDescription = slnFile.ProductDescription, - slnFile_VisualStudioVersion = slnFile.VisualStudioVersion, - slnFile_MinimumVisualStudioVersion = slnFile.MinimumVisualStudioVersion, - slnFile_BaseDirectory = slnFile.BaseDirectory, - slnFile_FullPath = slnFile.FullPath, - tmpFilePath = tmpFile - }.ToString()); - - slnFile.FormatVersion.Should().Be("12.00"); - slnFile.ProductDescription.Should().Be("Visual Studio 15"); - slnFile.VisualStudioVersion.Should().Be("15.0.26006.2"); - slnFile.MinimumVisualStudioVersion.Should().Be("10.0.40219.1"); - slnFile.BaseDirectory.Should().Be(Path.GetDirectoryName(tmpFile)); - slnFile.FullPath.Should().Be(Path.GetFullPath(tmpFile)); - - slnFile.Projects.Count.Should().Be(2); - var project = slnFile.Projects[0]; - project.Id.Should().Be("{7072A694-548F-4CAE-A58F-12D257D5F486}"); - project.TypeGuid.Should().Be("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"); - project.Name.Should().Be("App"); - project.FilePath.Should().Be(Path.Combine("App", "App.csproj")); - project = slnFile.Projects[1]; - project.Id.Should().Be("{21D9159F-60E6-4F65-BC6B-D01B71B15FFC}"); - project.TypeGuid.Should().Be("{13B669BE-BB05-4DDF-9536-439F39A36129}"); - project.Name.Should().Be("Lib"); - project.FilePath.Should().Be(Path.Combine("..", "Lib", "Lib.csproj")); - - slnFile.SolutionConfigurationsSection.Count.Should().Be(6); - slnFile.SolutionConfigurationsSection - .GetValue("Debug|Any CPU", string.Empty) - .Should().Be("Debug|Any CPU"); - slnFile.SolutionConfigurationsSection - .GetValue("Debug|x64", string.Empty) - .Should().Be("Debug|x64"); - slnFile.SolutionConfigurationsSection - .GetValue("Debug|x86", string.Empty) - .Should().Be("Debug|x86"); - slnFile.SolutionConfigurationsSection - .GetValue("Release|Any CPU", string.Empty) - .Should().Be("Release|Any CPU"); - slnFile.SolutionConfigurationsSection - .GetValue("Release|x64", string.Empty) - .Should().Be("Release|x64"); - slnFile.SolutionConfigurationsSection - .GetValue("Release|x86", string.Empty) - .Should().Be("Release|x86"); - - slnFile.ProjectConfigurationsSection.Count.Should().Be(2); - var projectConfigSection = slnFile - .ProjectConfigurationsSection - .GetPropertySet("{7072A694-548F-4CAE-A58F-12D257D5F486}"); - projectConfigSection.Count.Should().Be(12); - projectConfigSection - .GetValue("Debug|Any CPU.ActiveCfg", string.Empty) - .Should().Be("Debug|Any CPU"); - projectConfigSection - .GetValue("Debug|Any CPU.Build.0", string.Empty) - .Should().Be("Debug|Any CPU"); - projectConfigSection - .GetValue("Debug|x64.ActiveCfg", string.Empty) - .Should().Be("Debug|x64"); - projectConfigSection - .GetValue("Debug|x64.Build.0", string.Empty) - .Should().Be("Debug|x64"); - projectConfigSection - .GetValue("Debug|x86.ActiveCfg", string.Empty) - .Should().Be("Debug|x86"); - projectConfigSection - .GetValue("Debug|x86.Build.0", string.Empty) - .Should().Be("Debug|x86"); - projectConfigSection - .GetValue("Release|Any CPU.ActiveCfg", string.Empty) - .Should().Be("Release|Any CPU"); - projectConfigSection - .GetValue("Release|Any CPU.Build.0", string.Empty) - .Should().Be("Release|Any CPU"); - projectConfigSection - .GetValue("Release|x64.ActiveCfg", string.Empty) - .Should().Be("Release|x64"); - projectConfigSection - .GetValue("Release|x64.Build.0", string.Empty) - .Should().Be("Release|x64"); - projectConfigSection - .GetValue("Release|x86.ActiveCfg", string.Empty) - .Should().Be("Release|x86"); - projectConfigSection - .GetValue("Release|x86.Build.0", string.Empty) - .Should().Be("Release|x86"); - projectConfigSection = slnFile - .ProjectConfigurationsSection - .GetPropertySet("{21D9159F-60E6-4F65-BC6B-D01B71B15FFC}"); - projectConfigSection.Count.Should().Be(12); - projectConfigSection - .GetValue("Debug|Any CPU.ActiveCfg", string.Empty) - .Should().Be("Debug|Any CPU"); - projectConfigSection - .GetValue("Debug|Any CPU.Build.0", string.Empty) - .Should().Be("Debug|Any CPU"); - projectConfigSection - .GetValue("Debug|x64.ActiveCfg", string.Empty) - .Should().Be("Debug|x64"); - projectConfigSection - .GetValue("Debug|x64.Build.0", string.Empty) - .Should().Be("Debug|x64"); - projectConfigSection - .GetValue("Debug|x86.ActiveCfg", string.Empty) - .Should().Be("Debug|x86"); - projectConfigSection - .GetValue("Debug|x86.Build.0", string.Empty) - .Should().Be("Debug|x86"); - projectConfigSection - .GetValue("Release|Any CPU.ActiveCfg", string.Empty) - .Should().Be("Release|Any CPU"); - projectConfigSection - .GetValue("Release|Any CPU.Build.0", string.Empty) - .Should().Be("Release|Any CPU"); - projectConfigSection - .GetValue("Release|x64.ActiveCfg", string.Empty) - .Should().Be("Release|x64"); - projectConfigSection - .GetValue("Release|x64.Build.0", string.Empty) - .Should().Be("Release|x64"); - projectConfigSection - .GetValue("Release|x86.ActiveCfg", string.Empty) - .Should().Be("Release|x86"); - projectConfigSection - .GetValue("Release|x86.Build.0", string.Empty) - .Should().Be("Release|x86"); - - slnFile.Sections.Count.Should().Be(3); - var solutionPropertiesSection = slnFile.Sections.GetSection("SolutionProperties"); - solutionPropertiesSection.Properties.Count.Should().Be(1); - solutionPropertiesSection.Properties - .GetValue("HideSolutionNode", string.Empty) - .Should().Be("FALSE"); - } - - [Fact] - public void WhenGivenAValidReadOnlySlnFileItReadsContentsWithNoException() - { - var tmpFile = CreateFile(); - File.WriteAllText(tmpFile, SolutionWithAppAndLibProjects); - var attr = File.GetAttributes(tmpFile); - attr = attr | FileAttributes.ReadOnly; - File.SetAttributes(tmpFile, attr); - - Action act = () => SlnFile.Read(tmpFile); - act.Should().NotThrow("Because readonly file is not being modified."); - } - - [Fact] - public void WhenGivenAValidSlnFileItModifiesSavesAndVerifiesContents() - { - var tmpFile = CreateFile(); - File.WriteAllText(tmpFile, SolutionWithAppAndLibProjects); - - SlnFile slnFile = SlnFile.Read(tmpFile); - - slnFile.FormatVersion = "14.00"; - slnFile.ProductDescription = "Visual Studio 16"; - slnFile.VisualStudioVersion = "16.0.26006.2"; - slnFile.MinimumVisualStudioVersion = "11.0.40219.1"; - - slnFile.Projects.Count.Should().Be(2); - var project = slnFile.Projects[0]; - project.Id = "{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"; - project.TypeGuid = "{7072A694-548F-4CAE-A58F-12D257D5F486}"; - project.Name = "AppModified"; - project.FilePath = Path.Combine("AppModified", "AppModified.csproj"); - slnFile.Projects.Remove(slnFile.Projects[1]); - - slnFile.SolutionConfigurationsSection.Count.Should().Be(6); - slnFile.SolutionConfigurationsSection.Remove("Release|Any CPU"); - slnFile.SolutionConfigurationsSection.Remove("Release|x64"); - slnFile.SolutionConfigurationsSection.Remove("Release|x86"); - - slnFile.ProjectConfigurationsSection.Count.Should().Be(2); - var projectConfigSection = slnFile - .ProjectConfigurationsSection - .GetPropertySet("{21D9159F-60E6-4F65-BC6B-D01B71B15FFC}"); - slnFile.ProjectConfigurationsSection.Remove(projectConfigSection); - - slnFile.Sections.Count.Should().Be(3); - var solutionPropertiesSection = slnFile.Sections.GetSection("SolutionProperties"); - solutionPropertiesSection.Properties.Count.Should().Be(1); - solutionPropertiesSection.Properties.SetValue("HideSolutionNode", "TRUE"); - - slnFile.Write(); - - File.ReadAllText(tmpFile) - .Should().Be(SolutionModified); - } - - [Theory] - [InlineData("Microsoft Visual Studio Solution File, Format Version ", 1)] - [InlineData("First Line\nMicrosoft Visual Studio Solution File, Format Version ", 2)] - [InlineData("First Line\nMicrosoft Visual Studio Solution File, Format Version \nThird Line", 2)] - [InlineData("First Line\nSecondLine\nMicrosoft Visual Studio Solution File, Format Version \nFourth Line", 3)] - public void WhenGivenASolutionWithMissingHeaderVersionItThrows(string fileContents, int lineNum) - { - var tmpFile = CreateFile(identifier: fileContents.GetHashCode().ToString()); - File.WriteAllText(tmpFile, fileContents); - - Action action = () => - { - SlnFile.Read(tmpFile); - }; - - action.Should().Throw() - .WithMessage(FormatError(lineNum, LocalizableStrings.FileHeaderMissingVersionError)); - } - - [Theory] - [InlineData("Invalid Solution")] - [InlineData("Invalid Solution\nSpanning Multiple Lines")] - [InlineData("Microsoft Visual\nStudio Solution File,\nFormat Version ")] - public void WhenGivenASolutionWithMissingHeaderItThrows(string fileContents) - { - var tmpFile = CreateFile(identifier: fileContents.GetHashCode().ToString()); - File.WriteAllText(tmpFile, fileContents); - - Action action = () => - { - SlnFile.Read(tmpFile); - }; - - action.Should().Throw() - .WithMessage(LocalizableStrings.FileHeaderMissingError); - } - - [Fact] - public void WhenGivenASolutionWithMultipleGlobalSectionsItThrows() - { - const string SolutionFile = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -Global -EndGlobal -Global -EndGlobal -"; - var tmpFile = CreateFile(); - File.WriteAllText(tmpFile, SolutionFile); - - Action action = () => - { - SlnFile.Read(tmpFile); - }; - - action.Should().Throw() - .WithMessage(FormatError(5, LocalizableStrings.GlobalSectionMoreThanOnceError)); - } - - [Fact] - public void WhenGivenASolutionWithGlobalSectionNotClosedItThrows() - { - const string SolutionFile = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -Global -"; - var tmpFile = CreateFile(); - File.WriteAllText(tmpFile, SolutionFile); - - Action action = () => - { - SlnFile.Read(tmpFile); - }; - - action.Should().Throw() - .WithMessage(FormatError(3, LocalizableStrings.GlobalSectionNotClosedError)); - } - - [Fact] - public void WhenGivenASolutionWithProjectSectionNotClosedItThrows() - { - const string SolutionFile = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""App"", ""App\App.csproj"", ""{7072A694-548F-4CAE-A58F-12D257D5F486}"" -"; - var tmpFile = CreateFile(); - File.WriteAllText(tmpFile, SolutionFile); - - Action action = () => - { - SlnFile.Read(tmpFile); - }; - - action.Should().Throw() - .WithMessage(FormatError(3, LocalizableStrings.ProjectSectionNotClosedError)); - } - - [Fact] - public void WhenGivenASolutionWithInvalidProjectSectionItThrows() - { - const string SolutionFile = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -Project""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""App"", ""App\App.csproj"", ""{7072A694-548F-4CAE-A58F-12D257D5F486}"" -EndProject -"; - - var tmpFile = CreateFile(); - File.WriteAllText(tmpFile, SolutionFile); - - Action action = () => - { - SlnFile.Read(tmpFile); - }; - - action.Should().Throw() - .WithMessage(FormatError(3, LocalizableStrings.ProjectParsingErrorFormatString, "(", 0)); - } - - [Fact] - public void WhenGivenASolutionWithInvalidSectionTypeItThrows() - { - const string SolutionFile = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -Global - GlobalSection(SolutionConfigurationPlatforms) = thisIsUnknown - EndGlobalSection -EndGlobal -"; - var tmpFile = CreateFile(); - File.WriteAllText(tmpFile, SolutionFile); - - Action action = () => - { - SlnFile.Read(tmpFile); - }; - - action.Should().Throw() - .WithMessage(FormatError(4, LocalizableStrings.InvalidSectionTypeError, "thisIsUnknown")); - } - - [Fact] - public void WhenGivenASolutionWithMissingSectionIdTypeItThrows() - { - const string SolutionFile = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -Global - GlobalSection = preSolution - EndGlobalSection -EndGlobal -"; - var tmpFile = CreateFile(); - File.WriteAllText(tmpFile, SolutionFile); - - Action action = () => - { - SlnFile.Read(tmpFile); - }; - - action.Should().Throw() - .WithMessage(FormatError(4, LocalizableStrings.SectionIdMissingError)); - } - - [Fact] - public void WhenGivenASolutionWithSectionNotClosedItThrows() - { - const string SolutionFile = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution -EndGlobal -"; - var tmpFile = CreateFile(); - File.WriteAllText(tmpFile, SolutionFile); - - Action action = () => - { - SlnFile.Read(tmpFile); - }; - - action.Should().Throw() - .WithMessage(FormatError(6, LocalizableStrings.ClosingSectionTagNotFoundError)); - } - - [Fact] - public void WhenGivenASolutionWithInvalidPropertySetItThrows() - { - const string SolutionFile = @" -Microsoft Visual Studio Solution File, Format Version 12.00 -Project(""{7072A694-548F-4CAE-A58F-12D257D5F486}"") = ""AppModified"", ""AppModified\AppModified.csproj"", ""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"" -EndProject -Global - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7072A694-548F-4CAE-A58F-12D257D5F486} Debug|Any CPU ActiveCfg = Debug|Any CPU - EndGlobalSection -EndGlobal -"; - var tmpFile = CreateFile(); - File.WriteAllText(tmpFile, SolutionFile); - - Action action = () => - { - var slnFile = SlnFile.Read(tmpFile); - if (slnFile.ProjectConfigurationsSection.Count == 0) - { - // Need to force loading of nested property sets - } - }; - - action.Should().Throw() - .WithMessage(FormatError(7, LocalizableStrings.InvalidPropertySetFormatString, ".")); - } - - private static string FormatError(int line, string format, params object[] args) - { - return string.Format( - LocalizableStrings.ErrorMessageFormatString, - line, - string.Format(format, args)); - } - } -} diff --git a/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.csproj b/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.csproj deleted file mode 100644 index ac047650cc58..000000000000 --- a/test/Microsoft.DotNet.Cli.Sln.Internal.Tests/Microsoft.DotNet.Cli.Sln.Internal.Tests.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - Tests\$(MSBuildProjectName) - - - - - - $(ToolsetTargetFramework) - Exe - MicrosoftAspNetCore - - - - - - - - - From 24b44dcd437da77943407b67dc67e5541a9d7ae9 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Tue, 7 Jan 2025 09:20:19 -0800 Subject: [PATCH 15/27] Look for .sln and .slnx files --- .../dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs | 7 +++++-- src/Cli/dotnet/ReleasePropertyProjectLocator.cs | 5 ++++- .../dotnet-list-package/ListPackageReferencesCommand.cs | 5 ++++- .../dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs | 5 ++++- .../dotnet-workload/restore/WorkloadRestoreCommand.cs | 5 ++++- src/WebSdk/Publish/Tasks/WebConfigTelemetry.cs | 5 ++++- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/BuiltInTools/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs b/src/BuiltInTools/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs index f654dade542c..4bdc9001568a 100644 --- a/src/BuiltInTools/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs +++ b/src/BuiltInTools/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs @@ -76,8 +76,11 @@ private static (bool isSolution, string workspacePath) FindFile(string workspace return (isSolution, workspacePath); } - private static IEnumerable FindSolutionFiles(string basePath) => Directory.EnumerateFileSystemEntries(basePath, "*.sln", SearchOption.TopDirectoryOnly) - .Concat(Directory.EnumerateFileSystemEntries(basePath, "*.slnf", SearchOption.TopDirectoryOnly)); + private static IEnumerable FindSolutionFiles(string basePath) => [ + ..Directory.EnumerateFileSystemEntries(basePath, "*.sln", SearchOption.TopDirectoryOnly), + ..Directory.EnumerateFileSystemEntries(basePath, "*.slnf", SearchOption.TopDirectoryOnly), + ..Directory.EnumerateFileSystemEntries(basePath, "*.slnx", SearchOption.TopDirectoryOnly) + ]; private static IEnumerable FindProjectFiles(string basePath) => Directory.EnumerateFileSystemEntries(basePath, "*.*proj", SearchOption.TopDirectoryOnly) .Where(f => !DnxProjectExtension.Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase)); diff --git a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs index 0670ce36db15..cb55a3878339 100644 --- a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs +++ b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs @@ -131,7 +131,10 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() } catch (GracefulException) // Fall back to looking for a solution if multiple project files are found, or there's no project in the directory. { - string? potentialSln = Directory.GetFiles(arg, "*.sln", SearchOption.TopDirectoryOnly).FirstOrDefault(); + string? potentialSln = ((string[])[ + ..Directory.GetFiles(arg, "*.sln", SearchOption.TopDirectoryOnly), + ..Directory.GetFiles(arg, "*.slnx", SearchOption.TopDirectoryOnly), + ]).FirstOrDefault(); if (!string.IsNullOrEmpty(potentialSln)) { diff --git a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs index 9ca3b27a500f..063572c8aa2f 100644 --- a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs @@ -71,7 +71,10 @@ private string GetProjectOrSolution() if (Directory.Exists(resultPath)) { - var possibleSolutionPath = Directory.GetFiles(resultPath, "*.sln", SearchOption.TopDirectoryOnly); + string[] possibleSolutionPath = [ + ..Directory.GetFiles(resultPath, "*.sln", SearchOption.TopDirectoryOnly), + ..Directory.GetFiles(resultPath, "*.slnx", SearchOption.TopDirectoryOnly) + ]; //If more than a single sln file is found, an error is thrown since we can't determine which one to choose. if (possibleSolutionPath.Count() > 1) diff --git a/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs b/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs index 63afaeb19965..ec83cfe4c250 100644 --- a/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs +++ b/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs @@ -27,7 +27,10 @@ public DotnetSlnPostActionProcessor(Func, string?, internal static IReadOnlyList FindSolutionFilesAtOrAbovePath(IPhysicalFileSystem fileSystem, string outputBasePath) { - return FileFindHelpers.FindFilesAtOrAbovePath(fileSystem, outputBasePath, "*.sln"); + return [ + ..FileFindHelpers.FindFilesAtOrAbovePath(fileSystem, outputBasePath, "*.sln"), + ..FileFindHelpers.FindFilesAtOrAbovePath(fileSystem, outputBasePath, "*.slnx") + ]; } // The project files to add are a subset of the primary outputs, specifically the primary outputs indicated by the primaryOutputIndexes post action argument (semicolon separated) diff --git a/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs index 41fa1049bf3b..0054a570a87b 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs @@ -122,7 +122,10 @@ internal static List DiscoverAllProjects(string currentDirectory, var projectFiles = new List(); if (slnOrProjectArgument == null || !slnOrProjectArgument.Any()) { - slnFiles = Directory.GetFiles(currentDirectory, "*.sln").ToList(); + slnFiles = ((string[])[ + ..Directory.GetFiles(currentDirectory, "*.sln"), + ..Directory.GetFiles(currentDirectory, "*.slnx") + ]).ToList(); projectFiles.AddRange(Directory.GetFiles(currentDirectory, "*.*proj")); } else diff --git a/src/WebSdk/Publish/Tasks/WebConfigTelemetry.cs b/src/WebSdk/Publish/Tasks/WebConfigTelemetry.cs index 80e0e4385e31..f6660fbd72ff 100644 --- a/src/WebSdk/Publish/Tasks/WebConfigTelemetry.cs +++ b/src/WebSdk/Publish/Tasks/WebConfigTelemetry.cs @@ -66,7 +66,10 @@ public static string GetProjectGuidFromSolutionFile(string solutionFileFullPath, return null; } - IEnumerable solutionFiles = Directory.EnumerateFiles(solutionDirectory, "*.sln", SearchOption.TopDirectoryOnly); + IEnumerable solutionFiles = [ + ..Directory.EnumerateFiles(solutionDirectory, "*.sln", SearchOption.TopDirectoryOnly), + ..Directory.EnumerateFiles(solutionDirectory, "*.slnx", SearchOption.TopDirectoryOnly) + ]; foreach (string solutionFile in solutionFiles) { string projectGuid = GetProjectGuid(solutionFile, projectFileFullPath); From 485a16a0609728b08c171d4cf11b87e193dc55f0 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Wed, 8 Jan 2025 15:04:14 -0800 Subject: [PATCH 16/27] Centralize looking for sln(x/f) in directory --- src/Cli/dotnet/ReleasePropertyProjectLocator.cs | 5 +---- src/Cli/dotnet/SlnFileFactory.cs | 9 +++++++++ .../dotnet-list-package/ListPackageReferencesCommand.cs | 6 ++---- .../dotnet-workload/restore/WorkloadRestoreCommand.cs | 6 ++---- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs index cb55a3878339..27fb4611f94b 100644 --- a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs +++ b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs @@ -131,10 +131,7 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() } catch (GracefulException) // Fall back to looking for a solution if multiple project files are found, or there's no project in the directory. { - string? potentialSln = ((string[])[ - ..Directory.GetFiles(arg, "*.sln", SearchOption.TopDirectoryOnly), - ..Directory.GetFiles(arg, "*.slnx", SearchOption.TopDirectoryOnly), - ]).FirstOrDefault(); + string? potentialSln = SlnFileFactory.ListSolutionFilesInDirectory(arg).FirstOrDefault(); if (!string.IsNullOrEmpty(potentialSln)) { diff --git a/src/Cli/dotnet/SlnFileFactory.cs b/src/Cli/dotnet/SlnFileFactory.cs index e84e965a8cae..8178db926392 100644 --- a/src/Cli/dotnet/SlnFileFactory.cs +++ b/src/Cli/dotnet/SlnFileFactory.cs @@ -10,6 +10,15 @@ namespace Microsoft.DotNet.Tools.Common { public static class SlnFileFactory { + public static string[] ListSolutionFilesInDirectory(string directory, bool includeSolutionFilterFiles = false) + { + return [ + ..Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly), + ..Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly), + ..(includeSolutionFilterFiles ? Directory.GetFiles(directory, "*.slnf", SearchOption.TopDirectoryOnly) : Array.Empty()) + ]; + } + public static SolutionModel CreateFromFileOrDirectory(string fileOrDirectory) { if (File.Exists(fileOrDirectory)) diff --git a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs index 063572c8aa2f..b3a3ce5f09c7 100644 --- a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs @@ -4,6 +4,7 @@ using System.CommandLine; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Tools.Common; using Microsoft.DotNet.Tools.NuGet; namespace Microsoft.DotNet.Tools.List.PackageReferences @@ -71,10 +72,7 @@ private string GetProjectOrSolution() if (Directory.Exists(resultPath)) { - string[] possibleSolutionPath = [ - ..Directory.GetFiles(resultPath, "*.sln", SearchOption.TopDirectoryOnly), - ..Directory.GetFiles(resultPath, "*.slnx", SearchOption.TopDirectoryOnly) - ]; + string[] possibleSolutionPath = SlnFileFactory.ListSolutionFilesInDirectory(resultPath).ToArray(); //If more than a single sln file is found, an error is thrown since we can't determine which one to choose. if (possibleSolutionPath.Count() > 1) diff --git a/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs index 0054a570a87b..d5db66ad1ee9 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs @@ -8,6 +8,7 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.NuGetPackageDownloader; using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Tools.Common; using Microsoft.DotNet.Workloads.Workload.Install; using Microsoft.DotNet.Workloads.Workload.Update; using Microsoft.Extensions.EnvironmentAbstractions; @@ -122,10 +123,7 @@ internal static List DiscoverAllProjects(string currentDirectory, var projectFiles = new List(); if (slnOrProjectArgument == null || !slnOrProjectArgument.Any()) { - slnFiles = ((string[])[ - ..Directory.GetFiles(currentDirectory, "*.sln"), - ..Directory.GetFiles(currentDirectory, "*.slnx") - ]).ToList(); + slnFiles = SlnFileFactory.ListSolutionFilesInDirectory(currentDirectory).ToList(); projectFiles.AddRange(Directory.GetFiles(currentDirectory, "*.*proj")); } else From e0477853371093e05248c021642bbde3f438c1af Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Fri, 10 Jan 2025 08:47:35 -0800 Subject: [PATCH 17/27] Nit: Pr comments --- src/Cli/dotnet/ProjectInstanceExtensions.cs | 1 - src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Cli/dotnet/ProjectInstanceExtensions.cs b/src/Cli/dotnet/ProjectInstanceExtensions.cs index 67fddac7f032..eea4778316d3 100644 --- a/src/Cli/dotnet/ProjectInstanceExtensions.cs +++ b/src/Cli/dotnet/ProjectInstanceExtensions.cs @@ -21,7 +21,6 @@ public static string GetDefaultProjectTypeGuid(this ProjectInstance projectInsta string projectTypeGuid = projectInstance.GetPropertyValue("DefaultProjectTypeGuid"); if (string.IsNullOrEmpty(projectTypeGuid) && projectInstance.FullPath.EndsWith(".shproj", StringComparison.OrdinalIgnoreCase)) { - // TODO: Centralize project type guids projectTypeGuid = "{D954291E-2A0B-460D-934E-DC6B0785DB48}"; } return projectTypeGuid; diff --git a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs index f771a5c234b7..766ce64d9b7c 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs @@ -78,7 +78,6 @@ public override int Execute() { throw new GracefulException(CommonLocalizableStrings.InvalidSolutionFormatString, solutionFileFullPath, ex.Message); } - // TODO: Check if (ex.InnerException is GracefulException) { throw ex.InnerException; From e6b5114474d6b4ca1b621aa0f834599749b8e6f2 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Fri, 10 Jan 2025 12:01:56 -0800 Subject: [PATCH 18/27] Dont look for slnx by default --- src/Cli/dotnet/ReleasePropertyProjectLocator.cs | 2 +- src/Cli/dotnet/SlnFileFactory.cs | 4 ++-- .../dotnet-list-package/ListPackageReferencesCommand.cs | 2 +- .../dotnet-workload/restore/WorkloadRestoreCommand.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs index 27fb4611f94b..1f287fc00594 100644 --- a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs +++ b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs @@ -131,7 +131,7 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() } catch (GracefulException) // Fall back to looking for a solution if multiple project files are found, or there's no project in the directory. { - string? potentialSln = SlnFileFactory.ListSolutionFilesInDirectory(arg).FirstOrDefault(); + string? potentialSln = SlnFileFactory.ListSolutionFilesInDirectory(arg, false, false).FirstOrDefault(); if (!string.IsNullOrEmpty(potentialSln)) { diff --git a/src/Cli/dotnet/SlnFileFactory.cs b/src/Cli/dotnet/SlnFileFactory.cs index 8178db926392..48161c71a316 100644 --- a/src/Cli/dotnet/SlnFileFactory.cs +++ b/src/Cli/dotnet/SlnFileFactory.cs @@ -10,11 +10,11 @@ namespace Microsoft.DotNet.Tools.Common { public static class SlnFileFactory { - public static string[] ListSolutionFilesInDirectory(string directory, bool includeSolutionFilterFiles = false) + public static string[] ListSolutionFilesInDirectory(string directory, bool includeSolutionFilterFiles = false, bool includeSolutionXmlFiles = true) { return [ ..Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly), - ..Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly), + ..(includeSolutionXmlFiles ? Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly) : Array.Empty()), ..(includeSolutionFilterFiles ? Directory.GetFiles(directory, "*.slnf", SearchOption.TopDirectoryOnly) : Array.Empty()) ]; } diff --git a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs index b3a3ce5f09c7..2874f0323fec 100644 --- a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs @@ -72,7 +72,7 @@ private string GetProjectOrSolution() if (Directory.Exists(resultPath)) { - string[] possibleSolutionPath = SlnFileFactory.ListSolutionFilesInDirectory(resultPath).ToArray(); + string[] possibleSolutionPath = SlnFileFactory.ListSolutionFilesInDirectory(resultPath, false, false).ToArray(); //If more than a single sln file is found, an error is thrown since we can't determine which one to choose. if (possibleSolutionPath.Count() > 1) diff --git a/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs index d5db66ad1ee9..0f9bd45628b4 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs @@ -123,7 +123,7 @@ internal static List DiscoverAllProjects(string currentDirectory, var projectFiles = new List(); if (slnOrProjectArgument == null || !slnOrProjectArgument.Any()) { - slnFiles = SlnFileFactory.ListSolutionFilesInDirectory(currentDirectory).ToList(); + slnFiles = SlnFileFactory.ListSolutionFilesInDirectory(currentDirectory, false, false).ToList(); projectFiles.AddRange(Directory.GetFiles(currentDirectory, "*.*proj")); } else From 71f2896b6d3477cf7a44647ed8a95a13e911fad7 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Fri, 10 Jan 2025 13:10:50 -0800 Subject: [PATCH 19/27] Dont look for slnx by default --- src/Cli/dotnet/SlnFileFactory.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Cli/dotnet/SlnFileFactory.cs b/src/Cli/dotnet/SlnFileFactory.cs index 48161c71a316..277df84e19ea 100644 --- a/src/Cli/dotnet/SlnFileFactory.cs +++ b/src/Cli/dotnet/SlnFileFactory.cs @@ -19,7 +19,7 @@ public static string[] ListSolutionFilesInDirectory(string directory, bool inclu ]; } - public static SolutionModel CreateFromFileOrDirectory(string fileOrDirectory) + public static SolutionModel CreateFromFileOrDirectory(string fileOrDirectory, bool includeSolutionXmlFiles = true) { if (File.Exists(fileOrDirectory)) { @@ -27,7 +27,7 @@ public static SolutionModel CreateFromFileOrDirectory(string fileOrDirectory) } else { - return FromDirectory(fileOrDirectory); + return FromDirectory(fileOrDirectory, includeSolutionXmlFiles); } } @@ -52,7 +52,7 @@ private static SolutionModel FromFile(string solutionPath) return slnFile; } - private static SolutionModel FromDirectory(string solutionDirectory) + private static SolutionModel FromDirectory(string solutionDirectory, bool includeSolutionXmlFiles = true) { DirectoryInfo dir; try @@ -72,7 +72,12 @@ private static SolutionModel FromDirectory(string solutionDirectory) solutionDirectory); } - FileInfo[] files = [..dir.GetFiles("*.sln"), ..dir.GetFiles("*.slnx")]; + FileInfo[] files = dir.GetFiles("*.sln"); + if (includeSolutionXmlFiles) + { + files.Concat(dir.GetFiles(".slnx")); + } + if (files.Length == 0) { throw new GracefulException( From b0a612c515ac9671b21e1e4def54f4ec0a6fbeff Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Fri, 10 Jan 2025 13:12:55 -0800 Subject: [PATCH 20/27] Dont look for slnx by default --- src/Cli/dotnet/SlnFileFactory.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Cli/dotnet/SlnFileFactory.cs b/src/Cli/dotnet/SlnFileFactory.cs index 277df84e19ea..5513c707aae0 100644 --- a/src/Cli/dotnet/SlnFileFactory.cs +++ b/src/Cli/dotnet/SlnFileFactory.cs @@ -52,7 +52,7 @@ private static SolutionModel FromFile(string solutionPath) return slnFile; } - private static SolutionModel FromDirectory(string solutionDirectory, bool includeSolutionXmlFiles = true) + private static SolutionModel FromDirectory(string solutionDirectory, bool includeSolutionXmlFiles) { DirectoryInfo dir; try @@ -72,11 +72,10 @@ private static SolutionModel FromDirectory(string solutionDirectory, bool includ solutionDirectory); } - FileInfo[] files = dir.GetFiles("*.sln"); - if (includeSolutionXmlFiles) - { - files.Concat(dir.GetFiles(".slnx")); - } + FileInfo[] files = [ + ..dir.GetFiles("*.sln"), + ..(includeSolutionXmlFiles ? dir.GetFiles(".slnx") : Array.Empty()) + ]; if (files.Length == 0) { From 89331037312f45f2137c6d968fd3009fd1e76d9c Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Fri, 10 Jan 2025 13:13:48 -0800 Subject: [PATCH 21/27] Remove Microsoft.DotNet.Cli.Sln.Internal.csproj from filtered solution --- source-build.slnf | 1 - 1 file changed, 1 deletion(-) diff --git a/source-build.slnf b/source-build.slnf index dd00078d19a6..5090803b2313 100644 --- a/source-build.slnf +++ b/source-build.slnf @@ -8,7 +8,6 @@ "src\\BuiltInTools\\DotNetDeltaApplier\\Microsoft.Extensions.DotNetDeltaApplier.csproj", "src\\BuiltInTools\\DotNetWatchTasks\\DotNetWatchTasks.csproj", "src\\BuiltInTools\\dotnet-watch\\dotnet-watch.csproj", - "src\\Cli\\Microsoft.DotNet.Cli.Sln.Internal\\Microsoft.DotNet.Cli.Sln.Internal.csproj", "src\\Cli\\Microsoft.DotNet.Cli.Utils\\Microsoft.DotNet.Cli.Utils.csproj", "src\\Cli\\Microsoft.DotNet.Configurer\\Microsoft.DotNet.Configurer.csproj", "src\\Cli\\Microsoft.DotNet.InternalAbstractions\\Microsoft.DotNet.InternalAbstractions.csproj", From 3587ccd97400ced1be58772e42cbdc2c70ca993f Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Fri, 10 Jan 2025 13:36:50 -0800 Subject: [PATCH 22/27] Disable slnx search by default --- src/Cli/dotnet/ReleasePropertyProjectLocator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs index 1f287fc00594..0e7b3a8b260d 100644 --- a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs +++ b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs @@ -154,7 +154,7 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() SolutionModel sln = serializer.OpenAsync(slnPath, CancellationToken.None).Result; try { - sln = SlnFileFactory.CreateFromFileOrDirectory(slnPath); + sln = SlnFileFactory.CreateFromFileOrDirectory(slnPath, false); } catch (GracefulException) { From fb06ef5bd41cc33db065bc7fcefe9abe438168ed Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Fri, 10 Jan 2025 15:56:09 -0800 Subject: [PATCH 23/27] Fix tests and paths --- src/Cli/dotnet/ReleasePropertyProjectLocator.cs | 15 ++++++--------- .../ListPackageReferencesCommand.cs | 4 ++-- .../PostActions/DotnetSlnPostActionProcessor.cs | 6 ++---- .../restore/WorkloadRestoreCommand.cs | 12 ++++-------- .../Properties/launchSettings.json | 8 ++++++++ 5 files changed, 22 insertions(+), 23 deletions(-) create mode 100644 test/ArgumentsReflector/Properties/launchSettings.json diff --git a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs index 0e7b3a8b260d..a1f6dab8ca4c 100644 --- a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs +++ b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs @@ -131,7 +131,7 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() } catch (GracefulException) // Fall back to looking for a solution if multiple project files are found, or there's no project in the directory. { - string? potentialSln = SlnFileFactory.ListSolutionFilesInDirectory(arg, false, false).FirstOrDefault(); + string? potentialSln = SlnFileFactory.ListSolutionFilesInDirectory(arg, false).FirstOrDefault(); if (!string.IsNullOrEmpty(potentialSln)) { @@ -147,14 +147,11 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() /// Throws exception if two+ projects disagree in PublishRelease, PackRelease, or whatever _propertyToCheck is, and have it defined. public ProjectInstance? GetArbitraryProjectFromSolution(string slnPath, Dictionary globalProps) { - ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(slnPath) ?? throw new GracefulException( - CommonLocalizableStrings.CouldNotFindSolutionOrDirectory, - slnPath); - - SolutionModel sln = serializer.OpenAsync(slnPath, CancellationToken.None).Result; + string slnFullPath = Path.GetFullPath(slnPath); + SolutionModel sln; try { - sln = SlnFileFactory.CreateFromFileOrDirectory(slnPath, false); + sln = SlnFileFactory.CreateFromFileOrDirectory(slnFullPath, false); } catch (GracefulException) { @@ -169,13 +166,13 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() if (string.Equals(Environment.GetEnvironmentVariable(EnvironmentVariableNames.DOTNET_CLI_LAZY_PUBLISH_AND_PACK_RELEASE_FOR_SOLUTIONS), "true", StringComparison.OrdinalIgnoreCase)) { // Evaluate only one project for speed if this environment variable is used. Will break more customers if enabled (adding 8.0 project to SLN with other project TFMs with no Publish or PackRelease.) - return GetSingleProjectFromSolution(sln, slnPath, globalProps); + return GetSingleProjectFromSolution(sln, slnFullPath, globalProps); } Parallel.ForEach(sln.SolutionProjects.AsEnumerable(), (project, state) => { #pragma warning disable CS8604 // Possible null reference argument. - string projectFullPath = Path.Combine(Path.GetDirectoryName(slnPath), project.FilePath); + string projectFullPath = Path.Combine(Path.GetDirectoryName(slnFullPath), project.FilePath); #pragma warning restore CS8604 // Possible null reference argument. if (IsUnanalyzableProjectInSolution(project, projectFullPath)) return; diff --git a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs index 2874f0323fec..9a5b2f2a9e23 100644 --- a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs @@ -72,7 +72,7 @@ private string GetProjectOrSolution() if (Directory.Exists(resultPath)) { - string[] possibleSolutionPath = SlnFileFactory.ListSolutionFilesInDirectory(resultPath, false, false).ToArray(); + string[] possibleSolutionPath = SlnFileFactory.ListSolutionFilesInDirectory(resultPath, false); //If more than a single sln file is found, an error is thrown since we can't determine which one to choose. if (possibleSolutionPath.Count() > 1) @@ -82,7 +82,7 @@ private string GetProjectOrSolution() //If a single solution is found, use it. else if (possibleSolutionPath.Count() == 1) { - return possibleSolutionPath[0]; + return possibleSolutionPath.Single(); } //If no solutions are found, look for a project file else diff --git a/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs b/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs index ec83cfe4c250..28143fd21d82 100644 --- a/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs +++ b/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs @@ -27,10 +27,8 @@ public DotnetSlnPostActionProcessor(Func, string?, internal static IReadOnlyList FindSolutionFilesAtOrAbovePath(IPhysicalFileSystem fileSystem, string outputBasePath) { - return [ - ..FileFindHelpers.FindFilesAtOrAbovePath(fileSystem, outputBasePath, "*.sln"), - ..FileFindHelpers.FindFilesAtOrAbovePath(fileSystem, outputBasePath, "*.slnx") - ]; + return FileFindHelpers.FindFilesAtOrAbovePath(fileSystem, outputBasePath, "*.sln") + ?? FileFindHelpers.FindFilesAtOrAbovePath(fileSystem, outputBasePath, "*.slnx"); } // The project files to add are a subset of the primary outputs, specifically the primary outputs indicated by the primaryOutputIndexes post action argument (semicolon separated) diff --git a/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs index 0f9bd45628b4..f25690d52963 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs @@ -123,13 +123,13 @@ internal static List DiscoverAllProjects(string currentDirectory, var projectFiles = new List(); if (slnOrProjectArgument == null || !slnOrProjectArgument.Any()) { - slnFiles = SlnFileFactory.ListSolutionFilesInDirectory(currentDirectory, false, false).ToList(); + slnFiles = SlnFileFactory.ListSolutionFilesInDirectory(currentDirectory, false).ToList(); projectFiles.AddRange(Directory.GetFiles(currentDirectory, "*.*proj")); } else { slnFiles = slnOrProjectArgument - .Where(s => Path.GetExtension(s).Equals(".sln", StringComparison.OrdinalIgnoreCase)) + .Where(s => Path.GetExtension(s).Equals(".sln", StringComparison.OrdinalIgnoreCase) || Path.GetExtension(s).Equals(".slnx", StringComparison.OrdinalIgnoreCase)) .Select(Path.GetFullPath).ToList(); projectFiles = slnOrProjectArgument .Where(s => Path.GetExtension(s).EndsWith("proj", StringComparison.OrdinalIgnoreCase)) @@ -138,12 +138,8 @@ internal static List DiscoverAllProjects(string currentDirectory, foreach (string file in slnFiles) { - var solutionFile = SolutionFile.Parse(file); - var projects = solutionFile.ProjectsInOrder.Where(p => p.ProjectType != SolutionProjectType.SolutionFolder); - foreach (var p in projects) - { - projectFiles.Add(p.AbsolutePath); - } + var solutionFile = SlnFileFactory.CreateFromFileOrDirectory(file); + projectFiles.AddRange(solutionFile.SolutionProjects.Select(p => p.FilePath)); } if (projectFiles.Count == 0) diff --git a/test/ArgumentsReflector/Properties/launchSettings.json b/test/ArgumentsReflector/Properties/launchSettings.json new file mode 100644 index 000000000000..33504c948ad2 --- /dev/null +++ b/test/ArgumentsReflector/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "WSL": { + "commandName": "WSL2", + "distributionName": "" + } + } +} \ No newline at end of file From 0ea98a3032c0f233ad92fbab79e1b4b7b77aa961 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 13 Jan 2025 08:29:20 -0800 Subject: [PATCH 24/27] Fix test --- test/dotnet-list-package.Tests/GivenDotnetListPackage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs b/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs index bddc1d21aaff..d42a3772f1c4 100644 --- a/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs +++ b/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs @@ -118,7 +118,7 @@ public void ItRunOnSolution() new ListPackageCommand(Log) .WithWorkingDirectory(projectDirectory) - .Execute() + .Execute("App.sln") .Should() .Pass() .And.NotHaveStdErr() From f9531f1c0dfee3b2d861874b82058f3b9b248332 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 13 Jan 2025 09:31:40 -0800 Subject: [PATCH 25/27] dotnet-list: Fix tests --- test/dotnet-list-package.Tests/GivenDotnetListPackage.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs b/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs index d42a3772f1c4..46eaed94b8eb 100644 --- a/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs +++ b/test/dotnet-list-package.Tests/GivenDotnetListPackage.cs @@ -117,8 +117,9 @@ public void ItRunOnSolution() .And.NotHaveStdErr(); new ListPackageCommand(Log) + .WithProject("App.sln") .WithWorkingDirectory(projectDirectory) - .Execute("App.sln") + .Execute() .Should() .Pass() .And.NotHaveStdErr() From 24a6cdcfd0dbe5e14d101bb8753712e7713a23ab Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 13 Jan 2025 15:04:25 -0800 Subject: [PATCH 26/27] Nit: PR comments --- src/Cli/dotnet/ReleasePropertyProjectLocator.cs | 6 +++++- .../dotnet-list-package/ListPackageReferencesCommand.cs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs index a1f6dab8ca4c..2d52334b58f1 100644 --- a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs +++ b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs @@ -148,6 +148,10 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified() public ProjectInstance? GetArbitraryProjectFromSolution(string slnPath, Dictionary globalProps) { string slnFullPath = Path.GetFullPath(slnPath); + if (!Path.Exists(slnFullPath)) + { + return null; + } SolutionModel sln; try { @@ -265,7 +269,7 @@ private bool IsValidProjectFilePath(string path) /// Returns true if the path exists and is a sln file type. private bool IsValidSlnFilePath(string path) { - return File.Exists(path) && (Path.GetExtension(path) == ".sln" || Path.GetExtension(path) == ".slnx"); + return File.Exists(path) && (Path.GetExtension(path).Equals(".sln")|| Path.GetExtension(path).Equals(".slnx")); } /// A case-insensitive dictionary of any properties passed from the user and their values. diff --git a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs index 9a5b2f2a9e23..cbd10c1016f0 100644 --- a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs @@ -82,7 +82,7 @@ private string GetProjectOrSolution() //If a single solution is found, use it. else if (possibleSolutionPath.Count() == 1) { - return possibleSolutionPath.Single(); + return possibleSolutionPath[0]; } //If no solutions are found, look for a project file else From ca2635689788eb3f2135a228dad0412134ad4487 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 13 Jan 2025 15:24:11 -0800 Subject: [PATCH 27/27] NIT: PR Comments --- src/Cli/dotnet/SlnFileFactory.cs | 6 +++--- test/ArgumentsReflector/Properties/launchSettings.json | 8 -------- 2 files changed, 3 insertions(+), 11 deletions(-) delete mode 100644 test/ArgumentsReflector/Properties/launchSettings.json diff --git a/src/Cli/dotnet/SlnFileFactory.cs b/src/Cli/dotnet/SlnFileFactory.cs index 5513c707aae0..9402883da500 100644 --- a/src/Cli/dotnet/SlnFileFactory.cs +++ b/src/Cli/dotnet/SlnFileFactory.cs @@ -14,8 +14,8 @@ public static string[] ListSolutionFilesInDirectory(string directory, bool inclu { return [ ..Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly), - ..(includeSolutionXmlFiles ? Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly) : Array.Empty()), - ..(includeSolutionFilterFiles ? Directory.GetFiles(directory, "*.slnf", SearchOption.TopDirectoryOnly) : Array.Empty()) + ..(includeSolutionXmlFiles ? Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly) : []), + ..(includeSolutionFilterFiles ? Directory.GetFiles(directory, "*.slnf", SearchOption.TopDirectoryOnly) : []) ]; } @@ -74,7 +74,7 @@ private static SolutionModel FromDirectory(string solutionDirectory, bool includ FileInfo[] files = [ ..dir.GetFiles("*.sln"), - ..(includeSolutionXmlFiles ? dir.GetFiles(".slnx") : Array.Empty()) + ..(includeSolutionXmlFiles ? dir.GetFiles(".slnx") : []) ]; if (files.Length == 0) diff --git a/test/ArgumentsReflector/Properties/launchSettings.json b/test/ArgumentsReflector/Properties/launchSettings.json deleted file mode 100644 index 33504c948ad2..000000000000 --- a/test/ArgumentsReflector/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "WSL": { - "commandName": "WSL2", - "distributionName": "" - } - } -} \ No newline at end of file