2
0
Эх сурвалжийг харах

Merge pull request #100195 from raulsntos/dotnet/upgrade-tfm

[.NET] Upgrade user project's TargetFramework to `net8.0`
Rémi Verschelde 8 сар өмнө
parent
commit
6cba6299f9

+ 1 - 0
modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj

@@ -11,6 +11,7 @@
     <PackageReference Include="Microsoft.Build" Version="15.1.548" ExcludeAssets="runtime" />
     <PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
+    <PackageReference Include="NuGet.Frameworks" Version="6.12.1" />
   </ItemGroup>
 
   <ItemGroup>

+ 3 - 1
modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs

@@ -12,6 +12,8 @@ namespace GodotTools.ProjectEditor
     {
         public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GeneratedGodotNupkgsVersions.GodotNETSdk}";
 
+        public static string GodotMinimumRequiredTfm => "net8.0";
+
         public static ProjectRootElement GenGameProject(string name)
         {
             if (name.Length == 0)
@@ -22,7 +24,7 @@ namespace GodotTools.ProjectEditor
             root.Sdk = GodotSdkAttrValue;
 
             var mainGroup = root.AddPropertyGroup();
-            mainGroup.AddProperty("TargetFramework", "net8.0");
+            mainGroup.AddProperty("TargetFramework", GodotMinimumRequiredTfm);
 
             mainGroup.AddProperty("EnableDynamicLoading", "true");
 

+ 154 - 3
modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs

@@ -1,7 +1,11 @@
 using System;
+using System.Collections.Generic;
 using System.Linq;
+using System.Text.RegularExpressions;
 using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
 using Microsoft.Build.Locator;
+using NuGet.Frameworks;
 
 namespace GodotTools.ProjectEditor
 {
@@ -19,8 +23,21 @@ namespace GodotTools.ProjectEditor
         }
     }
 
-    public static class ProjectUtils
+    public static partial class ProjectUtils
     {
+        [GeneratedRegex(@"\s*'\$\(GodotTargetPlatform\)'\s*==\s*'(?<platform>[A-z]+)'\s*", RegexOptions.IgnoreCase)]
+        private static partial Regex GodotTargetPlatformConditionRegex();
+
+        private static readonly string[] _platformNames =
+        {
+            "windows",
+            "linuxbsd",
+            "macos",
+            "android",
+            "ios",
+            "web",
+        };
+
         public static void MSBuildLocatorRegisterLatest(out Version version, out string path)
         {
             var instance = MSBuildLocator.QueryVisualStudioInstances()
@@ -36,11 +53,22 @@ namespace GodotTools.ProjectEditor
 
         public static MSBuildProject? Open(string path)
         {
-            var root = ProjectRootElement.Open(path);
+            var root = ProjectRootElement.Open(path, ProjectCollection.GlobalProjectCollection, preserveFormatting: true);
             return root != null ? new MSBuildProject(root) : null;
         }
 
-        public static void MigrateToProjectSdksStyle(MSBuildProject project, string projectName)
+        public static void UpgradeProjectIfNeeded(MSBuildProject project, string projectName)
+        {
+            // NOTE: The order in which changes are made to the project is important.
+
+            // Migrate to MSBuild project Sdks style if using the old style.
+            MigrateToProjectSdksStyle(project, projectName);
+
+            EnsureGodotSdkIsUpToDate(project);
+            EnsureTargetFrameworkMatchesMinimumRequirement(project);
+        }
+
+        private static void MigrateToProjectSdksStyle(MSBuildProject project, string projectName)
         {
             var origRoot = project.Root;
 
@@ -64,5 +92,128 @@ namespace GodotTools.ProjectEditor
             root.Sdk = godotSdkAttrValue;
             project.HasUnsavedChanges = true;
         }
+
+        private static void EnsureTargetFrameworkMatchesMinimumRequirement(MSBuildProject project)
+        {
+            var root = project.Root;
+            string minTfmValue = ProjectGenerator.GodotMinimumRequiredTfm;
+            var minTfmVersion = NuGetFramework.Parse(minTfmValue).Version;
+
+            ProjectPropertyGroupElement? mainPropertyGroup = null;
+            ProjectPropertyElement? mainTargetFrameworkProperty = null;
+
+            var propertiesToChange = new List<ProjectPropertyElement>();
+
+            foreach (var propertyGroup in root.PropertyGroups)
+            {
+                bool groupHasCondition = !string.IsNullOrEmpty(propertyGroup.Condition);
+
+                // Check if the property group should be excluded from checking for 'TargetFramework' properties.
+                if (groupHasCondition && !ConditionMatchesGodotPlatform(propertyGroup.Condition))
+                {
+                    continue;
+                }
+
+                // Store a reference to the first property group without conditions,
+                // in case we need to add a new 'TargetFramework' property later.
+                if (mainPropertyGroup == null && !groupHasCondition)
+                {
+                    mainPropertyGroup = propertyGroup;
+                }
+
+                foreach (var property in propertyGroup.Properties)
+                {
+                    // We are looking for 'TargetFramework' properties.
+                    if (property.Name != "TargetFramework")
+                    {
+                        continue;
+                    }
+
+                    bool propertyHasCondition = !string.IsNullOrEmpty(property.Condition);
+
+                    // Check if the property should be excluded.
+                    if (propertyHasCondition && !ConditionMatchesGodotPlatform(property.Condition))
+                    {
+                        continue;
+                    }
+
+                    if (!groupHasCondition && !propertyHasCondition)
+                    {
+                        // Store a reference to the 'TargetFramework' that has no conditions
+                        // because it applies to all platforms.
+                        if (mainTargetFrameworkProperty == null)
+                        {
+                            mainTargetFrameworkProperty = property;
+                        }
+                        continue;
+                    }
+
+                    // If the 'TargetFramework' property is conditional, it may no longer be needed
+                    // when the main one is upgraded to the new minimum version.
+                    var tfmVersion = NuGetFramework.Parse(property.Value).Version;
+                    if (tfmVersion <= minTfmVersion)
+                    {
+                        propertiesToChange.Add(property);
+                    }
+                }
+            }
+
+            if (mainTargetFrameworkProperty == null)
+            {
+                // We haven't found a 'TargetFramework' property without conditions,
+                // we'll just add one in the first property group without conditions.
+                if (mainPropertyGroup == null)
+                {
+                    // We also don't have a property group without conditions,
+                    // so we'll add a new one to the project.
+                    mainPropertyGroup = root.AddPropertyGroup();
+                }
+
+                mainTargetFrameworkProperty = mainPropertyGroup.AddProperty("TargetFramework", minTfmValue);
+                project.HasUnsavedChanges = true;
+            }
+            else
+            {
+                var tfmVersion = NuGetFramework.Parse(mainTargetFrameworkProperty.Value).Version;
+                if (tfmVersion < minTfmVersion)
+                {
+                    mainTargetFrameworkProperty.Value = minTfmValue;
+                    project.HasUnsavedChanges = true;
+                }
+            }
+
+            var mainTfmVersion = NuGetFramework.Parse(mainTargetFrameworkProperty.Value).Version;
+            foreach (var property in propertiesToChange)
+            {
+                // If the main 'TargetFramework' property targets a version newer than
+                // the minimum required by Godot, we don't want to remove the conditional
+                // 'TargetFramework' properties, only upgrade them to the new minimum.
+                // Otherwise, it can be removed.
+                if (mainTfmVersion > minTfmVersion)
+                {
+                    property.Value = minTfmValue;
+                }
+                else
+                {
+                    property.Parent.RemoveChild(property);
+                }
+
+                project.HasUnsavedChanges = true;
+            }
+
+            static bool ConditionMatchesGodotPlatform(string condition)
+            {
+                // Check if the condition is checking the 'GodotTargetPlatform' for one of the
+                // Godot platforms with built-in support in the Godot.NET.Sdk.
+                var match = GodotTargetPlatformConditionRegex().Match(condition);
+                if (match.Success)
+                {
+                    string platform = match.Groups["platform"].Value;
+                    return _platformNames.Contains(platform, StringComparer.OrdinalIgnoreCase);
+                }
+
+                return false;
+            }
+        }
     }
 }

+ 1 - 6
modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs

@@ -439,12 +439,7 @@ namespace GodotTools
                 var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
                                      ?? throw new InvalidOperationException("Cannot open C# project.");
 
-                // NOTE: The order in which changes are made to the project is important
-
-                // Migrate to MSBuild project Sdks style if using the old style
-                ProjectUtils.MigrateToProjectSdksStyle(msbuildProject, GodotSharpDirs.ProjectAssemblyName);
-
-                ProjectUtils.EnsureGodotSdkIsUpToDate(msbuildProject);
+                ProjectUtils.UpgradeProjectIfNeeded(msbuildProject, GodotSharpDirs.ProjectAssemblyName);
 
                 if (msbuildProject.HasUnsavedChanges)
                 {