summaryrefslogtreecommitdiff
path: root/modules/mono/editor/GodotTools
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono/editor/GodotTools')
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ResponseAwaiter.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/NotifyAwaiter.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/SemaphoreExtensions.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj26
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe0
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs13
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets11
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.sln14
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs61
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs228
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs114
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs209
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs117
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs20
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs233
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs135
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs68
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs414
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs136
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj20
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs16
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs25
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs63
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs214
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs173
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/PlaySettings.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs265
46 files changed, 1361 insertions, 1323 deletions
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
index 2bf1cb7a18..01aa65bfc3 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
@@ -7,8 +7,6 @@ namespace GodotTools.BuildLogger
{
public class GodotBuildLogger : ILogger
{
- public static readonly string AssemblyPath = Path.GetFullPath(typeof(GodotBuildLogger).Assembly.Location);
-
public string Parameters { get; set; }
public LoggerVerbosity Verbosity { get; set; }
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
index 0afec970c6..9e36497b06 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
@@ -5,6 +5,6 @@
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Build.Framework" Version="16.5.0" />
+ <PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" ExcludeAssets="runtime" />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
index d6d8962f90..cfd5c88a58 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid>
- <TargetFramework>netstandard2.0</TargetFramework>
- <LangVersion>7.2</LangVersion>
+ <TargetFramework>net6.0</TargetFramework>
+ <LangVersion>10</LangVersion>
</PropertyGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
index 60a4f297c9..7c5502814f 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
@@ -34,7 +34,7 @@ namespace GodotTools.Core
path = path.Replace('\\', '/');
path = path[path.Length - 1] == '/' ? path.Substring(0, path.Length - 1) : path;
- string[] parts = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
+ string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim();
@@ -60,7 +60,7 @@ namespace GodotTools.Core
public static string ToSafeDirName(this string dirName, bool allowDirSeparator = false)
{
- var invalidChars = new List<string> {":", "*", "?", "\"", "<", ">", "|"};
+ var invalidChars = new List<string> { ":", "*", "?", "\"", "<", ">", "|" };
if (allowDirSeparator)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
index 303ca3a293..d2132115f3 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{B06C2951-C8E3-4F28-80B2-717CF327EB19}</ProjectGuid>
<OutputType>Exe</OutputType>
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
index bc09e1ebf9..72e2a1fc0d 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
@@ -123,12 +123,16 @@ namespace GodotTools.IdeMessaging
string projectMetadataDir = Path.Combine(godotProjectDir, ".godot", "mono", "metadata");
// FileSystemWatcher requires an existing directory
- if (!Directory.Exists(projectMetadataDir)) {
+ if (!Directory.Exists(projectMetadataDir))
+ {
// Check if the non hidden version exists
string nonHiddenProjectMetadataDir = Path.Combine(godotProjectDir, "godot", "mono", "metadata");
- if (Directory.Exists(nonHiddenProjectMetadataDir)) {
+ if (Directory.Exists(nonHiddenProjectMetadataDir))
+ {
projectMetadataDir = nonHiddenProjectMetadataDir;
- } else {
+ }
+ else
+ {
Directory.CreateDirectory(projectMetadataDir);
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs
index 686202e81e..2448a2953b 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotIdeMetadata.cs
@@ -25,10 +25,7 @@ namespace GodotTools.IdeMessaging
public override bool Equals(object obj)
{
- if (obj is GodotIdeMetadata metadata)
- return metadata == this;
-
- return false;
+ return obj is GodotIdeMetadata metadata && metadata == this;
}
public bool Equals(GodotIdeMetadata other)
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
index 10d7e1898e..dd3913b4f3 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs
@@ -78,7 +78,7 @@ namespace GodotTools.IdeMessaging
clientStream.WriteTimeout = ClientWriteTimeout;
clientReader = new StreamReader(clientStream, Encoding.UTF8);
- clientWriter = new StreamWriter(clientStream, Encoding.UTF8) {NewLine = "\n"};
+ clientWriter = new StreamWriter(clientStream, Encoding.UTF8) { NewLine = "\n" };
}
public async Task Process()
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ResponseAwaiter.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ResponseAwaiter.cs
index 548e7f06ee..a57c82b608 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ResponseAwaiter.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ResponseAwaiter.cs
@@ -17,7 +17,7 @@ namespace GodotTools.IdeMessaging
if (content.Status == MessageStatus.Ok)
SetResult(JsonConvert.DeserializeObject<T>(content.Body));
else
- SetResult(new T {Status = content.Status});
+ SetResult(new T { Status = content.Status });
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/NotifyAwaiter.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/NotifyAwaiter.cs
index d84a63c83c..a285d5fa97 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/NotifyAwaiter.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/NotifyAwaiter.cs
@@ -21,14 +21,14 @@ namespace GodotTools.IdeMessaging.Utils
public void OnCompleted(Action continuation)
{
if (this.continuation != null)
- throw new InvalidOperationException("This awaiter has already been listened");
+ throw new InvalidOperationException("This awaiter already has a continuation.");
this.continuation = continuation;
}
public void SetResult(T result)
{
if (IsCompleted)
- throw new InvalidOperationException("This awaiter is already completed");
+ throw new InvalidOperationException("This awaiter is already completed.");
IsCompleted = true;
this.result = result;
@@ -39,7 +39,7 @@ namespace GodotTools.IdeMessaging.Utils
public void SetException(Exception exception)
{
if (IsCompleted)
- throw new InvalidOperationException("This awaiter is already completed");
+ throw new InvalidOperationException("This awaiter is already completed.");
IsCompleted = true;
this.exception = exception;
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/SemaphoreExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/SemaphoreExtensions.cs
index 9d593fbf8a..ab21527344 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/SemaphoreExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Utils/SemaphoreExtensions.cs
@@ -13,7 +13,7 @@ namespace GodotTools.IdeMessaging.Utils
return waitAsyncTask.ContinueWith<IDisposable>(t => wrapper, cancellationToken).ConfigureAwait(false);
}
- private struct SemaphoreSlimWaitReleaseWrapper : IDisposable
+ private readonly struct SemaphoreSlimWaitReleaseWrapper : IDisposable
{
private readonly SemaphoreSlim semaphoreSlim;
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
index 5b3ed0b1b7..c05096bdcc 100644
--- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{EAFFF236-FA96-4A4D-BD23-0E51EF988277}</ProjectGuid>
<OutputType>Exe</OutputType>
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
index 7a4641dbbc..cc0deb6cc0 100644
--- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
@@ -249,8 +249,8 @@ namespace GodotTools.OpenVisualStudio
// Thread call was rejected, so try again.
int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
{
+ // flag = SERVERCALL_RETRYLATER
if (dwRejectType == 2)
- // flag = SERVERCALL_RETRYLATER
{
// Retry the thread call immediately if return >= 0 & < 100
return 99;
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
index 37123ba2b2..bde14b2b40 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
@@ -1,32 +1,16 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
- <TargetFramework>net472</TargetFramework>
- <LangVersion>7.2</LangVersion>
+ <TargetFramework>net6.0</TargetFramework>
+ <LangVersion>10</LangVersion>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Build" Version="16.5.0" />
+ <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" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
<ProjectReference Include="..\GodotTools.Shared\GodotTools.Shared.csproj" />
</ItemGroup>
- <!--
- The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
- here doesn't work with Microsoft.NETFramework.ReferenceAssemblies: https://github.com/microsoft/msbuild/issues/3486
- We need a MSBuild.exe file as there's an issue in Microsoft.Build where it executes platform dependent code when
- searching for MSBuild.exe before the fallback to not using it. A stub is fine as it should never be executed.
- -->
- <ItemGroup>
- <None Include="MSBuild.exe" CopyToOutputDirectory="Always" />
- </ItemGroup>
- <Target Name="CopyMSBuildStubWindows" AfterTargets="Build" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) ">
- <PropertyGroup>
- <GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
- <GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
- </PropertyGroup>
- <!-- Need to copy it here as well on Windows -->
- <Copy SourceFiles="MSBuild.exe" DestinationFiles="$(GodotOutputDataDir)\Mono\lib\mono\v4.0\MSBuild.exe" />
- </Target>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe
deleted file mode 100644
index e69de29bb2..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/MSBuild.exe
+++ /dev/null
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index 7d49d251dd..f3c8e89dff 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using System.IO;
using System.Text;
using Microsoft.Build.Construction;
@@ -14,14 +15,15 @@ namespace GodotTools.ProjectEditor
public static ProjectRootElement GenGameProject(string name)
{
if (name.Length == 0)
- throw new ArgumentException("Project name is empty", nameof(name));
+ throw new ArgumentException("Project name is empty.", nameof(name));
var root = ProjectRootElement.Create(NewProjectFileOptions.None);
root.Sdk = GodotSdkAttrValue;
var mainGroup = root.AddPropertyGroup();
- mainGroup.AddProperty("TargetFramework", "netstandard2.1");
+ mainGroup.AddProperty("TargetFramework", "net6.0");
+ mainGroup.AddProperty("EnableDynamicLoading", "true");
string sanitizedName = IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true);
@@ -35,7 +37,7 @@ namespace GodotTools.ProjectEditor
public static string GenAndSaveGameProject(string dir, string name)
{
if (name.Length == 0)
- throw new ArgumentException("Project name is empty", nameof(name));
+ throw new ArgumentException("Project name is empty.", nameof(name));
string path = Path.Combine(dir, name + ".csproj");
@@ -44,7 +46,7 @@ namespace GodotTools.ProjectEditor
// Save (without BOM)
root.Save(path, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
- return Guid.NewGuid().ToString().ToUpper();
+ return Guid.NewGuid().ToString().ToUpperInvariant();
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index cdac9acb25..7b1d5c228a 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -19,6 +19,16 @@ namespace GodotTools.ProjectEditor
public static class ProjectUtils
{
+ public static void MSBuildLocatorRegisterDefaults(out Version version, out string path)
+ {
+ var instance = Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
+ version = instance.Version;
+ path = instance.MSBuildPath;
+ }
+
+ public static void MSBuildLocatorRegisterMSBuildPath(string msbuildPath)
+ => Microsoft.Build.Locator.MSBuildLocator.RegisterMSBuildPath(msbuildPath);
+
public static MSBuildProject Open(string path)
{
var root = ProjectRootElement.Open(path);
@@ -42,7 +52,8 @@ namespace GodotTools.ProjectEditor
var root = project.Root;
string godotSdkAttrValue = ProjectGenerator.GodotSdkAttrValue;
- if (!string.IsNullOrEmpty(root.Sdk) && root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
+ if (!string.IsNullOrEmpty(root.Sdk) &&
+ root.Sdk.Trim().Equals(godotSdkAttrValue, StringComparison.OrdinalIgnoreCase))
return;
root.Sdk = godotSdkAttrValue;
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
index aab2d73bdd..37bd4a0be0 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
@@ -8,8 +8,8 @@
</Target>
<Target Name="GenerateGodotNupkgsVersionsFile"
- DependsOnTargets="PrepareForBuild;_GenerateGodotNupkgsVersionsFile"
- BeforeTargets="BeforeCompile;CoreCompile">
+ DependsOnTargets="_GenerateGodotNupkgsVersionsFile"
+ BeforeTargets="PrepareForBuild;CompileDesignTime;BeforeCompile;CoreCompile">
<ItemGroup>
<Compile Include="$(GeneratedGodotNupkgsVersionsFile)" />
<FileWrites Include="$(GeneratedGodotNupkgsVersionsFile)" />
@@ -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.Shared/GodotTools.Shared.csproj b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
index 3bc1698c15..d60e6343ea 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
@@ -1,6 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>netstandard2.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
+ <!-- Specify compile items manually to avoid including dangling generated items. -->
+ <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<Import Project="GenerateGodotNupkgsVersions.targets" />
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln
index d3107a69db..564775635d 100644
--- a/modules/mono/editor/GodotTools/GodotTools.sln
+++ b/modules/mono/editor/GodotTools/GodotTools.sln
@@ -1,4 +1,4 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.ProjectEditor", "GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
@@ -15,6 +15,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.Shared", "GodotTools.Shared\GodotTools.Shared.csproj", "{2758FFAF-8237-4CF2-B569-66BF8B3587BB}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators", "..\Godot.NET.Sdk\Godot.SourceGenerators\Godot.SourceGenerators.csproj", "{D8C421B2-8911-41EB-B983-F675C7141EB7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Internal", "..\..\glue\GodotSharp\Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj", "{55666071-BEC1-4A52-8A98-9A4A7A947DBF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -49,5 +53,13 @@ Global
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D8C421B2-8911-41EB-B983-F675C7141EB7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {55666071-BEC1-4A52-8A98-9A4A7A947DBF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
index 28bf57dc21..edbf53a389 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
@@ -4,28 +4,36 @@ using Godot.Collections;
using GodotTools.Internals;
using Path = System.IO.Path;
+#nullable enable
+
namespace GodotTools.Build
{
[Serializable]
- public sealed class BuildInfo : RefCounted // TODO Remove RefCounted once we have proper serialization
+ public sealed partial class BuildInfo : RefCounted // TODO Remove RefCounted once we have proper serialization
{
- public string Solution { get; }
- public string[] Targets { get; }
- public string Configuration { get; }
- public bool Restore { get; }
+ public string Solution { get; private set; }
+ public string Configuration { get; private set; }
+ public string? RuntimeIdentifier { get; private set; }
+ public string? PublishOutputDir { get; private set; }
+ public bool Restore { get; private set; }
+ public bool Rebuild { get; private set; }
+ public bool OnlyClean { get; private set; }
+
// TODO Use List once we have proper serialization
- public Array<string> CustomProperties { get; } = new Array<string>();
+ public Godot.Collections.Array CustomProperties { get; private set; } = new();
- public string LogsDirPath => Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");
+ public string LogsDirPath =>
+ Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
- if (obj is BuildInfo other)
- return other.Solution == Solution && other.Targets == Targets &&
- other.Configuration == Configuration && other.Restore == Restore &&
- other.CustomProperties == CustomProperties && other.LogsDirPath == LogsDirPath;
-
- return false;
+ return obj is BuildInfo other &&
+ other.Solution == Solution &&
+ other.Configuration == Configuration && other.RuntimeIdentifier == RuntimeIdentifier &&
+ other.PublishOutputDir == PublishOutputDir && other.Restore == Restore &&
+ other.Rebuild == Rebuild && other.OnlyClean == OnlyClean &&
+ other.CustomProperties == CustomProperties &&
+ other.LogsDirPath == LogsDirPath;
}
public override int GetHashCode()
@@ -34,25 +42,44 @@ namespace GodotTools.Build
{
int hash = 17;
hash = (hash * 29) + Solution.GetHashCode();
- hash = (hash * 29) + Targets.GetHashCode();
hash = (hash * 29) + Configuration.GetHashCode();
+ hash = (hash * 29) + (RuntimeIdentifier?.GetHashCode() ?? 0);
+ hash = (hash * 29) + (PublishOutputDir?.GetHashCode() ?? 0);
hash = (hash * 29) + Restore.GetHashCode();
+ hash = (hash * 29) + Rebuild.GetHashCode();
+ hash = (hash * 29) + OnlyClean.GetHashCode();
hash = (hash * 29) + CustomProperties.GetHashCode();
hash = (hash * 29) + LogsDirPath.GetHashCode();
return hash;
}
}
+ // Needed for instantiation from Godot, after reloading assemblies
private BuildInfo()
{
+ Solution = string.Empty;
+ Configuration = string.Empty;
+ }
+
+ public BuildInfo(string solution, string configuration, bool restore, bool rebuild, bool onlyClean)
+ {
+ Solution = solution;
+ Configuration = configuration;
+ Restore = restore;
+ Rebuild = rebuild;
+ OnlyClean = onlyClean;
}
- public BuildInfo(string solution, string[] targets, string configuration, bool restore)
+ public BuildInfo(string solution, string configuration, string runtimeIdentifier,
+ string publishOutputDir, bool restore, bool rebuild, bool onlyClean)
{
Solution = solution;
- Targets = targets;
Configuration = configuration;
+ RuntimeIdentifier = runtimeIdentifier;
+ PublishOutputDir = publishOutputDir;
Restore = restore;
+ Rebuild = rebuild;
+ OnlyClean = onlyClean;
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
index 21bff70b15..993c6d9217 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
@@ -1,12 +1,10 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading.Tasks;
-using GodotTools.Ides.Rider;
+using Godot;
using GodotTools.Internals;
-using JetBrains.Annotations;
-using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
-using OS = GodotTools.Utils.OS;
namespace GodotTools.Build
{
@@ -14,13 +12,8 @@ namespace GodotTools.Build
{
private static BuildInfo _buildInProgress;
- public const string PropNameMSBuildMono = "MSBuild (Mono)";
- public const string PropNameMSBuildVs = "MSBuild (VS Build Tools)";
- public const string PropNameMSBuildJetBrains = "MSBuild (JetBrains Rider)";
- public const string PropNameDotnetCli = "dotnet CLI";
-
public const string MsBuildIssuesFileName = "msbuild_issues.csv";
- public const string MsBuildLogFileName = "msbuild_log.txt";
+ private const string MsBuildLogFileName = "msbuild_log.txt";
public delegate void BuildLaunchFailedEventHandler(BuildInfo buildInfo, string reason);
@@ -62,14 +55,14 @@ namespace GodotTools.Build
private static void PrintVerbose(string text)
{
- if (Godot.OS.IsStdoutVerbose())
- Godot.GD.Print(text);
+ if (OS.IsStdoutVerbose())
+ GD.Print(text);
}
- public static bool Build(BuildInfo buildInfo)
+ private static bool Build(BuildInfo buildInfo)
{
if (_buildInProgress != null)
- throw new InvalidOperationException("A build is already in progress");
+ throw new InvalidOperationException("A build is already in progress.");
_buildInProgress = buildInfo;
@@ -103,7 +96,8 @@ namespace GodotTools.Build
}
catch (Exception e)
{
- BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo,
+ $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
@@ -117,7 +111,7 @@ namespace GodotTools.Build
public static async Task<bool> BuildAsync(BuildInfo buildInfo)
{
if (_buildInProgress != null)
- throw new InvalidOperationException("A build is already in progress");
+ throw new InvalidOperationException("A build is already in progress.");
_buildInProgress = buildInfo;
@@ -148,7 +142,8 @@ namespace GodotTools.Build
}
catch (Exception e)
{
- BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo,
+ $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
@@ -159,18 +154,54 @@ namespace GodotTools.Build
}
}
- public static bool BuildProjectBlocking(string config, [CanBeNull] string[] targets = null, [CanBeNull] string platform = null)
+ private static bool Publish(BuildInfo buildInfo)
{
- var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets ?? new[] {"Build"}, config, restore: true);
+ if (_buildInProgress != null)
+ throw new InvalidOperationException("A build is already in progress.");
- // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
- if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform))
- buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+ _buildInProgress = buildInfo;
- if (Internal.GodotIsRealTDouble())
- buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+ try
+ {
+ BuildStarted?.Invoke(buildInfo);
+
+ // Required in order to update the build tasks list
+ Internal.GodotMainIteration();
- return BuildProjectBlocking(buildInfo);
+ try
+ {
+ RemoveOldIssuesFile(buildInfo);
+ }
+ catch (IOException e)
+ {
+ BuildLaunchFailed?.Invoke(buildInfo, $"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
+ Console.Error.WriteLine(e);
+ }
+
+ try
+ {
+ int exitCode = BuildSystem.Publish(buildInfo, StdOutputReceived, StdErrorReceived);
+
+ if (exitCode != 0)
+ PrintVerbose(
+ $"dotnet publish exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
+
+ BuildFinished?.Invoke(exitCode == 0 ? BuildResult.Success : BuildResult.Error);
+
+ return exitCode == 0;
+ }
+ catch (Exception e)
+ {
+ BuildLaunchFailed?.Invoke(buildInfo,
+ $"The publish method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ Console.Error.WriteLine(e);
+ return false;
+ }
+ }
+ finally
+ {
+ _buildInProgress = null;
+ }
}
private static bool BuildProjectBlocking(BuildInfo buildInfo)
@@ -178,31 +209,109 @@ namespace GodotTools.Build
if (!File.Exists(buildInfo.Solution))
return true; // No solution to build
- // Make sure the API assemblies are up to date before building the project.
- // We may not have had the chance to update the release API assemblies, and the debug ones
- // may have been deleted by the user at some point after they were loaded by the Godot editor.
- string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(buildInfo.Configuration == "ExportRelease" ? "Release" : "Debug");
+ using var pr = new EditorProgress("dotnet_build_project", "Building .NET project...", 1);
+
+ pr.Step("Building project solution", 0);
- if (!string.IsNullOrEmpty(apiAssembliesUpdateError))
+ if (!Build(buildInfo))
{
- ShowBuildErrorDialog("Failed to update the Godot API assemblies");
+ ShowBuildErrorDialog("Failed to build project solution");
return false;
}
- using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
+ return true;
+ }
+
+ private static bool CleanProjectBlocking(BuildInfo buildInfo)
+ {
+ if (!File.Exists(buildInfo.Solution))
+ return true; // No solution to clean
+
+ using var pr = new EditorProgress("dotnet_clean_project", "Cleaning .NET project...", 1);
+
+ pr.Step("Cleaning project solution", 0);
+
+ if (!Build(buildInfo))
{
- pr.Step("Building project solution", 0);
+ ShowBuildErrorDialog("Failed to clean project solution");
+ return false;
+ }
- if (!Build(buildInfo))
- {
- ShowBuildErrorDialog("Failed to build project solution");
- return false;
- }
+ return true;
+ }
+
+ private static bool PublishProjectBlocking(BuildInfo buildInfo)
+ {
+ using var pr = new EditorProgress("dotnet_publish_project", "Publishing .NET project...", 1);
+
+ pr.Step("Running dotnet publish", 0);
+
+ if (!Publish(buildInfo))
+ {
+ ShowBuildErrorDialog("Failed to publish .NET project");
+ return false;
}
return true;
}
+ private static BuildInfo CreateBuildInfo(
+ [DisallowNull] string configuration,
+ [AllowNull] string platform = null,
+ bool rebuild = false,
+ bool onlyClean = false
+ )
+ {
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, configuration,
+ restore: true, rebuild, onlyClean);
+
+ // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
+ if (platform != null || Utils.OS.PlatformNameMap.TryGetValue(OS.GetName(), out platform))
+ buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+
+ if (Internal.GodotIsRealTDouble())
+ buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+
+ return buildInfo;
+ }
+
+ private static BuildInfo CreatePublishBuildInfo(
+ [DisallowNull] string configuration,
+ [DisallowNull] string platform,
+ [DisallowNull] string runtimeIdentifier,
+ [DisallowNull] string publishOutputDir
+ )
+ {
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, configuration,
+ runtimeIdentifier, publishOutputDir, restore: true, rebuild: false, onlyClean: false);
+
+ buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+
+ if (Internal.GodotIsRealTDouble())
+ buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+
+ return buildInfo;
+ }
+
+ public static bool BuildProjectBlocking(
+ [DisallowNull] string configuration,
+ [AllowNull] string platform = null,
+ bool rebuild = false
+ ) => BuildProjectBlocking(CreateBuildInfo(configuration, platform, rebuild));
+
+ public static bool CleanProjectBlocking(
+ [DisallowNull] string configuration,
+ [AllowNull] string platform = null
+ ) => CleanProjectBlocking(CreateBuildInfo(configuration, platform, rebuild: false));
+
+ public static bool PublishProjectBlocking(
+ [DisallowNull] string configuration,
+ [DisallowNull] string platform,
+ [DisallowNull] string runtimeIdentifier,
+ string publishOutputDir
+ ) => PublishProjectBlocking(CreatePublishBuildInfo(configuration,
+ platform, runtimeIdentifier, publishOutputDir));
+
public static bool EditorBuildCallback()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
@@ -215,7 +324,7 @@ namespace GodotTools.Build
}
catch (Exception e)
{
- Godot.GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
}
if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
@@ -226,47 +335,6 @@ namespace GodotTools.Build
public static void Initialize()
{
- // Build tool settings
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
-
- BuildTool msbuildDefault;
-
- if (OS.IsWindows)
- {
- if (RiderPathManager.IsExternalEditorSetToRider(editorSettings))
- msbuildDefault = BuildTool.JetBrainsMsBuild;
- else
- msbuildDefault = !string.IsNullOrEmpty(OS.PathWhich("dotnet")) ? BuildTool.DotnetCli : BuildTool.MsBuildVs;
- }
- else
- {
- msbuildDefault = !string.IsNullOrEmpty(OS.PathWhich("dotnet")) ? BuildTool.DotnetCli : BuildTool.MsBuildMono;
- }
-
- EditorDef("mono/builds/build_tool", msbuildDefault);
-
- string hintString;
-
- if (OS.IsWindows)
- {
- hintString = $"{PropNameMSBuildMono}:{(int)BuildTool.MsBuildMono}," +
- $"{PropNameMSBuildVs}:{(int)BuildTool.MsBuildVs}," +
- $"{PropNameMSBuildJetBrains}:{(int)BuildTool.JetBrainsMsBuild}," +
- $"{PropNameDotnetCli}:{(int)BuildTool.DotnetCli}";
- }
- else
- {
- hintString = $"{PropNameMSBuildMono}:{(int)BuildTool.MsBuildMono}," +
- $"{PropNameDotnetCli}:{(int)BuildTool.DotnetCli}";
- }
-
- editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
- {
- ["type"] = Godot.Variant.Type.Int,
- ["name"] = "mono/builds/build_tool",
- ["hint"] = Godot.PropertyHint.Enum,
- ["hint_string"] = hintString
- });
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
index ebdaca0ce8..4d40724a83 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
@@ -1,17 +1,16 @@
using Godot;
using System;
-using Godot.Collections;
+using System.Diagnostics.CodeAnalysis;
using GodotTools.Internals;
-using JetBrains.Annotations;
using File = GodotTools.Utils.File;
using Path = System.IO.Path;
namespace GodotTools.Build
{
- public class BuildOutputView : VBoxContainer, ISerializationListener
+ public partial class BuildOutputView : VBoxContainer, ISerializationListener
{
[Serializable]
- private class BuildIssue : RefCounted // TODO Remove RefCounted once we have proper serialization
+ private partial class BuildIssue : RefCounted // TODO Remove RefCounted once we have proper serialization
{
public bool Warning { get; set; }
public string File { get; set; }
@@ -22,7 +21,8 @@ namespace GodotTools.Build
public string ProjectFile { get; set; }
}
- [Signal] public event Action BuildStateChanged;
+ [Signal]
+ public delegate void BuildStateChangedEventHandler();
public bool HasBuildExited { get; private set; } = false;
@@ -58,7 +58,7 @@ namespace GodotTools.Build
}
// TODO Use List once we have proper serialization.
- private readonly Array<BuildIssue> _issues = new Array<BuildIssue>();
+ private Godot.Collections.Array<BuildIssue> _issues = new();
private ItemList _issuesList;
private PopupMenu _issuesListContextMenu;
private TextEdit _buildLog;
@@ -69,71 +69,63 @@ namespace GodotTools.Build
private void LoadIssuesFromFile(string csvFile)
{
- using (var file = new Godot.File())
+ using var file = FileAccess.Open(csvFile, FileAccess.ModeFlags.Read);
+
+ if (file == null)
+ return;
+
+ while (!file.EofReached())
{
- try
- {
- Error openError = file.Open(csvFile, Godot.File.ModeFlags.Read);
+ string[] csvColumns = file.GetCsvLine();
- if (openError != Error.Ok)
- return;
+ if (csvColumns.Length == 1 && string.IsNullOrEmpty(csvColumns[0]))
+ return;
- while (!file.EofReached())
- {
- string[] csvColumns = file.GetCsvLine();
-
- if (csvColumns.Length == 1 && string.IsNullOrEmpty(csvColumns[0]))
- return;
-
- if (csvColumns.Length != 7)
- {
- GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
- continue;
- }
-
- var issue = new BuildIssue
- {
- Warning = csvColumns[0] == "warning",
- File = csvColumns[1],
- Line = int.Parse(csvColumns[2]),
- Column = int.Parse(csvColumns[3]),
- Code = csvColumns[4],
- Message = csvColumns[5],
- ProjectFile = csvColumns[6]
- };
-
- if (issue.Warning)
- WarningCount += 1;
- else
- ErrorCount += 1;
-
- _issues.Add(issue);
- }
- }
- finally
+ if (csvColumns.Length != 7)
{
- file.Close(); // Disposing it is not enough. We need to call Close()
+ GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
+ continue;
}
+
+ var issue = new BuildIssue
+ {
+ Warning = csvColumns[0] == "warning",
+ File = csvColumns[1],
+ Line = int.Parse(csvColumns[2]),
+ Column = int.Parse(csvColumns[3]),
+ Code = csvColumns[4],
+ Message = csvColumns[5],
+ ProjectFile = csvColumns[6]
+ };
+
+ if (issue.Warning)
+ WarningCount += 1;
+ else
+ ErrorCount += 1;
+
+ _issues.Add(issue);
}
}
- private void IssueActivated(int idx)
+ private void IssueActivated(long idx)
{
if (idx < 0 || idx >= _issuesList.ItemCount)
- throw new IndexOutOfRangeException("Item list index out of range");
+ throw new ArgumentOutOfRangeException(nameof(idx), "Item list index out of range.");
// Get correct issue idx from issue list
- int issueIndex = (int)(long)_issuesList.GetItemMetadata(idx);
+ int issueIndex = (int)_issuesList.GetItemMetadata((int)idx);
if (issueIndex < 0 || issueIndex >= _issues.Count)
- throw new IndexOutOfRangeException("Issue index out of range");
+ throw new InvalidOperationException("Issue index out of range.");
BuildIssue issue = _issues[issueIndex];
if (string.IsNullOrEmpty(issue.ProjectFile) && string.IsNullOrEmpty(issue.File))
return;
- string projectDir = issue.ProjectFile.Length > 0 ? issue.ProjectFile.GetBaseDir() : _buildInfo.Solution.GetBaseDir();
+ string projectDir = !string.IsNullOrEmpty(issue.ProjectFile) ?
+ issue.ProjectFile.GetBaseDir() :
+ _buildInfo.Solution.GetBaseDir();
string file = Path.Combine(projectDir.SimplifyGodotPath(), issue.File.SimplifyGodotPath());
@@ -291,7 +283,7 @@ namespace GodotTools.Build
public void RestartBuild()
{
if (!HasBuildExited)
- throw new InvalidOperationException("Build already started");
+ throw new InvalidOperationException("Build already started.");
BuildManager.RestartBuild(this);
}
@@ -299,7 +291,7 @@ namespace GodotTools.Build
public void StopBuild()
{
if (!HasBuildExited)
- throw new InvalidOperationException("Build is not in progress");
+ throw new InvalidOperationException("Build is not in progress.");
BuildManager.StopBuild(this);
}
@@ -309,7 +301,7 @@ namespace GodotTools.Build
Copy
}
- private void IssuesListContextOptionPressed(int id)
+ private void IssuesListContextOptionPressed(long id)
{
switch ((IssuesContextMenuOption)id)
{
@@ -334,9 +326,9 @@ namespace GodotTools.Build
}
}
- private void IssuesListClicked(int index, Vector2 atPosition, int mouseButtonIndex)
+ private void IssuesListClicked(long index, Vector2 atPosition, long mouseButtonIndex)
{
- if (mouseButtonIndex != (int)MouseButton.Right)
+ if (mouseButtonIndex != (long)MouseButton.Right)
{
return;
}
@@ -412,6 +404,16 @@ namespace GodotTools.Build
{
// In case it didn't update yet. We don't want to have to serialize any pending output.
UpdateBuildLogText();
+
+ // NOTE:
+ // Currently, GodotTools is loaded in its own load context. This load context is not reloaded, but the script still are.
+ // Until that changes, we need workarounds like this one because events keep strong references to disposed objects.
+ BuildManager.BuildLaunchFailed -= BuildLaunchFailed;
+ BuildManager.BuildStarted -= BuildStarted;
+ BuildManager.BuildFinished -= BuildFinished;
+ // StdOutput/Error can be received from different threads, so we need to use CallDeferred
+ BuildManager.StdOutputReceived -= StdOutputReceived;
+ BuildManager.StdErrorReceived -= StdErrorReceived;
}
public void OnAfterDeserialize()
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index 02e9d98647..d0cd529d1f 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -1,61 +1,93 @@
-using GodotTools.Core;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
+using System.Linq;
+using System.Text;
using System.Threading.Tasks;
using GodotTools.BuildLogger;
-using GodotTools.Internals;
using GodotTools.Utils;
-using Directory = System.IO.Directory;
namespace GodotTools.Build
{
public static class BuildSystem
{
- private static string MonoWindowsBinDir
+ private static Process LaunchBuild(BuildInfo buildInfo, Action<string> stdOutHandler,
+ Action<string> stdErrHandler)
{
- get
- {
- string monoWinBinDir = Path.Combine(Internal.MonoWindowsInstallRoot, "bin");
+ string dotnetPath = DotNetFinder.FindDotNetExe();
- if (!Directory.Exists(monoWinBinDir))
- throw new FileNotFoundException("Cannot find the Windows Mono install bin directory.");
+ if (dotnetPath == null)
+ throw new FileNotFoundException("Cannot find the dotnet executable.");
- return monoWinBinDir;
- }
+ var startInfo = new ProcessStartInfo(dotnetPath);
+
+ BuildArguments(buildInfo, startInfo.ArgumentList);
+
+ string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString();
+ stdOutHandler?.Invoke(launchMessage);
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine(launchMessage);
+
+ startInfo.RedirectStandardOutput = true;
+ startInfo.RedirectStandardError = true;
+ startInfo.UseShellExecute = false;
+ startInfo.CreateNoWindow = true;
+
+ // Needed when running from Developer Command Prompt for VS
+ RemovePlatformVariable(startInfo.EnvironmentVariables);
+
+ var process = new Process { StartInfo = startInfo };
+
+ if (stdOutHandler != null)
+ process.OutputDataReceived += (_, e) => stdOutHandler.Invoke(e.Data);
+ if (stdErrHandler != null)
+ process.ErrorDataReceived += (_, e) => stdErrHandler.Invoke(e.Data);
+
+ process.Start();
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
+ return process;
}
- private static Godot.EditorSettings EditorSettings =>
- GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+ public static int Build(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ {
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
+ {
+ process.WaitForExit();
+
+ return process.ExitCode;
+ }
+ }
- private static bool UsingMonoMsBuildOnWindows
+ public static async Task<int> BuildAsync(BuildInfo buildInfo, Action<string> stdOutHandler,
+ Action<string> stdErrHandler)
{
- get
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
{
- if (OS.IsWindows)
- {
- return (BuildTool)EditorSettings.GetSetting("mono/builds/build_tool")
- == BuildTool.MsBuildMono;
- }
+ await process.WaitForExitAsync();
- return false;
+ return process.ExitCode;
}
}
- private static Process LaunchBuild(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ private static Process LaunchPublish(BuildInfo buildInfo, Action<string> stdOutHandler,
+ Action<string> stdErrHandler)
{
- (string msbuildPath, BuildTool buildTool) = MsBuildFinder.FindMsBuild();
+ string dotnetPath = DotNetFinder.FindDotNetExe();
- if (msbuildPath == null)
- throw new FileNotFoundException("Cannot find the MSBuild executable.");
+ if (dotnetPath == null)
+ throw new FileNotFoundException("Cannot find the dotnet executable.");
- string compilerArgs = BuildArguments(buildTool, buildInfo);
+ var startInfo = new ProcessStartInfo(dotnetPath);
- var startInfo = new ProcessStartInfo(msbuildPath, compilerArgs);
+ BuildPublishArguments(buildInfo, startInfo.ArgumentList);
- string launchMessage = $"Running: \"{startInfo.FileName}\" {startInfo.Arguments}";
+ string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Running: ")).ToString();
stdOutHandler?.Invoke(launchMessage);
if (Godot.OS.IsStdoutVerbose())
Console.WriteLine(launchMessage);
@@ -63,27 +95,16 @@ namespace GodotTools.Build
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
- startInfo.CreateNoWindow = true;
-
- if (UsingMonoMsBuildOnWindows)
- {
- // These environment variables are required for Mono's MSBuild to find the compilers.
- // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
- string monoWinBinDir = MonoWindowsBinDir;
- startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
- startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
- startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
- }
// Needed when running from Developer Command Prompt for VS
RemovePlatformVariable(startInfo.EnvironmentVariables);
- var process = new Process {StartInfo = startInfo};
+ var process = new Process { StartInfo = startInfo };
if (stdOutHandler != null)
- process.OutputDataReceived += (s, e) => stdOutHandler.Invoke(e.Data);
+ process.OutputDataReceived += (_, e) => stdOutHandler.Invoke(e.Data);
if (stdErrHandler != null)
- process.ErrorDataReceived += (s, e) => stdErrHandler.Invoke(e.Data);
+ process.ErrorDataReceived += (_, e) => stdErrHandler.Invoke(e.Data);
process.Start();
@@ -93,9 +114,9 @@ namespace GodotTools.Build
return process;
}
- public static int Build(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ public static int Publish(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
{
- using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
+ using (var process = LaunchPublish(buildInfo, stdOutHandler, stdErrHandler))
{
process.WaitForExit();
@@ -103,38 +124,102 @@ namespace GodotTools.Build
}
}
- public static async Task<int> BuildAsync(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
+ private static void BuildArguments(BuildInfo buildInfo, Collection<string> arguments)
{
- using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
+ // `dotnet clean` / `dotnet build` commands
+ arguments.Add(buildInfo.OnlyClean ? "clean" : "build");
+
+ // Solution
+ arguments.Add(buildInfo.Solution);
+
+ // `dotnet clean` doesn't recognize these options
+ if (!buildInfo.OnlyClean)
{
- await process.WaitForExitAsync();
+ // Restore
+ // `dotnet build` restores by default, unless requested not to
+ if (!buildInfo.Restore)
+ arguments.Add("--no-restore");
+
+ // Incremental or rebuild
+ if (buildInfo.Rebuild)
+ arguments.Add("--no-incremental");
+ }
- return process.ExitCode;
+ // Configuration
+ arguments.Add("-c");
+ arguments.Add(buildInfo.Configuration);
+
+ // Verbosity
+ arguments.Add("-v");
+ arguments.Add("normal");
+
+ // Logger
+ AddLoggerArgument(buildInfo, arguments);
+
+ // Custom properties
+ foreach (var customProperty in buildInfo.CustomProperties)
+ {
+ arguments.Add("-p:" + (string)customProperty);
}
}
- private static string BuildArguments(BuildTool buildTool, BuildInfo buildInfo)
+ private static void BuildPublishArguments(BuildInfo buildInfo, Collection<string> arguments)
{
- string arguments = string.Empty;
+ arguments.Add("publish"); // `dotnet publish` command
+
+ // Solution
+ arguments.Add(buildInfo.Solution);
+
+ // Restore
+ // `dotnet publish` restores by default, unless requested not to
+ if (!buildInfo.Restore)
+ arguments.Add("--no-restore");
- if (buildTool == BuildTool.DotnetCli)
- arguments += "msbuild"; // `dotnet msbuild` command
+ // Incremental or rebuild
+ // TODO: Not supported in `dotnet publish` (https://github.com/dotnet/sdk/issues/11099)
+ // if (buildInfo.Rebuild)
+ // arguments.Add("--no-incremental");
- arguments += $@" ""{buildInfo.Solution}""";
+ // Configuration
+ arguments.Add("-c");
+ arguments.Add(buildInfo.Configuration);
- if (buildInfo.Restore)
- arguments += " /restore";
+ // Runtime Identifier
+ arguments.Add("-r");
+ arguments.Add(buildInfo.RuntimeIdentifier!);
- arguments += $@" /t:{string.Join(",", buildInfo.Targets)} " +
- $@"""/p:{"Configuration=" + buildInfo.Configuration}"" /v:normal " +
- $@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{buildInfo.LogsDirPath}""";
+ // Self-published
+ arguments.Add("--self-contained");
+ arguments.Add("true");
- foreach (string customProperty in buildInfo.CustomProperties)
+ // Verbosity
+ arguments.Add("-v");
+ arguments.Add("normal");
+
+ // Logger
+ AddLoggerArgument(buildInfo, arguments);
+
+ // Custom properties
+ foreach (var customProperty in buildInfo.CustomProperties)
+ {
+ arguments.Add("-p:" + (string)customProperty);
+ }
+
+ // Publish output directory
+ if (buildInfo.PublishOutputDir != null)
{
- arguments += " /p:" + customProperty;
+ arguments.Add("-o");
+ arguments.Add(buildInfo.PublishOutputDir);
}
+ }
+
+ private static void AddLoggerArgument(BuildInfo buildInfo, Collection<string> arguments)
+ {
+ string buildLoggerPath = Path.Combine(Internals.GodotSharpDirs.DataEditorToolsDir,
+ "GodotTools.BuildLogger.dll");
- return arguments;
+ arguments.Add(
+ $"-l:{typeof(GodotBuildLogger).FullName},{buildLoggerPath};{buildInfo.LogsDirPath}");
}
private static void RemovePlatformVariable(StringDictionary environmentVariables)
@@ -145,7 +230,7 @@ namespace GodotTools.Build
foreach (string env in environmentVariables.Keys)
{
- if (env.ToUpper() == "PLATFORM")
+ if (env.ToUpperInvariant() == "PLATFORM")
platformEnvironmentVariables.Add(env);
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
deleted file mode 100644
index 837c8adddb..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildTool.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace GodotTools.Build
-{
- public enum BuildTool : long
- {
- MsBuildMono,
- MsBuildVs,
- JetBrainsMsBuild,
- DotnetCli
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
new file mode 100644
index 0000000000..b437c7e742
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs
@@ -0,0 +1,117 @@
+using System;
+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;
+
+namespace GodotTools.Build
+{
+ public static class DotNetFinder
+ {
+ [CanBeNull]
+ public static string FindDotNetExe()
+ {
+ // 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");
+ }
+
+ public static bool TryFindDotNetSdk(
+ Version expectedVersion,
+ [NotNullWhen(true)] out Version version,
+ [NotNullWhen(true)] out string path
+ )
+ {
+ version = null;
+ path = null;
+
+ string dotNetExe = FindDotNetExe();
+
+ if (string.IsNullOrEmpty(dotNetExe))
+ return false;
+
+ using Process process = new Process();
+ process.StartInfo = new ProcessStartInfo(dotNetExe, "--list-sdks")
+ {
+ UseShellExecute = false,
+ RedirectStandardOutput = true
+ };
+
+ process.StartInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] = "en-US";
+
+ var lines = new List<string>();
+
+ process.OutputDataReceived += (_, e) =>
+ {
+ if (!string.IsNullOrWhiteSpace(e.Data))
+ lines.Add(e.Data);
+ };
+
+ try
+ {
+ process.Start();
+ }
+ catch
+ {
+ return false;
+ }
+
+ process.BeginOutputReadLine();
+ process.WaitForExit();
+
+ Version latestVersionMatch = null;
+ string matchPath = null;
+
+ foreach (var line in lines)
+ {
+ string[] sdkLineParts = line.Trim()
+ .Split(' ', 2, StringSplitOptions.TrimEntries);
+
+ if (sdkLineParts.Length < 2)
+ continue;
+
+ if (!Version.TryParse(sdkLineParts[0], out var lineVersion))
+ continue;
+
+ // We're looking for the exact same major version
+ if (lineVersion.Major != expectedVersion.Major)
+ continue;
+
+ if (latestVersionMatch != null && lineVersion < latestVersionMatch)
+ continue;
+
+ latestVersionMatch = lineVersion;
+ matchPath = sdkLineParts[1].TrimStart('[').TrimEnd(']');
+ }
+
+ if (latestVersionMatch == null)
+ return false;
+
+ version = latestVersionMatch;
+ path = Path.Combine(matchPath!, version.ToString());
+
+ return true;
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 3c020a2589..237ac85267 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -1,13 +1,12 @@
using System;
using Godot;
using GodotTools.Internals;
-using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
namespace GodotTools.Build
{
- public class MSBuildPanel : VBoxContainer
+ public partial class MSBuildPanel : VBoxContainer
{
public BuildOutputView BuildOutputView { get; private set; }
@@ -28,7 +27,6 @@ namespace GodotTools.Build
BuildOutputView.UpdateIssuesList();
}
- [UsedImplicitly]
public void BuildSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
@@ -57,7 +55,6 @@ namespace GodotTools.Build
Internal.ReloadAssemblies(softReload: false);
}
- [UsedImplicitly]
private void RebuildSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
@@ -73,7 +70,7 @@ namespace GodotTools.Build
GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
}
- if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Rebuild" }))
+ if (!BuildManager.BuildProjectBlocking("Debug", rebuild: true))
return; // Build failed
// Notify running game for hot-reload
@@ -86,18 +83,17 @@ namespace GodotTools.Build
Internal.ReloadAssemblies(softReload: false);
}
- [UsedImplicitly]
private void CleanSolution()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return; // No solution to build
- BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Clean" });
+ _ = BuildManager.CleanProjectBlocking("Debug");
}
private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed;
- private void BuildMenuOptionPressed(int id)
+ private void BuildMenuOptionPressed(long id)
{
switch ((BuildMenuOptions)id)
{
@@ -126,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 };
@@ -143,7 +139,7 @@ namespace GodotTools.Build
_errorsBtn = new Button
{
- HintTooltip = "Show Errors".TTR(),
+ TooltipText = "Show Errors".TTR(),
Icon = GetThemeIcon("StatusError", "EditorIcons"),
ExpandIcon = false,
ToggleMode = true,
@@ -155,7 +151,7 @@ namespace GodotTools.Build
_warningsBtn = new Button
{
- HintTooltip = "Show Warnings".TTR(),
+ TooltipText = "Show Warnings".TTR(),
Icon = GetThemeIcon("NodeWarning", "EditorIcons"),
ExpandIcon = false,
ToggleMode = true,
@@ -179,7 +175,7 @@ namespace GodotTools.Build
AddChild(BuildOutputView);
}
- public override void _Notification(int what)
+ public override void _Notification(long what)
{
base._Notification(what);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
deleted file mode 100644
index a859c6f717..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ /dev/null
@@ -1,233 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using Godot;
-using GodotTools.Ides.Rider;
-using GodotTools.Internals;
-using Directory = System.IO.Directory;
-using Environment = System.Environment;
-using File = System.IO.File;
-using Path = System.IO.Path;
-using OS = GodotTools.Utils.OS;
-
-namespace GodotTools.Build
-{
- public static class MsBuildFinder
- {
- private static string _msbuildToolsPath = string.Empty;
- private static string _msbuildUnixPath = string.Empty;
-
- public static (string, BuildTool) FindMsBuild()
- {
- var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
- var buildTool = (BuildTool)editorSettings.GetSetting("mono/builds/build_tool");
-
- if (OS.IsWindows)
- {
- switch (buildTool)
- {
- case BuildTool.DotnetCli:
- {
- string dotnetCliPath = OS.PathWhich("dotnet");
- if (!string.IsNullOrEmpty(dotnetCliPath))
- return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Visual Studio.");
- goto case BuildTool.MsBuildVs;
- }
- case BuildTool.MsBuildVs:
- {
- if (string.IsNullOrEmpty(_msbuildToolsPath) || !File.Exists(_msbuildToolsPath))
- {
- // Try to search it again if it wasn't found last time or if it was removed from its location
- _msbuildToolsPath = FindMsBuildToolsPathOnWindows();
-
- if (string.IsNullOrEmpty(_msbuildToolsPath))
- throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildVs}'.");
- }
-
- if (!_msbuildToolsPath.EndsWith("\\"))
- _msbuildToolsPath += "\\";
-
- return (Path.Combine(_msbuildToolsPath, "MSBuild.exe"), BuildTool.MsBuildVs);
- }
- case BuildTool.MsBuildMono:
- {
- string msbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "msbuild.bat");
-
- if (!File.Exists(msbuildPath))
- throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildMono}'. Tried with path: {msbuildPath}");
-
- return (msbuildPath, BuildTool.MsBuildMono);
- }
- case BuildTool.JetBrainsMsBuild:
- {
- string editorPath = (string)editorSettings.GetSetting(RiderPathManager.EditorPathSettingName);
-
- if (!File.Exists(editorPath))
- throw new FileNotFoundException($"Cannot find Rider executable. Tried with path: {editorPath}");
-
- var riderDir = new FileInfo(editorPath).Directory?.Parent;
-
- string msbuildPath = Path.Combine(riderDir.FullName, @"tools\MSBuild\Current\Bin\MSBuild.exe");
-
- if (!File.Exists(msbuildPath))
- throw new FileNotFoundException($"Cannot find executable for '{BuildManager.PropNameMSBuildJetBrains}'. Tried with path: {msbuildPath}");
-
- return (msbuildPath, BuildTool.JetBrainsMsBuild);
- }
- default:
- throw new IndexOutOfRangeException("Invalid build tool in editor settings");
- }
- }
-
- if (OS.IsUnixLike)
- {
- switch (buildTool)
- {
- case BuildTool.DotnetCli:
- {
- string dotnetCliPath = FindBuildEngineOnUnix("dotnet");
- if (!string.IsNullOrEmpty(dotnetCliPath))
- return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Mono.");
- goto case BuildTool.MsBuildMono;
- }
- case BuildTool.MsBuildMono:
- {
- if (string.IsNullOrEmpty(_msbuildUnixPath) || !File.Exists(_msbuildUnixPath))
- {
- // Try to search it again if it wasn't found last time or if it was removed from its location
- _msbuildUnixPath = FindBuildEngineOnUnix("msbuild");
- }
-
- if (string.IsNullOrEmpty(_msbuildUnixPath))
- throw new FileNotFoundException($"Cannot find binary for '{BuildManager.PropNameMSBuildMono}'");
-
- return (_msbuildUnixPath, BuildTool.MsBuildMono);
- }
- default:
- throw new IndexOutOfRangeException("Invalid build tool in editor settings");
- }
- }
-
- throw new PlatformNotSupportedException();
- }
-
- private static IEnumerable<string> MsBuildHintDirs
- {
- get
- {
- var result = new List<string>();
-
- if (OS.IsMacOS)
- {
- result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
- result.Add("/opt/local/bin/");
- result.Add("/usr/local/var/homebrew/linked/mono/bin/");
- result.Add("/usr/local/bin/");
- result.Add("/usr/local/bin/dotnet/");
- result.Add("/usr/local/share/dotnet/");
- }
-
- result.Add("/opt/novell/mono/bin/");
-
- return result;
- }
- }
-
- private static string FindBuildEngineOnUnix(string name)
- {
- string ret = OS.PathWhich(name);
-
- if (!string.IsNullOrEmpty(ret))
- return ret;
-
- string retFallback = OS.PathWhich($"{name}.exe");
-
- if (!string.IsNullOrEmpty(retFallback))
- return retFallback;
-
- foreach (string hintDir in MsBuildHintDirs)
- {
- string hintPath = Path.Combine(hintDir, name);
-
- if (File.Exists(hintPath))
- return hintPath;
- }
-
- return string.Empty;
- }
-
- private static string FindMsBuildToolsPathOnWindows()
- {
- if (!OS.IsWindows)
- throw new PlatformNotSupportedException();
-
- // Try to find 15.0 with vswhere
-
- string[] envNames = Internal.GodotIs32Bits() ?
- envNames = new[] { "ProgramFiles", "ProgramW6432" } :
- envNames = new[] { "ProgramFiles(x86)", "ProgramFiles" };
-
- string vsWherePath = null;
- foreach (var envName in envNames)
- {
- vsWherePath = Environment.GetEnvironmentVariable(envName);
- if (!string.IsNullOrEmpty(vsWherePath))
- {
- vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
- if (File.Exists(vsWherePath))
- break;
- }
-
- vsWherePath = null;
- }
-
- var vsWhereArgs = new[] {"-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"};
-
- var outputArray = new Godot.Collections.Array<string>();
- int exitCode = Godot.OS.Execute(vsWherePath, vsWhereArgs,
- output: (Godot.Collections.Array)outputArray);
-
- if (exitCode != 0)
- return string.Empty;
-
- if (outputArray.Count == 0)
- return string.Empty;
-
- var lines = outputArray[0].Split('\n');
-
- foreach (string line in lines)
- {
- int sepIdx = line.IndexOf(':');
-
- if (sepIdx <= 0)
- continue;
-
- string key = line.Substring(0, sepIdx); // No need to trim
-
- if (key != "installationPath")
- continue;
-
- string value = line.Substring(sepIdx + 1).StripEdges();
-
- if (string.IsNullOrEmpty(value))
- throw new FormatException("installationPath value is empty");
-
- if (!value.EndsWith("\\"))
- value += "\\";
-
- // Since VS2019, the directory is simply named "Current"
- string msbuildDir = Path.Combine(value, "MSBuild\\Current\\Bin");
-
- if (Directory.Exists(msbuildDir))
- return msbuildDir;
-
- // Directory name "15.0" is used in VS 2017
- return Path.Combine(value, "MSBuild\\15.0\\Bin");
- }
-
- return string.Empty;
- }
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
index 63b97e981e..fe309b8102 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
@@ -21,70 +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 Exception("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
@@ -96,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];
@@ -109,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;
}
@@ -139,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
}
}
@@ -181,12 +125,13 @@ namespace GodotTools.Build
// - The sha512 of the nupkg is base64 encoded.
// - We can get the nuspec from the nupkg which is a Zip file.
- string packageIdLower = packageId.ToLower();
- string packageVersionLower = packageVersion.ToLower();
+ string packageIdLower = packageId.ToLowerInvariant();
+ string packageVersionLower = packageVersion.ToLowerInvariant();
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)
@@ -195,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);
@@ -227,9 +178,11 @@ namespace GodotTools.Build
var nuspecEntry = archive.GetEntry(packageId + ".nuspec");
if (nuspecEntry == null)
- throw new InvalidOperationException($"Failed to extract package {packageId}.{packageVersion}. Could not find the nuspec file.");
+ throw new InvalidOperationException(
+ $"Failed to extract package {packageId}.{packageVersion}. Could not find the nuspec file.");
- nuspecEntry.ExtractToFile(Path.Combine(destDir, nuspecEntry.Name.ToLower().SimplifyGodotPath()));
+ nuspecEntry.ExtractToFile(Path.Combine(destDir, nuspecEntry.Name
+ .ToLowerInvariant().SimplifyGodotPath()));
// Extract the other package files
@@ -247,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;
}
@@ -293,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/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
index e9718cc82c..94efcba3f1 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
@@ -22,7 +22,7 @@ namespace GodotTools.Export
public bool FullAot;
private bool _useInterpreter;
- public bool UseInterpreter { get => _useInterpreter && !LLVMOnly; set => _useInterpreter = value; }
+ public bool UseInterpreter { readonly get => _useInterpreter && !LLVMOnly; set => _useInterpreter = value; }
public string[] ExtraAotOptions;
public string[] ExtraOptimizerOptions;
@@ -75,8 +75,24 @@ namespace GodotTools.Export
}
else
{
- string bits = features.Contains("64") ? "64" : features.Contains("32") ? "32" : null;
- CompileAssembliesForDesktop(exporter, platform, isDebug, bits, aotOpts, aotTempDir, outputDataDir, assembliesPrepared, bclDir);
+ string arch = "";
+ if (features.Contains("x86_64"))
+ {
+ arch = "x86_64";
+ }
+ else if (features.Contains("x86_32"))
+ {
+ arch = "x86_32";
+ }
+ else if (features.Contains("arm64"))
+ {
+ arch = "arm64";
+ }
+ else if (features.Contains("arm32"))
+ {
+ arch = "arm32";
+ }
+ CompileAssembliesForDesktop(exporter, platform, isDebug, arch, aotOpts, aotTempDir, outputDataDir, assembliesPrepared, bclDir);
}
}
@@ -112,7 +128,7 @@ namespace GodotTools.Export
}
}
- public static void CompileAssembliesForDesktop(ExportPlugin exporter, string platform, bool isDebug, string bits, AotOptions aotOpts, string aotTempDir, string outputDataDir, IDictionary<string, string> assemblies, string bclDir)
+ public static void CompileAssembliesForDesktop(ExportPlugin exporter, string platform, bool isDebug, string arch, AotOptions aotOpts, string aotTempDir, string outputDataDir, IDictionary<string, string> assemblies, string bclDir)
{
foreach (var assembly in assemblies)
{
@@ -126,9 +142,9 @@ namespace GodotTools.Export
string outputFileName = assemblyName + ".dll" + outputFileExtension;
string tempOutputFilePath = Path.Combine(aotTempDir, outputFileName);
- var compilerArgs = GetAotCompilerArgs(platform, isDebug, bits, aotOpts, assemblyPath, tempOutputFilePath);
+ var compilerArgs = GetAotCompilerArgs(platform, isDebug, arch, aotOpts, assemblyPath, tempOutputFilePath);
- string compilerDirPath = GetMonoCrossDesktopDirName(platform, bits);
+ string compilerDirPath = GetMonoCrossDesktopDirName(platform, arch);
ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir);
@@ -203,7 +219,7 @@ namespace GodotTools.Export
int clangExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("clang"), clangArgs);
if (clangExitCode != 0)
- throw new Exception($"Command 'clang' exited with code: {clangExitCode}");
+ throw new InvalidOperationException($"Command 'clang' exited with code: {clangExitCode}.");
objFilePathsForiOSArch[arch].Add(objFilePath);
}
@@ -289,7 +305,7 @@ MONO_AOT_MODE_LAST = 1000,
// Archive the AOT object files into a static library
var arFilePathsForAllArchs = new List<string>();
- string projectAssemblyName = GodotSharpEditor.ProjectAssemblyName;
+ string projectAssemblyName = GodotSharpDirs.ProjectAssemblyName;
foreach (var archPathsPair in objFilePathsForiOSArch)
{
@@ -309,7 +325,7 @@ MONO_AOT_MODE_LAST = 1000,
int arExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("ar"), arArgs);
if (arExitCode != 0)
- throw new Exception($"Command 'ar' exited with code: {arExitCode}");
+ throw new InvalidOperationException($"Command 'ar' exited with code: {arExitCode}.");
arFilePathsForAllArchs.Add(arOutputFilePath);
}
@@ -327,7 +343,7 @@ MONO_AOT_MODE_LAST = 1000,
int lipoExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("lipo"), lipoArgs);
if (lipoExitCode != 0)
- throw new Exception($"Command 'lipo' exited with code: {lipoExitCode}");
+ throw new InvalidOperationException($"Command 'lipo' exited with code: {lipoExitCode}.");
// TODO: Add the AOT lib and interpreter libs as device only to suppress warnings when targeting the simulator
@@ -427,14 +443,14 @@ MONO_AOT_MODE_LAST = 1000,
}
else if (!Directory.Exists(androidToolchain))
{
- throw new FileNotFoundException("Android toolchain not found: " + androidToolchain);
+ throw new FileNotFoundException($"Android toolchain not found: '{androidToolchain}'.");
}
var androidToolPrefixes = new Dictionary<string, string>
{
- ["armeabi-v7a"] = "arm-linux-androideabi-",
- ["arm64-v8a"] = "aarch64-linux-android-",
- ["x86"] = "i686-linux-android-",
+ ["arm32"] = "arm-linux-androideabi-",
+ ["arm64"] = "aarch64-linux-android-",
+ ["x86_32"] = "i686-linux-android-",
["x86_64"] = "x86_64-linux-android-"
};
@@ -524,12 +540,12 @@ MONO_AOT_MODE_LAST = 1000,
Console.WriteLine($"Running: \"{process.StartInfo.FileName}\" {process.StartInfo.Arguments}");
if (!process.Start())
- throw new Exception("Failed to start process for Mono AOT compiler");
+ throw new InvalidOperationException("Failed to start process for Mono AOT compiler.");
process.WaitForExit();
if (process.ExitCode != 0)
- throw new Exception($"Mono AOT compiler exited with code: {process.ExitCode}");
+ throw new InvalidOperationException($"Mono AOT compiler exited with code: {process.ExitCode}.");
}
}
@@ -547,9 +563,9 @@ MONO_AOT_MODE_LAST = 1000,
{
var androidAbis = new[]
{
- "armeabi-v7a",
- "arm64-v8a",
- "x86",
+ "arm32",
+ "arm64",
+ "x86_32",
"x86_64"
};
@@ -560,9 +576,9 @@ MONO_AOT_MODE_LAST = 1000,
{
var abiArchs = new Dictionary<string, string>
{
- ["armeabi-v7a"] = "armv7",
- ["arm64-v8a"] = "aarch64-v8a",
- ["x86"] = "i686",
+ ["arm32"] = "armv7",
+ ["arm64"] = "aarch64-v8a",
+ ["x86_32"] = "i686",
["x86_64"] = "x86_64"
};
@@ -571,31 +587,25 @@ MONO_AOT_MODE_LAST = 1000,
return $"{arch}-linux-android";
}
- private static string GetMonoCrossDesktopDirName(string platform, string bits)
+ private static string GetMonoCrossDesktopDirName(string platform, string arch)
{
switch (platform)
{
case OS.Platforms.Windows:
case OS.Platforms.UWP:
{
- string arch = bits == "64" ? "x86_64" : "i686";
return $"windows-{arch}";
}
case OS.Platforms.MacOS:
{
- Debug.Assert(bits == null || bits == "64");
- string arch = "x86_64";
return $"{platform}-{arch}";
}
case OS.Platforms.LinuxBSD:
- case OS.Platforms.Server:
{
- string arch = bits == "64" ? "x86_64" : "i686";
return $"linux-{arch}";
}
case OS.Platforms.Haiku:
{
- string arch = bits == "64" ? "x86_64" : "i686";
return $"{platform}-{arch}";
}
default:
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index cca18a2a1f..745a8b73f8 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -4,11 +4,9 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Runtime.CompilerServices;
using GodotTools.Build;
using GodotTools.Core;
using GodotTools.Internals;
-using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using Directory = GodotTools.Utils.Directory;
using File = GodotTools.Utils.File;
@@ -17,61 +15,15 @@ using Path = System.IO.Path;
namespace GodotTools.Export
{
- public class ExportPlugin : EditorExportPlugin
+ public partial class ExportPlugin : EditorExportPlugin
{
- [Flags]
- private enum I18NCodesets : long
- {
- None = 0,
- CJK = 1,
- MidEast = 2,
- Other = 4,
- Rare = 8,
- West = 16,
- All = CJK | MidEast | Other | Rare | West
- }
-
- private string _maybeLastExportError;
-
- private void AddI18NAssemblies(Godot.Collections.Dictionary<string, string> assemblies, string bclDir)
- {
- var codesets = (I18NCodesets)ProjectSettings.GetSetting("mono/export/i18n_codesets");
-
- if (codesets == I18NCodesets.None)
- return;
-
- void AddI18NAssembly(string name) => assemblies.Add(name, Path.Combine(bclDir, $"{name}.dll"));
-
- AddI18NAssembly("I18N");
-
- if ((codesets & I18NCodesets.CJK) != 0)
- AddI18NAssembly("I18N.CJK");
- if ((codesets & I18NCodesets.MidEast) != 0)
- AddI18NAssembly("I18N.MidEast");
- if ((codesets & I18NCodesets.Other) != 0)
- AddI18NAssembly("I18N.Other");
- if ((codesets & I18NCodesets.Rare) != 0)
- AddI18NAssembly("I18N.Rare");
- if ((codesets & I18NCodesets.West) != 0)
- AddI18NAssembly("I18N.West");
- }
+ private List<string> _tempFolders = new List<string>();
public void RegisterExportSettings()
{
// TODO: These would be better as export preset options, but that doesn't seem to be supported yet
GlobalDef("mono/export/include_scripts_content", false);
- GlobalDef("mono/export/export_assemblies_inside_pck", true);
-
- GlobalDef("mono/export/i18n_codesets", I18NCodesets.All);
-
- ProjectSettings.AddPropertyInfo(new Godot.Collections.Dictionary
- {
- ["type"] = Variant.Type.Int,
- ["name"] = "mono/export/i18n_codesets",
- ["hint"] = PropertyHint.Flags,
- ["hint_string"] = "CJK,MidEast,Other,Rare,West"
- });
GlobalDef("mono/export/aot/enabled", false);
GlobalDef("mono/export/aot/full_aot", false);
@@ -85,11 +37,7 @@ namespace GodotTools.Export
GlobalDef("mono/export/aot/android_toolchain_path", "");
}
- private void AddFile(string srcPath, string dstPath, bool remap = false)
- {
- // Add file to the PCK
- AddFile(dstPath.Replace("\\", "/"), File.ReadAllBytes(srcPath), remap);
- }
+ private string _maybeLastExportError;
// With this method we can override how a file is exported in the PCK
public override void _ExportFile(string path, string type, string[] features)
@@ -100,7 +48,9 @@ namespace GodotTools.Export
return;
if (Path.GetExtension(path) != Internal.CSharpLanguageExtension)
- throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path));
+ throw new ArgumentException(
+ $"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}",
+ nameof(path));
// TODO What if the source file is not part of the game's C# project
@@ -119,7 +69,7 @@ namespace GodotTools.Export
}
}
- public override void _ExportBegin(string[] features, bool isDebug, string path, int flags)
+ public override void _ExportBegin(string[] features, bool isDebug, string path, long flags)
{
base._ExportBegin(features, isDebug, path, flags);
@@ -142,7 +92,7 @@ namespace GodotTools.Export
}
}
- private void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
+ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long flags)
{
_ = flags; // Unused
@@ -150,161 +100,111 @@ namespace GodotTools.Export
return;
if (!DeterminePlatformFromFeatures(features, out string platform))
- throw new NotSupportedException("Target platform not supported");
+ throw new NotSupportedException("Target platform not supported.");
+
+ if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS }
+ .Contains(platform))
+ {
+ throw new NotImplementedException("Target platform not yet implemented.");
+ }
string outputDir = new FileInfo(path).Directory?.FullName ??
- throw new FileNotFoundException("Base directory not found");
+ throw new FileNotFoundException("Output base directory not found.");
string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
- if (!BuildManager.BuildProjectBlocking(buildConfig, platform: platform))
- throw new Exception("Failed to build project");
-
- // Add dependency assemblies
-
- var assemblies = new Godot.Collections.Dictionary<string, string>();
-
- string projectDllName = GodotSharpEditor.ProjectAssemblyName;
- string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
- string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
-
- assemblies[projectDllName] = projectDllSrcPath;
-
- string bclDir = DeterminePlatformBclDir(platform);
-
- if (platform == OS.Platforms.Android)
+ var archs = new List<string>();
+ if (features.Contains("x86_64"))
{
- string godotAndroidExtProfileDir = GetBclProfileDir("godot_android_ext");
- string monoAndroidAssemblyPath = Path.Combine(godotAndroidExtProfileDir, "Mono.Android.dll");
-
- if (!File.Exists(monoAndroidAssemblyPath))
- throw new FileNotFoundException("Assembly not found: 'Mono.Android'", monoAndroidAssemblyPath);
-
- assemblies["Mono.Android"] = monoAndroidAssemblyPath;
+ archs.Add("x86_64");
}
- else if (platform == OS.Platforms.HTML5)
+ else if (features.Contains("x86_32"))
{
- // Ideally these would be added automatically since they're referenced by the wasm BCL assemblies.
- // However, at least in the case of 'WebAssembly.Net.Http' for some reason the BCL assemblies
- // reference a different version even though the assembly is the same, for some weird reason.
-
- var wasmFrameworkAssemblies = new[] { "WebAssembly.Bindings", "WebAssembly.Net.WebSockets" };
-
- foreach (string thisWasmFrameworkAssemblyName in wasmFrameworkAssemblies)
+ archs.Add("x86_32");
+ }
+ else if (features.Contains("arm64"))
+ {
+ archs.Add("arm64");
+ }
+ else if (features.Contains("universal"))
+ {
+ if (platform == OS.Platforms.MacOS)
{
- string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName + ".dll");
- if (!File.Exists(thisWasmFrameworkAssemblyPath))
- throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'", thisWasmFrameworkAssemblyPath);
- assemblies[thisWasmFrameworkAssemblyName] = thisWasmFrameworkAssemblyPath;
+ archs.Add("x86_64");
+ archs.Add("arm64");
}
+ }
- // Assemblies that can have a different name in a newer version. Newer version must come first and it has priority.
- (string newName, string oldName)[] wasmFrameworkAssembliesOneOf = new[]
- {
- ("System.Net.Http.WebAssemblyHttpHandler", "WebAssembly.Net.Http")
- };
-
- foreach (var thisWasmFrameworkAssemblyName in wasmFrameworkAssembliesOneOf)
+ foreach (var arch in archs)
+ {
+ string ridOS = DetermineRuntimeIdentifierOS(platform);
+ string ridArch = DetermineRuntimeIdentifierArch(arch);
+ string runtimeIdentifier = $"{ridOS}-{ridArch}";
+ string projectDataDirName = $"{DetermineDataDirNameForProject()}_{arch}";
+ if (platform == OS.Platforms.MacOS)
{
- string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll");
- if (File.Exists(thisWasmFrameworkAssemblyPath))
- {
- assemblies[thisWasmFrameworkAssemblyName.newName] = thisWasmFrameworkAssemblyPath;
- }
- else
- {
- thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll");
- if (!File.Exists(thisWasmFrameworkAssemblyPath))
- {
- throw new FileNotFoundException("Expected one of the following assemblies but none were found: " +
- $"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'",
- thisWasmFrameworkAssemblyPath);
- }
-
- assemblies[thisWasmFrameworkAssemblyName.oldName] = thisWasmFrameworkAssemblyPath;
- }
+ projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName);
}
- }
-
- var initialAssemblies = assemblies.Duplicate();
- internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies);
- AddI18NAssemblies(assemblies, bclDir);
+ // Create temporary publish output directory
- string outputDataDir = null;
+ string publishOutputTempDir = Path.Combine(Path.GetTempPath(), "godot-publish-dotnet",
+ $"{Process.GetCurrentProcess().Id}-{buildConfig}-{runtimeIdentifier}");
- if (PlatformHasTemplateDir(platform))
- outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir);
+ _tempFolders.Add(publishOutputTempDir);
- string apiConfig = isDebug ? "Debug" : "Release";
- string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
+ if (!Directory.Exists(publishOutputTempDir))
+ Directory.CreateDirectory(publishOutputTempDir);
- bool assembliesInsidePck = (bool)ProjectSettings.GetSetting("mono/export/export_assemblies_inside_pck") || outputDataDir == null;
-
- if (!assembliesInsidePck)
- {
- string outputDataGameAssembliesDir = Path.Combine(outputDataDir, "Assemblies");
- if (!Directory.Exists(outputDataGameAssembliesDir))
- Directory.CreateDirectory(outputDataGameAssembliesDir);
- }
+ // Execute dotnet publish
- foreach (var assembly in assemblies)
- {
- void AddToAssembliesDir(string fileSrcPath)
+ if (!BuildManager.PublishProjectBlocking(buildConfig, platform,
+ runtimeIdentifier, publishOutputTempDir))
{
- if (assembliesInsidePck)
- {
- string fileDstPath = Path.Combine(resAssembliesDir, fileSrcPath.GetFile());
- AddFile(fileSrcPath, fileDstPath);
- }
- else
- {
- Debug.Assert(outputDataDir != null);
- string fileDstPath = Path.Combine(outputDataDir, "Assemblies", fileSrcPath.GetFile());
- File.Copy(fileSrcPath, fileDstPath);
- }
+ throw new InvalidOperationException("Failed to build project.");
}
- string assemblySrcPath = assembly.Value;
+ string soExt = ridOS switch
+ {
+ OS.DotNetOS.Win or OS.DotNetOS.Win10 => "dll",
+ OS.DotNetOS.OSX or OS.DotNetOS.iOS => "dylib",
+ _ => "so"
+ };
- string assemblyPathWithoutExtension = Path.ChangeExtension(assemblySrcPath, null);
- string pdbSrcPath = assemblyPathWithoutExtension + ".pdb";
+ if (!File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.dll"))
+ // NativeAOT shared library output
+ && !File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.{soExt}")))
+ {
+ throw new NotSupportedException(
+ "Publish succeeded but project assembly not found in the output directory");
+ }
- AddToAssembliesDir(assemblySrcPath);
+ // Add to the exported project shared object list.
- if (File.Exists(pdbSrcPath))
- AddToAssembliesDir(pdbSrcPath);
+ foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories))
+ {
+ AddSharedObject(file, tags: null, projectDataDirName);
+ }
}
+ }
- // AOT compilation
- bool aotEnabled = platform == OS.Platforms.iOS || (bool)ProjectSettings.GetSetting("mono/export/aot/enabled");
+ private string DetermineRuntimeIdentifierOS(string platform)
+ => OS.DotNetOSPlatformMap[platform];
- if (aotEnabled)
+ private string DetermineRuntimeIdentifierArch(string arch)
+ {
+ return arch switch
{
- string aotToolchainPath = null;
-
- if (platform == OS.Platforms.Android)
- aotToolchainPath = (string)ProjectSettings.GetSetting("mono/export/aot/android_toolchain_path");
-
- if (aotToolchainPath == string.Empty)
- aotToolchainPath = null; // Don't risk it being used as current working dir
-
- // TODO: LLVM settings are hard-coded and disabled for now
- var aotOpts = new AotOptions
- {
- EnableLLVM = false,
- LLVMOnly = false,
- LLVMPath = "",
- LLVMOutputPath = "",
- FullAot = platform == OS.Platforms.iOS || (bool)(ProjectSettings.GetSetting("mono/export/aot/full_aot") ?? false),
- UseInterpreter = (bool)ProjectSettings.GetSetting("mono/export/aot/use_interpreter"),
- ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ?? Array.Empty<string>(),
- ExtraOptimizerOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ?? Array.Empty<string>(),
- ToolchainPath = aotToolchainPath
- };
-
- AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, assemblies);
- }
+ "x86" => "x86",
+ "x86_32" => "x86",
+ "x64" => "x64",
+ "x86_64" => "x64",
+ "armeabi-v7a" => "arm",
+ "arm64-v8a" => "arm64",
+ "armv7" => "arm",
+ "arm64" => "arm64",
+ _ => throw new ArgumentOutOfRangeException(nameof(arch), arch, "Unexpected architecture")
+ };
}
public override void _ExportEnd()
@@ -316,79 +216,29 @@ namespace GodotTools.Export
if (Directory.Exists(aotTempDir))
Directory.Delete(aotTempDir, recursive: true);
- // TODO: Just a workaround until the export plugins can be made to abort with errors
- if (!string.IsNullOrEmpty(_maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading
+ foreach (string folder in _tempFolders)
{
- string lastExportError = _maybeLastExportError;
- _maybeLastExportError = null;
-
- GodotSharpEditor.Instance.ShowErrorDialog(lastExportError, "Failed to export C# project");
+ Directory.Delete(folder, recursive: true);
}
- }
+ _tempFolders.Clear();
- [NotNull]
- private static string ExportDataDirectory(string[] features, string platform, bool isDebug, string outputDir)
- {
- string target = isDebug ? "release_debug" : "release";
+ // TODO: The following is just a workaround until the export plugins can be made to abort with errors
- // NOTE: Bits is ok for now as all platforms with a data directory only have one or two architectures.
- // However, this may change in the future if we add arm linux or windows desktop templates.
- string bits = features.Contains("64") ? "64" : "32";
-
- string TemplateDirName() => $"data.mono.{platform}.{bits}.{target}";
-
- string templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName());
- bool validTemplatePathFound = true;
-
- if (!Directory.Exists(templateDirPath))
+ // We check for empty as well, because it's set to empty after hot-reloading
+ if (!string.IsNullOrEmpty(_maybeLastExportError))
{
- validTemplatePathFound = false;
-
- if (isDebug)
- {
- target = "debug"; // Support both 'release_debug' and 'debug' for the template data directory name
- templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName());
- validTemplatePathFound = true;
-
- if (!Directory.Exists(templateDirPath))
- validTemplatePathFound = false;
- }
- }
-
- if (!validTemplatePathFound)
- throw new FileNotFoundException("Data template directory not found", templateDirPath);
-
- string outputDataDir = Path.Combine(outputDir, DetermineDataDirNameForProject());
-
- if (Directory.Exists(outputDataDir))
- Directory.Delete(outputDataDir, recursive: true); // Clean first
-
- Directory.CreateDirectory(outputDataDir);
-
- foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories))
- {
- Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1)));
- }
+ string lastExportError = _maybeLastExportError;
+ _maybeLastExportError = null;
- foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories))
- {
- File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1)));
+ GodotSharpEditor.Instance.ShowErrorDialog(lastExportError, "Failed to export C# project");
}
-
- return outputDataDir;
- }
-
- private static bool PlatformHasTemplateDir(string platform)
- {
- // macOS export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest.
- return !new[] { OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform);
}
private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform)
{
foreach (var feature in features)
{
- if (OS.PlatformNameMap.TryGetValue(feature, out platform))
+ if (OS.PlatformFeatureMap.TryGetValue(feature, out platform))
return true;
}
@@ -396,87 +246,11 @@ namespace GodotTools.Export
return false;
}
- private static string GetBclProfileDir(string profile)
- {
- string templatesDir = Internal.FullExportTemplatesDir;
- return Path.Combine(templatesDir, "bcl", profile);
- }
-
- private static string DeterminePlatformBclDir(string platform)
- {
- string templatesDir = Internal.FullExportTemplatesDir;
- string platformBclDir = Path.Combine(templatesDir, "bcl", platform);
-
- if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll")))
- {
- string profile = DeterminePlatformBclProfile(platform);
- platformBclDir = Path.Combine(templatesDir, "bcl", profile);
-
- if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll")))
- {
- if (PlatformRequiresCustomBcl(platform))
- throw new FileNotFoundException($"Missing BCL (Base Class Library) for platform: {platform}");
-
- platformBclDir = typeof(object).Assembly.Location.GetBaseDir(); // Use the one we're running on
- }
- }
-
- return platformBclDir;
- }
-
- /// <summary>
- /// Determines whether the BCL bundled with the Godot editor can be used for the target platform,
- /// or if it requires a custom BCL that must be distributed with the export templates.
- /// </summary>
- private static bool PlatformRequiresCustomBcl(string platform)
- {
- if (new[] { OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform))
- return true;
-
- // The 'net_4_x' BCL is not compatible between Windows and the other platforms.
- // We use the names 'net_4_x_win' and 'net_4_x' to differentiate between the two.
-
- bool isWinOrUwp = new[]
- {
- OS.Platforms.Windows,
- OS.Platforms.UWP
- }.Contains(platform);
-
- return OS.IsWindows ? !isWinOrUwp : isWinOrUwp;
- }
-
- private static string DeterminePlatformBclProfile(string platform)
- {
- switch (platform)
- {
- case OS.Platforms.Windows:
- case OS.Platforms.UWP:
- return "net_4_x_win";
- case OS.Platforms.MacOS:
- case OS.Platforms.LinuxBSD:
- case OS.Platforms.Server:
- case OS.Platforms.Haiku:
- return "net_4_x";
- case OS.Platforms.Android:
- return "monodroid";
- case OS.Platforms.iOS:
- return "monotouch";
- case OS.Platforms.HTML5:
- return "wasm";
- default:
- throw new NotSupportedException($"Platform not supported: {platform}");
- }
- }
-
private static string DetermineDataDirNameForProject()
{
string appName = (string)ProjectSettings.GetSetting("application/config/name");
string appNameSafe = appName.ToSafeDirName();
return $"data_{appNameSafe}";
}
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialAssemblies,
- string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencyAssemblies);
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs b/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
index 93ef837a83..4f5bebfb42 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
@@ -16,7 +16,7 @@ namespace GodotTools.Export
_XcodePath = FindXcode();
if (_XcodePath == null)
- throw new Exception("Could not find Xcode");
+ throw new FileNotFoundException("Could not find Xcode.");
}
return _XcodePath;
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index b39c3d1c0d..89364d1c02 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -13,13 +13,14 @@ using GodotTools.Internals;
using GodotTools.ProjectEditor;
using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
+using Environment = System.Environment;
using File = GodotTools.Utils.File;
using OS = GodotTools.Utils.OS;
using Path = System.IO.Path;
namespace GodotTools
{
- public class GodotSharpEditor : EditorPlugin, ISerializationListener
+ public partial class GodotSharpEditor : EditorPlugin, ISerializationListener
{
private EditorSettings _editorSettings;
@@ -39,28 +40,27 @@ namespace GodotTools
public bool SkipBuildBeforePlaying { get; set; } = false;
- public static string ProjectAssemblyName
+ [UsedImplicitly]
+ private bool CreateProjectSolutionIfNeeded()
{
- get
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath) || !File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
- string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
- projectAssemblyName = projectAssemblyName.ToSafeDirName();
- if (string.IsNullOrEmpty(projectAssemblyName))
- projectAssemblyName = "UnnamedProject";
- return projectAssemblyName;
+ return CreateProjectSolution();
}
+
+ return true;
}
private bool CreateProjectSolution()
{
- using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3))
+ using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2))
{
pr.Step("Generating C# project...".TTR());
string resourceDir = ProjectSettings.GlobalizePath("res://");
string path = resourceDir;
- string name = ProjectAssemblyName;
+ string name = GodotSharpDirs.ProjectAssemblyName;
string guid = CsProjOperations.GenerateGameProject(path, name);
@@ -75,7 +75,7 @@ namespace GodotTools
{
Guid = guid,
PathRelativeToSolution = name + ".csproj",
- Configs = new List<string> {"Debug", "ExportDebug", "ExportRelease"}
+ Configs = new List<string> { "Debug", "ExportDebug", "ExportRelease" }
};
solution.AddNewProject(name, projectInfo);
@@ -90,24 +90,6 @@ namespace GodotTools
return false;
}
- pr.Step("Updating Godot API assemblies...".TTR());
-
- string debugApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Debug");
-
- if (!string.IsNullOrEmpty(debugApiAssembliesError))
- {
- ShowErrorDialog("Failed to update the Godot API assemblies: " + debugApiAssembliesError);
- return false;
- }
-
- string releaseApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Release");
-
- if (!string.IsNullOrEmpty(releaseApiAssembliesError))
- {
- ShowErrorDialog("Failed to update the Godot API assemblies: " + releaseApiAssembliesError);
- return false;
- }
-
pr.Step("Done".TTR());
// Here, after all calls to progress_task_step
@@ -129,7 +111,7 @@ namespace GodotTools
_toolBarBuildButton.Show();
}
- private void _MenuOptionPressed(int id)
+ private void _MenuOptionPressed(long id)
{
switch ((MenuOptions)id)
{
@@ -141,7 +123,8 @@ namespace GodotTools
try
{
string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
- NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, fallbackFolder);
+ NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
+ fallbackFolder);
NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
}
catch (Exception e)
@@ -167,13 +150,6 @@ namespace GodotTools
Instance.MSBuildPanel.BuildSolution();
}
- public override void _Ready()
- {
- base._Ready();
-
- MSBuildPanel.BuildOutputView.BuildStateChanged += BuildStateChanged;
- }
-
private enum MenuOptions
{
CreateSln,
@@ -197,7 +173,7 @@ namespace GodotTools
[UsedImplicitly]
public Error OpenInExternalEditor(Script script, int line, int col)
{
- var editorId = (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor");
+ var editorId = (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor");
switch (editorId)
{
@@ -219,13 +195,15 @@ namespace GodotTools
try
{
if (Godot.OS.IsStdoutVerbose())
- Console.WriteLine($"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}");
+ Console.WriteLine(
+ $"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}");
OS.RunProcess(command, args);
}
catch (Exception e)
{
- GD.PushError($"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'");
+ GD.PushError(
+ $"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'");
}
break;
@@ -347,7 +325,8 @@ namespace GodotTools
[UsedImplicitly]
public bool OverridesExternalEditor()
{
- return (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditorId.None;
+ return (ExternalEditorId)(int)_editorSettings.GetSetting("mono/editor/external_editor") !=
+ ExternalEditorId.None;
}
public override bool _Build()
@@ -363,12 +342,12 @@ namespace GodotTools
DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
- ?? throw new Exception("Cannot open C# project");
+ ?? 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, ProjectAssemblyName);
+ ProjectUtils.MigrateToProjectSdksStyle(msbuildProject, GodotSharpDirs.ProjectAssemblyName);
ProjectUtils.EnsureGodotSdkIsUpToDate(msbuildProject);
@@ -400,18 +379,49 @@ namespace GodotTools
throw new InvalidOperationException();
Instance = this;
+ var dotNetSdkSearchVersion = Environment.Version;
+
+ // First we try to find the .NET Sdk ourselves to make sure we get the
+ // correct version first (`RegisterDefaults` always picks the latest).
+ if (DotNetFinder.TryFindDotNetSdk(dotNetSdkSearchVersion, out var sdkVersion, out string sdkPath))
+ {
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine($"Found .NET Sdk version '{sdkVersion}': {sdkPath}");
+
+ ProjectUtils.MSBuildLocatorRegisterMSBuildPath(sdkPath);
+ }
+ else
+ {
+ try
+ {
+ ProjectUtils.MSBuildLocatorRegisterDefaults(out sdkVersion, out sdkPath);
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine($"Found .NET Sdk version '{sdkVersion}': {sdkPath}");
+ }
+ catch (InvalidOperationException e)
+ {
+ if (Godot.OS.IsStdoutVerbose())
+ GD.PrintErr(e.ToString());
+ GD.PushError($".NET Sdk not found. The required version is '{dotNetSdkSearchVersion}'.");
+ }
+ }
+
var editorInterface = GetEditorInterface();
var editorBaseControl = editorInterface.GetBaseControl();
_editorSettings = editorInterface.GetEditorSettings();
+ GodotSharpDirs.RegisterProjectSettings();
+
_errorDialog = new AcceptDialog();
editorBaseControl.AddChild(_errorDialog);
MSBuildPanel = new MSBuildPanel();
+ MSBuildPanel.Ready += () =>
+ MSBuildPanel.BuildOutputView.BuildStateChanged += BuildStateChanged;
_bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR());
- AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
+ AddChild(new HotReloadAssemblyWatcher { Name = "HotReloadAssemblyWatcher" });
_menuPopup = new PopupMenu();
_menuPopup.Hide();
@@ -423,7 +433,7 @@ namespace GodotTools
_toolBarBuildButton = new Button
{
Text = "Build",
- HintTooltip = "Build Solution".TTR(),
+ TooltipText = "Build Solution".TTR(),
FocusMode = Control.FocusModeEnum.None,
Shortcut = buildSolutionShortcut,
ShortcutInTooltip = true
@@ -472,9 +482,9 @@ namespace GodotTools
_editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
- ["type"] = Variant.Type.Int,
+ ["type"] = (int)Variant.Type.Int,
["name"] = "mono/editor/external_editor",
- ["hint"] = PropertyHint.Enum,
+ ["hint"] = (int)PropertyHint.Enum,
["hint_string"] = settingsHintStr
});
@@ -487,7 +497,8 @@ namespace GodotTools
try
{
// At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
- NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, NuGetUtils.GodotFallbackFolderPath);
+ NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName,
+ NuGetUtils.GodotFallbackFolderPath);
}
catch (Exception e)
{
@@ -503,20 +514,23 @@ namespace GodotTools
protected override void Dispose(bool disposing)
{
- base.Dispose(disposing);
-
- if (_exportPluginWeak != null)
+ if (disposing)
{
- // We need to dispose our export plugin before the editor destroys EditorSettings.
- // Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
- // will be freed after EditorSettings already was, and its device polling thread
- // will try to access the EditorSettings singleton, resulting in null dereferencing.
- (_exportPluginWeak.GetRef() as ExportPlugin)?.Dispose();
+ if (IsInstanceValid(_exportPluginWeak))
+ {
+ // We need to dispose our export plugin before the editor destroys EditorSettings.
+ // Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
+ // will be freed after EditorSettings already was, and its device polling thread
+ // will try to access the EditorSettings singleton, resulting in null dereferencing.
+ (_exportPluginWeak.GetRef().AsGodotObject() as ExportPlugin)?.Dispose();
+
+ _exportPluginWeak.Dispose();
+ }
- _exportPluginWeak.Dispose();
+ GodotIdeManager?.Dispose();
}
- GodotIdeManager?.Dispose();
+ base.Dispose(disposing);
}
public void OnBeforeSerialize()
@@ -533,8 +547,10 @@ namespace GodotTools
public static GodotSharpEditor Instance { get; private set; }
[UsedImplicitly]
- private GodotSharpEditor()
+ private static IntPtr InternalCreateInstance(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
{
+ Internal.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
+ return new GodotSharpEditor().NativeInstance;
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index f1d45463c5..30525ba04a 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -1,14 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
- <TargetFramework>net472</TargetFramework>
- <LangVersion>7.2</LangVersion>
+ <TargetFramework>net6.0</TargetFramework>
+ <EnableDynamicLoading>true</EnableDynamicLoading>
+ <LangVersion>10</LangVersion>
<!-- The Godot editor uses the Debug Godot API assemblies -->
<GodotApiConfiguration>Debug</GodotApiConfiguration>
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
<GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
<GodotApiAssembliesDir>$(GodotOutputDataDir)/Api/$(GodotApiConfiguration)</GodotApiAssembliesDir>
+ <ProduceReferenceAssembly>false</ProduceReferenceAssembly>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
+ <!-- Needed for our source generators to work despite this not being a Godot game project -->
+ <PropertyGroup>
+ <IsGodotToolsProject>true</IsGodotToolsProject>
+ </PropertyGroup>
+ <ItemGroup>
+ <CompilerVisibleProperty Include="IsGodotToolsProject" />
+ </ItemGroup>
<PropertyGroup Condition=" Exists('$(GodotApiAssembliesDir)/GodotSharp.dll') ">
<!-- The project is part of the Godot source tree -->
<!-- Use the Godot source tree output folder instead of '$(ProjectDir)/bin' -->
@@ -20,6 +30,8 @@
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+ <!-- For RiderPathLocator -->
+ <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<Reference Include="GodotSharp">
<HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath>
<Private>False</Private>
@@ -30,6 +42,10 @@
</Reference>
</ItemGroup>
<ItemGroup>
+ <ProjectReference Include="..\..\Godot.NET.Sdk\Godot.SourceGenerators\Godot.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ <ProjectReference Include="..\..\..\glue\GodotSharp\Godot.SourceGenerators.Internal\Godot.SourceGenerators.Internal.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ </ItemGroup>
+ <ItemGroup>
<ProjectReference Include="..\GodotTools.BuildLogger\GodotTools.BuildLogger.csproj" />
<ProjectReference Include="..\GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj" />
<ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj" />
diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
index dd05f28af0..89ac8058b9 100644
--- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
@@ -1,14 +1,15 @@
using Godot;
using GodotTools.Internals;
+using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
namespace GodotTools
{
- public class HotReloadAssemblyWatcher : Node
+ public partial class HotReloadAssemblyWatcher : Node
{
private Timer _watchTimer;
- public override void _Notification(int what)
+ public override void _Notification(long what)
{
if (what == Node.NotificationWmWindowFocusIn)
{
@@ -25,6 +26,7 @@ namespace GodotTools
Internal.ReloadAssemblies(softReload: false);
}
+ [UsedImplicitly]
public void RestartTimer()
{
_watchTimer.Stop();
@@ -38,7 +40,7 @@ namespace GodotTools
_watchTimer = new Timer
{
OneShot = false,
- WaitTime = (float)EditorDef("mono/assembly_watch_interval_sec", 0.5)
+ WaitTime = 0.5f
};
_watchTimer.Timeout += TimerTimeout;
AddChild(_watchTimer);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
index 23339fe50b..9df90ac608 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
@@ -8,7 +8,7 @@ using GodotTools.Internals;
namespace GodotTools.Ides
{
- public sealed class GodotIdeManager : Node, ISerializationListener
+ public sealed partial class GodotIdeManager : Node, ISerializationListener
{
private MessagingServer _messagingServer;
@@ -76,7 +76,7 @@ namespace GodotTools.Ides
public async Task<EditorPick?> LaunchIdeAsync(int millisecondsTimeout = 10000)
{
- var editorId = (ExternalEditorId)GodotSharpEditor.Instance.GetEditorInterface()
+ var editorId = (ExternalEditorId)(int)GodotSharpEditor.Instance.GetEditorInterface()
.GetEditorSettings().GetSetting("mono/editor/external_editor");
string editorIdentity = GetExternalEditorIdentity(editorId);
@@ -153,7 +153,7 @@ namespace GodotTools.Ides
}
default:
- throw new ArgumentOutOfRangeException();
+ throw new ArgumentOutOfRangeException(nameof(editorId));
}
}
@@ -180,17 +180,17 @@ namespace GodotTools.Ides
public void SendOpenFile(string file)
{
- SendRequest<OpenFileResponse>(new OpenFileRequest {File = file});
+ SendRequest<OpenFileResponse>(new OpenFileRequest { File = file });
}
public void SendOpenFile(string file, int line)
{
- SendRequest<OpenFileResponse>(new OpenFileRequest {File = file, Line = line});
+ SendRequest<OpenFileResponse>(new OpenFileRequest { File = file, Line = line });
}
public void SendOpenFile(string file, int line, int column)
{
- SendRequest<OpenFileResponse>(new OpenFileRequest {File = file, Line = line, Column = column});
+ SendRequest<OpenFileResponse>(new OpenFileRequest { File = file, Line = line, Column = column });
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
index 6f11831b80..62db6e3af5 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
@@ -385,7 +385,7 @@ namespace GodotTools.Ides
// However, it doesn't fix resource loading if the rest of the path is also case insensitive.
string scriptFileLocalized = FsPathUtils.LocalizePathWithCaseChecked(request.ScriptFile);
- var response = new CodeCompletionResponse {Kind = request.Kind, ScriptFile = request.ScriptFile};
+ var response = new CodeCompletionResponse { Kind = request.Kind, ScriptFile = request.ScriptFile };
response.Suggestions = await Task.Run(() =>
Internal.CodeCompletionRequest(response.Kind, scriptFileLocalized ?? request.ScriptFile));
return response;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
index 71055f0125..dad6e35344 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
@@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
+using System.Runtime.Versioning;
using Godot;
-using JetBrains.Annotations;
using Microsoft.Win32;
using Newtonsoft.Json;
using Directory = System.IO.Directory;
@@ -41,7 +42,7 @@ namespace GodotTools.Ides.Rider
{
return CollectAllRiderPathsLinux();
}
- throw new Exception("Unexpected OS.");
+ throw new InvalidOperationException("Unexpected OS.");
}
catch (Exception e)
{
@@ -113,6 +114,7 @@ namespace GodotTools.Ides.Rider
return installInfos.ToArray();
}
+ [SupportedOSPlatform("windows")]
private static RiderInfo[] CollectRiderInfosWindows()
{
var installInfos = new List<RiderInfo>();
@@ -214,9 +216,10 @@ namespace GodotTools.Ides.Rider
return "../../build.txt";
if (OS.IsMacOS)
return "Contents/Resources/build.txt";
- throw new Exception("Unknown OS.");
+ throw new InvalidOperationException("Unknown OS.");
}
+ [SupportedOSPlatform("windows")]
private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths)
{
using (var key = Registry.CurrentUser.OpenSubKey(registryKey))
@@ -229,6 +232,7 @@ namespace GodotTools.Ides.Rider
}
}
+ [SupportedOSPlatform("windows")]
private static void CollectPathsFromRegistry(List<string> installPaths, RegistryKey key)
{
if (key == null) return;
@@ -324,7 +328,7 @@ namespace GodotTools.Ides.Rider
{
public string install_location;
- [CanBeNull]
+ [return: MaybeNull]
public static string GetInstallLocationFromJson(string json)
{
try
@@ -378,7 +382,7 @@ namespace GodotTools.Ides.Rider
public string version;
public string versionSuffix;
- [CanBeNull]
+ [return: MaybeNull]
internal static ProductInfo GetProductInfo(string json)
{
try
@@ -402,7 +406,7 @@ namespace GodotTools.Ides.Rider
// ReSharper disable once InconsistentNaming
public ActiveApplication active_application;
- [CanBeNull]
+ [return: MaybeNull]
public static string GetLatestBuildFromJson(string json)
{
try
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
index 3440eb701c..60602a5847 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
@@ -22,7 +22,7 @@ namespace GodotTools.Ides.Rider
public static void Initialize()
{
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
- var editor = (ExternalEditorId)editorSettings.GetSetting("mono/editor/external_editor");
+ var editor = (ExternalEditorId)(int)editorSettings.GetSetting("mono/editor/external_editor");
if (editor == ExternalEditorId.Rider)
{
if (!editorSettings.HasSetting(EditorPathSettingName))
@@ -30,9 +30,9 @@ namespace GodotTools.Ides.Rider
Globals.EditorDef(EditorPathSettingName, "Optional");
editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
- ["type"] = Variant.Type.String,
+ ["type"] = (int)Variant.Type.String,
["name"] = EditorPathSettingName,
- ["hint"] = PropertyHint.File,
+ ["hint"] = (int)PropertyHint.File,
["hint_string"] = ""
});
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs
index 70ba7c733a..8f39ad063e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs
@@ -1,6 +1,7 @@
using System;
using System.Runtime.CompilerServices;
using Godot;
+using Godot.NativeInterop;
namespace GodotTools.Internals
{
@@ -8,19 +9,12 @@ namespace GodotTools.Internals
{
public string Task { get; }
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_Create(string task, string label, int amount, bool canCancel);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_Dispose(string task);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_Step(string task, string state, int step, bool forceRefresh);
-
public EditorProgress(string task, string label, int amount, bool canCancel = false)
{
Task = task;
- internal_Create(task, label, amount, canCancel);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(task);
+ using godot_string labelIn = Marshaling.ConvertStringToNative(label);
+ Internal.godot_icall_EditorProgress_Create(taskIn, labelIn, amount, canCancel);
}
~EditorProgress()
@@ -33,18 +27,23 @@ namespace GodotTools.Internals
public void Dispose()
{
- internal_Dispose(Task);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(Task);
+ Internal.godot_icall_EditorProgress_Dispose(taskIn);
GC.SuppressFinalize(this);
}
public void Step(string state, int step = -1, bool forceRefresh = true)
{
- internal_Step(Task, state, step, forceRefresh);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(Task);
+ using godot_string stateIn = Marshaling.ConvertStringToNative(state);
+ Internal.godot_icall_EditorProgress_Step(taskIn, stateIn, step, forceRefresh);
}
public bool TryStep(string state, int step = -1, bool forceRefresh = true)
{
- return internal_Step(Task, state, step, forceRefresh);
+ using godot_string taskIn = Marshaling.ConvertStringToNative(Task);
+ using godot_string stateIn = Marshaling.ConvertStringToNative(state);
+ return Internal.godot_icall_EditorProgress_Step(taskIn, stateIn, step, forceRefresh);
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
index 5c5ced8c29..acb7cc3ab0 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
@@ -1,3 +1,4 @@
+using Godot.NativeInterop;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
@@ -5,35 +6,41 @@ namespace GodotTools.Internals
{
public static class Globals
{
- public static float EditorScale => internal_EditorScale();
-
- public static object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) =>
- internal_GlobalDef(setting, defaultValue, restartIfChanged);
-
- public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) =>
- internal_EditorDef(setting, defaultValue, restartIfChanged);
-
- public static object EditorShortcut(string setting) =>
- internal_EditorShortcut(setting);
+ public static float EditorScale => Internal.godot_icall_Globals_EditorScale();
+
+ public static unsafe object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false)
+ {
+ using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
+ using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue);
+ Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
+ using (result)
+ return Marshaling.ConvertVariantToManagedObject(result);
+ }
+
+ public static unsafe object EditorDef(string setting, object defaultValue, bool restartIfChanged = false)
+ {
+ using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
+ using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue);
+ Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result);
+ using (result)
+ return Marshaling.ConvertVariantToManagedObject(result);
+ }
+
+ public static object EditorShortcut(string setting)
+ {
+ using godot_string settingIn = Marshaling.ConvertStringToNative(setting);
+ Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result);
+ using (result)
+ return Marshaling.ConvertVariantToManagedObject(result);
+ }
[SuppressMessage("ReSharper", "InconsistentNaming")]
- public static string TTR(this string text) => internal_TTR(text);
-
- // Internal Calls
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern float internal_EditorScale();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern object internal_GlobalDef(string setting, object defaultValue, bool restartIfChanged);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern object internal_EditorShortcut(string setting);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_TTR(string text);
+ public static string TTR(this string text)
+ {
+ using godot_string textIn = Marshaling.ConvertStringToNative(text);
+ Internal.godot_icall_Globals_TTR(textIn, out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
index 5e70c399b2..14285cc0f1 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
@@ -1,103 +1,125 @@
-using System.Runtime.CompilerServices;
+using System.IO;
+using Godot;
+using Godot.NativeInterop;
+using GodotTools.Core;
+using static GodotTools.Internals.Globals;
namespace GodotTools.Internals
{
public static class GodotSharpDirs
{
- public static string ResDataDir => internal_ResDataDir();
- public static string ResMetadataDir => internal_ResMetadataDir();
- public static string ResAssembliesBaseDir => internal_ResAssembliesBaseDir();
- public static string ResAssembliesDir => internal_ResAssembliesDir();
- public static string ResConfigDir => internal_ResConfigDir();
- public static string ResTempDir => internal_ResTempDir();
- public static string ResTempAssembliesBaseDir => internal_ResTempAssembliesBaseDir();
- public static string ResTempAssembliesDir => internal_ResTempAssembliesDir();
-
- public static string MonoUserDir => internal_MonoUserDir();
- public static string MonoLogsDir => internal_MonoLogsDir();
-
- #region Tools-only
- public static string MonoSolutionsDir => internal_MonoSolutionsDir();
- public static string BuildLogsDirs => internal_BuildLogsDirs();
-
- public static string ProjectSlnPath => internal_ProjectSlnPath();
- public static string ProjectCsProjPath => internal_ProjectCsProjPath();
-
- public static string DataEditorToolsDir => internal_DataEditorToolsDir();
- public static string DataEditorPrebuiltApiDir => internal_DataEditorPrebuiltApiDir();
- #endregion
-
- public static string DataMonoEtcDir => internal_DataMonoEtcDir();
- public static string DataMonoLibDir => internal_DataMonoLibDir();
-
- #region Windows-only
- public static string DataMonoBinDir => internal_DataMonoBinDir();
- #endregion
-
-
- #region Internal
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResDataDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResMetadataDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResAssembliesBaseDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResAssembliesDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResConfigDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResTempDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResTempAssembliesBaseDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ResTempAssembliesDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoUserDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoLogsDir();
-
- #region Tools-only
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoSolutionsDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_BuildLogsDirs();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ProjectSlnPath();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_ProjectCsProjPath();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataEditorToolsDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataEditorPrebuiltApiDir();
- #endregion
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataMonoEtcDir();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataMonoLibDir();
-
- #region Windows-only
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_DataMonoBinDir();
- #endregion
-
- #endregion
+ public static string ResMetadataDir
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static string MonoUserDir
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_MonoUserDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static string BuildLogsDirs
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static string DataEditorToolsDir
+ {
+ get
+ {
+ Internal.godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
+
+ public static void RegisterProjectSettings()
+ {
+ GlobalDef("dotnet/project/assembly_name", "");
+ GlobalDef("dotnet/project/solution_directory", "");
+ GlobalDef("dotnet/project/c#_project_directory", "");
+ }
+
+ private static void DetermineProjectLocation()
+ {
+ static string DetermineProjectName()
+ {
+ string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
+ projectAssemblyName = projectAssemblyName.ToSafeDirName();
+ if (string.IsNullOrEmpty(projectAssemblyName))
+ projectAssemblyName = "UnnamedProject";
+ return projectAssemblyName;
+ }
+
+ _projectAssemblyName = (string)ProjectSettings.GetSetting("dotnet/project/assembly_name");
+ if (string.IsNullOrEmpty(_projectAssemblyName))
+ {
+ _projectAssemblyName = DetermineProjectName();
+ ProjectSettings.SetSetting("dotnet/project/assembly_name", _projectAssemblyName);
+ }
+
+ string slnParentDir = (string)ProjectSettings.GetSetting("dotnet/project/solution_directory");
+ if (string.IsNullOrEmpty(slnParentDir))
+ slnParentDir = "res://";
+
+ string csprojParentDir = (string)ProjectSettings.GetSetting("dotnet/project/c#_project_directory");
+ if (string.IsNullOrEmpty(csprojParentDir))
+ csprojParentDir = "res://";
+
+ _projectSlnPath = Path.Combine(ProjectSettings.GlobalizePath(slnParentDir),
+ string.Concat(_projectAssemblyName, ".sln"));
+
+ _projectCsProjPath = Path.Combine(ProjectSettings.GlobalizePath(csprojParentDir),
+ string.Concat(_projectAssemblyName, ".csproj"));
+ }
+
+ private static string _projectAssemblyName;
+ private static string _projectSlnPath;
+ private static string _projectCsProjPath;
+
+ public static string ProjectAssemblyName
+ {
+ get
+ {
+ if (_projectAssemblyName == null)
+ DetermineProjectLocation();
+ return _projectAssemblyName;
+ }
+ }
+
+ public static string ProjectSlnPath
+ {
+ get
+ {
+ if (_projectSlnPath == null)
+ DetermineProjectLocation();
+ return _projectSlnPath;
+ }
+ }
+
+ public static string ProjectCsProjPath
+ {
+ get
+ {
+ if (_projectCsProjPath == null)
+ DetermineProjectLocation();
+ return _projectCsProjPath;
+ }
+ }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 12c90178c9..fd810996f7 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -1,114 +1,161 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Godot;
+using Godot.NativeInterop;
+using Godot.SourceGenerators.Internal;
using GodotTools.IdeMessaging.Requests;
namespace GodotTools.Internals
{
- public static class Internal
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ [GenerateUnmanagedCallbacks(typeof(InternalUnmanagedCallbacks))]
+ internal static partial class Internal
{
public const string CSharpLanguageType = "CSharpScript";
public const string CSharpLanguageExtension = ".cs";
- public static string UpdateApiAssembliesFromPrebuilt(string config) =>
- internal_UpdateApiAssembliesFromPrebuilt(config);
+ public static string FullExportTemplatesDir
+ {
+ get
+ {
+ godot_icall_Internal_FullExportTemplatesDir(out godot_string dest);
+ using (dest)
+ return Marshaling.ConvertStringToManaged(dest);
+ }
+ }
- public static string FullExportTemplatesDir =>
- internal_FullExportTemplatesDir();
+ public static string SimplifyGodotPath(this string path) => Godot.StringExtensions.SimplifyPath(path);
- public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path);
+ public static bool IsMacOSAppBundleInstalled(string bundleId)
+ {
+ using godot_string bundleIdIn = Marshaling.ConvertStringToNative(bundleId);
+ return godot_icall_Internal_IsMacOSAppBundleInstalled(bundleIdIn);
+ }
- public static bool IsMacOSAppBundleInstalled(string bundleId) => internal_IsMacOSAppBundleInstalled(bundleId);
+ public static bool GodotIs32Bits() => godot_icall_Internal_GodotIs32Bits();
- public static bool GodotIs32Bits() => internal_GodotIs32Bits();
+ public static bool GodotIsRealTDouble() => godot_icall_Internal_GodotIsRealTDouble();
- public static bool GodotIsRealTDouble() => internal_GodotIsRealTDouble();
+ public static void GodotMainIteration() => godot_icall_Internal_GodotMainIteration();
- public static void GodotMainIteration() => internal_GodotMainIteration();
+ public static bool IsAssembliesReloadingNeeded() => godot_icall_Internal_IsAssembliesReloadingNeeded();
- public static ulong GetCoreApiHash() => internal_GetCoreApiHash();
+ public static void ReloadAssemblies(bool softReload) => godot_icall_Internal_ReloadAssemblies(softReload);
- public static ulong GetEditorApiHash() => internal_GetEditorApiHash();
+ public static void EditorDebuggerNodeReloadScripts() => godot_icall_Internal_EditorDebuggerNodeReloadScripts();
- public static bool IsAssembliesReloadingNeeded() => internal_IsAssembliesReloadingNeeded();
+ public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
+ godot_icall_Internal_ScriptEditorEdit(resource.NativeInstance, line, col, grabFocus);
- public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload);
+ public static void EditorNodeShowScriptScreen() => godot_icall_Internal_EditorNodeShowScriptScreen();
- public static void EditorDebuggerNodeReloadScripts() => internal_EditorDebuggerNodeReloadScripts();
+ public static void EditorRunPlay() => godot_icall_Internal_EditorRunPlay();
- public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
- internal_ScriptEditorEdit(resource, line, col, grabFocus);
+ public static void EditorRunStop() => godot_icall_Internal_EditorRunStop();
- public static void EditorNodeShowScriptScreen() => internal_EditorNodeShowScriptScreen();
+ public static void ScriptEditorDebugger_ReloadScripts() =>
+ godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();
- public static string MonoWindowsInstallRoot => internal_MonoWindowsInstallRoot();
+ public static string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind,
+ string scriptFile)
+ {
+ using godot_string scriptFileIn = Marshaling.ConvertStringToNative(scriptFile);
+ godot_icall_Internal_CodeCompletionRequest((int)kind, scriptFileIn, out godot_packed_string_array res);
+ using (res)
+ return Marshaling.ConvertNativePackedStringArrayToSystemArray(res);
+ }
- public static void EditorRunPlay() => internal_EditorRunPlay();
+ #region Internal
- public static void EditorRunStop() => internal_EditorRunStop();
+ private static bool initialized = false;
- public static void ScriptEditorDebugger_ReloadScripts() => internal_ScriptEditorDebugger_ReloadScripts();
+ // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Global
+ internal static unsafe void Initialize(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
+ {
+ if (initialized)
+ throw new InvalidOperationException("Already initialized.");
+ initialized = true;
- public static string[] CodeCompletionRequest(CodeCompletionRequest.CompletionKind kind, string scriptFile) =>
- internal_CodeCompletionRequest((int)kind, scriptFile);
+ if (unmanagedCallbacksSize != sizeof(InternalUnmanagedCallbacks))
+ throw new ArgumentException("Unmanaged callbacks size mismatch.", nameof(unmanagedCallbacksSize));
- #region Internal
+ _unmanagedCallbacks = Unsafe.AsRef<InternalUnmanagedCallbacks>((void*)unmanagedCallbacks);
+ }
+
+ private partial struct InternalUnmanagedCallbacks
+ {
+ }
+
+ /*
+ * IMPORTANT:
+ * The order of the methods defined in NativeFuncs must match the order
+ * in the array defined at the bottom of 'editor/editor_internal_calls.cpp'.
+ */
+
+ public static partial void godot_icall_GodotSharpDirs_ResMetadataDir(out godot_string r_dest);
+
+ public static partial void godot_icall_GodotSharpDirs_MonoUserDir(out godot_string r_dest);
+
+ public static partial void godot_icall_GodotSharpDirs_BuildLogsDirs(out godot_string r_dest);
+
+ public static partial void godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string r_dest);
+
+ public static partial void godot_icall_EditorProgress_Create(in godot_string task, in godot_string label,
+ int amount, bool canCancel);
+
+ public static partial void godot_icall_EditorProgress_Dispose(in godot_string task);
+
+ public static partial bool godot_icall_EditorProgress_Step(in godot_string task, in godot_string state,
+ int step,
+ bool forceRefresh);
+
+ private static partial void godot_icall_Internal_FullExportTemplatesDir(out godot_string dest);
+
+ private static partial bool godot_icall_Internal_IsMacOSAppBundleInstalled(in godot_string bundleId);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config);
+ private static partial bool godot_icall_Internal_GodotIs32Bits();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_FullExportTemplatesDir();
+ private static partial bool godot_icall_Internal_GodotIsRealTDouble();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_SimplifyGodotPath(this string path);
+ private static partial void godot_icall_Internal_GodotMainIteration();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_IsMacOSAppBundleInstalled(string bundleId);
+ private static partial bool godot_icall_Internal_IsAssembliesReloadingNeeded();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_GodotIs32Bits();
+ private static partial void godot_icall_Internal_ReloadAssemblies(bool softReload);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_GodotIsRealTDouble();
+ private static partial void godot_icall_Internal_EditorDebuggerNodeReloadScripts();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_GodotMainIteration();
+ private static partial bool godot_icall_Internal_ScriptEditorEdit(IntPtr resource, int line, int col,
+ bool grabFocus);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern ulong internal_GetCoreApiHash();
+ private static partial void godot_icall_Internal_EditorNodeShowScriptScreen();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern ulong internal_GetEditorApiHash();
+ private static partial void godot_icall_Internal_EditorRunPlay();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_IsAssembliesReloadingNeeded();
+ private static partial void godot_icall_Internal_EditorRunStop();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_ReloadAssemblies(bool softReload);
+ private static partial void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorDebuggerNodeReloadScripts();
+ private static partial void godot_icall_Internal_CodeCompletionRequest(int kind, in godot_string scriptFile,
+ out godot_packed_string_array res);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus);
+ public static partial float godot_icall_Globals_EditorScale();
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorNodeShowScriptScreen();
+ public static partial void godot_icall_Globals_GlobalDef(in godot_string setting, in godot_variant defaultValue,
+ bool restartIfChanged, out godot_variant result);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_MonoWindowsInstallRoot();
+ public static partial void godot_icall_Globals_EditorDef(in godot_string setting, in godot_variant defaultValue,
+ bool restartIfChanged, out godot_variant result);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorRunPlay();
+ public static partial void
+ godot_icall_Globals_EditorShortcut(in godot_string setting, out godot_variant result);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_EditorRunStop();
+ public static partial void godot_icall_Globals_TTR(in godot_string text, out godot_string dest);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void internal_ScriptEditorDebugger_ReloadScripts();
+ public static partial void godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string[] internal_CodeCompletionRequest(int kind, string scriptFile);
+ public static partial bool godot_icall_Utils_OS_UnixFileHasExecutableAccess(in godot_string filePath);
#endregion
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/PlaySettings.cs b/modules/mono/editor/GodotTools/GodotTools/PlaySettings.cs
index 820d0c0b83..9a8fdcc7c5 100644
--- a/modules/mono/editor/GodotTools/GodotTools/PlaySettings.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/PlaySettings.cs
@@ -1,6 +1,6 @@
namespace GodotTools
{
- public struct PlaySettings
+ public readonly struct PlaySettings
{
public bool HasDebugger { get; }
public string DebuggerHost { get; }
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
index 05499339b1..89bda704bb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
@@ -1,14 +1,14 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using Godot;
using GodotTools.Core;
-using JetBrains.Annotations;
namespace GodotTools.Utils
{
public static class FsPathUtils
{
- private static readonly string _resourcePath = ProjectSettings.GlobalizePath("res://");
+ private static readonly string ResourcePath = ProjectSettings.GlobalizePath("res://");
private static bool PathStartsWithAlreadyNorm(this string childPath, string parentPath)
{
@@ -30,11 +30,11 @@ namespace GodotTools.Utils
return childPathNorm.PathStartsWithAlreadyNorm(parentPathNorm);
}
- [CanBeNull]
+ [return: MaybeNull]
public static string LocalizePathWithCaseChecked(string path)
{
string pathNorm = path.NormalizePath() + Path.DirectorySeparatorChar;
- string resourcePathNorm = _resourcePath.NormalizePath() + Path.DirectorySeparatorChar;
+ string resourcePathNorm = ResourcePath.NormalizePath() + Path.DirectorySeparatorChar;
if (!pathNorm.PathStartsWithAlreadyNorm(resourcePathNorm))
return null;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
index 5cef6e5c3c..c16f803226 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
@@ -1,24 +1,24 @@
+using Godot.NativeInterop;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
-using System.Runtime.CompilerServices;
-using JetBrains.Annotations;
+using System.Runtime.Versioning;
+using System.Text;
+using GodotTools.Internals;
namespace GodotTools.Utils
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static class OS
{
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string GetPlatformName();
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern bool UnixFileHasExecutableAccess(string filePath);
-
- public static class Names
+ /// <summary>
+ /// Display names for the OS platforms.
+ /// </summary>
+ private static class Names
{
public const string Windows = "Windows";
public const string MacOS = "macOS";
@@ -26,27 +26,58 @@ namespace GodotTools.Utils
public const string FreeBSD = "FreeBSD";
public const string NetBSD = "NetBSD";
public const string BSD = "BSD";
- public const string Server = "Server";
public const string UWP = "UWP";
public const string Haiku = "Haiku";
public const string Android = "Android";
public const string iOS = "iOS";
- public const string HTML5 = "HTML5";
+ public const string Web = "Web";
}
+ /// <summary>
+ /// Godot platform identifiers.
+ /// </summary>
public static class Platforms
{
public const string Windows = "windows";
public const string MacOS = "macos";
public const string LinuxBSD = "linuxbsd";
- public const string Server = "server";
public const string UWP = "uwp";
public const string Haiku = "haiku";
public const string Android = "android";
public const string iOS = "ios";
- public const string HTML5 = "javascript";
+ public const string Web = "web";
+ }
+
+ /// <summary>
+ /// OS name part of the .NET runtime identifier (RID).
+ /// See https://docs.microsoft.com/en-us/dotnet/core/rid-catalog.
+ /// </summary>
+ public static class DotNetOS
+ {
+ public const string Win = "win";
+ public const string OSX = "osx";
+ public const string Linux = "linux";
+ public const string Win10 = "win10";
+ public const string Android = "android";
+ public const string iOS = "ios";
+ public const string Browser = "browser";
}
+ public static readonly Dictionary<string, string> PlatformFeatureMap = new Dictionary<string, string>(
+ // Export `features` may be in lower case
+ StringComparer.InvariantCultureIgnoreCase
+ )
+ {
+ ["Windows"] = Platforms.Windows,
+ ["macOS"] = Platforms.MacOS,
+ ["Linux"] = Platforms.LinuxBSD,
+ ["UWP"] = Platforms.UWP,
+ ["Haiku"] = Platforms.Haiku,
+ ["Android"] = Platforms.Android,
+ ["iOS"] = Platforms.iOS,
+ ["Web"] = Platforms.Web
+ };
+
public static readonly Dictionary<string, string> PlatformNameMap = new Dictionary<string, string>
{
[Names.Windows] = Platforms.Windows,
@@ -55,55 +86,85 @@ namespace GodotTools.Utils
[Names.FreeBSD] = Platforms.LinuxBSD,
[Names.NetBSD] = Platforms.LinuxBSD,
[Names.BSD] = Platforms.LinuxBSD,
- [Names.Server] = Platforms.Server,
[Names.UWP] = Platforms.UWP,
[Names.Haiku] = Platforms.Haiku,
[Names.Android] = Platforms.Android,
[Names.iOS] = Platforms.iOS,
- [Names.HTML5] = Platforms.HTML5
+ [Names.Web] = Platforms.Web
+ };
+
+ public static readonly Dictionary<string, string> DotNetOSPlatformMap = new Dictionary<string, string>
+ {
+ [Platforms.Windows] = DotNetOS.Win,
+ [Platforms.MacOS] = DotNetOS.OSX,
+ // TODO:
+ // Does .NET 6 support BSD variants? If it does, it may need the name `unix`
+ // instead of `linux` in the runtime identifier. This would be a problem as
+ // Godot has a single export profile for both, named LinuxBSD.
+ [Platforms.LinuxBSD] = DotNetOS.Linux,
+ [Platforms.UWP] = DotNetOS.Win10,
+ [Platforms.Android] = DotNetOS.Android,
+ [Platforms.iOS] = DotNetOS.iOS,
+ [Platforms.Web] = DotNetOS.Browser
};
private static bool IsOS(string name)
{
- return name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+ Internal.godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
+ using (dest)
+ {
+ string platformName = Marshaling.ConvertStringToManaged(dest);
+ return name.Equals(platformName, StringComparison.OrdinalIgnoreCase);
+ }
}
private static bool IsAnyOS(IEnumerable<string> names)
{
- return names.Any(p => p.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase));
+ Internal.godot_icall_Utils_OS_GetPlatformName(out godot_string dest);
+ using (dest)
+ {
+ string platformName = Marshaling.ConvertStringToManaged(dest);
+ return names.Any(p => p.Equals(platformName, StringComparison.OrdinalIgnoreCase));
+ }
}
private static readonly IEnumerable<string> LinuxBSDPlatforms =
new[] { Names.Linux, Names.FreeBSD, Names.NetBSD, Names.BSD };
private static readonly IEnumerable<string> UnixLikePlatforms =
- new[] { Names.MacOS, Names.Server, Names.Haiku, Names.Android, Names.iOS }
+ new[] { Names.MacOS, Names.Haiku, Names.Android, Names.iOS }
.Concat(LinuxBSDPlatforms).ToArray();
- private static readonly Lazy<bool> _isWindows = new Lazy<bool>(() => IsOS(Names.Windows));
- private static readonly Lazy<bool> _isMacOS = new Lazy<bool>(() => IsOS(Names.MacOS));
- private static readonly Lazy<bool> _isLinuxBSD = new Lazy<bool>(() => IsAnyOS(LinuxBSDPlatforms));
- private static readonly Lazy<bool> _isServer = new Lazy<bool>(() => IsOS(Names.Server));
- private static readonly Lazy<bool> _isUWP = new Lazy<bool>(() => IsOS(Names.UWP));
- private static readonly Lazy<bool> _isHaiku = new Lazy<bool>(() => IsOS(Names.Haiku));
- private static readonly Lazy<bool> _isAndroid = new Lazy<bool>(() => IsOS(Names.Android));
- private static readonly Lazy<bool> _isiOS = new Lazy<bool>(() => IsOS(Names.iOS));
- private static readonly Lazy<bool> _isHTML5 = new Lazy<bool>(() => IsOS(Names.HTML5));
- private static readonly Lazy<bool> _isUnixLike = new Lazy<bool>(() => IsAnyOS(UnixLikePlatforms));
-
- public static bool IsWindows => _isWindows.Value || IsUWP;
- public static bool IsMacOS => _isMacOS.Value;
- public static bool IsLinuxBSD => _isLinuxBSD.Value;
- public static bool IsServer => _isServer.Value;
- public static bool IsUWP => _isUWP.Value;
+ private static readonly Lazy<bool> _isWindows = new(() => IsOS(Names.Windows));
+ private static readonly Lazy<bool> _isMacOS = new(() => IsOS(Names.MacOS));
+ private static readonly Lazy<bool> _isLinuxBSD = new(() => IsAnyOS(LinuxBSDPlatforms));
+ private static readonly Lazy<bool> _isUWP = new(() => IsOS(Names.UWP));
+ private static readonly Lazy<bool> _isHaiku = new(() => IsOS(Names.Haiku));
+ private static readonly Lazy<bool> _isAndroid = new(() => IsOS(Names.Android));
+ private static readonly Lazy<bool> _isiOS = new(() => IsOS(Names.iOS));
+ private static readonly Lazy<bool> _isWeb = new(() => IsOS(Names.Web));
+ private static readonly Lazy<bool> _isUnixLike = new(() => IsAnyOS(UnixLikePlatforms));
+
+ [SupportedOSPlatformGuard("windows")] public static bool IsWindows => _isWindows.Value || IsUWP;
+
+ [SupportedOSPlatformGuard("osx")] public static bool IsMacOS => _isMacOS.Value;
+
+ [SupportedOSPlatformGuard("linux")] public static bool IsLinuxBSD => _isLinuxBSD.Value;
+
+ [SupportedOSPlatformGuard("windows")] public static bool IsUWP => _isUWP.Value;
+
public static bool IsHaiku => _isHaiku.Value;
- public static bool IsAndroid => _isAndroid.Value;
- public static bool IsiOS => _isiOS.Value;
- public static bool IsHTML5 => _isHTML5.Value;
+
+ [SupportedOSPlatformGuard("android")] public static bool IsAndroid => _isAndroid.Value;
+
+ [SupportedOSPlatformGuard("ios")] public static bool IsiOS => _isiOS.Value;
+
+ [SupportedOSPlatformGuard("browser")] public static bool IsWeb => _isWeb.Value;
public static bool IsUnixLike => _isUnixLike.Value;
public static char PathSep => IsWindows ? ';' : ':';
+ [return: MaybeNull]
public static string PathWhich([NotNull] string name)
{
if (IsWindows)
@@ -112,9 +173,11 @@ namespace GodotTools.Utils
return PathWhichUnix(name);
}
+ [return: MaybeNull]
private static string PathWhichWindows([NotNull] string name)
{
- string[] windowsExts = Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
+ string[] windowsExts =
+ Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
char[] invalidPathChars = Path.GetInvalidPathChars();
@@ -133,7 +196,7 @@ namespace GodotTools.Utils
string nameExt = Path.GetExtension(name);
bool hasPathExt = !string.IsNullOrEmpty(nameExt) &&
- windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
+ windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
@@ -147,6 +210,7 @@ namespace GodotTools.Utils
select path + ext).FirstOrDefault(File.Exists);
}
+ [return: MaybeNull]
private static string PathWhichUnix([NotNull] string name)
{
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
@@ -168,19 +232,16 @@ namespace GodotTools.Utils
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
return searchDirs.Select(dir => Path.Combine(dir, name))
- .FirstOrDefault(path => File.Exists(path) && UnixFileHasExecutableAccess(path));
+ .FirstOrDefault(path =>
+ {
+ using godot_string pathIn = Marshaling.ConvertStringToNative(path);
+ return File.Exists(path) && Internal.godot_icall_Utils_OS_UnixFileHasExecutableAccess(pathIn);
+ });
}
public static void RunProcess(string command, IEnumerable<string> arguments)
{
- // TODO: Once we move to .NET Standard 2.1 we can use ProcessStartInfo.ArgumentList instead
- string CmdLineArgsToString(IEnumerable<string> args)
- {
- // Not perfect, but as long as we are careful...
- return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg));
- }
-
- var startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments))
+ var startInfo = new ProcessStartInfo(command)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
@@ -188,44 +249,104 @@ namespace GodotTools.Utils
CreateNoWindow = true
};
- using (Process process = Process.Start(startInfo))
- {
- if (process == null)
- throw new Exception("No process was started");
+ foreach (string arg in arguments)
+ startInfo.ArgumentList.Add(arg);
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
- if (IsWindows && process.Id > 0)
- User32Dll.AllowSetForegroundWindow(process.Id); // allows application to focus itself
- }
+ using Process process = Process.Start(startInfo);
+
+ if (process == null)
+ throw new InvalidOperationException("No process was started.");
+
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+
+ if (IsWindows && process.Id > 0)
+ User32Dll.AllowSetForegroundWindow(process.Id); // Allows application to focus itself
}
public static int ExecuteCommand(string command, IEnumerable<string> arguments)
{
- // TODO: Once we move to .NET Standard 2.1 we can use ProcessStartInfo.ArgumentList instead
- string CmdLineArgsToString(IEnumerable<string> args)
+ var startInfo = new ProcessStartInfo(command)
{
- // Not perfect, but as long as we are careful...
- return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg));
- }
+ // Print the output
+ RedirectStandardOutput = false,
+ RedirectStandardError = false,
+ UseShellExecute = false
+ };
+
+ foreach (string arg in arguments)
+ startInfo.ArgumentList.Add(arg);
- var startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments));
+ Console.WriteLine(startInfo.GetCommandLineDisplay(new StringBuilder("Executing: ")).ToString());
- Console.WriteLine($"Executing: \"{startInfo.FileName}\" {startInfo.Arguments}");
+ using var process = new Process { StartInfo = startInfo };
+ process.Start();
+ process.WaitForExit();
+
+ return process.ExitCode;
+ }
- // Print the output
- startInfo.RedirectStandardOutput = false;
- startInfo.RedirectStandardError = false;
+ private static void AppendProcessFileNameForDisplay(this StringBuilder builder, string fileName)
+ {
+ if (builder.Length > 0)
+ builder.Append(' ');
+
+ if (fileName.Contains(' '))
+ {
+ builder.Append('"');
+ builder.Append(fileName);
+ builder.Append('"');
+ }
+ else
+ {
+ builder.Append(fileName);
+ }
+ }
- startInfo.UseShellExecute = false;
+ private static void AppendProcessArgumentsForDisplay(this StringBuilder builder,
+ Collection<string> argumentList)
+ {
+ // This is intended just for reading. It doesn't need to be a valid command line.
+ // E.g.: We don't handle escaping of quotes.
- using (var process = new Process { StartInfo = startInfo })
+ foreach (string argument in argumentList)
{
- process.Start();
- process.WaitForExit();
+ if (builder.Length > 0)
+ builder.Append(' ');
- return process.ExitCode;
+ if (argument.Contains(' '))
+ {
+ builder.Append('"');
+ builder.Append(argument);
+ builder.Append('"');
+ }
+ else
+ {
+ builder.Append(argument);
+ }
}
}
+
+ public static StringBuilder GetCommandLineDisplay(
+ this ProcessStartInfo startInfo,
+ StringBuilder optionalBuilder = null
+ )
+ {
+ var builder = optionalBuilder ?? new StringBuilder();
+
+ builder.AppendProcessFileNameForDisplay(startInfo.FileName);
+
+ if (startInfo.ArgumentList.Count == 0)
+ {
+ builder.Append(' ');
+ builder.Append(startInfo.Arguments);
+ }
+ else
+ {
+ builder.AppendProcessArgumentsForDisplay(startInfo.ArgumentList);
+ }
+
+ return builder;
+ }
}
}