Skip to content

Commit 2df8257

Browse files
committed
add proper slnx support
1 parent f2d0399 commit 2df8257

17 files changed

+56
-49
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Licensed to the projects contributors.
2+
// The license conditions are provided in the LICENSE file located in the project root
3+
4+
namespace NuGetUtility.Extensions
5+
{
6+
public static class TaskExtensions
7+
{
8+
public static async Task<IEnumerable<T1>> SelectManyAsync<T, T1>(this IEnumerable<T> enumeration, Func<T, Task<IEnumerable<T1>>> func)
9+
{
10+
return (await Task.WhenAll(enumeration.Select(func))).SelectMany(s => s);
11+
}
12+
}
13+
}

src/NuGetUtility/NuGetUtility.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<ItemGroup>
2828
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
2929
<PackageReference Include="Microsoft.Build.Locator" Version="1.7.8" />
30+
<PackageReference Include="Microsoft.VisualStudio.SolutionPersistence" Version="1.0.28" />
3031
<PackageReference Include="NuGet.Commands" Version="6.12.1" />
3132
<PackageReference Include="NuGet.Packaging" Version="6.12.1" />
3233
<PackageReference Include="Tethys.SPDX.ExpressionParser" Version="2.1.2" />

src/NuGetUtility/Program.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ private async Task<int> OnExecuteAsync(CancellationToken cancellationToken)
128128
ignoredPackages);
129129

130130
string[] excludedProjects = GetExcludedProjects();
131-
IEnumerable<string> projects = inputFiles.SelectMany(projectCollector.GetProjects).Where(p => !Array.Exists(excludedProjects, ignored => p.Like(ignored)));
131+
IEnumerable<string> projects = (await inputFiles.SelectManyAsync(projectCollector.GetProjectsAsync)).Where(p => !Array.Exists(excludedProjects, ignored => p.Like(ignored)));
132132
IEnumerable<ProjectWithReferencedPackages> packagesForProject = GetPackagesPerProject(projects, projectReader, out IReadOnlyCollection<Exception>? projectReaderExceptions);
133133
IAsyncEnumerable<ReferencedPackageWithContext> downloadedLicenseInformation =
134134
packagesForProject.SelectMany(p => GetPackageInformations(p, overridePackageInformation, cancellationToken));

src/NuGetUtility/ReferencedPackagesReader/ProjectsCollector.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ public ProjectsCollector(IMsBuildAbstraction msBuild)
1313
_msBuild = msBuild;
1414
}
1515

16-
public IEnumerable<string> GetProjects(string inputPath)
16+
public async Task<IEnumerable<string>> GetProjectsAsync(string inputPath)
1717
{
1818
return Path.GetExtension(inputPath).StartsWith(".sln")
19-
? _msBuild.GetProjectsFromSolution(Path.GetFullPath(inputPath)).Where(File.Exists).Select(Path.GetFullPath)
19+
? (await _msBuild.GetProjectsFromSolutionAsync(Path.GetFullPath(inputPath))).Where(File.Exists).Select(Path.GetFullPath)
2020
: new[] { Path.GetFullPath(inputPath) };
2121
}
2222
}

src/NuGetUtility/Wrapper/MsBuildWrapper/IMsBuildAbstraction.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ namespace NuGetUtility.Wrapper.MsBuildWrapper
66
public interface IMsBuildAbstraction
77
{
88
IProject GetProject(string projectPath);
9-
IEnumerable<string> GetProjectsFromSolution(string inputPath);
9+
Task<IEnumerable<string>> GetProjectsFromSolutionAsync(string inputPath);
1010
}
1111
}

src/NuGetUtility/Wrapper/MsBuildWrapper/MsBuildAbstraction.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// Licensed to the projects contributors.
22
// The license conditions are provided in the LICENSE file located in the project root
33

4-
using Microsoft.Build.Construction;
54
using Microsoft.Build.Evaluation;
65
using Microsoft.Build.Locator;
6+
using Microsoft.VisualStudio.SolutionPersistence;
7+
using Microsoft.VisualStudio.SolutionPersistence.Serializer;
78

89
namespace NuGetUtility.Wrapper.MsBuildWrapper
910
{
@@ -32,11 +33,12 @@ public IProject GetProject(string projectPath)
3233
return new ProjectWrapper(project);
3334
}
3435

35-
public IEnumerable<string> GetProjectsFromSolution(string inputPath)
36+
public async Task<IEnumerable<string>> GetProjectsFromSolutionAsync(string inputPath)
3637
{
37-
string absolutePath = Path.IsPathRooted(inputPath) ? inputPath : Path.Combine(Environment.CurrentDirectory, inputPath);
38-
var sln = SolutionFile.Parse(absolutePath);
39-
return sln.ProjectsInOrder.Select(p => p.AbsolutePath);
38+
ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(inputPath) ?? throw new MsBuildAbstractionException("Failed to determine serializer for solution");
39+
40+
Microsoft.VisualStudio.SolutionPersistence.Model.SolutionModel model = await serializer.OpenAsync(inputPath, CancellationToken.None);
41+
return model.SolutionProjects.Select(p => p.FilePath);
4042
}
4143

4244
private static void RegisterMsBuildLocatorIfNeeded()

tests/NuGetUtility.Test/LicenseValidator/UrlToLicenseMappingTest.cs

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using NuGetUtility.LicenseValidator;
55
using OpenQA.Selenium;
66
using OpenQA.Selenium.Chrome;
7-
using OpenQA.Selenium.Firefox;
87

98
namespace NuGetUtility.Test.LicenseValidator
109
{

tests/NuGetUtility.Test/ReferencedPackagesReader/ProjectsCollectorTest.GetProjectsFromSolution_Should_ReturnProjectsInActualSolutionFileAbsolutePath.Linux.verified.txt

-1
This file was deleted.

tests/NuGetUtility.Test/ReferencedPackagesReader/ProjectsCollectorTest.GetProjectsFromSolution_Should_ReturnProjectsInActualSolutionFileAbsolutePath.OSX.verified.txt

-1
This file was deleted.

tests/NuGetUtility.Test/ReferencedPackagesReader/ProjectsCollectorTest.GetProjectsFromSolution_Should_ReturnProjectsInActualSolutionFileAbsolutePath.Windows.verified.txt

-1
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{SolutionDirectory}tests/targets/PackageReferenceProject/PackageReferenceProject.csproj,{SolutionDirectory}tests/targets/PackagesConfigProject/PackagesConfigProject.csproj,{SolutionDirectory}tests/targets/ProjectWithoutNugetReferences/ProjectWithoutNugetReferences.csproj,{SolutionDirectory}tests/targets/ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj,{SolutionDirectory}tests/targets/ProjectWithTransitiveReferences/ProjectWithTransitiveReferences.csproj
1+
{CurrentDirectory}tests/targets/PackageReferenceProject/PackageReferenceProject.csproj,{CurrentDirectory}tests/targets/PackagesConfigProject/PackagesConfigProject.csproj,{CurrentDirectory}tests/targets/ProjectWithoutNugetReferences/ProjectWithoutNugetReferences.csproj,{CurrentDirectory}tests/targets/ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj,{CurrentDirectory}tests/targets/ProjectWithTransitiveReferences/ProjectWithTransitiveReferences.csproj
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{SolutionDirectory}tests/targets/PackageReferenceProject/PackageReferenceProject.csproj,{SolutionDirectory}tests/targets/PackagesConfigProject/PackagesConfigProject.csproj,{SolutionDirectory}tests/targets/ProjectWithoutNugetReferences/ProjectWithoutNugetReferences.csproj,{SolutionDirectory}tests/targets/ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj,{SolutionDirectory}tests/targets/ProjectWithTransitiveReferences/ProjectWithTransitiveReferences.csproj
1+
{CurrentDirectory}tests/targets/PackageReferenceProject/PackageReferenceProject.csproj,{CurrentDirectory}tests/targets/PackagesConfigProject/PackagesConfigProject.csproj,{CurrentDirectory}tests/targets/ProjectWithoutNugetReferences/ProjectWithoutNugetReferences.csproj,{CurrentDirectory}tests/targets/ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj,{CurrentDirectory}tests/targets/ProjectWithTransitiveReferences/ProjectWithTransitiveReferences.csproj
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{SolutionDirectory}tests\targets\PackageReferenceProject\PackageReferenceProject.csproj,{SolutionDirectory}tests\targets\PackagesConfigProject\PackagesConfigProject.csproj,{SolutionDirectory}tests\targets\ProjectWithoutNugetReferences\ProjectWithoutNugetReferences.csproj,{SolutionDirectory}tests\targets\ProjectWithTransitiveNuget\ProjectWithTransitiveNuget.csproj,{SolutionDirectory}tests\targets\ProjectWithTransitiveReferences\ProjectWithTransitiveReferences.csproj
1+
{CurrentDirectory}PackageReferenceProject\PackageReferenceProject.csproj,{CurrentDirectory}PackagesConfigProject\PackagesConfigProject.csproj,{CurrentDirectory}ProjectWithoutNugetReferences\ProjectWithoutNugetReferences.csproj,{CurrentDirectory}ProjectWithTransitiveNuget\ProjectWithTransitiveNuget.csproj,{CurrentDirectory}ProjectWithTransitiveReferences\ProjectWithTransitiveReferences.csproj
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ProjectDirectory}bin\Debug\ProjectWithTransitiveNuget\ProjectWithTransitiveNuget.csproj
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ProjectDirectory}bin/Debug/ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ProjectDirectory}bin/Debug/ProjectWithTransitiveNuget/ProjectWithTransitiveNuget.csproj

tests/NuGetUtility.Test/ReferencedPackagesReader/ProjectsCollectorTest.cs

+25-33
Original file line numberDiff line numberDiff line change
@@ -34,110 +34,102 @@ public void SetUp()
3434
[TestCase("B.fsproj")]
3535
[TestCase("C.vbproj")]
3636
[TestCase("D.dbproj")]
37-
public void GetProjects_Should_ReturnProjectsAsListDirectly(string projectFile)
37+
public async Task GetProjects_Should_ReturnProjectsAsListDirectly(string projectFile)
3838
{
39-
IEnumerable<string> result = _uut.GetProjects(projectFile);
39+
IEnumerable<string> result = await _uut.GetProjectsAsync(projectFile);
4040
Assert.That(result, Is.EqualTo(new[] { Path.GetFullPath(projectFile) }));
41-
_msBuild.DidNotReceive().GetProjectsFromSolution(Arg.Any<string>());
41+
await _msBuild.DidNotReceive().GetProjectsFromSolutionAsync(Arg.Any<string>());
4242
}
4343

4444
[TestCase("A.sln")]
4545
[TestCase("B.sln")]
4646
[TestCase("C.sln")]
4747
[TestCase("A.slnx")]
48-
public void GetProjects_Should_QueryMsBuildToGetProjectsForSolutionFiles(string solutionFile)
48+
public async Task GetProjects_Should_QueryMsBuildToGetProjectsForSolutionFiles(string solutionFile)
4949
{
50-
_ = _uut.GetProjects(solutionFile);
50+
_ = await _uut.GetProjectsAsync(solutionFile);
5151

52-
_msBuild.Received(1).GetProjectsFromSolution(Path.GetFullPath(solutionFile));
52+
await _msBuild.Received(1).GetProjectsFromSolutionAsync(Path.GetFullPath(solutionFile));
5353
}
5454

5555
[TestCase("A.sln")]
5656
[TestCase("B.sln")]
5757
[TestCase("C.sln")]
5858
[TestCase("C.slnx")]
59-
public void GetProjects_Should_ReturnEmptyArray_If_SolutionContainsNoProjects(string solutionFile)
59+
public async Task GetProjects_Should_ReturnEmptyArray_If_SolutionContainsNoProjects(string solutionFile)
6060
{
61-
_msBuild.GetProjectsFromSolution(Arg.Any<string>()).Returns(Enumerable.Empty<string>());
61+
_msBuild.GetProjectsFromSolutionAsync(Arg.Any<string>()).Returns(Task.FromResult(Enumerable.Empty<string>()));
6262

63-
IEnumerable<string> result = _uut.GetProjects(solutionFile);
63+
IEnumerable<string> result = await _uut.GetProjectsAsync(solutionFile);
6464
Assert.That(result, Is.Empty);
6565

66-
_msBuild.Received(1).GetProjectsFromSolution(Path.GetFullPath(solutionFile));
66+
await _msBuild.Received(1).GetProjectsFromSolutionAsync(Path.GetFullPath(solutionFile));
6767
}
6868

6969
[TestCase("A.sln")]
7070
[TestCase("B.sln")]
7171
[TestCase("C.sln")]
7272
[TestCase("B.slnx")]
73-
public void GetProjects_Should_ReturnEmptyArray_If_SolutionContainsProjectsThatDontExist(string solutionFile)
73+
public async Task GetProjects_Should_ReturnEmptyArray_If_SolutionContainsProjectsThatDontExist(string solutionFile)
7474
{
7575
IEnumerable<string> projects = _fixture.CreateMany<string>();
76-
_msBuild.GetProjectsFromSolution(Arg.Any<string>()).Returns(projects);
76+
_msBuild.GetProjectsFromSolutionAsync(Arg.Any<string>()).Returns(Task.FromResult(projects));
7777

78-
IEnumerable<string> result = _uut.GetProjects(solutionFile);
78+
IEnumerable<string> result = await _uut.GetProjectsAsync(solutionFile);
7979
Assert.That(result, Is.Empty);
8080

81-
_msBuild.Received(1).GetProjectsFromSolution(Path.GetFullPath(solutionFile));
81+
await _msBuild.Received(1).GetProjectsFromSolutionAsync(Path.GetFullPath(solutionFile));
8282
}
8383

8484
[TestCase("A.sln")]
8585
[TestCase("B.sln")]
8686
[TestCase("C.sln")]
8787
[TestCase("C.slnx")]
88-
public void GetProjects_Should_ReturnArrayOfProjects_If_SolutionContainsProjectsThatDoExist(string solutionFile)
88+
public async Task GetProjects_Should_ReturnArrayOfProjects_If_SolutionContainsProjectsThatDoExist(string solutionFile)
8989
{
9090
string[] projects = _fixture.CreateMany<string>().ToArray();
9191
CreateFiles(projects);
92-
_msBuild.GetProjectsFromSolution(Arg.Any<string>()).Returns(projects);
92+
_msBuild.GetProjectsFromSolutionAsync(Arg.Any<string>()).Returns(Task.FromResult<IEnumerable<string>>(projects));
9393

94-
IEnumerable<string> result = _uut.GetProjects(solutionFile);
94+
IEnumerable<string> result = await _uut.GetProjectsAsync(solutionFile);
9595
Assert.That(result, Is.EqualTo(projects.Select(Path.GetFullPath)));
9696

97-
_msBuild.Received(1).GetProjectsFromSolution(Path.GetFullPath(solutionFile));
97+
await _msBuild.Received(1).GetProjectsFromSolutionAsync(Path.GetFullPath(solutionFile));
9898
}
9999

100100
[TestCase("A.sln")]
101101
[TestCase("B.sln")]
102102
[TestCase("C.sln")]
103103
[TestCase("A.slnx")]
104-
public void GetProjects_Should_ReturnOnlyExistingProjectsInSolutionFile(string solutionFile)
104+
public async Task GetProjects_Should_ReturnOnlyExistingProjectsInSolutionFile(string solutionFile)
105105
{
106106
string[] existingProjects = _fixture.CreateMany<string>().ToArray();
107107
IEnumerable<string> missingProjects = _fixture.CreateMany<string>();
108108

109109
CreateFiles(existingProjects);
110110

111-
_msBuild.GetProjectsFromSolution(Arg.Any<string>())
111+
_msBuild.GetProjectsFromSolutionAsync(Arg.Any<string>())
112112
.Returns(existingProjects.Concat(missingProjects).Shuffle(54321));
113113

114-
IEnumerable<string> result = _uut.GetProjects(solutionFile);
114+
IEnumerable<string> result = await _uut.GetProjectsAsync(solutionFile);
115115
Assert.That(result, Is.EquivalentTo(existingProjects.Select(Path.GetFullPath)));
116116

117-
_msBuild.Received(1).GetProjectsFromSolution(Path.GetFullPath(solutionFile));
117+
await _msBuild.Received(1).GetProjectsFromSolutionAsync(Path.GetFullPath(solutionFile));
118118
}
119119

120120
[Test]
121121
public async Task GetProjectsFromSolution_Should_ReturnProjectsInActualSolutionFileRelativePath()
122122
{
123123
var msbuild = new MsBuildAbstraction();
124-
IEnumerable<string> result = msbuild.GetProjectsFromSolution("../../../../targets/Projects.sln");
125-
await Verify(string.Join(",", result), _osPlatformSpecificVerifySettings);
126-
}
127-
128-
[Test, Ignore("Ignore this specific test as long as msbuild does not fully support slnx solutions everywhere")]
129-
public async Task GetProjectsFromXmlSolution_Should_ReturnProjectsInActualSolutionFileRelativePath()
130-
{
131-
var msbuild = new MsBuildAbstraction();
132-
IEnumerable<string> result = msbuild.GetProjectsFromSolution("../../../../targets/slnx/slnx.slnx");
124+
IEnumerable<string> result = await msbuild.GetProjectsFromSolutionAsync("../../../../targets/Projects.sln");
133125
await Verify(string.Join(",", result), _osPlatformSpecificVerifySettings);
134126
}
135127

136128
[Test]
137-
public async Task GetProjectsFromSolution_Should_ReturnProjectsInActualSolutionFileAbsolutePath()
129+
public async Task GetProjectsFromXmlSolution_Should_ReturnProjectsInActualSolutionFileRelativePath()
138130
{
139131
var msbuild = new MsBuildAbstraction();
140-
IEnumerable<string> result = msbuild.GetProjectsFromSolution(Path.GetFullPath("../../../../targets/Projects.sln"));
132+
IEnumerable<string> result = await msbuild.GetProjectsFromSolutionAsync("../../../../targets/slnx/slnx.slnx");
141133
await Verify(string.Join(",", result), _osPlatformSpecificVerifySettings);
142134
}
143135

0 commit comments

Comments
 (0)