From baecbd8d43d75784ef0b94d4c51c7a978995a7ff Mon Sep 17 00:00:00 2001 From: Justin Goshi Date: Fri, 20 Jan 2017 17:37:11 -0800 Subject: [PATCH 1/4] WIP --- .../NoSolutionItemsAfterMigration.sln | 39 +++++++ .../ReadmeSolutionItemAfterMigration.sln | 40 +++++++ .../TestApp/Program.cs | 15 +++ .../TestApp/TestApp.xproj | 18 ++++ .../TestApp/project.json | 26 +++++ .../global.json | 3 + .../readme.txt | 1 + .../commands/dotnet-migrate/MigrateCommand.cs | 18 ++++ .../GivenThatIWantToMigrateSolutions.cs | 100 ++++++++++++++++++ 9 files changed, 260 insertions(+) create mode 100644 TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/NoSolutionItemsAfterMigration.sln create mode 100644 TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/ReadmeSolutionItemAfterMigration.sln create mode 100644 TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/Program.cs create mode 100644 TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/TestApp.xproj create mode 100644 TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/project.json create mode 100644 TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/global.json create mode 100644 TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/readme.txt diff --git a/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/NoSolutionItemsAfterMigration.sln b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/NoSolutionItemsAfterMigration.sln new file mode 100644 index 000000000..da0ae5935 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/NoSolutionItemsAfterMigration.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}") = "TestApp", "TestApp\TestApp.xproj", "{D65E5A1F-719F-4F95-8835-88BDD67AD457}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FAACC4BE-31AE-4EB7-A4C8-5BB4617EB4AF}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + EndProjectSection +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 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.ActiveCfg = Debug|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.Build.0 = Debug|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.ActiveCfg = Debug|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.Build.0 = Debug|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.Build.0 = Release|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.ActiveCfg = Release|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.Build.0 = Release|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.ActiveCfg = Release|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/ReadmeSolutionItemAfterMigration.sln b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/ReadmeSolutionItemAfterMigration.sln new file mode 100644 index 000000000..05aecb75d --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/ReadmeSolutionItemAfterMigration.sln @@ -0,0 +1,40 @@ + +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}") = "TestApp", "TestApp\TestApp.xproj", "{D65E5A1F-719F-4F95-8835-88BDD67AD457}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FAACC4BE-31AE-4EB7-A4C8-5BB4617EB4AF}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + readme.txt = readme.txt + EndProjectSection +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 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.ActiveCfg = Debug|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.Build.0 = Debug|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.ActiveCfg = Debug|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.Build.0 = Debug|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.Build.0 = Release|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.ActiveCfg = Release|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.Build.0 = Release|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.ActiveCfg = Release|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/Program.cs b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/Program.cs new file mode 100644 index 000000000..2289ac741 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/Program.cs @@ -0,0 +1,15 @@ +// 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 System; +namespace TestApp +{ + public class Program + { + public static int Main(string[] args) + { + Console.WriteLine("Hello World!"); + return 0; + } + } +} diff --git a/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/TestApp.xproj b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/TestApp.xproj new file mode 100644 index 000000000..d18702195 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/TestApp.xproj @@ -0,0 +1,18 @@ + + + + 14.0.23107 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 0138cb8f-4aa9-4029-a21e-c07c30f425ba + TestAppWithContents + ..\..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\..\artifacts\ + + + 2.0 + + + diff --git a/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/project.json b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/project.json new file mode 100644 index 000000000..166d41c2b --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/TestApp/project.json @@ -0,0 +1,26 @@ +{ + "version": "1.0.0-*", + "buildOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + "dependencies": { + "Microsoft.NETCore.App": "1.0.1" + }, + "frameworks": { + "netcoreapp1.0": {} + }, + "runtimes": { + "win7-x64": {}, + "win7-x86": {}, + "osx.10.10-x64": {}, + "osx.10.11-x64": {}, + "ubuntu.14.04-x64": {}, + "ubuntu.16.04-x64": {}, + "centos.7-x64": {}, + "rhel.7.2-x64": {}, + "debian.8-x64": {}, + "fedora.23-x64": {}, + "opensuse.13.2-x64": {} + } +} diff --git a/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/global.json b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/global.json new file mode 100644 index 000000000..22936715c --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/global.json @@ -0,0 +1,3 @@ +{ + "projects": [ "." ] +} diff --git a/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/readme.txt b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/readme.txt new file mode 100644 index 000000000..6e3eb33f4 --- /dev/null +++ b/TestAssets/NonRestoredTestProjects/PJAppWithSlnAndSolutionItemsToMoveToBackup/readme.txt @@ -0,0 +1 @@ +This is just for our test to verify that we do not remove the readme.txt link from the solution. diff --git a/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs b/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs index ab5875acc..00c620819 100644 --- a/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs +++ b/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs @@ -153,6 +153,8 @@ namespace Microsoft.DotNet.Tools.Migrate _slnFile.MinimumVisualStudioVersion = MinimumVisualStudioVersion; } + RemoveReferencesToMigratedFiles(_slnFile); + _slnFile.Write(); foreach (var csprojFile in csprojFilesToAdd) @@ -161,6 +163,22 @@ namespace Microsoft.DotNet.Tools.Migrate } } + private void RemoveReferencesToMigratedFiles(SlnFile slnFile) + { + // TODO: Need to merge my PRs then I can call the ProjectCollection extension methods. + // And create new extension methods. + var solutionFolders = slnFile.Projects.Where(p => p.TypeGuid == ProjectTypeGuids.SolutionFolderGuid); + + foreach (var solutionFolder in solutionFolders) + { + var solutionItems = solutionFolder.Sections.GetSection("SolutionItems"); + if (solutionItems != null && solutionItems.Properties.ContainsKey("global.json")) + { + solutionItems.Properties.Remove("global.json"); + } + } + } + private void AddProject(string slnPath, string csprojPath) { List args = new List() diff --git a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs index 07f68b5b8..b93690e94 100644 --- a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs +++ b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs @@ -14,6 +14,83 @@ namespace Microsoft.DotNet.Migration.Tests { public class GivenThatIWantToMigrateSolutions : TestBase { + private const string ExpectedSlnFileAfterRemovingAllSolutionItems = @" +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}"") = ""TestApp"", ""TestApp\TestApp.csproj"", ""{D65E5A1F-719F-4F95-8835-88BDD67AD457}"" +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 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.ActiveCfg = Debug|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.Build.0 = Debug|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.ActiveCfg = Debug|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.Build.0 = Debug|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.Build.0 = Release|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.ActiveCfg = Release|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.Build.0 = Release|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.ActiveCfg = Release|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal +"; + + private const string ExpectedSlnFileAfterRemovingAllSolutionItemsExceptReadme = @" +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}"") = ""TestApp"", ""TestApp\TestApp.csproj"", ""{D65E5A1F-719F-4F95-8835-88BDD67AD457}"" +EndProject +Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""Solution Items"", ""Solution Items"", ""{FAACC4BE-31AE-4EB7-A4C8-5BB4617EB4AF}"" + ProjectSection(SolutionItems) = preProject + readme.txt = readme.txt + EndProjectSection +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 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.ActiveCfg = Debug|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.Build.0 = Debug|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.ActiveCfg = Debug|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.Build.0 = Debug|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.Build.0 = Release|Any CPU + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.ActiveCfg = Release|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.Build.0 = Release|x64 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.ActiveCfg = Release|x86 + {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal +"; + [Theory] [InlineData("PJAppWithSlnVersion14", "Visual Studio 15", "15.0.26114.2", "10.0.40219.1")] [InlineData("PJAppWithSlnVersion15", "Visual Studio 15 Custom", "15.9.12345.4", "10.9.1234.5")] @@ -96,6 +173,29 @@ namespace Microsoft.DotNet.Migration.Tests "PJAppWithSlnAndXprojRefThatRefsCsprojWhereSlnDoesNotRefCsproj"); } + [Theory] + [InlineData("NoSolutionItemsAfterMigration.sln", ExpectedSlnFileAfterRemovingAllSolutionItems)] + [InlineData("ReadmeSolutionItemAfterMigration.sln", ExpectedSlnFileAfterRemovingAllSolutionItemsExceptReadme)] + public void WhenMigratingAnSlnLinksReferencingItemsMovedToBackupAreRemoved( + string slnFileName, + string expectedSlnContents) + { + var projectDirectory = TestAssets + .Get("NonRestoredTestProjects", "PJAppWithSlnAndSolutionItemsToMoveToBackup") + .CreateInstance(Path.GetFileNameWithoutExtension(slnFileName)) + .WithSourceFiles() + .Root + .FullName; + + new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .Execute($"migrate \"{slnFileName}\"") + .Should().Pass(); + + File.ReadAllText(Path.Combine(projectDirectory, slnFileName)) + .Should().BeVisuallyEquivalentTo(expectedSlnContents); + } + private void MigrateAndBuild(string groupName, string projectName, [CallerMemberName] string callingMethod = "", string identifier = "") { var projectDirectory = TestAssets From a2088e26413160325dd8532b2b8497354e09ee8f Mon Sep 17 00:00:00 2001 From: Justin Goshi Date: Mon, 23 Jan 2017 13:01:58 -0800 Subject: [PATCH 2/4] Refactor and finish the feature --- src/dotnet/SlnFileExtensions.cs | 310 ++++++++++++++++++ .../commands/dotnet-migrate/MigrateCommand.cs | 10 +- src/dotnet/commands/dotnet-sln/add/Program.cs | 165 +--------- .../commands/dotnet-sln/remove/Program.cs | 121 +------ .../GivenThatIWantToMigrateSolutions.cs | 2 +- 5 files changed, 322 insertions(+), 286 deletions(-) create mode 100644 src/dotnet/SlnFileExtensions.cs diff --git a/src/dotnet/SlnFileExtensions.cs b/src/dotnet/SlnFileExtensions.cs new file mode 100644 index 000000000..fc8054ef8 --- /dev/null +++ b/src/dotnet/SlnFileExtensions.cs @@ -0,0 +1,310 @@ +// 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.Build.Construction; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Execution; +using Microsoft.DotNet.Cli.Sln.Internal; +using Microsoft.DotNet.Cli.Utils; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Microsoft.DotNet.Tools.Common +{ + public static class SlnFileExtensions + { + public static void AddProject(this SlnFile slnFile, string fullProjectPath) + { + var relativeProjectPath = PathUtility.GetRelativePath( + PathUtility.EnsureTrailingSlash(slnFile.BaseDirectory), + fullProjectPath); + + if (slnFile.Projects.Any((p) => + string.Equals(p.FilePath, relativeProjectPath, StringComparison.OrdinalIgnoreCase))) + { + Reporter.Output.WriteLine(string.Format( + CommonLocalizableStrings.SolutionAlreadyContainsProject, + slnFile.FullPath, + relativeProjectPath)); + } + else + { + var projectInstance = new ProjectInstance(fullProjectPath); + + var slnProject = new SlnProject + { + Id = projectInstance.GetProjectId(), + TypeGuid = projectInstance.GetProjectTypeGuid(), + Name = Path.GetFileNameWithoutExtension(relativeProjectPath), + FilePath = relativeProjectPath + }; + + slnFile.AddDefaultBuildConfigurations(slnProject); + + slnFile.AddSolutionFolders(slnProject); + + slnFile.Projects.Add(slnProject); + + Reporter.Output.WriteLine( + string.Format(CommonLocalizableStrings.ProjectAddedToTheSolution, relativeProjectPath)); + } + } + + public static void AddDefaultBuildConfigurations(this SlnFile slnFile, SlnProject slnProject) + { + var defaultConfigurations = new List() + { + "Debug|Any CPU", + "Debug|x64", + "Debug|x86", + "Release|Any CPU", + "Release|x64", + "Release|x86", + }; + + // NOTE: The order you create the sections determines the order they are written to the sln + // file. In the case of an empty sln file, in order to make sure the solution configurations + // section comes first we need to add it first. This doesn't affect correctness but does + // stop VS from re-ordering things later on. Since we are keeping the SlnFile class low-level + // it shouldn't care about the VS implementation details. That's why we handle this here. + AddDefaultSolutionConfigurations(defaultConfigurations, slnFile.SolutionConfigurationsSection); + AddDefaultProjectConfigurations( + defaultConfigurations, + slnFile.ProjectConfigurationsSection.GetOrCreatePropertySet(slnProject.Id)); + } + + private static void AddDefaultSolutionConfigurations( + List defaultConfigurations, + SlnPropertySet solutionConfigs) + { + foreach (var config in defaultConfigurations) + { + if (!solutionConfigs.ContainsKey(config)) + { + solutionConfigs[config] = config; + } + } + } + + private static void AddDefaultProjectConfigurations( + List defaultConfigurations, + SlnPropertySet projectConfigs) + { + foreach (var config in defaultConfigurations) + { + var activeCfgKey = $"{config}.ActiveCfg"; + if (!projectConfigs.ContainsKey(activeCfgKey)) + { + projectConfigs[activeCfgKey] = config; + } + + var build0Key = $"{config}.Build.0"; + if (!projectConfigs.ContainsKey(build0Key)) + { + projectConfigs[build0Key] = config; + } + } + } + + public static void AddSolutionFolders(this SlnFile slnFile, SlnProject slnProject) + { + var solutionFolders = slnProject.GetSolutionFoldersFromProject(); + + if (solutionFolders.Any()) + { + var nestedProjectsSection = slnFile.Sections.GetOrCreateSection( + "NestedProjects", + SlnSectionType.PreProcess); + + var pathToGuidMap = slnFile.GetSolutionFolderPaths(nestedProjectsSection.Properties); + + string parentDirGuid = null; + var solutionFolderHierarchy = string.Empty; + foreach (var dir in solutionFolders) + { + solutionFolderHierarchy = Path.Combine(solutionFolderHierarchy, dir); + if (pathToGuidMap.ContainsKey(solutionFolderHierarchy)) + { + parentDirGuid = pathToGuidMap[solutionFolderHierarchy]; + } + else + { + 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; + } + } + + private static IDictionary GetSolutionFolderPaths( + this SlnFile slnFile, + SlnPropertySet nestedProjects) + { + var solutionFolderPaths = new Dictionary(); + + var solutionFolderProjects = slnFile.Projects.GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid); + foreach (var slnProject in solutionFolderProjects) + { + var path = slnProject.FilePath; + var id = slnProject.Id; + while (nestedProjects.ContainsKey(id)) + { + id = nestedProjects[id]; + var parentSlnProject = solutionFolderProjects.Where(p => p.Id == id).Single(); + path = Path.Combine(parentSlnProject.FilePath, path); + } + + solutionFolderPaths[path] = slnProject.Id; + } + + return solutionFolderPaths; + } + + public static bool RemoveProject(this SlnFile slnFile, string projectPath) + { + var projectPathNormalized = PathUtility.GetPathWithDirectorySeparator(projectPath); + + var projectsToRemove = slnFile.Projects.Where((p) => + string.Equals(p.FilePath, projectPathNormalized, StringComparison.OrdinalIgnoreCase)).ToList(); + + bool projectRemoved = false; + if (projectsToRemove.Count == 0) + { + Reporter.Output.WriteLine(string.Format( + CommonLocalizableStrings.ProjectReferenceCouldNotBeFound, + projectPath)); + } + else + { + foreach (var slnProject in projectsToRemove) + { + var buildConfigsToRemove = slnFile.ProjectConfigurationsSection.GetPropertySet(slnProject.Id); + if (buildConfigsToRemove != null) + { + slnFile.ProjectConfigurationsSection.Remove(buildConfigsToRemove); + } + + var nestedProjectsSection = slnFile.Sections.GetSection( + "NestedProjects", + SlnSectionType.PreProcess); + if (nestedProjectsSection != null && nestedProjectsSection.Properties.ContainsKey(slnProject.Id)) + { + nestedProjectsSection.Properties.Remove(slnProject.Id); + } + + slnFile.Projects.Remove(slnProject); + Reporter.Output.WriteLine( + string.Format(CommonLocalizableStrings.ProjectReferenceRemoved, slnProject.FilePath)); + } + + projectRemoved = true; + } + + return projectRemoved; + } + + public static void RemoveEmptyConfigurationSections(this SlnFile slnFile) + { + if (slnFile.Projects.Count == 0) + { + var solutionConfigs = slnFile.Sections.GetSection("SolutionConfigurationPlatforms"); + if (solutionConfigs != null) + { + slnFile.Sections.Remove(solutionConfigs); + } + + var projectConfigs = slnFile.Sections.GetSection("ProjectConfigurationPlatforms"); + if (projectConfigs != null) + { + slnFile.Sections.Remove(projectConfigs); + } + } + } + + public static void RemoveEmptySolutionFolders(this SlnFile slnFile) + { + var solutionFolderProjects = slnFile.Projects + .GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid) + .ToList(); + + if (solutionFolderProjects.Any()) + { + var nestedProjectsSection = slnFile.Sections.GetSection( + "NestedProjects", + SlnSectionType.PreProcess); + + if (nestedProjectsSection == null) + { + foreach (var solutionFolderProject in solutionFolderProjects) + { + if (solutionFolderProject.Sections.Count() == 0) + { + slnFile.Projects.Remove(solutionFolderProject); + } + } + } + else + { + var solutionFoldersInUse = slnFile.GetSolutionFoldersThatContainProjectsInItsHierarchy( + nestedProjectsSection.Properties); + + foreach (var solutionFolderProject in solutionFolderProjects) + { + if (!solutionFoldersInUse.Contains(solutionFolderProject.Id)) + { + nestedProjectsSection.Properties.Remove(solutionFolderProject.Id); + if (solutionFolderProject.Sections.Count() == 0) + { + slnFile.Projects.Remove(solutionFolderProject); + } + } + } + + if (nestedProjectsSection.IsEmpty) + { + slnFile.Sections.Remove(nestedProjectsSection); + } + } + } + } + + private static HashSet GetSolutionFoldersThatContainProjectsInItsHierarchy( + this SlnFile slnFile, + SlnPropertySet nestedProjects) + { + var solutionFoldersInUse = new HashSet(); + + var nonSolutionFolderProjects = slnFile.Projects.GetProjectsNotOfType( + ProjectTypeGuids.SolutionFolderGuid); + + foreach (var nonSolutionFolderProject in nonSolutionFolderProjects) + { + var id = nonSolutionFolderProject.Id; + while (nestedProjects.ContainsKey(id)) + { + id = nestedProjects[id]; + solutionFoldersInUse.Add(id); + } + } + + return solutionFoldersInUse; + } + } +} diff --git a/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs b/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs index 70c9cd68d..c76293d71 100644 --- a/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs +++ b/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs @@ -174,9 +174,7 @@ namespace Microsoft.DotNet.Tools.Migrate private void RemoveReferencesToMigratedFiles(SlnFile slnFile) { - // TODO: Need to merge my PRs then I can call the ProjectCollection extension methods. - // And create new extension methods. - var solutionFolders = slnFile.Projects.Where(p => p.TypeGuid == ProjectTypeGuids.SolutionFolderGuid); + var solutionFolders = slnFile.Projects.GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid); foreach (var solutionFolder in solutionFolders) { @@ -184,8 +182,14 @@ namespace Microsoft.DotNet.Tools.Migrate if (solutionItems != null && solutionItems.Properties.ContainsKey("global.json")) { solutionItems.Properties.Remove("global.json"); + if (solutionItems.IsEmpty) + { + solutionFolder.Sections.Remove(solutionItems); + } } } + + slnFile.RemoveEmptySolutionFolders(); } private void AddProject(string slnPath, string csprojPath) diff --git a/src/dotnet/commands/dotnet-sln/add/Program.cs b/src/dotnet/commands/dotnet-sln/add/Program.cs index 889506662..6b6124a29 100644 --- a/src/dotnet/commands/dotnet-sln/add/Program.cs +++ b/src/dotnet/commands/dotnet-sln/add/Program.cs @@ -1,9 +1,6 @@ // 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.Build.Construction; -using Microsoft.Build.Evaluation; -using Microsoft.Build.Execution; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; @@ -49,7 +46,7 @@ namespace Microsoft.DotNet.Tools.Sln.Add int preAddProjectCount = slnFile.Projects.Count; foreach (var fullProjectPath in fullProjectPaths) { - AddProject(slnFile, fullProjectPath); + slnFile.AddProject(fullProjectPath); } if (slnFile.Projects.Count > preAddProjectCount) @@ -59,165 +56,5 @@ namespace Microsoft.DotNet.Tools.Sln.Add return 0; } - - private void AddProject(SlnFile slnFile, string fullProjectPath) - { - var relativeProjectPath = PathUtility.GetRelativePath( - PathUtility.EnsureTrailingSlash(slnFile.BaseDirectory), - fullProjectPath); - - if (slnFile.Projects.Any((p) => - string.Equals(p.FilePath, relativeProjectPath, StringComparison.OrdinalIgnoreCase))) - { - Reporter.Output.WriteLine(string.Format( - CommonLocalizableStrings.SolutionAlreadyContainsProject, - slnFile.FullPath, - relativeProjectPath)); - } - else - { - var projectInstance = new ProjectInstance(fullProjectPath); - - var slnProject = new SlnProject - { - Id = projectInstance.GetProjectId(), - TypeGuid = projectInstance.GetProjectTypeGuid(), - Name = Path.GetFileNameWithoutExtension(relativeProjectPath), - FilePath = relativeProjectPath - }; - - AddDefaultBuildConfigurations(slnFile, slnProject); - - AddSolutionFolders(slnFile, slnProject); - - slnFile.Projects.Add(slnProject); - - Reporter.Output.WriteLine( - string.Format(CommonLocalizableStrings.ProjectAddedToTheSolution, relativeProjectPath)); - } - } - - private void AddDefaultBuildConfigurations(SlnFile slnFile, SlnProject slnProject) - { - var defaultConfigurations = new List() - { - "Debug|Any CPU", - "Debug|x64", - "Debug|x86", - "Release|Any CPU", - "Release|x64", - "Release|x86", - }; - - // NOTE: The order you create the sections determines the order they are written to the sln - // file. In the case of an empty sln file, in order to make sure the solution configurations - // section comes first we need to add it first. This doesn't affect correctness but does - // stop VS from re-ordering things later on. Since we are keeping the SlnFile class low-level - // it shouldn't care about the VS implementation details. That's why we handle this here. - AddDefaultSolutionConfigurations(defaultConfigurations, slnFile.SolutionConfigurationsSection); - AddDefaultProjectConfigurations( - defaultConfigurations, - slnFile.ProjectConfigurationsSection.GetOrCreatePropertySet(slnProject.Id)); - } - - private void AddDefaultSolutionConfigurations( - List defaultConfigurations, - SlnPropertySet solutionConfigs) - { - foreach (var config in defaultConfigurations) - { - if (!solutionConfigs.ContainsKey(config)) - { - solutionConfigs[config] = config; - } - } - } - - private void AddDefaultProjectConfigurations( - List defaultConfigurations, - SlnPropertySet projectConfigs) - { - foreach (var config in defaultConfigurations) - { - var activeCfgKey = $"{config}.ActiveCfg"; - if (!projectConfigs.ContainsKey(activeCfgKey)) - { - projectConfigs[activeCfgKey] = config; - } - - var build0Key = $"{config}.Build.0"; - if (!projectConfigs.ContainsKey(build0Key)) - { - projectConfigs[build0Key] = config; - } - } - } - - private void AddSolutionFolders(SlnFile slnFile, SlnProject slnProject) - { - var solutionFolders = slnProject.GetSolutionFoldersFromProject(); - - if (solutionFolders.Any()) - { - var nestedProjectsSection = slnFile.Sections.GetOrCreateSection( - "NestedProjects", - SlnSectionType.PreProcess); - - var pathToGuidMap = GetSolutionFolderPaths(slnFile, nestedProjectsSection.Properties); - - string parentDirGuid = null; - var solutionFolderHierarchy = string.Empty; - foreach (var dir in solutionFolders) - { - solutionFolderHierarchy = Path.Combine(solutionFolderHierarchy, dir); - if (pathToGuidMap.ContainsKey(solutionFolderHierarchy)) - { - parentDirGuid = pathToGuidMap[solutionFolderHierarchy]; - } - else - { - 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; - } - } - - private IDictionary GetSolutionFolderPaths(SlnFile slnFile, SlnPropertySet nestedProjects) - { - var solutionFolderPaths = new Dictionary(); - - var solutionFolderProjects = slnFile.Projects.GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid); - foreach (var slnProject in solutionFolderProjects) - { - var path = slnProject.FilePath; - var id = slnProject.Id; - while (nestedProjects.ContainsKey(id)) - { - id = nestedProjects[id]; - var parentSlnProject = solutionFolderProjects.Where(p => p.Id == id).Single(); - path = Path.Combine(parentSlnProject.FilePath, path); - } - - solutionFolderPaths[path] = slnProject.Id; - } - - return solutionFolderPaths; - } } } diff --git a/src/dotnet/commands/dotnet-sln/remove/Program.cs b/src/dotnet/commands/dotnet-sln/remove/Program.cs index 18f3866e5..c147052cd 100644 --- a/src/dotnet/commands/dotnet-sln/remove/Program.cs +++ b/src/dotnet/commands/dotnet-sln/remove/Program.cs @@ -48,12 +48,12 @@ namespace Microsoft.DotNet.Tools.Sln.Remove bool slnChanged = false; foreach (var path in relativeProjectPaths) { - slnChanged |= RemoveProject(slnFile, path); + slnChanged |= slnFile.RemoveProject(path); } - RemoveEmptyConfigurationSections(slnFile); + slnFile.RemoveEmptyConfigurationSections(); - RemoveEmptySolutionFolders(slnFile); + slnFile.RemoveEmptySolutionFolders(); if (slnChanged) { @@ -62,120 +62,5 @@ namespace Microsoft.DotNet.Tools.Sln.Remove return 0; } - - private bool RemoveProject(SlnFile slnFile, string projectPath) - { - var projectPathNormalized = PathUtility.GetPathWithDirectorySeparator(projectPath); - - var projectsToRemove = slnFile.Projects.Where((p) => - string.Equals(p.FilePath, projectPathNormalized, StringComparison.OrdinalIgnoreCase)).ToList(); - - bool projectRemoved = false; - if (projectsToRemove.Count == 0) - { - Reporter.Output.WriteLine(string.Format( - CommonLocalizableStrings.ProjectReferenceCouldNotBeFound, - projectPath)); - } - else - { - foreach (var slnProject in projectsToRemove) - { - var buildConfigsToRemove = slnFile.ProjectConfigurationsSection.GetPropertySet(slnProject.Id); - if (buildConfigsToRemove != null) - { - slnFile.ProjectConfigurationsSection.Remove(buildConfigsToRemove); - } - - var nestedProjectsSection = slnFile.Sections.GetSection( - "NestedProjects", - SlnSectionType.PreProcess); - if (nestedProjectsSection != null && nestedProjectsSection.Properties.ContainsKey(slnProject.Id)) - { - nestedProjectsSection.Properties.Remove(slnProject.Id); - } - - slnFile.Projects.Remove(slnProject); - Reporter.Output.WriteLine( - string.Format(CommonLocalizableStrings.ProjectReferenceRemoved, slnProject.FilePath)); - } - - projectRemoved = true; - } - - return projectRemoved; - } - - private void RemoveEmptyConfigurationSections(SlnFile slnFile) - { - if (slnFile.Projects.Count == 0) - { - var solutionConfigs = slnFile.Sections.GetSection("SolutionConfigurationPlatforms"); - if (solutionConfigs != null) - { - slnFile.Sections.Remove(solutionConfigs); - } - - var projectConfigs = slnFile.Sections.GetSection("ProjectConfigurationPlatforms"); - if (projectConfigs != null) - { - slnFile.Sections.Remove(projectConfigs); - } - } - } - - private void RemoveEmptySolutionFolders(SlnFile slnFile) - { - var solutionFolderProjects = slnFile.Projects - .GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid) - .ToList(); - - if (solutionFolderProjects.Any()) - { - var nestedProjectsSection = slnFile.Sections.GetSection( - "NestedProjects", - SlnSectionType.PreProcess); - - var solutionFoldersInUse = GetSolutionFoldersThatContainProjectsInItsHierarchy( - slnFile, - nestedProjectsSection.Properties); - - foreach (var solutionFolderProject in solutionFolderProjects) - { - if (!solutionFoldersInUse.Contains(solutionFolderProject.Id)) - { - slnFile.Projects.Remove(solutionFolderProject); - nestedProjectsSection.Properties.Remove(solutionFolderProject.Id); - } - } - - if (nestedProjectsSection.IsEmpty) - { - slnFile.Sections.Remove(nestedProjectsSection); - } - } - } - - private HashSet GetSolutionFoldersThatContainProjectsInItsHierarchy( - SlnFile slnFile, - SlnPropertySet nestedProjects) - { - var solutionFoldersInUse = new HashSet(); - - var nonSolutionFolderProjects = slnFile.Projects.GetProjectsNotOfType( - ProjectTypeGuids.SolutionFolderGuid); - - foreach (var nonSolutionFolderProject in nonSolutionFolderProjects) - { - var id = nonSolutionFolderProject.Id; - while (nestedProjects.ContainsKey(id)) - { - id = nestedProjects[id]; - solutionFoldersInUse.Add(id); - } - } - - return solutionFoldersInUse; - } } } diff --git a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs index f70ce7c08..0a670cb7f 100644 --- a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs +++ b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs @@ -234,7 +234,7 @@ EndGlobal string expectedSlnContents) { var projectDirectory = TestAssets - .Get("NonRestoredTestProjects", "PJAppWithSlnAndSolutionItemsToMoveToBackup") + .GetProjectJson(TestAssetKinds.NonRestoredTestProjects, "PJAppWithSlnAndSolutionItemsToMoveToBackup") .CreateInstance(Path.GetFileNameWithoutExtension(slnFileName)) .WithSourceFiles() .Root From 731ab92c1fd9ba7ac0fb15ed6fb2f79723bacb45 Mon Sep 17 00:00:00 2001 From: Justin Goshi Date: Tue, 24 Jan 2017 10:57:07 -0800 Subject: [PATCH 3/4] Update the test --- .../GivenThatIWantToMigrateSolutions.cs | 99 ++++--------------- 1 file changed, 17 insertions(+), 82 deletions(-) diff --git a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs index 0a670cb7f..cbfa8ba50 100644 --- a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs +++ b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateSolutions.cs @@ -14,83 +14,6 @@ namespace Microsoft.DotNet.Migration.Tests { public class GivenThatIWantToMigrateSolutions : TestBase { - private const string ExpectedSlnFileAfterRemovingAllSolutionItems = @" -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}"") = ""TestApp"", ""TestApp\TestApp.csproj"", ""{D65E5A1F-719F-4F95-8835-88BDD67AD457}"" -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 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.ActiveCfg = Debug|x64 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.Build.0 = Debug|x64 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.ActiveCfg = Debug|x86 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.Build.0 = Debug|x86 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.Build.0 = Release|Any CPU - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.ActiveCfg = Release|x64 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.Build.0 = Release|x64 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.ActiveCfg = Release|x86 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal -"; - - private const string ExpectedSlnFileAfterRemovingAllSolutionItemsExceptReadme = @" -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}"") = ""TestApp"", ""TestApp\TestApp.csproj"", ""{D65E5A1F-719F-4F95-8835-88BDD67AD457}"" -EndProject -Project(""{2150E333-8FDC-42A3-9474-1A3956D46DE8}"") = ""Solution Items"", ""Solution Items"", ""{FAACC4BE-31AE-4EB7-A4C8-5BB4617EB4AF}"" - ProjectSection(SolutionItems) = preProject - readme.txt = readme.txt - EndProjectSection -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 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.ActiveCfg = Debug|x64 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x64.Build.0 = Debug|x64 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.ActiveCfg = Debug|x86 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Debug|x86.Build.0 = Debug|x86 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|Any CPU.Build.0 = Release|Any CPU - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.ActiveCfg = Release|x64 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x64.Build.0 = Release|x64 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.ActiveCfg = Release|x86 - {D65E5A1F-719F-4F95-8835-88BDD67AD457}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal -"; - [Theory] [InlineData("PJAppWithSlnVersion14", "Visual Studio 15", "15.0.26114.2", "10.0.40219.1")] [InlineData("PJAppWithSlnVersion15", "Visual Studio 15 Custom", "15.9.12345.4", "10.9.1234.5")] @@ -227,11 +150,11 @@ EndGlobal } [Theory] - [InlineData("NoSolutionItemsAfterMigration.sln", ExpectedSlnFileAfterRemovingAllSolutionItems)] - [InlineData("ReadmeSolutionItemAfterMigration.sln", ExpectedSlnFileAfterRemovingAllSolutionItemsExceptReadme)] + [InlineData("NoSolutionItemsAfterMigration.sln", false)] + [InlineData("ReadmeSolutionItemAfterMigration.sln", true)] public void WhenMigratingAnSlnLinksReferencingItemsMovedToBackupAreRemoved( string slnFileName, - string expectedSlnContents) + bool solutionItemsContainsReadme) { var projectDirectory = TestAssets .GetProjectJson(TestAssetKinds.NonRestoredTestProjects, "PJAppWithSlnAndSolutionItemsToMoveToBackup") @@ -245,8 +168,20 @@ EndGlobal .Execute($"migrate \"{slnFileName}\"") .Should().Pass(); - File.ReadAllText(Path.Combine(projectDirectory, slnFileName)) - .Should().BeVisuallyEquivalentTo(expectedSlnContents); + var slnFile = SlnFile.Read(Path.Combine(projectDirectory, slnFileName)); + var solutionFolders = slnFile.Projects.Where(p => p.TypeGuid == ProjectTypeGuids.SolutionFolderGuid); + if (solutionItemsContainsReadme) + { + solutionFolders.Count().Should().Be(1); + var solutionItems = solutionFolders.Single().Sections.GetSection("SolutionItems"); + solutionItems.Should().NotBeNull(); + solutionItems.Properties.Count().Should().Be(1); + solutionItems.Properties["readme.txt"].Should().Be("readme.txt"); + } + else + { + solutionFolders.Count().Should().Be(0); + } } private void MigrateAndBuild(string groupName, string projectName, [CallerMemberName] string callingMethod = "", string identifier = "") From 861d1edfd36dec214126191cc6890528bd2608a4 Mon Sep 17 00:00:00 2001 From: Justin Goshi Date: Tue, 24 Jan 2017 15:02:19 -0800 Subject: [PATCH 4/4] Address PR comments --- src/dotnet/SlnFileExtensions.cs | 22 +++++++++++++++++++- src/dotnet/SlnProjectCollectionExtensions.cs | 2 +- src/dotnet/SlnProjectExtensions.cs | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/dotnet/SlnFileExtensions.cs b/src/dotnet/SlnFileExtensions.cs index fc8054ef8..7fbf0fb70 100644 --- a/src/dotnet/SlnFileExtensions.cs +++ b/src/dotnet/SlnFileExtensions.cs @@ -13,10 +13,15 @@ using System.Linq; namespace Microsoft.DotNet.Tools.Common { - public static class SlnFileExtensions + internal static class SlnFileExtensions { public static void AddProject(this SlnFile slnFile, string fullProjectPath) { + if (string.IsNullOrEmpty(fullProjectPath)) + { + throw new ArgumentException(); + } + var relativeProjectPath = PathUtility.GetRelativePath( PathUtility.EnsureTrailingSlash(slnFile.BaseDirectory), fullProjectPath); @@ -54,6 +59,11 @@ namespace Microsoft.DotNet.Tools.Common public static void AddDefaultBuildConfigurations(this SlnFile slnFile, SlnProject slnProject) { + if (slnProject == null) + { + throw new ArgumentException(); + } + var defaultConfigurations = new List() { "Debug|Any CPU", @@ -110,6 +120,11 @@ namespace Microsoft.DotNet.Tools.Common public static void AddSolutionFolders(this SlnFile slnFile, SlnProject slnProject) { + if (slnProject == null) + { + throw new ArgumentException(); + } + var solutionFolders = slnProject.GetSolutionFoldersFromProject(); if (solutionFolders.Any()) @@ -179,6 +194,11 @@ namespace Microsoft.DotNet.Tools.Common public static bool RemoveProject(this SlnFile slnFile, string projectPath) { + if (string.IsNullOrEmpty(projectPath)) + { + throw new ArgumentException(); + } + var projectPathNormalized = PathUtility.GetPathWithDirectorySeparator(projectPath); var projectsToRemove = slnFile.Projects.Where((p) => diff --git a/src/dotnet/SlnProjectCollectionExtensions.cs b/src/dotnet/SlnProjectCollectionExtensions.cs index d99b34eee..09499e5a2 100644 --- a/src/dotnet/SlnProjectCollectionExtensions.cs +++ b/src/dotnet/SlnProjectCollectionExtensions.cs @@ -8,7 +8,7 @@ using System.Linq; namespace Microsoft.DotNet.Tools.Common { - public static class SlnProjectCollectionExtensions + internal static class SlnProjectCollectionExtensions { public static IEnumerable GetProjectsByType( this SlnProjectCollection projects, diff --git a/src/dotnet/SlnProjectExtensions.cs b/src/dotnet/SlnProjectExtensions.cs index 925089beb..80ebf5d3e 100644 --- a/src/dotnet/SlnProjectExtensions.cs +++ b/src/dotnet/SlnProjectExtensions.cs @@ -8,7 +8,7 @@ using System.Linq; namespace Microsoft.DotNet.Tools.Common { - public static class SlnProjectExtensions + internal static class SlnProjectExtensions { public static IList GetSolutionFoldersFromProject(this SlnProject project) {