diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/App.sln b/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/App.sln new file mode 100644 index 000000000..c8573d702 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/App.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26006.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "src\App\App.csproj", "{DDF3765C-59FB-4AA6-BE83-779ED13AA64A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{72BFCA87-B033-4721-8712-4D12166B4A39}" +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 + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Debug|x64.ActiveCfg = Debug|x64 + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Debug|x64.Build.0 = Debug|x64 + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Debug|x86.ActiveCfg = Debug|x86 + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Debug|x86.Build.0 = Debug|x86 + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Release|Any CPU.Build.0 = Release|Any CPU + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Release|x64.ActiveCfg = Release|x64 + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Release|x64.Build.0 = Release|x64 + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Release|x86.ActiveCfg = Release|x86 + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {DDF3765C-59FB-4AA6-BE83-779ED13AA64A} = {72BFCA87-B033-4721-8712-4D12166B4A39} + EndGlobalSection +EndGlobal diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/src/App/App.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/src/App/App.csproj new file mode 100644 index 000000000..b38669c62 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/src/App/App.csproj @@ -0,0 +1,15 @@ + + + Exe + netcoreapp1.0 + + + + + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/src/App/Program.cs b/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/src/App/Program.cs new file mode 100644 index 000000000..abb853a4a --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/src/App/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/TestAppWithSlnAndCaseSensitiveSolutionFolders/src/Lib/Lib.csproj b/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/src/Lib/Lib.csproj new file mode 100644 index 000000000..9f8bcbb51 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/src/Lib/Lib.csproj @@ -0,0 +1,11 @@ + + + + netstandard1.4 + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/src/Lib/Library.cs b/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/src/Lib/Library.cs new file mode 100644 index 000000000..205c42a01 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndCaseSensitiveSolutionFolders/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/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/App.sln b/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/App.sln new file mode 100644 index 000000000..17cd062c5 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/App.sln @@ -0,0 +1,36 @@ + +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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lib", "Lib\\Lib.csproj", "{B38B1FA5-B4C9-456A-8B71-8FCD62ACF400}" +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/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/App/App.csproj b/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/App/App.csproj new file mode 100644 index 000000000..27e43f08e --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/App/App.csproj @@ -0,0 +1,19 @@ + + + Exe + netcoreapp1.0 + + + + + + + + + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/App/Program.cs b/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/App/Program.cs new file mode 100644 index 000000000..abb853a4a --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/App/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/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/Lib/Lib.csproj b/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/Lib/Lib.csproj new file mode 100644 index 000000000..9f8bcbb51 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/Lib/Lib.csproj @@ -0,0 +1,11 @@ + + + + netstandard1.4 + + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/Lib/Library.cs b/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/Lib/Library.cs new file mode 100644 index 000000000..205c42a01 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep/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/build/Microsoft.DotNet.Cli.Compile.targets b/build/Microsoft.DotNet.Cli.Compile.targets index fe9359a2b..9f8802765 100644 --- a/build/Microsoft.DotNet.Cli.Compile.targets +++ b/build/Microsoft.DotNet.Cli.Compile.targets @@ -126,32 +126,6 @@ VersionSuffix="$(VersionSuffix)" ProjectPath="$(SrcDirectory)/tool_roslyn/tool_roslyn.csproj" /> - - - - - - u=rw,g=r,o=r - - - - - u=rwx,g=rx,o=rx - - - - - u=rwx,g=rx,o=rx - - - - - @@ -224,6 +198,32 @@ @(CompileStageSdkDirectories); $(SharedFrameworkNameVersionPath)" /> + + + + + + u=rw,g=r,o=r + + + + + u=rwx,g=rx,o=rx + + + + + u=rwx,g=rx,o=rx + + + + + 1.1.1 - 15.1.546 + 15.1.548 2.0.0-rc4-61325-08 1.0.0-alpha-20170213-1 4.0.0-rtm-2283 1.0.0-alpha-20170130-3-281 - 15.0.0-preview-20170125-04 + 15.0.0-preview-20170210-02 1.0.0-beta1-20170202-111 1.0.0-beta1-20170206-112 1.1.1 diff --git a/build/Microsoft.DotNet.Cli.Publish.targets b/build/Microsoft.DotNet.Cli.Publish.targets index 8ebfcba7a..d9f4341d6 100644 --- a/build/Microsoft.DotNet.Cli.Publish.targets +++ b/build/Microsoft.DotNet.Cli.Publish.targets @@ -43,7 +43,7 @@ - + diff --git a/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs b/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs index 7eda0d91e..93df94b85 100644 --- a/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs +++ b/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs @@ -274,7 +274,8 @@ namespace Microsoft.DotNet.Cli.Sln.Internal } set { - _filePath = PathUtility.GetPathWithDirectorySeparator(value); + _filePath = PathUtility.RemoveExtraPathSeparators( + PathUtility.GetPathWithDirectorySeparator(value)); } } diff --git a/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs b/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs index 2f2353cbe..895257d28 100644 --- a/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs +++ b/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs @@ -261,6 +261,32 @@ namespace Microsoft.DotNet.Tools.Common } } + public static string RemoveExtraPathSeparators(string path) + { + if (string.IsNullOrEmpty(path)) + { + return path; + } + + var components = path.Split(Path.DirectorySeparatorChar); + var result = string.Empty; + + foreach (var component in components) + { + if (!string.IsNullOrEmpty(component)) + { + result = Path.Combine(result, component); + } + } + + if (path[path.Length-1] == Path.DirectorySeparatorChar) + { + result += Path.DirectorySeparatorChar; + } + + return result; + } + public static bool HasExtension(string filePath, string extension) { var comparison = StringComparison.Ordinal; diff --git a/src/dotnet/SlnFileExtensions.cs b/src/dotnet/SlnFileExtensions.cs index 7fbf0fb70..39e34a806 100644 --- a/src/dotnet/SlnFileExtensions.cs +++ b/src/dotnet/SlnFileExtensions.cs @@ -172,7 +172,7 @@ namespace Microsoft.DotNet.Tools.Common this SlnFile slnFile, SlnPropertySet nestedProjects) { - var solutionFolderPaths = new Dictionary(); + var solutionFolderPaths = new Dictionary(StringComparer.OrdinalIgnoreCase); var solutionFolderProjects = slnFile.Projects.GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid); foreach (var slnProject in solutionFolderProjects) diff --git a/test/dotnet-sln-add.Tests/GivenDotnetSlnAdd.cs b/test/dotnet-sln-add.Tests/GivenDotnetSlnAdd.cs index cdf0efc91..740fd6d76 100644 --- a/test/dotnet-sln-add.Tests/GivenDotnetSlnAdd.cs +++ b/test/dotnet-sln-add.Tests/GivenDotnetSlnAdd.cs @@ -502,11 +502,13 @@ EndGlobal .Count().Should().Be(1, $"Lib {reasonString}"); } - [Fact] - public void WhenSolutionAlreadyContainsProjectItDoesntDuplicate() + [Theory] + [InlineData("TestAppWithSlnAndExistingCsprojReferences")] + [InlineData("TestAppWithSlnAndExistingCsprojReferencesWithEscapedDirSep")] + public void WhenSolutionAlreadyContainsProjectItDoesntDuplicate(string testAsset) { var projectDirectory = TestAssets - .Get("TestAppWithSlnAndExistingCsprojReferences") + .Get(testAsset) .CreateInstance() .WithSourceFiles() .Root @@ -606,6 +608,28 @@ EndGlobal nonSolutionFolderProjects.Single().TypeGuid.Should().Be(expectedTypeGuid); } + [Fact] + private void WhenSlnContainsSolutionFolderWithDifferentCasingItDoesNotCreateDuplicate() + { + var projectDirectory = TestAssets + .Get("TestAppWithSlnAndCaseSensitiveSolutionFolders") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var projectToAdd = Path.Combine("src", "Lib", "Lib.csproj"); + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .Execute($"sln App.sln add {projectToAdd}"); + cmd.Should().Pass(); + + var slnFile = SlnFile.Read(Path.Combine(projectDirectory, "App.sln")); + var solutionFolderProjects = slnFile.Projects.Where( + p => p.TypeGuid == ProjectTypeGuids.SolutionFolderGuid); + solutionFolderProjects.Count().Should().Be(1); + } + private string GetExpectedSlnContents( string slnPath, string slnTemplate,