From d0151a6111bd64eaece20bc729f9935a2618ab45 Mon Sep 17 00:00:00 2001 From: Justin Goshi Date: Wed, 4 Jan 2017 18:32:09 -1000 Subject: [PATCH] Add/remove solution items based on directory structure (#5197) * WIP support solution folders for dotnet add and remove * Add/remove solution folders based on directory hierarchy * Fix tests * Disable the solution building tests * Address PR comments * Fix a build break due to a new tool version used in the build * Create SlnProjectExtensions and SlnProjectCollectionExtensions per PR comments --- .../App.csproj | 19 ++ .../TestAppWithSlnAndCsprojInSubDir/App.sln | 34 +++ .../Program.cs | 10 + .../src/Lib/Lib.csproj | 16 ++ .../src/Lib/Library.cs | 12 ++ .../App.csproj | 12 ++ .../App.sln | 62 ++++++ .../Program.cs | 10 + .../src/Lib/Lib.csproj | 16 ++ .../src/Lib/Library.cs | 12 ++ .../App.csproj | 12 ++ .../App.sln | 53 +++++ .../Program.cs | 10 + .../src/Lib/Lib.csproj | 16 ++ .../src/Lib/Library.cs | 12 ++ .../ProjectTypeGuids.cs | 1 + src/dotnet/SlnProjectCollectionExtensions.cs | 43 ++++ src/dotnet/SlnProjectExtensions.cs | 28 +++ .../dotnet-add/dotnet-add-proj/Program.cs | 36 ++++ .../dotnet-remove-proj/Program.cs | 42 ++++ .../GivenDotnetAddProj.cs | 196 ++++++++++++++---- .../GivenThatIWantToMigrateSolutions.cs | 13 +- .../GivenDotnetRemoveProj.cs | 138 +++++++++++- 23 files changed, 757 insertions(+), 46 deletions(-) create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/App.csproj create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/App.sln create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/Program.cs create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/src/Lib/Lib.csproj create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/src/Lib/Library.cs create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/App.csproj create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/App.sln create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/Program.cs create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/src/Lib/Lib.csproj create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/src/Lib/Library.cs create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/App.csproj create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/App.sln create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/Program.cs create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/src/Lib/Lib.csproj create mode 100644 TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/src/Lib/Library.cs create mode 100644 src/dotnet/SlnProjectCollectionExtensions.cs create mode 100644 src/dotnet/SlnProjectExtensions.cs diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/App.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/App.csproj new file mode 100644 index 000000000..a957f128c --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/App.csproj @@ -0,0 +1,19 @@ + + + Exe + netcoreapp1.0 + + + + + + + + + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/App.sln b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/App.sln new file mode 100644 index 000000000..7bd8cded0 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/App.sln @@ -0,0 +1,34 @@ + +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 diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/Program.cs b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/Program.cs new file mode 100644 index 000000000..abb853a4a --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/Program.cs @@ -0,0 +1,10 @@ +using System; + +class Program +{ + static void Main(string[] args) + { + Console.WriteLine("Hello from the main app"); + Console.WriteLine(Lib.Library.GetMessage()); + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/src/Lib/Lib.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/src/Lib/Lib.csproj new file mode 100644 index 000000000..aacaac752 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/src/Lib/Lib.csproj @@ -0,0 +1,16 @@ + + + + netstandard1.4 + {84A45D44-B677-492D-A6DA-B3A71135AB8E} + + + + + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/src/Lib/Library.cs b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/src/Lib/Library.cs new file mode 100644 index 000000000..205c42a01 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDir/src/Lib/Library.cs @@ -0,0 +1,12 @@ +using System; + +namespace Lib +{ + public class Library + { + public static string GetMessage() + { + return "Message from Lib"; + } + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/App.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/App.csproj new file mode 100644 index 000000000..c5617ba4b --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/App.csproj @@ -0,0 +1,12 @@ + + + Exe + netcoreapp1.0 + + + + + + + + \ No newline at end of file diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/App.sln b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/App.sln new file mode 100644 index 000000000..b00300a34 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/App.sln @@ -0,0 +1,62 @@ + +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 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NotLastProjInSrc", "NotLastProjInSrc", "{1C5EE322-7073-4298-A077-B7816B1CE15F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NotLastProjInSrc", "src\NotLastProjInSrc\NotLastProjInSrc.csproj", "{96E9FA7D-FE59-4866-AE1E-F9EC2BB2FC67}" +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} + {1C5EE322-7073-4298-A077-B7816B1CE15F} = {7B86CE74-F620-4B32-99FE-82D40F8D6BF2} + {96E9FA7D-FE59-4866-AE1E-F9EC2BB2FC67} = {1C5EE322-7073-4298-A077-B7816B1CE15F} + EndGlobalSection +EndGlobal diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/Program.cs b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/Program.cs new file mode 100644 index 000000000..abb853a4a --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/Program.cs @@ -0,0 +1,10 @@ +using System; + +class Program +{ + static void Main(string[] args) + { + Console.WriteLine("Hello from the main app"); + Console.WriteLine(Lib.Library.GetMessage()); + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/src/Lib/Lib.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/src/Lib/Lib.csproj new file mode 100644 index 000000000..aacaac752 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/src/Lib/Lib.csproj @@ -0,0 +1,16 @@ + + + + netstandard1.4 + {84A45D44-B677-492D-A6DA-B3A71135AB8E} + + + + + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/src/Lib/Library.cs b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/src/Lib/Library.cs new file mode 100644 index 000000000..205c42a01 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCsprojInSubDirToRemove/src/Lib/Library.cs @@ -0,0 +1,12 @@ +using System; + +namespace Lib +{ + public class Library + { + public static string GetMessage() + { + return "Message from Lib"; + } + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/App.csproj b/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/App.csproj new file mode 100644 index 000000000..c5617ba4b --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/App.csproj @@ -0,0 +1,12 @@ + + + Exe + netcoreapp1.0 + + + + + + + + \ No newline at end of file diff --git a/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/App.sln b/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/App.sln new file mode 100644 index 000000000..ac77ad340 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/App.sln @@ -0,0 +1,53 @@ + +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(NestedProjects) = preSolution + {EAB71280-AF32-4531-8703-43CDBA261AA3} = {7B86CE74-F620-4B32-99FE-82D40F8D6BF2} + {84A45D44-B677-492D-A6DA-B3A71135AB8E} = {EAB71280-AF32-4531-8703-43CDBA261AA3} + EndGlobalSection +EndGlobal diff --git a/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/Program.cs b/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/Program.cs new file mode 100644 index 000000000..abb853a4a --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/Program.cs @@ -0,0 +1,10 @@ +using System; + +class Program +{ + static void Main(string[] args) + { + Console.WriteLine("Hello from the main app"); + Console.WriteLine(Lib.Library.GetMessage()); + } +} diff --git a/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/src/Lib/Lib.csproj b/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/src/Lib/Lib.csproj new file mode 100644 index 000000000..aacaac752 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/src/Lib/Lib.csproj @@ -0,0 +1,16 @@ + + + + netstandard1.4 + {84A45D44-B677-492D-A6DA-B3A71135AB8E} + + + + + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/src/Lib/Library.cs b/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/src/Lib/Library.cs new file mode 100644 index 000000000..205c42a01 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndLastCsprojInSubDirToRemove/src/Lib/Library.cs @@ -0,0 +1,12 @@ +using System; + +namespace Lib +{ + public class Library + { + public static string GetMessage() + { + return "Message from Lib"; + } + } +} diff --git a/src/Microsoft.DotNet.Cli.Sln.Internal/ProjectTypeGuids.cs b/src/Microsoft.DotNet.Cli.Sln.Internal/ProjectTypeGuids.cs index 71d7798df..723c5f4f1 100644 --- a/src/Microsoft.DotNet.Cli.Sln.Internal/ProjectTypeGuids.cs +++ b/src/Microsoft.DotNet.Cli.Sln.Internal/ProjectTypeGuids.cs @@ -7,5 +7,6 @@ namespace Microsoft.DotNet.Cli.Sln.Internal { public const string CPSProjectTypeGuid = "{13B669BE-BB05-4DDF-9536-439F39A36129}"; public const string CSharpProjectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"; + public const string SolutionFolderGuid = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}"; } } diff --git a/src/dotnet/SlnProjectCollectionExtensions.cs b/src/dotnet/SlnProjectCollectionExtensions.cs new file mode 100644 index 000000000..1e964f775 --- /dev/null +++ b/src/dotnet/SlnProjectCollectionExtensions.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Sln.Internal; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Microsoft.DotNet.Tools.Common +{ + public static class SlnProjectCollectionExtensions + { + public static HashSet GetReferencedSolutionFolders(this SlnProjectCollection projects) + { + var referencedSolutionFolders = new HashSet(); + + var solutionFolderProjects = projects + .Where(p => p.TypeGuid == ProjectTypeGuids.SolutionFolderGuid) + .ToList(); + + if (solutionFolderProjects.Any()) + { + var nonSolutionFolderProjects = projects + .Where(p => p.TypeGuid != ProjectTypeGuids.SolutionFolderGuid) + .ToList(); + + foreach (var project in nonSolutionFolderProjects) + { + var solutionFolders = project.GetSolutionFoldersFromProject(); + foreach (var solutionFolder in solutionFolders) + { + if (!referencedSolutionFolders.Contains(solutionFolder)) + { + referencedSolutionFolders.Add(solutionFolder); + } + } + } + } + + return referencedSolutionFolders; + } + } +} diff --git a/src/dotnet/SlnProjectExtensions.cs b/src/dotnet/SlnProjectExtensions.cs new file mode 100644 index 000000000..14f730329 --- /dev/null +++ b/src/dotnet/SlnProjectExtensions.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.DotNet.Cli.Sln.Internal; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Microsoft.DotNet.Tools.Common +{ + public static class SlnProjectExtensions + { + public static IList GetSolutionFoldersFromProject(this SlnProject project) + { + var currentDirString = $".{Path.DirectorySeparatorChar}"; + + var directoryPath = Path.GetDirectoryName(project.FilePath); + if (directoryPath.StartsWith(currentDirString)) + { + directoryPath = directoryPath.Substring(currentDirString.Length); + } + + return directoryPath.StartsWith("..") + ? new List() + : new List(directoryPath.Split(Path.DirectorySeparatorChar)); + } + } +} diff --git a/src/dotnet/commands/dotnet-add/dotnet-add-proj/Program.cs b/src/dotnet/commands/dotnet-add/dotnet-add-proj/Program.cs index 858bc2a90..f6a57db28 100644 --- a/src/dotnet/commands/dotnet-add/dotnet-add-proj/Program.cs +++ b/src/dotnet/commands/dotnet-add/dotnet-add-proj/Program.cs @@ -106,6 +106,8 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToSolution AddDefaultBuildConfigurations(slnFile, slnProject); + AddSolutionFolders(slnFile, slnProject); + slnFile.Projects.Add(slnProject); Reporter.Output.WriteLine( @@ -168,5 +170,39 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToSolution } } } + + private void AddSolutionFolders(SlnFile slnFile, SlnProject slnProject) + { + var solutionFolders = slnProject.GetSolutionFoldersFromProject(); + + if (solutionFolders.Any()) + { + var nestedProjectsSection = slnFile.Sections.GetOrCreateSection( + "NestedProjects", + SlnSectionType.PreProcess); + + string parentDirGuid = null; + foreach (var dir in solutionFolders) + { + 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; + } + + nestedProjectsSection.Properties[slnProject.Id] = parentDirGuid; + } + } } } diff --git a/src/dotnet/commands/dotnet-remove/dotnet-remove-proj/Program.cs b/src/dotnet/commands/dotnet-remove/dotnet-remove-proj/Program.cs index 719c585a8..5fca7d695 100644 --- a/src/dotnet/commands/dotnet-remove/dotnet-remove-proj/Program.cs +++ b/src/dotnet/commands/dotnet-remove/dotnet-remove-proj/Program.cs @@ -6,6 +6,7 @@ using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools.Common; using System; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -51,6 +52,8 @@ namespace Microsoft.DotNet.Tools.Remove.ProjectFromSolution RemoveEmptyConfigurationSections(slnFile); + RemoveEmptySolutionFolders(slnFile); + if (slnChanged) { slnFile.Write(); @@ -82,6 +85,15 @@ namespace Microsoft.DotNet.Tools.Remove.ProjectFromSolution { 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.ProjectReferenceRemoved, slnProject.FilePath)); @@ -110,5 +122,35 @@ namespace Microsoft.DotNet.Tools.Remove.ProjectFromSolution } } } + + private void RemoveEmptySolutionFolders(SlnFile slnFile) + { + var referencedSolutionFolders = slnFile.Projects.GetReferencedSolutionFolders(); + + var solutionFolderProjects = slnFile.Projects + .Where(p => p.TypeGuid == ProjectTypeGuids.SolutionFolderGuid) + .ToList(); + + if (solutionFolderProjects.Any()) + { + var nestedProjectsSection = slnFile.Sections.GetSection( + "NestedProjects", + SlnSectionType.PreProcess); + + foreach (var solutionFolderProject in solutionFolderProjects) + { + if (!referencedSolutionFolders.Contains(solutionFolderProject.Name)) + { + slnFile.Projects.Remove(solutionFolderProject); + nestedProjectsSection.Properties.Remove(solutionFolderProject.Id); + } + } + + if (nestedProjectsSection.IsEmpty) + { + slnFile.Sections.Remove(nestedProjectsSection); + } + } + } } } diff --git a/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs b/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs index ce804eaf8..1428292e2 100644 --- a/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs +++ b/test/dotnet-add-proj.Tests/GivenDotnetAddProj.cs @@ -35,7 +35,9 @@ 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"", ""__PROJECTGUID__"" +Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""Lib"", ""Lib"", ""__LIB_FOLDER_GUID__"" +EndProject +Project(""{13B669BE-BB05-4DDF-9536-439F39A36129}"") = ""Lib"", ""Lib\Lib.csproj"", ""__LIB_PROJECT_GUID__"" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -59,22 +61,25 @@ Global {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 - __PROJECTGUID__.Debug|Any CPU.ActiveCfg = Debug|Any CPU - __PROJECTGUID__.Debug|Any CPU.Build.0 = Debug|Any CPU - __PROJECTGUID__.Debug|x64.ActiveCfg = Debug|x64 - __PROJECTGUID__.Debug|x64.Build.0 = Debug|x64 - __PROJECTGUID__.Debug|x86.ActiveCfg = Debug|x86 - __PROJECTGUID__.Debug|x86.Build.0 = Debug|x86 - __PROJECTGUID__.Release|Any CPU.ActiveCfg = Release|Any CPU - __PROJECTGUID__.Release|Any CPU.Build.0 = Release|Any CPU - __PROJECTGUID__.Release|x64.ActiveCfg = Release|x64 - __PROJECTGUID__.Release|x64.Build.0 = Release|x64 - __PROJECTGUID__.Release|x86.ActiveCfg = Release|x86 - __PROJECTGUID__.Release|x86.Build.0 = Release|x86 + __LIB_PROJECT_GUID__.Debug|Any CPU.ActiveCfg = Debug|Any CPU + __LIB_PROJECT_GUID__.Debug|Any CPU.Build.0 = Debug|Any CPU + __LIB_PROJECT_GUID__.Debug|x64.ActiveCfg = Debug|x64 + __LIB_PROJECT_GUID__.Debug|x64.Build.0 = Debug|x64 + __LIB_PROJECT_GUID__.Debug|x86.ActiveCfg = Debug|x86 + __LIB_PROJECT_GUID__.Debug|x86.Build.0 = Debug|x86 + __LIB_PROJECT_GUID__.Release|Any CPU.ActiveCfg = Release|Any CPU + __LIB_PROJECT_GUID__.Release|Any CPU.Build.0 = Release|Any CPU + __LIB_PROJECT_GUID__.Release|x64.ActiveCfg = Release|x64 + __LIB_PROJECT_GUID__.Release|x64.Build.0 = Release|x64 + __LIB_PROJECT_GUID__.Release|x86.ActiveCfg = Release|x86 + __LIB_PROJECT_GUID__.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + __LIB_PROJECT_GUID__ = __LIB_FOLDER_GUID__ + EndGlobalSection EndGlobal "; @@ -83,7 +88,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26006.2 MinimumVisualStudioVersion = 10.0.40219.1 -Project(""{13B669BE-BB05-4DDF-9536-439F39A36129}"") = ""Lib"", ""Lib\Lib.csproj"", ""__PROJECTGUID__"" +Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""Lib"", ""Lib"", ""__LIB_FOLDER_GUID__"" +EndProject +Project(""{13B669BE-BB05-4DDF-9536-439F39A36129}"") = ""Lib"", ""Lib\Lib.csproj"", ""__LIB_PROJECT_GUID__"" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -95,18 +102,79 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - __PROJECTGUID__.Debug|Any CPU.ActiveCfg = Debug|Any CPU - __PROJECTGUID__.Debug|Any CPU.Build.0 = Debug|Any CPU - __PROJECTGUID__.Debug|x64.ActiveCfg = Debug|x64 - __PROJECTGUID__.Debug|x64.Build.0 = Debug|x64 - __PROJECTGUID__.Debug|x86.ActiveCfg = Debug|x86 - __PROJECTGUID__.Debug|x86.Build.0 = Debug|x86 - __PROJECTGUID__.Release|Any CPU.ActiveCfg = Release|Any CPU - __PROJECTGUID__.Release|Any CPU.Build.0 = Release|Any CPU - __PROJECTGUID__.Release|x64.ActiveCfg = Release|x64 - __PROJECTGUID__.Release|x64.Build.0 = Release|x64 - __PROJECTGUID__.Release|x86.ActiveCfg = Release|x86 - __PROJECTGUID__.Release|x86.Build.0 = Release|x86 + __LIB_PROJECT_GUID__.Debug|Any CPU.ActiveCfg = Debug|Any CPU + __LIB_PROJECT_GUID__.Debug|Any CPU.Build.0 = Debug|Any CPU + __LIB_PROJECT_GUID__.Debug|x64.ActiveCfg = Debug|x64 + __LIB_PROJECT_GUID__.Debug|x64.Build.0 = Debug|x64 + __LIB_PROJECT_GUID__.Debug|x86.ActiveCfg = Debug|x86 + __LIB_PROJECT_GUID__.Debug|x86.Build.0 = Debug|x86 + __LIB_PROJECT_GUID__.Release|Any CPU.ActiveCfg = Release|Any CPU + __LIB_PROJECT_GUID__.Release|Any CPU.Build.0 = Release|Any CPU + __LIB_PROJECT_GUID__.Release|x64.ActiveCfg = Release|x64 + __LIB_PROJECT_GUID__.Release|x64.Build.0 = Release|x64 + __LIB_PROJECT_GUID__.Release|x86.ActiveCfg = Release|x86 + __LIB_PROJECT_GUID__.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + __LIB_PROJECT_GUID__ = __LIB_FOLDER_GUID__ + EndGlobalSection +EndGlobal +"; + + private const string ExpectedSlnFileAfterAddingNestedProj = @" +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"", ""__SRC_FOLDER_GUID__"" +EndProject +Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""Lib"", ""Lib"", ""__LIB_FOLDER_GUID__"" +EndProject +Project(""{13B669BE-BB05-4DDF-9536-439F39A36129}"") = ""Lib"", ""src\Lib\Lib.csproj"", ""__LIB_PROJECT_GUID__"" +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 + __LIB_PROJECT_GUID__.Debug|Any CPU.ActiveCfg = Debug|Any CPU + __LIB_PROJECT_GUID__.Debug|Any CPU.Build.0 = Debug|Any CPU + __LIB_PROJECT_GUID__.Debug|x64.ActiveCfg = Debug|x64 + __LIB_PROJECT_GUID__.Debug|x64.Build.0 = Debug|x64 + __LIB_PROJECT_GUID__.Debug|x86.ActiveCfg = Debug|x86 + __LIB_PROJECT_GUID__.Debug|x86.Build.0 = Debug|x86 + __LIB_PROJECT_GUID__.Release|Any CPU.ActiveCfg = Release|Any CPU + __LIB_PROJECT_GUID__.Release|Any CPU.Build.0 = Release|Any CPU + __LIB_PROJECT_GUID__.Release|x64.ActiveCfg = Release|x64 + __LIB_PROJECT_GUID__.Release|x64.Build.0 = Release|x64 + __LIB_PROJECT_GUID__.Release|x86.ActiveCfg = Release|x86 + __LIB_PROJECT_GUID__.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + __LIB_FOLDER_GUID__ = __SRC_FOLDER_GUID__ + __LIB_PROJECT_GUID__ = __LIB_FOLDER_GUID__ EndGlobalSection EndGlobal "; @@ -253,6 +321,28 @@ EndGlobal cmd.StdOut.Should().BeVisuallyEquivalentTo(HelpText); } + [Fact] + public void WhenNestedProjectIsAddedSolutionFoldersAreCreated() + { + var projectDirectory = TestAssets + .Get("TestAppWithSlnAndCsprojInSubDir") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var projectToAdd = Path.Combine("src", "Lib", "Lib.csproj"); + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput($"add App.sln project {projectToAdd}"); + cmd.Should().Pass(); + + var slnPath = Path.Combine(projectDirectory, "App.sln"); + var expectedSlnContents = GetExpectedSlnContents(slnPath, ExpectedSlnFileAfterAddingNestedProj); + File.ReadAllText(slnPath) + .Should().BeVisuallyEquivalentTo(expectedSlnContents); + } + [Theory] [InlineData("TestAppWithSlnAndCsprojFiles", ExpectedSlnFileAfterAddingLibProj, "")] [InlineData("TestAppWithSlnAndCsprojProjectGuidFiles", ExpectedSlnFileAfterAddingLibProj, "{84A45D44-B677-492D-A6DA-B3A71135AB8E}")] @@ -277,19 +367,11 @@ EndGlobal var slnPath = Path.Combine(projectDirectory, "App.sln"); - if (string.IsNullOrEmpty(expectedProjectGuid)) - { - var slnFile = SlnFile.Read(slnPath); - var matchingProjects = slnFile.Projects - .Where((p) => p.Name == "Lib") - .ToList(); + var expectedSlnContents = GetExpectedSlnContents( + slnPath, + expectedSlnContentsTemplate, + expectedProjectGuid); - matchingProjects.Count.Should().Be(1); - var slnProject = matchingProjects[0]; - expectedProjectGuid = slnProject.Id; - } - - var expectedSlnContents = expectedSlnContentsTemplate.Replace("__PROJECTGUID__", expectedProjectGuid); File.ReadAllText(slnPath) .Should().BeVisuallyEquivalentTo(expectedSlnContents); } @@ -317,7 +399,7 @@ EndGlobal cmd.StdErr.Should().BeEmpty(); } - //ISSUE: https://github.com/dotnet/sdk/issues/545 + //ISSUE: https://github.com/dotnet/cli/issues/5205 //[Theory] //[InlineData("TestAppWithSlnAndCsprojFiles")] //[InlineData("TestAppWithSlnAndCsprojProjectGuidFiles")] @@ -410,5 +492,41 @@ EndGlobal File.ReadAllText(slnFullPath) .Should().BeVisuallyEquivalentTo(contentBefore); } + + private string GetExpectedSlnContents( + string slnPath, + string slnTemplate, + string expectedLibProjectGuid = null) + { + var slnFile = SlnFile.Read(slnPath); + + if (string.IsNullOrEmpty(expectedLibProjectGuid)) + { + var matchingProjects = slnFile.Projects + .Where((p) => p.FilePath.EndsWith("Lib.csproj")) + .ToList(); + + matchingProjects.Count.Should().Be(1); + var slnProject = matchingProjects[0]; + expectedLibProjectGuid = slnProject.Id; + } + var slnContents = slnTemplate.Replace("__LIB_PROJECT_GUID__", expectedLibProjectGuid); + + var matchingLibFolder = slnFile.Projects + .Where((p) => p.FilePath == "Lib") + .ToList(); + matchingLibFolder.Count.Should().Be(1); + slnContents = slnContents.Replace("__LIB_FOLDER_GUID__", matchingLibFolder[0].Id); + + var matchingSrcFolder = slnFile.Projects + .Where((p) => p.FilePath == "src") + .ToList(); + if (matchingSrcFolder.Count == 1) + { + slnContents = slnContents.Replace("__SRC_FOLDER_GUID__", matchingSrcFolder[0].Id); + } + + return slnContents; + } } } diff --git a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs index 37620c979..f54fc2b98 100644 --- a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs +++ b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs @@ -60,24 +60,27 @@ namespace Microsoft.DotNet.Migration.Tests .Execute($"restore \"{solutionRelPath}\"") .Should().Pass(); - //ISSUE: https://github.com/dotnet/sdk/issues/545 + //ISSUE: https://github.com/dotnet/cli/issues/5205 //new DotnetCommand() // .WithWorkingDirectory(projectDirectory) // .Execute($"build \"{solutionRelPath}\"") // .Should().Pass(); SlnFile slnFile = SlnFile.Read(Path.Combine(projectDirectory.FullName, solutionRelPath)); - slnFile.Projects.Count.Should().Be(3); + var nonSolutionFolderProjects = slnFile.Projects + .Where(p => p.TypeGuid != ProjectTypeGuids.SolutionFolderGuid); - var slnProject = slnFile.Projects.Where((p) => p.Name == "TestApp").Single(); + nonSolutionFolderProjects.Count().Should().Be(3); + + var slnProject = nonSolutionFolderProjects.Where((p) => p.Name == "TestApp").Single(); slnProject.TypeGuid.Should().Be(ProjectTypeGuids.CSharpProjectTypeGuid); slnProject.FilePath.Should().Be("TestApp.csproj"); - slnProject = slnFile.Projects.Where((p) => p.Name == "TestLibrary").Single(); + slnProject = nonSolutionFolderProjects.Where((p) => p.Name == "TestLibrary").Single(); slnProject.TypeGuid.Should().Be(ProjectTypeGuids.CSharpProjectTypeGuid); slnProject.FilePath.Should().Be(Path.Combine("..", "TestLibrary", "TestLibrary.csproj")); - slnProject = slnFile.Projects.Where((p) => p.Name == "subdir").Single(); + slnProject = nonSolutionFolderProjects.Where((p) => p.Name == "subdir").Single(); slnProject.TypeGuid.Should().Be(subdirProjectTypeGuid); slnProject.FilePath.Should().Be(Path.Combine("src", "subdir", "subdir.csproj")); } diff --git a/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs b/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs index 208d5df58..74eebdd0e 100644 --- a/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs +++ b/test/dotnet-remove-proj.Tests/GivenDotnetRemoveProj.cs @@ -67,6 +67,97 @@ 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 "; [Theory] @@ -341,8 +432,7 @@ Project reference `idontexisteither.csproj` could not be found."; .Should().BeVisuallyEquivalentTo(ExpectedSlnContentsAfterRemove); } - //ISSUE: https://github.com/dotnet/sdk/issues/545 - //[Fact] + [Fact] public void WhenReferenceIsRemovedSlnBuilds() { var projectDirectory = TestAssets @@ -408,5 +498,49 @@ Project reference `idontexisteither.csproj` could not be found."; File.ReadAllText(solutionPath) .Should().BeVisuallyEquivalentTo(ExpectedSlnContentsAfterRemoveAllProjects); } + + [Fact] + public void WhenNestedProjectIsRemovedItsSolutionFoldersAreRemoved() + { + var projectDirectory = TestAssets + .Get("TestAppWithSlnAndCsprojInSubDirToRemove") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var solutionPath = Path.Combine(projectDirectory, "App.sln"); + + var projectToRemove = Path.Combine("src", "NotLastProjInSrc", "NotLastProjInSrc.csproj"); + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput($"remove project {projectToRemove}"); + cmd.Should().Pass(); + + File.ReadAllText(solutionPath) + .Should().BeVisuallyEquivalentTo(ExpectedSlnContentsAfterRemoveNestedProj); + } + + [Fact] + public void WhenFinalNestedProjectIsRemovedSolutionFoldersAreRemoved() + { + var projectDirectory = TestAssets + .Get("TestAppWithSlnAndLastCsprojInSubDirToRemove") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var solutionPath = Path.Combine(projectDirectory, "App.sln"); + + var projectToRemove = Path.Combine("src", "Lib", "Lib.csproj"); + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput($"remove project {projectToRemove}"); + cmd.Should().Pass(); + + File.ReadAllText(solutionPath) + .Should().BeVisuallyEquivalentTo(ExpectedSlnContentsAfterRemoveLastNestedProj); + } } }