Skip to content

Commit b53af55

Browse files
samuelll3dEric Johnson
authored andcommitted
Find solution file by project assembly name
Instead of looking for a solution file with the same name as the project and its assembly, this commit updates the logic to find all .sln and .slnx files in the specified solution directory. If no matching solution is found, it will fall back to the old behaviour. This commit will also consider .. markings to go up one directory level allowing for Godot projects as part of multi-project solutions. Co-authored-by: Eric Johnson <[email protected]>
1 parent 07f4c06 commit b53af55

File tree

2 files changed

+63
-6
lines changed

2 files changed

+63
-6
lines changed

modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" />
3838
<PackageReference Include="JetBrains.Rider.PathLocator" Version="1.0.12" />
3939
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
40+
<PackageReference Include="Microsoft.VisualStudio.SolutionPersistence" Version="1.0.52" />
4041
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
4142
<Reference Include="GodotSharp">
4243
<HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath>

modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
using System;
2+
using System.Collections.Generic;
13
using System.Diagnostics.CodeAnalysis;
24
using System.IO;
5+
using System.Threading;
36
using Godot;
47
using Godot.NativeInterop;
5-
using GodotTools.Core;
6-
using static GodotTools.Internals.Globals;
8+
using Microsoft.VisualStudio.SolutionPersistence;
9+
using Microsoft.VisualStudio.SolutionPersistence.Serializer;
710

811
namespace GodotTools.Internals
912
{
@@ -73,17 +76,70 @@ public static void DetermineProjectLocation()
7376
string? slnParentDir = (string?)ProjectSettings.GetSetting("dotnet/project/solution_directory");
7477
if (string.IsNullOrEmpty(slnParentDir))
7578
slnParentDir = "res://";
76-
else if (!slnParentDir.StartsWith("res://", System.StringComparison.Ordinal))
79+
else if (!slnParentDir.StartsWith("res://", StringComparison.Ordinal))
7780
slnParentDir = "res://" + slnParentDir;
7881

7982
// The csproj should be in the same folder as project.godot.
8083
string csprojParentDir = "res://";
8184

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
8586
_projectCsProjPath = Path.Combine(ProjectSettings.GlobalizePath(csprojParentDir),
8687
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");
87143
}
88144

89145
private static string? _projectAssemblyName;

0 commit comments

Comments
 (0)