Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve folder GUIDs in existing solutions #248

Merged
merged 1 commit into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnFileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,12 @@ public void TryParseExistingSolution()
[@"test\Microsoft.VisualStudio.SlnGen.UnitTests\Microsoft.VisualStudio.SlnGen.UnitTests.csproj"] = new Guid("B55ACBF0-DC34-44BA-8535-8F81325B6D70"),
};

Dictionary<string, Guid> folders = new Dictionary<string, Guid>
{
[@"FolderA"] = new Guid("9C915FE4-72A5-4368-8979-32B3983E6041"),
[@"FolderB"] = new Guid("D3A9F802-38CC-4F8D-8DE9-8DF9C8B7EADC"),
};

Dictionary<FileInfo, Guid> projectFiles = projects.ToDictionary(i => new FileInfo(Path.Combine(solutionFilePath.DirectoryName!, i.Key)), i => i.Value);

foreach (KeyValuePair<FileInfo, Guid> item in projectFiles)
Expand Down Expand Up @@ -465,7 +471,7 @@ public void TryParseExistingSolution()

solutionGuid.ShouldBe(Guid.Parse("CFFC4187-96EE-4465-B5B3-0BAFD3C14BB6"));

projectGuidsByPath.ShouldBe(projectFiles.Select(i => new KeyValuePair<string, Guid>(i.Key.FullName, i.Value)));
projectGuidsByPath.ShouldBe(projectFiles.Select(i => new KeyValuePair<string, Guid>(i.Key.FullName, i.Value)).Concat(folders));
}

[Fact]
Expand Down
54 changes: 49 additions & 5 deletions src/Microsoft.VisualStudio.SlnGen/SlnFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Microsoft.VisualStudio.SlnGen
{
Expand Down Expand Up @@ -51,6 +52,11 @@ public sealed class SlnFile
/// </summary>
private static readonly string[] ProjectSectionSeparator = { "\", \"" };

/// <summary>
/// A regular expression used to parse the project section.
/// </summary>
private static readonly Regex GuidRegex = new (@"(?<Guid>\{[0-9a-fA-F\-]+\})");

/// <summary>
/// The file format version.
/// </summary>
Expand Down Expand Up @@ -227,11 +233,41 @@ public static bool TryParseExistingSolution(string path, out Guid solutionGuid,

if (projectDetails.Length == 3)
{
FileInfo projectFullPath = new FileInfo(Path.Combine(solutionFileDirectory, projectDetails[1].Trim().Trim('\"')));
Match projectGuidMatch = GuidRegex.Match(projectDetails[2]);

if (!projectGuidMatch.Groups["Guid"].Success)
{
continue;
}

string projectGuidString = projectGuidMatch.Groups["Guid"].Value;

Match projectTypeGuidMatch = GuidRegex.Match(projectDetails[0]);

if (!projectTypeGuidMatch.Groups["Guid"].Success)
{
continue;
}

if (projectFullPath.Exists && Guid.TryParse(projectDetails[2].Trim('\"'), out Guid projectGuid))
if (!Guid.TryParse(projectGuidString, out Guid projectGuid) || !Guid.TryParse(projectTypeGuidMatch.Groups["Guid"].Value, out Guid projectTypeGuid))
{
projectGuids[projectFullPath.FullName] = projectGuid;
continue;
}

string projectPath = projectDetails[1].Trim().Trim('\"');

if (projectTypeGuid.Equals(SlnFolder.FolderProjectTypeGuid))
{
projectGuids[projectPath] = projectGuid;
}
else
{
FileInfo projectFileInfo = new FileInfo(Path.Combine(solutionFileDirectory, projectPath));

if (projectFileInfo.Exists)
{
projectGuids[projectFileInfo.FullName] = projectGuid;
}
}
}

Expand Down Expand Up @@ -355,7 +391,7 @@ internal void Save(string rootPath, TextWriter writer, bool useFolders, bool col

if (SolutionItems.Count > 0)
{
writer.WriteLine($@"Project(""{SlnFolder.FolderProjectTypeGuid}"") = ""Solution Items"", ""Solution Items"", ""{Guid.NewGuid().ToSolutionString()}"" ");
writer.WriteLine($@"Project(""{SlnFolder.FolderProjectTypeGuidString}"") = ""Solution Items"", ""Solution Items"", ""{Guid.NewGuid().ToSolutionString()}"" ");
writer.WriteLine(" ProjectSection(SolutionItems) = preProject");
foreach (string solutionItem in SolutionItems.Select(i => i.ToRelativePath(rootPath)))
{
Expand Down Expand Up @@ -387,7 +423,15 @@ internal void Save(string rootPath, TextWriter writer, bool useFolders, bool col
{
foreach (SlnFolder folder in hierarchy.Folders)
{
writer.WriteLine($@"Project(""{folder.ProjectTypeGuid}"") = ""{folder.Name}"", ""{(useFolders ? folder.FullPath.ToRelativePath(rootPath) : folder.Name)}"", ""{folder.FolderGuid.ToSolutionString()}""");
string projectPath = useFolders ? folder.FullPath.ToRelativePath(rootPath) : folder.Name;

// Try to preserve the folder GUID if a matching relative folder path was parsed from an existing solution
if (ExistingProjectGuids != null && ExistingProjectGuids.TryGetValue(projectPath, out Guid projectGuid))
{
folder.FolderGuid = projectGuid;
}

writer.WriteLine($@"Project(""{folder.ProjectTypeGuidString}"") = ""{folder.Name}"", ""{projectPath}"", ""{folder.FolderGuid.ToSolutionString()}""");
writer.WriteLine("EndProject");
}
}
Expand Down
11 changes: 8 additions & 3 deletions src/Microsoft.VisualStudio.SlnGen/SlnFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ public sealed class SlnFolder
/// <summary>
/// The project type GUID for a folder.
/// </summary>
public static readonly string FolderProjectTypeGuid = new Guid(VisualStudioProjectTypeGuids.SolutionFolder).ToSolutionString();
public static readonly Guid FolderProjectTypeGuid = new (VisualStudioProjectTypeGuids.SolutionFolder);

/// <summary>
/// The project type GUID for a folder as a solution string.
/// </summary>
public static readonly string FolderProjectTypeGuidString = FolderProjectTypeGuid.ToSolutionString();

/// <summary>
/// Initializes a new instance of the <see cref="SlnFolder"/> class.
Expand All @@ -32,7 +37,7 @@ public SlnFolder(string path)
/// <summary>
/// Gets the <see cref="Guid" /> of the folder.
/// </summary>
public Guid FolderGuid { get; }
public Guid FolderGuid { get; set; }

/// <summary>
/// Gets a <see cref="List{SlnFolder}" /> of child folders.
Expand Down Expand Up @@ -62,6 +67,6 @@ public SlnFolder(string path)
/// <summary>
/// Gets the project type GUID of the folder.
/// </summary>
public string ProjectTypeGuid => FolderProjectTypeGuid;
public string ProjectTypeGuidString => FolderProjectTypeGuidString;
}
}