|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
1 | 3 | using System.Diagnostics.CodeAnalysis; |
2 | 4 | using System.IO; |
| 5 | +using System.Threading; |
3 | 6 | using Godot; |
4 | 7 | using Godot.NativeInterop; |
5 | | -using GodotTools.Core; |
6 | | -using static GodotTools.Internals.Globals; |
| 8 | +using Microsoft.VisualStudio.SolutionPersistence; |
| 9 | +using Microsoft.VisualStudio.SolutionPersistence.Serializer; |
7 | 10 |
|
8 | 11 | namespace GodotTools.Internals |
9 | 12 | { |
@@ -73,17 +76,70 @@ public static void DetermineProjectLocation() |
73 | 76 | string? slnParentDir = (string?)ProjectSettings.GetSetting("dotnet/project/solution_directory"); |
74 | 77 | if (string.IsNullOrEmpty(slnParentDir)) |
75 | 78 | slnParentDir = "res://"; |
76 | | - else if (!slnParentDir.StartsWith("res://", System.StringComparison.Ordinal)) |
| 79 | + else if (!slnParentDir.StartsWith("res://", StringComparison.Ordinal)) |
77 | 80 | slnParentDir = "res://" + slnParentDir; |
78 | 81 |
|
79 | 82 | // The csproj should be in the same folder as project.godot. |
80 | 83 | string csprojParentDir = "res://"; |
81 | 84 |
|
82 | | - _projectSlnPath = Path.Combine(ProjectSettings.GlobalizePath(slnParentDir), |
83 | | - string.Concat(_projectAssemblyName, ".sln")); |
84 | | - |
| 85 | + // Set csproj path first and use it to find the sln/slnx file with the assembly |
85 | 86 | _projectCsProjPath = Path.Combine(ProjectSettings.GlobalizePath(csprojParentDir), |
86 | 87 | string.Concat(_projectAssemblyName, ".csproj")); |
| 88 | + |
| 89 | + _projectSlnPath = FindSolutionFileWithAssemblyName(slnParentDir, _projectAssemblyName); |
| 90 | + } |
| 91 | + |
| 92 | + private static string FindSolutionFileWithAssemblyName(string directory, string assemblyName) |
| 93 | + { |
| 94 | + // Will convert ".." to load solutions from parent directory when appropriate |
| 95 | + string slnAbsolutePath = Path.GetFullPath(ProjectSettings.GlobalizePath(directory)); |
| 96 | + |
| 97 | + List<string> solutionFilePaths = new(); |
| 98 | + solutionFilePaths.AddRange(Directory.GetFiles(slnAbsolutePath, "*.sln")); |
| 99 | + solutionFilePaths.AddRange(Directory.GetFiles(slnAbsolutePath, "*.slnx")); |
| 100 | + |
| 101 | + if (solutionFilePaths.Count == 0) |
| 102 | + return Path.Combine(slnAbsolutePath, $"{assemblyName}.sln"); |
| 103 | + |
| 104 | + List<string> matchingSolutions = new(); |
| 105 | + |
| 106 | + foreach (string solutionFilePath in solutionFilePaths) |
| 107 | + { |
| 108 | + ISolutionSerializer? serializer = SolutionSerializers.GetSerializerByMoniker(solutionFilePath); |
| 109 | + if (serializer is null) |
| 110 | + continue; |
| 111 | + |
| 112 | + string? solutionDirectory = Path.GetDirectoryName(solutionFilePath); |
| 113 | + if (solutionDirectory is null) |
| 114 | + continue; |
| 115 | + |
| 116 | + var solution = serializer.OpenAsync(solutionFilePath, CancellationToken.None).Result; |
| 117 | + |
| 118 | + foreach (var project in solution.SolutionProjects) |
| 119 | + { |
| 120 | + // Convert '\' path separators on Windows to '/' to match Godot's Unix style separators |
| 121 | + var absoluteProjectFilePath = Path.GetFullPath(project.FilePath, solutionDirectory).Replace('\\', '/'); |
| 122 | + |
| 123 | + if (string.Equals(absoluteProjectFilePath, _projectCsProjPath, StringComparison.Ordinal)) |
| 124 | + matchingSolutions.Add(solutionFilePath); |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + switch (matchingSolutions.Count) |
| 129 | + { |
| 130 | + case 1: |
| 131 | + return matchingSolutions[0]; |
| 132 | + |
| 133 | + case > 1: |
| 134 | + GD.PushError( |
| 135 | + $"Multiple solutions containing a project with assembly name '{assemblyName}' were found:\n" |
| 136 | + + $"{string.Join('\n', matchingSolutions).Replace('\\', '/')}\n" |
| 137 | + + "Please ensure only one solution contains the project assembly.\n" |
| 138 | + + "If you have recently migrated to .slnx please ensure that you have removed the unused .sln."); |
| 139 | + break; |
| 140 | + } |
| 141 | + |
| 142 | + return Path.Combine(slnAbsolutePath, $"{assemblyName}.sln"); |
87 | 143 | } |
88 | 144 |
|
89 | 145 | private static string? _projectAssemblyName; |
|
0 commit comments