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);
+ }
}
}