Skip to content

Commit 8d41b5a

Browse files
committed
[.NET] Upgrade user project's TargetFramework to net8.0
- Upgrades the TFM for new created projects to `net8.0`. - Implements system to upgrade TFM for existing projects to `net8.0`.
1 parent 6e2cf2a commit 8d41b5a

File tree

4 files changed

+159
-10
lines changed

4 files changed

+159
-10
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<PackageReference Include="Microsoft.Build" Version="15.1.548" ExcludeAssets="runtime" />
1212
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
1313
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
14+
<PackageReference Include="NuGet.Frameworks" Version="6.12.1" />
1415
</ItemGroup>
1516

1617
<ItemGroup>

modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ public static class ProjectGenerator
1212
{
1313
public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GeneratedGodotNupkgsVersions.GodotNETSdk}";
1414

15+
public static string GodotMinimumRequiredTfm => "net8.0";
16+
1517
public static ProjectRootElement GenGameProject(string name)
1618
{
1719
if (name.Length == 0)
@@ -22,7 +24,7 @@ public static ProjectRootElement GenGameProject(string name)
2224
root.Sdk = GodotSdkAttrValue;
2325

2426
var mainGroup = root.AddPropertyGroup();
25-
mainGroup.AddProperty("TargetFramework", "net8.0");
27+
mainGroup.AddProperty("TargetFramework", GodotMinimumRequiredTfm);
2628

2729
mainGroup.AddProperty("EnableDynamicLoading", "true");
2830

modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs

Lines changed: 154 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
4+
using System.Text.RegularExpressions;
35
using Microsoft.Build.Construction;
6+
using Microsoft.Build.Evaluation;
47
using Microsoft.Build.Locator;
8+
using NuGet.Frameworks;
59

610
namespace GodotTools.ProjectEditor
711
{
@@ -19,8 +23,21 @@ public MSBuildProject(ProjectRootElement root)
1923
}
2024
}
2125

22-
public static class ProjectUtils
26+
public static partial class ProjectUtils
2327
{
28+
[GeneratedRegex(@"\s*'\$\(GodotTargetPlatform\)'\s*==\s*'(?<platform>[A-z]+)'\s*", RegexOptions.IgnoreCase)]
29+
private static partial Regex GodotTargetPlatformConditionRegex();
30+
31+
private static readonly string[] _platformNames =
32+
{
33+
"windows",
34+
"linuxbsd",
35+
"macos",
36+
"android",
37+
"ios",
38+
"web",
39+
};
40+
2441
public static void MSBuildLocatorRegisterLatest(out Version version, out string path)
2542
{
2643
var instance = MSBuildLocator.QueryVisualStudioInstances()
@@ -36,11 +53,22 @@ public static void MSBuildLocatorRegisterMSBuildPath(string msbuildPath)
3653

3754
public static MSBuildProject? Open(string path)
3855
{
39-
var root = ProjectRootElement.Open(path);
56+
var root = ProjectRootElement.Open(path, ProjectCollection.GlobalProjectCollection, preserveFormatting: true);
4057
return root != null ? new MSBuildProject(root) : null;
4158
}
4259

43-
public static void MigrateToProjectSdksStyle(MSBuildProject project, string projectName)
60+
public static void UpgradeProjectIfNeeded(MSBuildProject project, string projectName)
61+
{
62+
// NOTE: The order in which changes are made to the project is important.
63+
64+
// Migrate to MSBuild project Sdks style if using the old style.
65+
MigrateToProjectSdksStyle(project, projectName);
66+
67+
EnsureGodotSdkIsUpToDate(project);
68+
EnsureTargetFrameworkMatchesMinimumRequirement(project);
69+
}
70+
71+
private static void MigrateToProjectSdksStyle(MSBuildProject project, string projectName)
4472
{
4573
var origRoot = project.Root;
4674

@@ -64,5 +92,128 @@ public static void EnsureGodotSdkIsUpToDate(MSBuildProject project)
6492
root.Sdk = godotSdkAttrValue;
6593
project.HasUnsavedChanges = true;
6694
}
95+
96+
private static void EnsureTargetFrameworkMatchesMinimumRequirement(MSBuildProject project)
97+
{
98+
var root = project.Root;
99+
string minTfmValue = ProjectGenerator.GodotMinimumRequiredTfm;
100+
var minTfmVersion = NuGetFramework.Parse(minTfmValue).Version;
101+
102+
ProjectPropertyGroupElement? mainPropertyGroup = null;
103+
ProjectPropertyElement? mainTargetFrameworkProperty = null;
104+
105+
var propertiesToChange = new List<ProjectPropertyElement>();
106+
107+
foreach (var propertyGroup in root.PropertyGroups)
108+
{
109+
bool groupHasCondition = !string.IsNullOrEmpty(propertyGroup.Condition);
110+
111+
// Check if the property group should be excluded from checking for 'TargetFramework' properties.
112+
if (groupHasCondition && !ConditionMatchesGodotPlatform(propertyGroup.Condition))
113+
{
114+
continue;
115+
}
116+
117+
// Store a reference to the first property group without conditions,
118+
// in case we need to add a new 'TargetFramework' property later.
119+
if (mainPropertyGroup == null && !groupHasCondition)
120+
{
121+
mainPropertyGroup = propertyGroup;
122+
}
123+
124+
foreach (var property in propertyGroup.Properties)
125+
{
126+
// We are looking for 'TargetFramework' properties.
127+
if (property.Name != "TargetFramework")
128+
{
129+
continue;
130+
}
131+
132+
bool propertyHasCondition = !string.IsNullOrEmpty(property.Condition);
133+
134+
// Check if the property should be excluded.
135+
if (propertyHasCondition && !ConditionMatchesGodotPlatform(property.Condition))
136+
{
137+
continue;
138+
}
139+
140+
if (!groupHasCondition && !propertyHasCondition)
141+
{
142+
// Store a reference to the 'TargetFramework' that has no conditions
143+
// because it applies to all platforms.
144+
if (mainTargetFrameworkProperty == null)
145+
{
146+
mainTargetFrameworkProperty = property;
147+
}
148+
continue;
149+
}
150+
151+
// If the 'TargetFramework' property is conditional, it may no longer be needed
152+
// when the main one is upgraded to the new minimum version.
153+
var tfmVersion = NuGetFramework.Parse(property.Value).Version;
154+
if (tfmVersion <= minTfmVersion)
155+
{
156+
propertiesToChange.Add(property);
157+
}
158+
}
159+
}
160+
161+
if (mainTargetFrameworkProperty == null)
162+
{
163+
// We haven't found a 'TargetFramework' property without conditions,
164+
// we'll just add one in the first property group without conditions.
165+
if (mainPropertyGroup == null)
166+
{
167+
// We also don't have a property group without conditions,
168+
// so we'll add a new one to the project.
169+
mainPropertyGroup = root.AddPropertyGroup();
170+
}
171+
172+
mainTargetFrameworkProperty = mainPropertyGroup.AddProperty("TargetFramework", minTfmValue);
173+
project.HasUnsavedChanges = true;
174+
}
175+
else
176+
{
177+
var tfmVersion = NuGetFramework.Parse(mainTargetFrameworkProperty.Value).Version;
178+
if (tfmVersion < minTfmVersion)
179+
{
180+
mainTargetFrameworkProperty.Value = minTfmValue;
181+
project.HasUnsavedChanges = true;
182+
}
183+
}
184+
185+
var mainTfmVersion = NuGetFramework.Parse(mainTargetFrameworkProperty.Value).Version;
186+
foreach (var property in propertiesToChange)
187+
{
188+
// If the main 'TargetFramework' property targets a version newer than
189+
// the minimum required by Godot, we don't want to remove the conditional
190+
// 'TargetFramework' properties, only upgrade them to the new minimum.
191+
// Otherwise, it can be removed.
192+
if (mainTfmVersion > minTfmVersion)
193+
{
194+
property.Value = minTfmValue;
195+
}
196+
else
197+
{
198+
property.Parent.RemoveChild(property);
199+
}
200+
201+
project.HasUnsavedChanges = true;
202+
}
203+
204+
static bool ConditionMatchesGodotPlatform(string condition)
205+
{
206+
// Check if the condition is checking the 'GodotTargetPlatform' for one of the
207+
// Godot platforms with built-in support in the Godot.NET.Sdk.
208+
var match = GodotTargetPlatformConditionRegex().Match(condition);
209+
if (match.Success)
210+
{
211+
string platform = match.Groups["platform"].Value;
212+
return _platformNames.Contains(platform, StringComparer.OrdinalIgnoreCase);
213+
}
214+
215+
return false;
216+
}
217+
}
67218
}
68219
}

modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -439,12 +439,7 @@ private void ApplyNecessaryChangesToSolution()
439439
var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
440440
?? throw new InvalidOperationException("Cannot open C# project.");
441441

442-
// NOTE: The order in which changes are made to the project is important
443-
444-
// Migrate to MSBuild project Sdks style if using the old style
445-
ProjectUtils.MigrateToProjectSdksStyle(msbuildProject, GodotSharpDirs.ProjectAssemblyName);
446-
447-
ProjectUtils.EnsureGodotSdkIsUpToDate(msbuildProject);
442+
ProjectUtils.UpgradeProjectIfNeeded(msbuildProject, GodotSharpDirs.ProjectAssemblyName);
448443

449444
if (msbuildProject.HasUnsavedChanges)
450445
{

0 commit comments

Comments
 (0)