|
1 | 1 | using System; |
2 | 2 | using System.Collections.Generic; |
| 3 | +using System.IO; |
3 | 4 | using System.Linq; |
4 | 5 | using System.Threading; |
5 | 6 | using System.Threading.Tasks; |
|
8 | 9 |
|
9 | 10 | using Microsoft.Build.Construction; |
10 | 11 | using Microsoft.Extensions.Logging; |
| 12 | +using Microsoft.VisualStudio.SolutionPersistence.Model; |
| 13 | +using Microsoft.VisualStudio.SolutionPersistence.Serializer; |
11 | 14 |
|
12 | 15 | namespace Eigenverft.Distributed.Drydock.Services |
13 | 16 | { |
@@ -52,87 +55,93 @@ public SolutionProjectService(ILogger<SolutionProjectService> logger) |
52 | 55 | public async Task<List<string>> GetCsProjAbsolutPathsFromSolutions(string solutionLocation, CancellationToken cancellationToken) |
53 | 56 | { |
54 | 57 | List<string> retval = new List<string>(); |
| 58 | + |
55 | 59 | try |
56 | 60 | { |
57 | | - List<ProjectInSolution> sln = SolutionFile.Parse(solutionLocation).ProjectsInOrder.Where(e => e.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat).ToList(); |
58 | | - List<ProjectRootElement> projects = new List<ProjectRootElement>(); |
| 61 | + // Load solution (.sln OR .slnx) |
| 62 | + var serializer = SolutionSerializers.GetSerializerByMoniker(solutionLocation); |
| 63 | + if (serializer is null) |
| 64 | + { |
| 65 | + _logger.LogError("Unsupported solution file type: {SolutionLocation}", solutionLocation); |
| 66 | + return null; |
| 67 | + } |
59 | 68 |
|
60 | | - // Load each project file. |
61 | | - foreach (var item in sln) |
| 69 | + SolutionModel solution; |
| 70 | + try |
62 | 71 | { |
63 | | - try |
64 | | - { |
65 | | - ProjectRootElement projectRoot = ProjectRootElement.Open(item.AbsolutePath); |
| 72 | + solution = await serializer.OpenAsync(solutionLocation, cancellationToken); |
| 73 | + } |
| 74 | + catch (SolutionException ex) |
| 75 | + { |
| 76 | + _logger.LogError(ex, "Failed to parse solution file: {SolutionLocation}", solutionLocation); |
| 77 | + return null; |
| 78 | + } |
66 | 79 |
|
67 | | - var globalProperties = new Dictionary<string, string> |
68 | | - { |
69 | | - ["Configuration"] = "Debug", |
70 | | - ["Platform"] = "AnyCPU", |
71 | | - //["MSBuildRuntimeType"] = "Core" |
72 | | - }; |
| 80 | + string solutionDir = Path.GetDirectoryName(Path.GetFullPath(solutionLocation)) ?? Directory.GetCurrentDirectory(); |
73 | 81 |
|
74 | | - //var projectload = new Project(item.AbsolutePath, globalProperties, null); |
| 82 | + // Keep it simple: only take csproj entries |
| 83 | + var csprojPaths = solution.SolutionProjects |
| 84 | + .Select(p => p.FilePath) |
| 85 | + .Where(p => p.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase)) |
| 86 | + .Select(p => Path.GetFullPath(Path.Combine(solutionDir, p))) |
| 87 | + .ToList(); |
75 | 88 |
|
| 89 | + if (csprojPaths.Count == 0) |
| 90 | + { |
| 91 | + _logger.LogWarning("Solution contained no C# projects: {SolutionLocation}", solutionLocation); |
| 92 | + return null; |
| 93 | + } |
| 94 | + |
| 95 | + // Load each project file (your existing logic) |
| 96 | + List<ProjectRootElement> projects = new List<ProjectRootElement>(); |
| 97 | + foreach (var projectPath in csprojPaths) |
| 98 | + { |
| 99 | + try |
| 100 | + { |
| 101 | + ProjectRootElement projectRoot = ProjectRootElement.Open(projectPath); |
76 | 102 | projects.Add(projectRoot); |
77 | 103 | } |
78 | 104 | catch (Exception ex) |
79 | 105 | { |
80 | | - _logger.LogError(ex, "Failed to open project file: {ProjectLocation}", item.AbsolutePath); |
| 106 | + _logger.LogError(ex, "Failed to open project file: {ProjectLocation}", projectPath); |
81 | 107 | return null; |
82 | 108 | } |
83 | 109 | } |
84 | 110 |
|
85 | | - // Sort projects so that any project that has a PackageReference with Include="Microsoft.NET.Test.Sdk" appears first. |
86 | | - // Test projects: projects with any PackageReference that has Include "Microsoft.NET.Test.Sdk". |
| 111 | + // Your existing sorting logic (unchanged) |
87 | 112 | var testProjects = projects.Where(project => |
88 | 113 | project.Items.Any(item => |
89 | 114 | item.ElementName == "PackageReference" && |
90 | 115 | string.Equals(item.Include, "Microsoft.NET.Test.Sdk", StringComparison.OrdinalIgnoreCase) |
91 | 116 | ) |
92 | 117 | ).ToList(); |
93 | 118 |
|
94 | | - // Non-test projects: projects that do NOT have any PackageReference with Include "Microsoft.NET.Test.Sdk". |
95 | 119 | var nonTestProjects = projects.Where(project => |
96 | 120 | !project.Items.Any(item => |
97 | 121 | item.ElementName == "PackageReference" && |
98 | 122 | string.Equals(item.Include, "Microsoft.NET.Test.Sdk", StringComparison.OrdinalIgnoreCase) |
99 | 123 | ) |
100 | 124 | ).ToList(); |
101 | 125 |
|
102 | | - nonTestProjects = nonTestProjects.OrderBy( |
103 | | - i => i.Items.Any(f => f.ElementName.Contains("ProjectReference")) |
104 | | - ).ToList(); |
| 126 | + nonTestProjects = nonTestProjects |
| 127 | + .OrderBy(i => i.Items.Any(f => f.ElementName.Contains("ProjectReference"))) |
| 128 | + .ToList(); |
105 | 129 |
|
106 | | - foreach (var item in testProjects) |
107 | | - { |
108 | | - retval.Add(item.FullPath); |
109 | | - } |
| 130 | + foreach (var item in testProjects) retval.Add(item.FullPath); |
| 131 | + foreach (var item in nonTestProjects) retval.Add(item.FullPath); |
110 | 132 |
|
111 | | - foreach (var item in nonTestProjects) |
112 | | - { |
113 | | - retval.Add(item.FullPath); |
114 | | - } |
115 | | - |
116 | | - //projects.Items.where // dont' know here items ElementName == "PackageReference" and on this if there is an Include == "Microsoft.NET.Test.Sdk" this project should be sorted first. |
117 | | - //projects.OrderBy(e => e.Items == "PackageReference"); |
118 | | - |
119 | | - if (retval.Count == 0) |
120 | | - { |
121 | | - _logger.LogWarning("No csproj files were found in the solution: {SolutionLocation}", solutionLocation); |
122 | | - return null; |
123 | | - } |
124 | | - else |
125 | | - { |
126 | | - //_logger.LogDebug("Found {Count} csproj files in the solution: {SolutionLocation}", retval.Count, solutionLocation); |
127 | | - } |
| 133 | + return retval.Count == 0 ? null : retval; |
| 134 | + } |
| 135 | + catch (OperationCanceledException) |
| 136 | + { |
| 137 | + _logger.LogWarning("Solution parsing canceled: {SolutionLocation}", solutionLocation); |
| 138 | + return null; |
128 | 139 | } |
129 | 140 | catch (Exception ex) |
130 | 141 | { |
131 | 142 | _logger.LogError(ex, "Error while parsing the solution file: {SolutionLocation}", solutionLocation); |
132 | 143 | throw; |
133 | 144 | } |
134 | | - |
135 | | - return retval; |
136 | 145 | } |
137 | 146 |
|
138 | 147 | public async Task<string?> GetProjectProperty(string projectLocation, string? propertyName, CsProjCommand.ElementScope? scopeType, CancellationToken cancellationToken) |
|
0 commit comments