From 3ba13c238b824701a11e5c92877eb5ddad2e15f7 Mon Sep 17 00:00:00 2001 From: Meera Ruxmohan Date: Mon, 27 Nov 2023 08:14:34 -0800 Subject: [PATCH] Use MSBuild properties to update solution file name (#557) --- .../SlnFileTests.cs | 95 +++++++++++++++++++ .../ProgramArguments.cs | 32 ++++++- src/Microsoft.VisualStudio.SlnGen/SlnFile.cs | 10 +- 3 files changed, 133 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnFileTests.cs b/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnFileTests.cs index 98df57c9..6f223232 100644 --- a/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnFileTests.cs +++ b/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnFileTests.cs @@ -490,6 +490,87 @@ public void PathsWorkForAllDirectorySeparatorChars() actualSolutionText.ShouldBe(solutionText, StringCompareShould.IgnoreLineEndings); } + [Fact] + public void TestSlnGenProjectNamePropertyForSolutionName() + { + Project[] projects = + { + ProjectCreator.Templates.SdkCsproj(path: GetTempProjectFile("ProjectA")) + .Property("SlnGenProjectName", "RandomComponent") + .Save(), + }; + + string solutionFilePath = GetSolutionFilePath(projects); + solutionFilePath.ShouldContain("RandomComponent.sln"); + } + + [Fact] + public void TestDefaultSolutionName() + { + Project[] projects = + { + ProjectCreator.Templates.SdkCsproj(path: GetTempProjectFile("ProjectA")).Save(), + }; + + string solutionFilePath = GetSolutionFilePath(projects); + solutionFilePath.ShouldContain("ProjectA.sln"); + } + + [Fact] + public void TestSlnGenFoldersPropertyToEnableFolderCreation() + { + Project projectA = ProjectCreator.Templates.SdkCsproj(path: GetTempProjectFile("ProjectA")) + .Property("SlnGenFolders", "true") + .Save(); + + Project projectB = ProjectCreator.Templates.SdkCsproj(path: GetTempProjectFile("ProjectB", directoryPath: "testB")) + .Property("SlnGenFolders", "true") + .Save(); + + Project projectC = ProjectCreator.Templates.SdkCsproj(path: GetTempProjectFile("ProjectC", directoryPath: "testC")) + .Property("SlnGenFolders", "true") + .Save(); + + string solutionFilePath = GetSolutionFilePath(new Project[] { projectA, projectB, projectC }); + string contents = File.ReadAllText(solutionFilePath); + contents.ShouldContain("\"..\\testB\","); + contents.ShouldContain("\"..\\testC\","); + } + + [Fact] + public void TestSlnGenFoldersPropertyToDisableFolderCreation() + { + Project projectA = ProjectCreator.Templates.SdkCsproj(path: GetTempProjectFile("ProjectA")) + .Property("SlnGenFolders", "false") + .Save(); + + Project projectB = ProjectCreator.Templates.SdkCsproj(path: GetTempProjectFile("ProjectB", directoryPath: "testB")) + .Property("SlnGenFolders", "false") + .Save(); + + Project projectC = ProjectCreator.Templates.SdkCsproj(path: GetTempProjectFile("ProjectC", directoryPath: "testC")) + .Property("SlnGenFolders", "false") + .Save(); + + string solutionFilePath = GetSolutionFilePath(new Project[] { projectA, projectB, projectC }); + string contents = File.ReadAllText(solutionFilePath); + contents.ShouldNotContain("\"..\\testB\","); + contents.ShouldNotContain("\"..\\testC\","); + } + + [Fact] + public void TestNoFolderCreation() + { + Project projectA = ProjectCreator.Templates.SdkCsproj(path: GetTempProjectFile("ProjectA")).Save(); + Project projectB = ProjectCreator.Templates.SdkCsproj(path: GetTempProjectFile("ProjectB", directoryPath: "testB")).Save(); + Project projectC = ProjectCreator.Templates.SdkCsproj(path: GetTempProjectFile("ProjectC", directoryPath: "testC")).Save(); + + string solutionFilePath = GetSolutionFilePath(new Project[] { projectA, projectB, projectC }); + string contents = File.ReadAllText(solutionFilePath); + contents.ShouldNotContain("\"..\\testB\","); + contents.ShouldNotContain("\"..\\testC\","); + } + [Fact] public void ProjectConfigurationPlatformOrderingSameAsProjects() { @@ -1206,6 +1287,20 @@ public void SlnProject_IsBuildable_ReflectedAsProjectConfigurationInSolutionIncl false); } + private string GetSolutionFilePath(Project[] projects) + { + ProgramArguments programArguments = new () + { + LaunchVisualStudio = new[] { bool.FalseString }, + }; + + TestLogger testLogger = new (); + + (string solutionFileFullPath, _, _, _) = SlnFile.GenerateSolutionFile(programArguments, projects, testLogger); + + return solutionFileFullPath; + } + private void ValidateProjectInSolution(Action customValidator, SlnProject[] projects, bool useFolders) { string solutionFilePath = GetTempFileName(); diff --git a/src/Microsoft.VisualStudio.SlnGen/ProgramArguments.cs b/src/Microsoft.VisualStudio.SlnGen/ProgramArguments.cs index c2e130a7..a20dcf0a 100644 --- a/src/Microsoft.VisualStudio.SlnGen/ProgramArguments.cs +++ b/src/Microsoft.VisualStudio.SlnGen/ProgramArguments.cs @@ -325,8 +325,23 @@ public ProgramArguments() /// /// Gets a value indicating whether or not folders should be created in the solution. /// + /// The SlnGenFolders property value if it exists />. /// true if folders should be used, otherwise false. - public bool EnableFolders() => GetBoolean(Folders); + public bool EnableFolders(string slnGenFoldersPropertyValue) + { + bool? enableFolders = TryGetBoolean(Folders); + if (enableFolders != null) + { + return enableFolders.Value; // Use the value when available + } + + if (bool.TryParse(slnGenFoldersPropertyValue, out bool result)) + { + return result; // Fall back to the SlnGenFolders property value + } + + return false; + } /// /// Gets the Configuration values based on what was specified as command-line arguments. @@ -517,5 +532,20 @@ private bool GetBoolean(string[] values, bool defaultValue = false) return defaultValue; } + + private bool? TryGetBoolean(string[] values) + { + if (values == null || values.Length == 0) + { + return null; + } + + if (bool.TryParse(values.Last(), out bool result)) + { + return result; + } + + return null; + } } } \ No newline at end of file diff --git a/src/Microsoft.VisualStudio.SlnGen/SlnFile.cs b/src/Microsoft.VisualStudio.SlnGen/SlnFile.cs index 93624415..88aa3091 100644 --- a/src/Microsoft.VisualStudio.SlnGen/SlnFile.cs +++ b/src/Microsoft.VisualStudio.SlnGen/SlnFile.cs @@ -155,7 +155,8 @@ public static (string solutionFileFullPath, int customProjectTypeGuidCount, int solutionDirectoryFullPath = firstProject.DirectoryPath; } - string solutionFileName = Path.ChangeExtension(Path.GetFileName(firstProject.FullPath), "sln"); + var firstProjectName = firstProject.GetPropertyValueOrDefault(MSBuildPropertyNames.SlnGenProjectName, Path.GetFileName(firstProject.FullPath)); + string solutionFileName = Path.ChangeExtension(firstProjectName, "sln"); solutionFileFullPath = Path.Combine(solutionDirectoryFullPath!, solutionFileName); } @@ -197,7 +198,7 @@ public static (string solutionFileFullPath, int customProjectTypeGuidCount, int } } - if (SlnFile.TryParseExistingSolution(solutionFileFullPath, out Guid solutionGuid, out IReadOnlyDictionary projectGuidsByPath)) + if (TryParseExistingSolution(solutionFileFullPath, out Guid solutionGuid, out IReadOnlyDictionary projectGuidsByPath)) { logger.LogMessageNormal("Updating existing solution file and reusing Visual Studio cache"); @@ -211,9 +212,12 @@ public static (string solutionFileFullPath, int customProjectTypeGuidCount, int solution.AddSolutionItems(solutionItems); + string slnGenFoldersPropertyValue = firstProject.GetPropertyValueOrDefault(MSBuildPropertyNames.SlnGenFolders, "false"); + var enableFolders = arguments.EnableFolders(slnGenFoldersPropertyValue); + if (!logger.HasLoggedErrors) { - solution.Save(solutionFileFullPath, arguments.EnableFolders(), logger, arguments.EnableCollapseFolders(), arguments.EnableAlwaysBuild()); + solution.Save(solutionFileFullPath, enableFolders, logger, arguments.EnableCollapseFolders(), arguments.EnableAlwaysBuild()); } return (solutionFileFullPath, customProjectTypeGuids.Count, solutionItems.Count, solution.SolutionGuid);