Skip to content

Commit

Permalink
.slnx support - use the new parser for .sln and .slnx (#10836)
Browse files Browse the repository at this point in the history
* use new parser for .sln (under change wave) and .slnx
  • Loading branch information
surayya-MS authored Nov 1, 2024
1 parent 18eb9ed commit 2e2d372
Show file tree
Hide file tree
Showing 10 changed files with 911 additions and 1,237 deletions.
3 changes: 3 additions & 0 deletions documentation/wiki/ChangeWaves.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ A wave of features is set to "rotate out" (i.e. become standard functionality) t

## Current Rotation of Change Waves

### 17.14
- [.SLNX support - use the new parser for .sln and .slnx](https://github.com/dotnet/msbuild/pull/10836)

### 17.12
- [Log TaskParameterEvent for scalar parameters](https://github.com/dotnet/msbuild/pull/9908)
- [Convert.ToString during a property evaluation uses the InvariantCulture for all types](https://github.com/dotnet/msbuild/pull/9874)
Expand Down
5 changes: 3 additions & 2 deletions eng/BootStrapMsBuild.targets
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
<_NuGetRuntimeDependencies Include="%(RuntimeCopyLocalItems.Identity)" Condition="'@(RuntimeCopyLocalItems->Contains('Newtonsoft.Json'))' == 'true'" />
<_NuGetRuntimeDependencies Include="%(RuntimeCopyLocalItems.Identity)" Condition="'@(RuntimeCopyLocalItems->Contains('NuGetSdkResolver'))' == 'true'" />
<_NuGetRuntimeDependencies Include="%(RuntimeCopyLocalItems.Identity)" Condition="'@(RuntimeCopyLocalItems->Contains('Microsoft.Extensions.'))' == 'true'" />

<_NuGetRuntimeDependencies Include="%(RuntimeCopyLocalItems.Identity)" Condition="'@(RuntimeCopyLocalItems->Contains('Microsoft.VisualStudio.SolutionPersistence'))' == 'true'" />

<!-- NuGet.targets and NuGet.RestoreEx.targets will be in the RuntimeTargetsCopyLocalItems ItemGroup -->
<_NuGetRuntimeDependencies Include="%(RuntimeTargetsCopyLocalItems.Identity)" Condition="'@(RuntimeTargetsCopyLocalItems->Contains('NuGet.'))' == 'true'" />

Expand All @@ -48,7 +49,7 @@

<Target Name="RemoveExtraAssemblyReferences" BeforeTargets="ResolveAssemblyReferences">
<!-- This is really hacky, but these references will cause issues when trying to 'build' this project.
To acquire the NuGet binaries we depend on for local run-time ('bootstrap'), we we are using a PackageReference (to
To acquire the NuGet binaries we depend on for local run-time ('bootstrap'), we are using a PackageReference (to
'NuGet.Build.Tasks' and 'Microsoft.Build.NuGetSdkResolver'). This has the advantage of using NuGets compatibility
check to ensure we choose the right version of those assemblies. But, at 'bootstrap' time these runtime dependencies
need to be in a specific location that does not mesh with NuGet. To resolve this, we include the default
Expand Down
694 changes: 75 additions & 619 deletions src/Build.OM.UnitTests/Construction/SolutionFile_Tests.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.Build.Construction;
using Microsoft.Build.Exceptions;
using Microsoft.Build.Shared;
Expand All @@ -16,11 +17,11 @@

namespace Microsoft.Build.UnitTests.Construction
{
public class SolutionFile_Tests
public class SolutionFile_OldParser_Tests
{
public ITestOutputHelper TestOutputHelper { get; }

public SolutionFile_Tests(ITestOutputHelper testOutputHelper)
public SolutionFile_OldParser_Tests(ITestOutputHelper testOutputHelper)
{
TestOutputHelper = testOutputHelper;
}
Expand Down Expand Up @@ -104,6 +105,42 @@ public void ParseFirstProjectLineWithDifferentSpacing()
proj.ProjectGuid.ShouldBe("Unique name-GUID");
}

/// <summary>
/// A slightly more complicated test where there is some different whitespace.
/// </summary>
[Fact]
public void ParseSolutionWithDifferentSpacing()
{
string solutionFileContents =
@"
Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project(' { Project GUID} ') = ' Project name ', ' Relative path to project file ' , ' {0ABED153-9451-483C-8140-9E8D7306B216} '
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|AnyCPU = Debug|AnyCPU
Release|AnyCPU = Release|AnyCPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0ABED153-9451-483C-8140-9E8D7306B216}.Debug|AnyCPU.ActiveCfg = Debug|AnyCPU
{0ABED153-9451-483C-8140-9E8D7306B216}.Debug|AnyCPU.Build.0 = Debug|AnyCPU
{0ABED153-9451-483C-8140-9E8D7306B216}.Release|AnyCPU.ActiveCfg = Release|AnyCPU
{0ABED153-9451-483C-8140-9E8D7306B216}.Release|AnyCPU.Build.0 = Release|AnyCPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
";

SolutionFile solution = ParseSolutionHelper(solutionFileContents);

Assert.Equal("Project name", solution.ProjectsInOrder[0].ProjectName);
Assert.Equal("Relative path to project file", solution.ProjectsInOrder[0].RelativePath);
Assert.Equal("{0ABED153-9451-483C-8140-9E8D7306B216}", solution.ProjectsInOrder[0].ProjectGuid);
}

/// <summary>
/// First project line with an empty project name. This is somewhat malformed, but we should
/// still behave reasonably instead of crashing.
Expand Down Expand Up @@ -687,6 +724,43 @@ public void ParseFirstProjectLineWhereProjectNameHasSpecialCharacters()
proj.ProjectGuid.ShouldBe("Unique name-GUID");
}

/// <summary>
/// Test some characters that are valid in a file name but that also could be
/// considered a delimiter by a parser. Does quoting work for special characters?
/// </summary>
[Fact]
public void ParseSolutionWhereProjectNameHasSpecialCharacters()
{
string solutionFileContents =
@"
Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project('{Project GUID}') = 'MyProject,(=IsGreat)', 'Relative path to project file' , '{0ABED153-9451-483C-8140-9E8D7306B216}'
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|AnyCPU = Debug|AnyCPU
Release|AnyCPU = Release|AnyCPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0ABED153-9451-483C-8140-9E8D7306B216}.Debug|AnyCPU.ActiveCfg = Debug|AnyCPU
{0ABED153-9451-483C-8140-9E8D7306B216}.Debug|AnyCPU.Build.0 = Debug|AnyCPU
{0ABED153-9451-483C-8140-9E8D7306B216}.Release|AnyCPU.ActiveCfg = Release|AnyCPU
{0ABED153-9451-483C-8140-9E8D7306B216}.Release|AnyCPU.Build.0 = Release|AnyCPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
";

SolutionFile solution = ParseSolutionHelper(solutionFileContents);

Assert.Equal("MyProject,(=IsGreat)", solution.ProjectsInOrder[0].ProjectName);
Assert.Equal("Relative path to project file", solution.ProjectsInOrder[0].RelativePath);
Assert.Equal("{0ABED153-9451-483C-8140-9E8D7306B216}", solution.ProjectsInOrder[0].ProjectGuid);
}

/// <summary>
/// Test some characters that are valid in a file name but that also could be
/// considered a delimiter by a parser. Does quoting work for special characters?
Expand Down Expand Up @@ -2355,5 +2429,58 @@ public void ParseSolutionWithParentedPaths()
solution.ProjectsInOrder[0].AbsolutePath.ShouldBe(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(solution.FullPath)!, expectedRelativePath)));
solution.ProjectsInOrder[0].ProjectGuid.ShouldBe("{0ABED153-9451-483C-8140-9E8D7306B216}");
}

/// <summary>
/// Parse solution file with comments
/// </summary>
[Fact]
public void ParseSolutionWithComments()
{
const string solutionFileContent = @"
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29123.89
MinimumVisualStudioVersion = 10.0.40219.1
Project('{9A19103F-16F7-4668-BE54-9A1E7A4F7556}') = 'SlnCommentTest', 'SlnCommentTest.csproj', '{00000000-0000-0000-FFFF-FFFFFFFFFFFF}'
EndProject
Project('{2150E333-8FDC-42A3-9474-1A3956D46DE8}') = 'Solution Items', 'Solution Items', '{054DED3B-B890-4652-B449-839F581E5D86}'
ProjectSection(SolutionItems) = preProject
SlnFile.txt = SlnFile.txt
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{00000000-0000-0000-FFFF-FFFFFFFFFFFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{00000000-0000-0000-FFFF-FFFFFFFFFFFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{00000000-0000-0000-FFFF-FFFFFFFFFFFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{00000000-0000-0000-FFFF-FFFFFFFFFFFF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FFFFFFFF-FFFF-FFFF-0000-000000000000}
EndGlobalSection
EndGlobal
";

StringBuilder stringBuilder = new StringBuilder();

// Put comment between all lines
const string comment = "\t# comment";
string[] lines = solutionFileContent.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < lines.Length; i++)
{
stringBuilder.AppendLine(comment);
stringBuilder.AppendLine(lines[i]);
}
stringBuilder.AppendLine(comment);

Should.NotThrow(() => ParseSolutionHelper(stringBuilder.ToString()));
}
}
}
8 changes: 4 additions & 4 deletions src/Build.UnitTests/Construction/SolutionFilter_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ public void SolutionFilterFiltersProjects(bool graphBuild)
");
// Slashes here (and in the .slnf) are hardcoded as backslashes intentionally to support the common case.
TransientTestFile solutionFile = testEnvironment.CreateFile(simpleProjectFolder, "SimpleProject.sln",
@"
"""
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29326.124
MinimumVisualStudioVersion = 10.0.40219.1
Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""SimpleProject"", ""SimpleProject\SimpleProject.csproj"", ""{79B5EBA6-5D27-4976-BC31-14422245A59A}""
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleProject", "SimpleProject\SimpleProject.csproj", "{79B5EBA6-5D27-4976-BC31-14422245A59A}"
EndProject
Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""ClassLibrary"", ""..\ClassLibrary\ClassLibrary\ClassLibrary.csproj"", ""{8EFCCA22-9D51-4268-90F7-A595E11FCB2D}""
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassLibrary", "..\ClassLibrary\ClassLibrary\ClassLibrary.csproj", "{8EFCCA22-9D51-4268-90F7-A595E11FCB2D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -106,7 +106,7 @@ public void SolutionFilterFiltersProjects(bool graphBuild)
SolutionGuid = {DE7234EC-0C4D-4070-B66A-DCF1B4F0CFEF}
EndGlobalSection
EndGlobal
");
""");
TransientTestFile filterFile = testEnvironment.CreateFile(folder, "solutionFilter.slnf",
@"
{
Expand Down
Loading

0 comments on commit 2e2d372

Please sign in to comment.