summaryrefslogtreecommitdiff
path: root/modules/mono/editor
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono/editor')
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs5
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs18
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs16
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs12
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs16
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets7
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs18
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs125
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs4
-rw-r--r--modules/mono/editor/bindings_generator.cpp36
-rw-r--r--modules/mono/editor/hostfxr_resolver.cpp335
-rw-r--r--modules/mono/editor/hostfxr_resolver.h45
-rw-r--r--modules/mono/editor/semver.cpp149
-rw-r--r--modules/mono/editor/semver.h106
18 files changed, 761 insertions, 143 deletions
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
index 59ce1da17b..0459257106 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
@@ -62,7 +62,7 @@
</PropertyGroup>
<PropertyGroup>
- <GodotRealTIsDouble Condition=" '$(GodotRealTIsDouble)' == '' ">false</GodotRealTIsDouble>
+ <GodotFloat64 Condition=" '$(GodotFloat64)' == '' ">false</GodotFloat64>
</PropertyGroup>
<!-- Godot DefineConstants. -->
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
index aad4ea4553..859ea52c93 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
@@ -10,14 +10,14 @@
<!--
Define constant to determine whether the real_t type in Godot is double precision or not.
By default this is false, like the official Godot builds. If someone is using a custom
- Godot build where real_t is double, they can override the GodotRealTIsDouble property.
+ Godot build where real_t is double, they can override the GodotFloat64 property.
-->
- <DefineConstants Condition=" '$(GodotRealTIsDouble)' == 'true' ">GODOT_REAL_T_IS_DOUBLE;$(DefineConstants)</DefineConstants>
+ <DefineConstants Condition=" '$(GodotFloat64)' == 'true' ">GODOT_REAL_T_IS_DOUBLE;$(DefineConstants)</DefineConstants>
</PropertyGroup>
<!-- C# source generators -->
<ItemGroup Condition=" '$(DisableImplicitGodotGeneratorReferences)' != 'true' ">
- <PackageReference Include="Godot.SourceGenerators" Version="$(PackageFloatingVersion_Godot)" />
+ <PackageReference Include="Godot.SourceGenerators" Version="$(PackageVersion_Godot_SourceGenerators)" />
</ItemGroup>
<!-- Godot API references -->
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
index 7aaadb27be..98ca534c66 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
@@ -39,6 +39,11 @@ namespace Godot.SourceGenerators
for (int i = 0; i < typeArgListSyntax.Arguments.Count; i++)
{
var typeSyntax = typeArgListSyntax.Arguments[i];
+
+ // Ignore omitted type arguments, e.g.: List<>, Dictionary<,>, etc
+ if (typeSyntax is OmittedTypeArgumentSyntax)
+ continue;
+
var typeSymbol = sm.GetSymbolInfo(typeSyntax).Symbol as ITypeSymbol;
Debug.Assert(typeSymbol != null);
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
index 5ac4f4a47e..1ee31eb1a9 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs
@@ -133,7 +133,9 @@ namespace Godot.SourceGenerators
.Distinct(new MethodOverloadEqualityComparer())
.ToArray();
- source.Append(" private partial class GodotInternal {\n");
+ source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+ source.Append($" public new class MethodName : {symbol.BaseType.FullQualifiedName()}.MethodName {{\n");
// Generate cached StringNames for methods and properties, for fast lookup
@@ -144,7 +146,7 @@ namespace Godot.SourceGenerators
foreach (string methodName in distinctMethodNames)
{
- source.Append(" public static readonly StringName MethodName_");
+ source.Append(" public new static readonly StringName ");
source.Append(methodName);
source.Append(" = \"");
source.Append(methodName);
@@ -157,8 +159,6 @@ namespace Godot.SourceGenerators
if (godotClassMethods.Length > 0)
{
- source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
-
const string listType = "System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>";
source.Append(" internal new static ")
@@ -179,10 +179,10 @@ namespace Godot.SourceGenerators
source.Append(" return methods;\n");
source.Append(" }\n");
-
- source.Append("#pragma warning restore CS0109\n");
}
+ source.Append("#pragma warning restore CS0109\n");
+
// Generate InvokeGodotClassMethod
if (godotClassMethods.Length > 0)
@@ -242,7 +242,7 @@ namespace Godot.SourceGenerators
private static void AppendMethodInfo(StringBuilder source, MethodInfo methodInfo)
{
- source.Append(" methods.Add(new(name: GodotInternal.MethodName_")
+ source.Append(" methods.Add(new(name: MethodName.")
.Append(methodInfo.Name)
.Append(", returnVal: ");
@@ -350,7 +350,7 @@ namespace Godot.SourceGenerators
source.Append(" ");
if (!isFirstEntry)
source.Append("else ");
- source.Append("if (method == GodotInternal.MethodName_");
+ source.Append("if (method == MethodName.");
source.Append(methodName);
source.Append(") {\n return true;\n }\n");
}
@@ -362,7 +362,7 @@ namespace Godot.SourceGenerators
{
string methodName = method.Method.Name;
- source.Append(" if (method == GodotInternal.MethodName_");
+ source.Append(" if (method == MethodName.");
source.Append(methodName);
source.Append(" && argCount == ");
source.Append(method.ParamTypes.Length);
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
index fc46d82dff..b331e2e794 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -122,14 +122,16 @@ namespace Godot.SourceGenerators
var godotClassProperties = propertySymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
var godotClassFields = fieldSymbols.WhereIsGodotCompatibleType(typeCache).ToArray();
- source.Append(" private partial class GodotInternal {\n");
+ source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+ source.Append($" public new class PropertyName : {symbol.BaseType.FullQualifiedName()}.PropertyName {{\n");
// Generate cached StringNames for methods and properties, for fast lookup
foreach (var property in godotClassProperties)
{
string propertyName = property.PropertySymbol.Name;
- source.Append(" public static readonly StringName PropName_");
+ source.Append(" public new static readonly StringName ");
source.Append(propertyName);
source.Append(" = \"");
source.Append(propertyName);
@@ -139,7 +141,7 @@ namespace Godot.SourceGenerators
foreach (var field in godotClassFields)
{
string fieldName = field.FieldSymbol.Name;
- source.Append(" public static readonly StringName PropName_");
+ source.Append(" public new static readonly StringName ");
source.Append(fieldName);
source.Append(" = \"");
source.Append(fieldName);
@@ -214,8 +216,6 @@ namespace Godot.SourceGenerators
// Generate GetGodotPropertyList
- source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
-
string dictionaryType = "System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>";
source.Append(" internal new static ")
@@ -289,7 +289,7 @@ namespace Godot.SourceGenerators
if (!isFirstEntry)
source.Append("else ");
- source.Append("if (name == GodotInternal.PropName_")
+ source.Append("if (name == PropertyName.")
.Append(propertyMemberName)
.Append(") {\n")
.Append(" ")
@@ -313,7 +313,7 @@ namespace Godot.SourceGenerators
if (!isFirstEntry)
source.Append("else ");
- source.Append("if (name == GodotInternal.PropName_")
+ source.Append("if (name == PropertyName.")
.Append(propertyMemberName)
.Append(") {\n")
.Append(" value = ")
@@ -342,7 +342,7 @@ namespace Godot.SourceGenerators
{
source.Append(" properties.Add(new(type: (Godot.Variant.Type)")
.Append((int)propertyInfo.Type)
- .Append(", name: GodotInternal.PropName_")
+ .Append(", name: PropertyName.")
.Append(propertyInfo.Name)
.Append(", hint: (Godot.PropertyHint)")
.Append((int)propertyInfo.Hint)
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
index c7745391d0..65dadcb801 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
@@ -243,7 +243,7 @@ namespace Godot.SourceGenerators
source.Append(" = ");
source.Append(exportedMember.Value ?? "default");
source.Append(";\n");
- source.Append(" values.Add(GodotInternal.PropName_");
+ source.Append(" values.Add(PropertyName.");
source.Append(exportedMember.Name);
source.Append(", ");
source.Append(defaultValueLocalName);
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
index 39a99ff8ba..a40220565f 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs
@@ -159,7 +159,7 @@ namespace Godot.SourceGenerators
{
string propertyName = property.PropertySymbol.Name;
- source.Append(" info.AddProperty(GodotInternal.PropName_")
+ source.Append(" info.AddProperty(PropertyName.")
.Append(propertyName)
.Append(", ")
.AppendManagedToVariantExpr(string.Concat("this.", propertyName), property.Type)
@@ -172,7 +172,7 @@ namespace Godot.SourceGenerators
{
string fieldName = field.FieldSymbol.Name;
- source.Append(" info.AddProperty(GodotInternal.PropName_")
+ source.Append(" info.AddProperty(PropertyName.")
.Append(fieldName)
.Append(", ")
.AppendManagedToVariantExpr(string.Concat("this.", fieldName), field.Type)
@@ -185,7 +185,7 @@ namespace Godot.SourceGenerators
{
string signalName = signalDelegate.Name;
- source.Append(" info.AddSignalEventDelegate(GodotInternal.SignalName_")
+ source.Append(" info.AddSignalEventDelegate(SignalName.")
.Append(signalName)
.Append(", this.backing_")
.Append(signalName)
@@ -204,7 +204,7 @@ namespace Godot.SourceGenerators
{
string propertyName = property.PropertySymbol.Name;
- source.Append(" if (info.TryGetProperty(GodotInternal.PropName_")
+ source.Append(" if (info.TryGetProperty(PropertyName.")
.Append(propertyName)
.Append(", out var _value_")
.Append(propertyName)
@@ -223,7 +223,7 @@ namespace Godot.SourceGenerators
{
string fieldName = field.FieldSymbol.Name;
- source.Append(" if (info.TryGetProperty(GodotInternal.PropName_")
+ source.Append(" if (info.TryGetProperty(PropertyName.")
.Append(fieldName)
.Append(", out var _value_")
.Append(fieldName)
@@ -245,7 +245,7 @@ namespace Godot.SourceGenerators
source.Append(" if (info.TryGetSignalEventDelegate<")
.Append(signalDelegateQualifiedName)
- .Append(">(GodotInternal.SignalName_")
+ .Append(">(SignalName.")
.Append(signalName)
.Append(", out var _value_")
.Append(signalName)
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 6b06f10db1..1df41a905b 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -173,14 +173,16 @@ namespace Godot.SourceGenerators
godotSignalDelegates.Add(new(signalName, signalDelegateSymbol, invokeMethodData.Value));
}
- source.Append(" private partial class GodotInternal {\n");
+ source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
+
+ source.Append($" public new class SignalName : {symbol.BaseType.FullQualifiedName()}.SignalName {{\n");
// Generate cached StringNames for methods and properties, for fast lookup
foreach (var signalDelegate in godotSignalDelegates)
{
string signalName = signalDelegate.Name;
- source.Append(" public static readonly StringName SignalName_");
+ source.Append(" public new static readonly StringName ");
source.Append(signalName);
source.Append(" = \"");
source.Append(signalName);
@@ -193,8 +195,6 @@ namespace Godot.SourceGenerators
if (godotSignalDelegates.Count > 0)
{
- source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
-
const string listType = "System.Collections.Generic.List<global::Godot.Bridge.MethodInfo>";
source.Append(" internal new static ")
@@ -215,10 +215,10 @@ namespace Godot.SourceGenerators
source.Append(" return signals;\n");
source.Append(" }\n");
-
- source.Append("#pragma warning restore CS0109\n");
}
+ source.Append("#pragma warning restore CS0109\n");
+
// Generate signal event
foreach (var signalDelegate in godotSignalDelegates)
@@ -291,7 +291,7 @@ namespace Godot.SourceGenerators
private static void AppendMethodInfo(StringBuilder source, MethodInfo methodInfo)
{
- source.Append(" signals.Add(new(name: GodotInternal.SignalName_")
+ source.Append(" signals.Add(new(name: SignalName.")
.Append(methodInfo.Name)
.Append(", returnVal: ");
@@ -400,7 +400,7 @@ namespace Godot.SourceGenerators
string signalName = signal.Name;
var invokeMethodData = signal.InvokeMethodData;
- source.Append(" if (signal == GodotInternal.SignalName_");
+ source.Append(" if (signal == SignalName.");
source.Append(signalName);
source.Append(" && argCount == ");
source.Append(invokeMethodData.ParamTypes.Length);
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
index 4baae77b34..37bd4a0be0 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
@@ -21,10 +21,13 @@
Outputs="$(GeneratedGodotNupkgsVersionsFile)">
<PropertyGroup>
<GenerateGodotNupkgsVersionsCode><![CDATA[
-namespace $(RootNamespace) {
- public class GeneratedGodotNupkgsVersions {
+namespace $(RootNamespace)
+{
+ public class GeneratedGodotNupkgsVersions
+ {
public const string GodotNETSdk = "$(PackageVersion_Godot_NET_Sdk)"%3b
public const string GodotSourceGenerators = "$(PackageVersion_Godot_SourceGenerators)"%3b
+ public const string GodotSharp = "$(PackageVersion_GodotSharp)"%3b
}
}
]]></GenerateGodotNupkgsVersionsCode>
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
index 7bce53308c..b437c7e742 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
+using System.Runtime.InteropServices;
using JetBrains.Annotations;
using OS = GodotTools.Utils.OS;
@@ -16,6 +17,23 @@ namespace GodotTools.Build
// In the future, this method may do more than just search in PATH. We could look in
// known locations or use Godot's linked nethost to search from the hostfxr location.
+ if (OS.IsMacOS)
+ {
+ if (RuntimeInformation.OSArchitecture == Architecture.X64)
+ {
+ string dotnet_x64 = "/usr/local/share/dotnet/x64/dotnet"; // Look for x64 version, when running under Rosetta 2.
+ if (File.Exists(dotnet_x64))
+ {
+ return dotnet_x64;
+ }
+ }
+ string dotnet = "/usr/local/share/dotnet/dotnet"; // Look for native version.
+ if (File.Exists(dotnet))
+ {
+ return dotnet;
+ }
+ }
+
return OS.PathWhich("dotnet");
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 4041026426..237ac85267 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -122,7 +122,7 @@ namespace GodotTools.Build
{
base._Ready();
- CustomMinimumSize = new Vector2(0, 228) * EditorScale;
+ CustomMinimumSize = new Vector2i(0, (int)(228 * EditorScale));
SizeFlagsVertical = (int)SizeFlags.ExpandFill;
var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
index d2e0e128b5..fe309b8102 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
@@ -22,71 +22,13 @@ namespace GodotTools.Build
public static string GodotFallbackFolderPath
=> Path.Combine(GodotSharpDirs.MonoUserDir, "GodotNuGetFallbackFolder");
- private static void AddFallbackFolderToNuGetConfig(string nuGetConfigPath, string name, string path)
- {
- var xmlDoc = new XmlDocument();
- xmlDoc.Load(nuGetConfigPath);
-
- const string nuGetConfigRootName = "configuration";
-
- var rootNode = xmlDoc.DocumentElement;
-
- if (rootNode == null)
- {
- // No root node, create it
- rootNode = xmlDoc.CreateElement(nuGetConfigRootName);
- xmlDoc.AppendChild(rootNode);
-
- // Since this can be considered pretty much a new NuGet.Config, add the default nuget.org source as well
- XmlElement nugetOrgSourceEntry = xmlDoc.CreateElement("add");
- nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = "nuget.org";
- nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value =
- "https://api.nuget.org/v3/index.json";
- nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("protocolVersion")).Value = "3";
- rootNode.AppendChild(xmlDoc.CreateElement("packageSources")).AppendChild(nugetOrgSourceEntry);
- }
- else
- {
- // Check that the root node is the expected one
- if (rootNode.Name != nuGetConfigRootName)
- throw new FormatException("Invalid root Xml node for NuGet.Config. " +
- $"Expected '{nuGetConfigRootName}' got '{rootNode.Name}'.");
- }
-
- var fallbackFoldersNode = rootNode["fallbackPackageFolders"] ??
- rootNode.AppendChild(xmlDoc.CreateElement("fallbackPackageFolders"));
-
- // Check if it already has our fallback package folder
- for (var xmlNode = fallbackFoldersNode.FirstChild; xmlNode != null; xmlNode = xmlNode.NextSibling)
- {
- if (xmlNode.NodeType != XmlNodeType.Element)
- continue;
-
- var xmlElement = (XmlElement)xmlNode;
- if (xmlElement.Name == "add" &&
- xmlElement.Attributes["key"]?.Value == name &&
- xmlElement.Attributes["value"]?.Value == path)
- {
- return;
- }
- }
-
- XmlElement newEntry = xmlDoc.CreateElement("add");
- newEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = name;
- newEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = path;
-
- fallbackFoldersNode.AppendChild(newEntry);
-
- xmlDoc.Save(nuGetConfigPath);
- }
-
/// <summary>
- /// Returns all the paths where the user NuGet.Config files can be found.
+ /// Returns all the paths where the Godot.Offline.Config files can be found.
/// Does not determine whether the returned files exist or not.
/// </summary>
- private static string[] GetAllUserNuGetConfigFilePaths()
+ private static string[] GetAllGodotNuGetConfigFilePaths()
{
- // Where to find 'NuGet/NuGet.Config':
+ // Where to find 'NuGet/config/Godot.Offline.Config':
//
// - Mono/.NETFramework (standalone NuGet):
// Uses Environment.SpecialFolder.ApplicationData
@@ -98,10 +40,12 @@ namespace GodotTools.Build
string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ const string configFileName = "Godot.Offline.Config";
+
if (Utils.OS.IsWindows)
{
// %APPDATA% for both
- return new[] { Path.Combine(applicationData, "NuGet", "NuGet.Config") };
+ return new[] { Path.Combine(applicationData, "NuGet", "config", configFileName) };
}
var paths = new string[2];
@@ -111,20 +55,20 @@ namespace GodotTools.Build
string dotnetCliHome = Environment.GetEnvironmentVariable("DOTNET_CLI_HOME");
if (!string.IsNullOrEmpty(dotnetCliHome))
{
- paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "NuGet.Config");
+ paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "config", configFileName);
}
else
{
string home = Environment.GetEnvironmentVariable("HOME");
if (string.IsNullOrEmpty(home))
throw new InvalidOperationException("Required environment variable 'HOME' is not set.");
- paths[0] = Path.Combine(home, ".nuget", "NuGet", "NuGet.Config");
+ paths[0] = Path.Combine(home, ".nuget", "NuGet", "config", configFileName);
}
// Mono/.NETFramework (standalone NuGet)
// ApplicationData is $HOME/.config on Linux/macOS
- paths[1] = Path.Combine(applicationData, "NuGet", "NuGet.Config");
+ paths[1] = Path.Combine(applicationData, "NuGet", "config", configFileName);
return paths;
}
@@ -141,28 +85,26 @@ namespace GodotTools.Build
// The nuspec is not lower case inside the nupkg but must be made lower case when extracted.
/// <summary>
- /// Adds the specified fallback folder to the user NuGet.Config files,
+ /// Adds the specified fallback folder to the Godot.Offline.Config files,
/// for both standalone NuGet (Mono/.NETFramework) and dotnet CLI NuGet.
/// </summary>
- public static void AddFallbackFolderToUserNuGetConfigs(string name, string path)
+ public static void AddFallbackFolderToGodotNuGetConfigs(string name, string path)
{
- foreach (string nuGetConfigPath in GetAllUserNuGetConfigFilePaths())
+ // Make sure the fallback folder exists to avoid error:
+ // MSB4018: The "ResolvePackageAssets" task failed unexpectedly.
+ System.IO.Directory.CreateDirectory(path);
+
+ foreach (string nuGetConfigPath in GetAllGodotNuGetConfigFilePaths())
{
- if (!System.IO.File.Exists(nuGetConfigPath))
- {
- // It doesn't exist, so we create a default one
- const string defaultConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
+ string defaultConfig = @$"<?xml version=""1.0"" encoding=""utf-8""?>
<configuration>
- <packageSources>
- <add key=""nuget.org"" value=""https://api.nuget.org/v3/index.json"" protocolVersion=""3"" />
- </packageSources>
+ <fallbackPackageFolders>
+ <add key=""{name}"" value=""{path}"" />
+ </fallbackPackageFolders>
</configuration>
";
- System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath));
- System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
- }
-
- AddFallbackFolderToNuGetConfig(nuGetConfigPath, name, path);
+ System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath));
+ System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
}
}
@@ -189,6 +131,7 @@ namespace GodotTools.Build
string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower);
string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg");
string nupkgSha512DestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg.sha512");
+ string nupkgMetadataDestPath = Path.Combine(destDir, ".nupkg.metadata");
if (File.Exists(nupkgDestPath) && File.Exists(nupkgSha512DestPath))
return; // Already added (for speed we don't check if every file is properly extracted)
@@ -197,12 +140,18 @@ namespace GodotTools.Build
// Generate .nupkg.sha512 file
- using (var alg = SHA512.Create())
- {
- alg.ComputeHash(File.ReadAllBytes(nupkgPath));
- string base64Hash = Convert.ToBase64String(alg.Hash);
- File.WriteAllText(nupkgSha512DestPath, base64Hash);
- }
+ byte[] hash = SHA512.HashData(File.ReadAllBytes(nupkgPath));
+ string base64Hash = Convert.ToBase64String(hash);
+ File.WriteAllText(nupkgSha512DestPath, base64Hash);
+
+ // Generate .nupkg.metadata file
+ // Spec: https://github.com/NuGet/Home/wiki/Nupkg-Metadata-File
+
+ File.WriteAllText(nupkgMetadataDestPath, @$"{{
+ ""version"": 2,
+ ""contentHash"": ""{base64Hash}"",
+ ""source"": null
+}}");
// Extract nupkg
ExtractNupkg(destDir, nupkgPath, packageId, packageVersion);
@@ -251,7 +200,7 @@ namespace GodotTools.Build
entryFullName.EndsWith(".nupkg.sha512", StringComparison.OrdinalIgnoreCase) ||
entryFullName.EndsWith(".nupkg.metadata", StringComparison.OrdinalIgnoreCase) ||
// Nuspec at root level. We already extracted it previously but in lower case.
- entryFullName.IndexOf('/') == -1 && entryFullName.EndsWith(".nuspec"))
+ !entryFullName.Contains('/') && entryFullName.EndsWith(".nuspec"))
{
continue;
}
@@ -297,6 +246,8 @@ namespace GodotTools.Build
{
("Godot.NET.Sdk", GeneratedGodotNupkgsVersions.GodotNETSdk),
("Godot.SourceGenerators", GeneratedGodotNupkgsVersions.GodotSourceGenerators),
+ ("GodotSharp", GeneratedGodotNupkgsVersions.GodotSharp),
+ ("GodotSharpEditor", GeneratedGodotNupkgsVersions.GodotSharp),
};
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 1cfaea3ec9..89364d1c02 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -123,7 +123,7 @@ namespace GodotTools
try
{
string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
- NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
+ NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
fallbackFolder);
NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
}
@@ -497,7 +497,7 @@ namespace GodotTools
try
{
// At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
- NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
+ NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
NuGetUtils.GodotFallbackFolderPath);
}
catch (Exception e)
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 15a40c8ca5..ce4ac9b796 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -113,7 +113,7 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToManaged"
// Types that will be ignored by the generator and won't be available in C#.
-const Vector<String> ignored_types = { "PhysicsServer3DExtension" };
+const Vector<String> ignored_types = { "PhysicsServer2DExtension", "PhysicsServer3DExtension" };
void BindingsGenerator::TypeInterface::postsetup_enum_type(BindingsGenerator::TypeInterface &r_enum_itype) {
// C interface for enums is the same as that of 'uint32_t'. Remember to apply
@@ -2229,6 +2229,26 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
// Generate signal
{
+ if (p_isignal.is_deprecated) {
+ if (p_isignal.deprecation_message.is_empty()) {
+ WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
+ }
+
+ p_output.append(MEMBER_BEGIN "[Obsolete(\"");
+ p_output.append(p_isignal.deprecation_message);
+ p_output.append("\")]");
+ }
+
+ String delegate_name = p_isignal.proxy_name;
+ delegate_name += "EventHandler"; // Delegate name is [SignalName]EventHandler
+
+ // Generate delegate
+ p_output.append(MEMBER_BEGIN "public delegate void ");
+ p_output.append(delegate_name);
+ p_output.append("(");
+ p_output.append(arguments_sig);
+ p_output.append(");\n");
+
if (p_isignal.method_doc && p_isignal.method_doc->description.size()) {
String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
@@ -2247,25 +2267,11 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
}
if (p_isignal.is_deprecated) {
- if (p_isignal.deprecation_message.is_empty()) {
- WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
- }
-
p_output.append(MEMBER_BEGIN "[Obsolete(\"");
p_output.append(p_isignal.deprecation_message);
p_output.append("\")]");
}
- String delegate_name = p_isignal.proxy_name;
- delegate_name += "EventHandler"; // Delegate name is [SignalName]EventHandler
-
- // Generate delegate
- p_output.append(MEMBER_BEGIN "public delegate void ");
- p_output.append(delegate_name);
- p_output.append("(");
- p_output.append(arguments_sig);
- p_output.append(");\n");
-
// TODO:
// Could we assume the StringName instance of signal name will never be freed (it's stored in ClassDB) before the managed world is unloaded?
// If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here.
diff --git a/modules/mono/editor/hostfxr_resolver.cpp b/modules/mono/editor/hostfxr_resolver.cpp
new file mode 100644
index 0000000000..ea5978b2cd
--- /dev/null
+++ b/modules/mono/editor/hostfxr_resolver.cpp
@@ -0,0 +1,335 @@
+/*************************************************************************/
+/* hostfxr_resolver.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Adapted to Godot from the nethost library: https://github.com/dotnet/runtime/tree/main/src/native/corehost
+*/
+
+/*
+The MIT License (MIT)
+
+Copyright (c) .NET Foundation and Contributors
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "hostfxr_resolver.h"
+
+#include "core/config/engine.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
+#include "core/os/os.h"
+
+#ifdef WINDOWS_ENABLED
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include "../utils/path_utils.h"
+#include "semver.h"
+
+// We don't use libnethost as it gives us issues with some compilers.
+// This file tries to mimic libnethost's hostfxr_resolver search logic. We try to use the
+// same function names for easier comparing in case we need to update this in the future.
+
+namespace {
+
+String get_hostfxr_file_name() {
+#if defined(WINDOWS_ENABLED) || defined(UWP_ENABLED)
+ return "hostfxr.dll";
+#elif defined(MACOS_ENABLED) || defined(IOS_ENABLED)
+ return "libhostfxr.dylib";
+#else
+ return "libhostfxr.so";
+#endif
+}
+
+bool get_latest_fxr(const String &fxr_root, String &r_fxr_path) {
+ godotsharp::SemVerParser sem_ver_parser;
+
+ bool found_ver = false;
+ godotsharp::SemVer latest_ver;
+ String latest_ver_str;
+
+ Ref<DirAccess> da = DirAccess::open(fxr_root);
+ da->list_dir_begin();
+ for (String dir = da->get_next(); !dir.is_empty(); dir = da->get_next()) {
+ if (!da->current_is_dir() || dir == "." || dir == "..") {
+ continue;
+ }
+
+ String ver = dir.get_file();
+
+ godotsharp::SemVer fx_ver;
+ if (sem_ver_parser.parse(ver, fx_ver)) {
+ if (!found_ver || fx_ver > latest_ver) {
+ latest_ver = fx_ver;
+ latest_ver_str = ver;
+ found_ver = true;
+ }
+ }
+ }
+
+ if (!found_ver) {
+ return false;
+ }
+
+ String fxr_with_ver = path::join(fxr_root, latest_ver_str);
+ String hostfxr_file_path = path::join(fxr_with_ver, get_hostfxr_file_name());
+
+ ERR_FAIL_COND_V_MSG(!FileAccess::exists(hostfxr_file_path), false, "Missing hostfxr library in directory: " + fxr_with_ver);
+
+ r_fxr_path = hostfxr_file_path;
+
+ return true;
+}
+
+#ifdef WINDOWS_ENABLED
+typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
+
+BOOL is_wow64() {
+ BOOL wow64 = FALSE;
+
+ LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
+
+ if (fnIsWow64Process) {
+ if (!fnIsWow64Process(GetCurrentProcess(), &wow64)) {
+ wow64 = FALSE;
+ }
+ }
+
+ return wow64;
+}
+#endif
+
+static const char *arch_name_map[][2] = {
+ { "arm32", "arm" },
+ { "arm64", "arm64" },
+ { "rv64", "riscv64" },
+ { "x86_64", "x64" },
+ { "x86_32", "x86" },
+ { nullptr, nullptr }
+};
+
+String get_dotnet_arch() {
+ String arch = Engine::get_singleton()->get_architecture_name();
+
+ int idx = 0;
+ while (arch_name_map[idx][0] != nullptr) {
+ if (arch_name_map[idx][0] == arch) {
+ return arch_name_map[idx][1];
+ }
+ idx++;
+ }
+
+ return "";
+}
+
+bool get_default_installation_dir(String &r_dotnet_root) {
+#if defined(WINDOWS_ENABLED)
+ String program_files_env;
+ if (is_wow64()) {
+ // Running x86 on x64, looking for x86 install
+ program_files_env = "ProgramFiles(x86)";
+ } else {
+ program_files_env = "ProgramFiles";
+ }
+
+ String program_files_dir = OS::get_singleton()->get_environment(program_files_env);
+
+ if (program_files_dir.is_empty()) {
+ return false;
+ }
+
+#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
+ // When emulating x64 on arm
+ String dotnet_root_emulated = path::join(program_files_dir, "dotnet", "x64");
+ if (FileAccess::exists(path::join(dotnet_root_emulated, "dotnet.exe"))) {
+ r_dotnet_root = dotnet_root_emulated;
+ return true;
+ }
+#endif
+
+ r_dotnet_root = path::join(program_files_dir, "dotnet");
+ return true;
+#elif defined(MACOS_ENABLED)
+ r_dotnet_root = "/usr/local/share/dotnet";
+
+#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
+ // When emulating x64 on arm
+ String dotnet_root_emulated = path::join(r_dotnet_root, "x64");
+ if (FileAccess::exists(path::join(dotnet_root_emulated, "dotnet"))) {
+ r_dotnet_root = dotnet_root_emulated;
+ return true;
+ }
+#endif
+
+ return true;
+#else
+ r_dotnet_root = "/usr/share/dotnet";
+ return true;
+#endif
+}
+
+bool get_install_location_from_file(const String &p_file_path, String &r_dotnet_root) {
+ Error err = OK;
+ Ref<FileAccess> f = FileAccess::open(p_file_path, FileAccess::READ, &err);
+
+ if (f.is_null() || err != OK) {
+ return false;
+ }
+
+ String line = f->get_line();
+
+ if (line.is_empty()) {
+ return false;
+ }
+
+ r_dotnet_root = line;
+ return true;
+}
+
+bool get_dotnet_self_registered_dir(String &r_dotnet_root) {
+#if defined(WINDOWS_ENABLED)
+ String sub_key = "SOFTWARE\\dotnet\\Setup\\InstalledVersions\\" + get_dotnet_arch();
+ Char16String value = String("InstallLocation").utf16();
+
+ HKEY hkey = NULL;
+ LSTATUS result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(sub_key.utf16().get_data()), 0, KEY_READ | KEY_WOW64_32KEY, &hkey);
+ if (result != ERROR_SUCCESS) {
+ return false;
+ }
+
+ DWORD size = 0;
+ result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, nullptr, &size);
+ if (result != ERROR_SUCCESS || size == 0) {
+ RegCloseKey(hkey);
+ return false;
+ }
+
+ Vector<WCHAR> buffer;
+ buffer.resize(size / sizeof(WCHAR));
+ result = RegGetValueW(hkey, nullptr, (LPCWSTR)(value.get_data()), RRF_RT_REG_SZ, nullptr, (LPBYTE)buffer.ptrw(), &size);
+ if (result != ERROR_SUCCESS) {
+ RegCloseKey(hkey);
+ return false;
+ }
+
+ r_dotnet_root = String::utf16((const char16_t *)buffer.ptr());
+ RegCloseKey(hkey);
+ return true;
+#else
+ String install_location_file = path::join("/etc/dotnet", "install_location_" + get_dotnet_arch().to_lower());
+ if (get_install_location_from_file(install_location_file, r_dotnet_root)) {
+ return true;
+ }
+
+ if (FileAccess::exists(install_location_file)) {
+ // Don't try with the legacy location, this will fall back to the hard-coded default install location
+ return false;
+ }
+
+ String legacy_install_location_file = path::join("/etc/dotnet", "install_location");
+ return get_install_location_from_file(legacy_install_location_file, r_dotnet_root);
+#endif
+}
+
+bool get_file_path_from_env(const String &p_env_key, String &r_dotnet_root) {
+ String env_value = OS::get_singleton()->get_environment(p_env_key);
+
+ if (!env_value.is_empty()) {
+ env_value = path::realpath(env_value);
+
+ if (DirAccess::exists(env_value)) {
+ r_dotnet_root = env_value;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool get_dotnet_root_from_env(String &r_dotnet_root) {
+ String dotnet_root_env = "DOTNET_ROOT";
+ String arch_for_env = get_dotnet_arch();
+
+ if (!arch_for_env.is_empty()) {
+ // DOTNET_ROOT_<arch>
+ if (get_file_path_from_env(dotnet_root_env + "_" + arch_for_env.to_upper(), r_dotnet_root)) {
+ return true;
+ }
+ }
+
+#ifdef WINDOWS_ENABLED
+ // WoW64-only: DOTNET_ROOT(x86)
+ if (is_wow64() && get_file_path_from_env("DOTNET_ROOT(x86)", r_dotnet_root)) {
+ return true;
+ }
+#endif
+
+ // DOTNET_ROOT
+ return get_file_path_from_env(dotnet_root_env, r_dotnet_root);
+}
+
+} //namespace
+
+bool godotsharp::hostfxr_resolver::try_get_path_from_dotnet_root(const String &p_dotnet_root, String &r_fxr_path) {
+ String fxr_dir = path::join(p_dotnet_root, "host", "fxr");
+ ERR_FAIL_COND_V_MSG(!DirAccess::exists(fxr_dir), false, "The host fxr folder does not exist: " + fxr_dir);
+ return get_latest_fxr(fxr_dir, r_fxr_path);
+}
+
+bool godotsharp::hostfxr_resolver::try_get_path(String &r_dotnet_root, String &r_fxr_path) {
+ if (!get_dotnet_root_from_env(r_dotnet_root) &&
+ !get_dotnet_self_registered_dir(r_dotnet_root) &&
+ !get_default_installation_dir(r_dotnet_root)) {
+ return false;
+ }
+
+ return try_get_path_from_dotnet_root(r_dotnet_root, r_fxr_path);
+}
diff --git a/modules/mono/editor/hostfxr_resolver.h b/modules/mono/editor/hostfxr_resolver.h
new file mode 100644
index 0000000000..0f029ab7ae
--- /dev/null
+++ b/modules/mono/editor/hostfxr_resolver.h
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* hostfxr_resolver.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef HOSTFXR_RESOLVER_H
+#define HOSTFXR_RESOLVER_H
+
+#include "core/string/ustring.h"
+
+namespace godotsharp {
+namespace hostfxr_resolver {
+
+bool try_get_path_from_dotnet_root(const String &p_dotnet_root, String &r_out_fxr_path);
+bool try_get_path(String &r_out_dotnet_root, String &r_out_fxr_path);
+
+} //namespace hostfxr_resolver
+} //namespace godotsharp
+
+#endif // HOSTFXR_RESOLVER_H
diff --git a/modules/mono/editor/semver.cpp b/modules/mono/editor/semver.cpp
new file mode 100644
index 0000000000..1656d0932f
--- /dev/null
+++ b/modules/mono/editor/semver.cpp
@@ -0,0 +1,149 @@
+/*************************************************************************/
+/* semver.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "semver.h"
+
+bool godotsharp::SemVer::parse_digit_only_field(const String &p_field, uint64_t &r_result) {
+ if (p_field.is_empty()) {
+ return false;
+ }
+
+ int64_t integer = 0;
+
+ for (int i = 0; i < p_field.length(); i++) {
+ char32_t c = p_field[i];
+ if (is_digit(c)) {
+ bool overflow = ((uint64_t)integer > UINT64_MAX / 10) || ((uint64_t)integer == UINT64_MAX / 10 && c > '5');
+ ERR_FAIL_COND_V_MSG(overflow, false, "Cannot represent '" + p_field + "' as a 64-bit unsigned integer, since the value is too large.");
+ integer *= 10;
+ integer += c - '0';
+ } else {
+ return false;
+ }
+ }
+
+ r_result = (uint64_t)integer;
+ return true;
+}
+
+int godotsharp::SemVer::cmp(const godotsharp::SemVer &p_a, const godotsharp::SemVer &p_b) {
+ if (p_a.major != p_b.major) {
+ return p_a.major > p_b.major ? 1 : -1;
+ }
+
+ if (p_a.minor != p_b.minor) {
+ return p_a.minor > p_b.minor ? 1 : -1;
+ }
+
+ if (p_a.patch != p_b.patch) {
+ return p_a.patch > p_b.patch ? 1 : -1;
+ }
+
+ if (p_a.prerelease.is_empty() && p_b.prerelease.is_empty()) {
+ return 0;
+ }
+
+ if (p_a.prerelease.is_empty() || p_b.prerelease.is_empty()) {
+ return p_a.prerelease.is_empty() ? 1 : -1;
+ }
+
+ if (p_a.prerelease != p_b.prerelease) {
+ // This could be optimized, but I'm too lazy
+
+ Vector<String> a_field_set = p_a.prerelease.split(".");
+ Vector<String> b_field_set = p_b.prerelease.split(".");
+
+ int a_field_count = a_field_set.size();
+ int b_field_count = b_field_set.size();
+
+ int min_field_count = MIN(a_field_count, b_field_count);
+
+ for (int i = 0; i < min_field_count; i++) {
+ const String &a_field = a_field_set[i];
+ const String &b_field = b_field_set[i];
+
+ if (a_field == b_field) {
+ continue;
+ }
+
+ uint64_t a_num;
+ bool a_is_digit_only = parse_digit_only_field(a_field, a_num);
+
+ uint64_t b_num;
+ bool b_is_digit_only = parse_digit_only_field(b_field, b_num);
+
+ if (a_is_digit_only && b_is_digit_only) {
+ // Identifiers consisting of only digits are compared numerically.
+
+ if (a_num == b_num) {
+ continue;
+ }
+
+ return a_num > b_num ? 1 : -1;
+ }
+
+ if (a_is_digit_only || b_is_digit_only) {
+ // Numeric identifiers always have lower precedence than non-numeric identifiers.
+ return b_is_digit_only ? 1 : -1;
+ }
+
+ // Identifiers with letters or hyphens are compared lexically in ASCII sort order.
+ return a_field > b_field ? 1 : -1;
+ }
+
+ if (a_field_count != b_field_count) {
+ // A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal.
+ return a_field_count > b_field_count ? 1 : -1;
+ }
+ }
+
+ return 0;
+}
+
+bool godotsharp::SemVerParser::parse(const String &p_ver_text, godotsharp::SemVer &r_semver) {
+ if (!regex.is_valid() && regex.get_pattern().is_empty()) {
+ regex.compile("^(?P<major>0|[1-9]\\d*)\\.(?P<minor>0|[1-9]\\d*)\\.(?P<patch>0|[1-9]\\d*)(?:-(?P<prerelease>(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$");
+ ERR_FAIL_COND_V(!regex.is_valid(), false);
+ }
+
+ Ref<RegExMatch> match = regex.search(p_ver_text);
+
+ if (match.is_valid()) {
+ r_semver = SemVer(
+ match->get_string("major").to_int(),
+ match->get_string("minor").to_int(),
+ match->get_string("patch").to_int(),
+ match->get_string("prerelease"),
+ match->get_string("buildmetadata"));
+ return true;
+ }
+
+ return false;
+}
diff --git a/modules/mono/editor/semver.h b/modules/mono/editor/semver.h
new file mode 100644
index 0000000000..48ea8b043e
--- /dev/null
+++ b/modules/mono/editor/semver.h
@@ -0,0 +1,106 @@
+/*************************************************************************/
+/* semver.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SEMVER_H
+#define SEMVER_H
+
+#include "core/string/ustring.h"
+#include "modules/regex/regex.h"
+
+// <sys/sysmacros.h> is included somewhere, which defines major(dev) to gnu_dev_major(dev)
+#if defined(major)
+#undef major
+#endif
+#if defined(minor)
+#undef minor
+#endif
+
+namespace godotsharp {
+
+struct SemVer {
+private:
+ static bool parse_digit_only_field(const String &p_field, uint64_t &r_result);
+
+ static int cmp(const SemVer &p_a, const SemVer &p_b);
+
+public:
+ int major = 0;
+ int minor = 0;
+ int patch = 0;
+ String prerelease;
+ String build_metadata;
+
+ bool operator==(const SemVer &b) const {
+ return cmp(*this, b) == 0;
+ }
+
+ bool operator!=(const SemVer &b) const {
+ return !operator==(b);
+ }
+
+ bool operator<(const SemVer &b) const {
+ return cmp(*this, b) < 0;
+ }
+
+ bool operator>(const SemVer &b) const {
+ return cmp(*this, b) > 0;
+ }
+
+ bool operator<=(const SemVer &b) const {
+ return cmp(*this, b) <= 0;
+ }
+
+ bool operator>=(const SemVer &b) const {
+ return cmp(*this, b) >= 0;
+ }
+
+ SemVer() {}
+
+ SemVer(int p_major, int p_minor, int p_patch,
+ const String &p_prerelease, const String &p_build_metadata) :
+ major(p_major),
+ minor(p_minor),
+ patch(p_patch),
+ prerelease(p_prerelease),
+ build_metadata(p_build_metadata) {
+ }
+};
+
+struct SemVerParser {
+private:
+ RegEx regex;
+
+public:
+ bool parse(const String &p_ver_text, SemVer &r_semver);
+};
+
+} //namespace godotsharp
+
+#endif // SEMVER_H