summaryrefslogtreecommitdiff
path: root/modules/mono/editor
diff options
context:
space:
mode:
authorIgnacio Etcheverry <ignalfonsore@gmail.com>2019-11-13 20:12:36 +0100
committerIgnacio Etcheverry <ignalfonsore@gmail.com>2019-11-15 03:22:18 +0100
commit2b67924a0b5f50175da418408dcb8768c2bd3646 (patch)
tree0a41dd828ac66449867ffad041ec0705feace98c /modules/mono/editor
parentde7c2ad21b4cc2d889a5aeda64ead962036d2aa4 (diff)
Mono/C#: Initial exporter support for AOT compilation
Diffstat (limited to 'modules/mono/editor')
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildManager.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs456
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs18
8 files changed, 454 insertions, 45 deletions
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index 9a2b2e3a26..da90c960e5 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -44,7 +44,7 @@ namespace GodotTools.Build
{
get
{
- if (OS.IsWindows())
+ if (OS.IsWindows)
{
return (BuildManager.BuildTool) EditorSettings.GetSetting("mono/builds/build_tool")
== BuildManager.BuildTool.MsBuildMono;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
index b7e773ee88..ad8a6516ab 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
@@ -21,7 +21,7 @@ namespace GodotTools.Build
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
var buildTool = (BuildManager.BuildTool) editorSettings.GetSetting("mono/builds/build_tool");
- if (OS.IsWindows())
+ if (OS.IsWindows)
{
switch (buildTool)
{
@@ -59,7 +59,7 @@ namespace GodotTools.Build
}
}
- if (OS.IsUnix())
+ if (OS.IsUnixLike())
{
if (buildTool == BuildManager.BuildTool.MsBuildMono)
{
@@ -128,7 +128,7 @@ namespace GodotTools.Build
private static string FindMsBuildToolsPathOnWindows()
{
- if (!OS.IsWindows())
+ if (!OS.IsWindows)
throw new PlatformNotSupportedException();
// Try to find 15.0 with vswhere
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
index ab37d89955..217bf5c144 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
@@ -246,7 +246,7 @@ namespace GodotTools
{
// Build tool settings
- EditorDef("mono/builds/build_tool", OS.IsWindows() ? BuildTool.MsBuildVs : BuildTool.MsBuildMono);
+ EditorDef("mono/builds/build_tool", OS.IsWindows ? BuildTool.MsBuildVs : BuildTool.MsBuildMono);
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
@@ -255,7 +255,7 @@ namespace GodotTools
["type"] = Godot.Variant.Type.Int,
["name"] = "mono/builds/build_tool",
["hint"] = Godot.PropertyHint.Enum,
- ["hint_string"] = OS.IsWindows() ?
+ ["hint_string"] = OS.IsWindows ?
$"{PropNameMsbuildMono},{PropNameMsbuildVs}" :
$"{PropNameMsbuildMono}"
});
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index 6b8b1a3cee..f65d2a39f4 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -1,11 +1,13 @@
using Godot;
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using GodotTools.Core;
using GodotTools.Internals;
+using static GodotTools.Internals.Globals;
using Directory = GodotTools.Utils.Directory;
using File = GodotTools.Utils.File;
using OS = GodotTools.Utils.OS;
@@ -15,6 +17,25 @@ namespace GodotTools.Export
{
public class ExportPlugin : EditorExportPlugin
{
+ 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/aot/enabled", false);
+ GlobalDef("mono/export/aot/full_aot", false);
+
+ // --aot or --aot=opt1,opt2 (use 'mono --aot=help AuxAssembly.dll' to list AOT options)
+ GlobalDef("mono/export/aot/extra_aot_options", new string[] { });
+ // --optimize/-O=opt1,opt2 (use 'mono --list-opt'' to list optimize options)
+ GlobalDef("mono/export/aot/extra_optimizer_options", new string[] { });
+
+ GlobalDef("mono/export/aot/android_toolchain_path", "");
+ }
+
+ private string maybeLastExportError;
+
private void AddFile(string srcPath, string dstPath, bool remap = false)
{
AddFile(dstPath, File.ReadAllBytes(srcPath), remap);
@@ -36,8 +57,11 @@ namespace GodotTools.Export
if (!includeScriptsContent)
{
- // We don't want to include the source code on exported games
- AddFile(path, new byte[] { }, remap: false);
+ // We don't want to include the source code on exported games.
+
+ // Sadly, Godot prints errors when adding an empty file (nothing goes wrong, it's just noise).
+ // Because of this, we add a file which contains a line break.
+ AddFile(path, System.Text.Encoding.UTF8.GetBytes("\n"), remap: false);
Skip();
}
}
@@ -49,27 +73,28 @@ namespace GodotTools.Export
try
{
_ExportBeginImpl(features, isDebug, path, flags);
- // TODO: Handle _ExportBeginImpl return value. Do something on error once _ExportBegin supports failing.
}
catch (Exception e)
{
+ maybeLastExportError = e.Message;
GD.PushError($"Failed to export project: {e.Message}");
Console.Error.WriteLine(e);
// TODO: Do something on error once _ExportBegin supports failing.
}
}
- private bool _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
+ private void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
- return true;
+ return;
string platform = DeterminePlatformFromFeatures(features);
if (platform == null)
throw new NotSupportedException("Target platform not supported");
- // TODO Right now there is no way to stop the export process with an error
+ string outputDir = new FileInfo(path).Directory?.FullName ??
+ throw new FileNotFoundException("Base directory not found");
string buildConfig = isDebug ? "Debug" : "Release";
@@ -82,10 +107,7 @@ namespace GodotTools.Export
var godotDefines = features;
if (!BuildManager.BuildProjectBlocking(buildConfig, godotDefines))
- {
- GD.PushError("Failed to build project");
- return false;
- }
+ throw new Exception("Failed to build project");
// Add dependency assemblies
@@ -103,12 +125,9 @@ namespace GodotTools.Export
dependencies[projectDllName] = projectDllSrcPath;
{
- string templatesDir = Internal.FullTemplatesDir;
- string platformBclDir = Path.Combine(templatesDir, $"{platform}-bcl", platform);
+ string platformBclDir = DeterminePlatformBclDir(platform);
- string customBclDir = Directory.Exists(platformBclDir) ? platformBclDir : string.Empty;
-
- internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customBclDir, dependencies);
+ internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, platformBclDir, dependencies);
}
string apiConfig = isDebug ? "Debug" : "Release";
@@ -122,18 +141,44 @@ namespace GodotTools.Export
}
// Mono specific export template extras (data dir)
- ExportDataDirectory(features, platform, isDebug, path);
+ string outputDataDir = null;
+
+ if (PlatformHasTemplateDir(platform))
+ outputDataDir = ExportDataDirectory(features, platform, isDebug, outputDir);
+
+ // AOT
- return true;
+ if ((bool) ProjectSettings.GetSetting("mono/export/aot/enabled"))
+ {
+ AotCompileDependencies(features, platform, isDebug, outputDir, outputDataDir, dependencies);
+ }
}
- private static void ExportDataDirectory(ICollection<string> features, string platform, bool debug, string path)
+ public override void _ExportEnd()
{
- if (!PlatformHasTemplateDir(platform))
- return;
+ base._ExportEnd();
+
+ string aotTempDir = Path.Combine(Path.GetTempPath(), $"godot-aot-{Process.GetCurrentProcess().Id}");
+
+ 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
+ {
+ string lastExportError = maybeLastExportError;
+ maybeLastExportError = null;
+
+ GodotSharpEditor.Instance.ShowErrorDialog(lastExportError, "C# export failed");
+ }
+ }
+
+ private static string ExportDataDirectory(string[] features, string platform, bool isDebug, string outputDir)
+ {
+ string target = isDebug ? "release_debug" : "release";
+
+ // NOTE: Bits is ok for now as all platforms with a data directory have it, but that may change in the future.
string bits = features.Contains("64") ? "64" : "32";
- string target = debug ? "release_debug" : "release";
string TemplateDirName() => $"data.mono.{platform}.{bits}.{target}";
@@ -142,8 +187,8 @@ namespace GodotTools.Export
if (!Directory.Exists(templateDirPath))
{
templateDirPath = null;
-
- if (debug)
+
+ if (isDebug)
{
target = "debug"; // Support both 'release_debug' and 'debug' for the template data directory name
templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName());
@@ -156,9 +201,6 @@ namespace GodotTools.Export
if (templateDirPath == null)
throw new FileNotFoundException("Data template directory not found");
- string outputDir = new FileInfo(path).Directory?.FullName ??
- throw new FileNotFoundException("Base directory not found");
-
string outputDataDir = Path.Combine(outputDir, DataDirName);
if (Directory.Exists(outputDataDir))
@@ -175,6 +217,331 @@ namespace GodotTools.Export
{
File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1)));
}
+
+ return outputDataDir;
+ }
+
+ private void AotCompileDependencies(string[] features, string platform, bool isDebug, string outputDir, string outputDataDir, IDictionary<string, string> dependencies)
+ {
+ // TODO: WASM
+
+ string bclDir = DeterminePlatformBclDir(platform) ?? typeof(object).Assembly.Location.GetBaseDir();
+
+ string aotTempDir = Path.Combine(Path.GetTempPath(), $"godot-aot-{Process.GetCurrentProcess().Id}");
+
+ if (!Directory.Exists(aotTempDir))
+ Directory.CreateDirectory(aotTempDir);
+
+ var assemblies = new Dictionary<string, string>();
+
+ foreach (var dependency in dependencies)
+ {
+ string assemblyName = dependency.Key;
+ string assemblyPath = dependency.Value;
+
+ string assemblyPathInBcl = Path.Combine(bclDir, assemblyName + ".dll");
+
+ if (File.Exists(assemblyPathInBcl))
+ {
+ // Don't create teporaries for assemblies from the BCL
+ assemblies.Add(assemblyName, assemblyPathInBcl);
+ }
+ else
+ {
+ string tempAssemblyPath = Path.Combine(aotTempDir, assemblyName + ".dll");
+ File.Copy(assemblyPath, tempAssemblyPath);
+ assemblies.Add(assemblyName, tempAssemblyPath);
+ }
+ }
+
+ foreach (var assembly in assemblies)
+ {
+ string assemblyName = assembly.Key;
+ string assemblyPath = assembly.Value;
+
+ string sharedLibExtension = platform == OS.Platforms.Windows ? ".dll" :
+ platform == OS.Platforms.OSX ? ".dylib" :
+ platform == OS.Platforms.HTML5 ? ".wasm" :
+ ".so";
+
+ string outputFileName = assemblyName + ".dll" + sharedLibExtension;
+
+ if (platform == OS.Platforms.Android)
+ {
+ // Not sure if the 'lib' prefix is an Android thing or just Godot being picky,
+ // but we use '-aot-' as well just in case to avoid conflicts with other libs.
+ outputFileName = "lib-aot-" + outputFileName;
+ }
+
+ string outputFilePath = null;
+ string tempOutputFilePath;
+
+ switch (platform)
+ {
+ case OS.Platforms.OSX:
+ tempOutputFilePath = Path.Combine(aotTempDir, outputFileName);
+ break;
+ case OS.Platforms.Android:
+ tempOutputFilePath = Path.Combine(aotTempDir, "%%ANDROID_ABI%%", outputFileName);
+ break;
+ case OS.Platforms.HTML5:
+ tempOutputFilePath = Path.Combine(aotTempDir, outputFileName);
+ outputFilePath = Path.Combine(outputDir, outputFileName);
+ break;
+ default:
+ tempOutputFilePath = Path.Combine(aotTempDir, outputFileName);
+ outputFilePath = Path.Combine(outputDataDir, "Mono", platform == OS.Platforms.Windows ? "bin" : "lib", outputFileName);
+ break;
+ }
+
+ var data = new Dictionary<string, string>();
+ var enabledAndroidAbis = platform == OS.Platforms.Android ? GetEnabledAndroidAbis(features).ToArray() : null;
+
+ if (platform == OS.Platforms.Android)
+ {
+ Debug.Assert(enabledAndroidAbis != null);
+
+ foreach (var abi in enabledAndroidAbis)
+ {
+ data["abi"] = abi;
+ var outputFilePathForThisAbi = tempOutputFilePath.Replace("%%ANDROID_ABI%%", abi);
+
+ AotCompileAssembly(platform, isDebug, data, assemblyPath, outputFilePathForThisAbi);
+
+ AddSharedObject(outputFilePathForThisAbi, tags: new[] {abi});
+ }
+ }
+ else
+ {
+ string bits = features.Contains("64") ? "64" : features.Contains("64") ? "32" : null;
+
+ if (bits != null)
+ data["bits"] = bits;
+
+ AotCompileAssembly(platform, isDebug, data, assemblyPath, tempOutputFilePath);
+
+ if (platform == OS.Platforms.OSX)
+ {
+ AddSharedObject(tempOutputFilePath, tags: null);
+ }
+ else
+ {
+ Debug.Assert(outputFilePath != null);
+ File.Copy(tempOutputFilePath, outputFilePath);
+ }
+ }
+ }
+ }
+
+ private static void AotCompileAssembly(string platform, bool isDebug, Dictionary<string, string> data, string assemblyPath, string outputFilePath)
+ {
+ // Make sure the output directory exists
+ Directory.CreateDirectory(outputFilePath.GetBaseDir());
+
+ string exeExt = OS.IsWindows ? ".exe" : string.Empty;
+
+ string monoCrossDirName = DetermineMonoCrossDirName(platform, data);
+ string monoCrossRoot = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "aot-compilers", monoCrossDirName);
+ string monoCrossBin = Path.Combine(monoCrossRoot, "bin");
+
+ string toolPrefix = DetermineToolPrefix(monoCrossBin);
+ string monoExeName = System.IO.File.Exists(Path.Combine(monoCrossBin, $"{toolPrefix}mono{exeExt}")) ? "mono" : "mono-sgen";
+
+ string compilerCommand = Path.Combine(monoCrossBin, $"{toolPrefix}{monoExeName}{exeExt}");
+
+ bool fullAot = (bool) ProjectSettings.GetSetting("mono/export/aot/full_aot");
+
+ string EscapeOption(string option) => option.Contains(',') ? $"\"{option}\"" : option;
+ string OptionsToString(IEnumerable<string> options) => string.Join(",", options.Select(EscapeOption));
+
+ var aotOptions = new List<string>();
+ var optimizerOptions = new List<string>();
+
+ if (fullAot)
+ aotOptions.Add("full");
+
+ aotOptions.Add(isDebug ? "soft-debug" : "nodebug");
+
+ if (platform == OS.Platforms.Android)
+ {
+ string abi = data["abi"];
+
+ string androidToolchain = (string) ProjectSettings.GetSetting("mono/export/aot/android_toolchain_path");
+
+ if (string.IsNullOrEmpty(androidToolchain))
+ {
+ androidToolchain = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "android-toolchains", $"{abi}"); // TODO: $"{abi}-{apiLevel}{(clang?"clang":"")}"
+
+ if (!Directory.Exists(androidToolchain))
+ throw new FileNotFoundException("Missing android toolchain. Specify one in the AOT export settings.");
+ }
+ else if (!Directory.Exists(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-",
+ ["x86_64"] = "x86_64-linux-android-"
+ };
+
+ aotOptions.Add("tool-prefix=" + Path.Combine(androidToolchain, "bin", androidToolPrefixes[abi]));
+
+ string triple = GetAndroidTriple(abi);
+ aotOptions.Add ($"mtriple={triple}");
+ }
+
+ aotOptions.Add($"outfile={outputFilePath}");
+
+ var extraAotOptions = (string[]) ProjectSettings.GetSetting("mono/export/aot/extra_aot_options");
+ var extraOptimizerOptions = (string[]) ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options");
+
+ if (extraAotOptions.Length > 0)
+ aotOptions.AddRange(extraAotOptions);
+
+ if (extraOptimizerOptions.Length > 0)
+ optimizerOptions.AddRange(extraOptimizerOptions);
+
+ var compilerArgs = new List<string>();
+
+ if (isDebug)
+ compilerArgs.Add("--debug"); // Required for --aot=soft-debug
+
+ compilerArgs.Add(aotOptions.Count > 0 ? $"--aot={OptionsToString(aotOptions)}" : "--aot");
+
+ if (optimizerOptions.Count > 0)
+ compilerArgs.Add($"-O={OptionsToString(optimizerOptions)}");
+
+ compilerArgs.Add(ProjectSettings.GlobalizePath(assemblyPath));
+
+ // 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));
+ }
+
+ using (var process = new Process())
+ {
+ process.StartInfo = new ProcessStartInfo(compilerCommand, CmdLineArgsToString(compilerArgs))
+ {
+ UseShellExecute = false
+ };
+
+ string platformBclDir = DeterminePlatformBclDir(platform);
+ process.StartInfo.EnvironmentVariables.Add("MONO_PATH", string.IsNullOrEmpty(platformBclDir) ?
+ typeof(object).Assembly.Location.GetBaseDir() :
+ platformBclDir);
+
+ Console.WriteLine($"Running: \"{process.StartInfo.FileName}\" {process.StartInfo.Arguments}");
+
+ if (!process.Start())
+ throw new Exception("Failed to start process for Mono AOT compiler");
+
+ process.WaitForExit();
+
+ if (process.ExitCode != 0)
+ throw new Exception($"Mono AOT compiler exited with error code: {process.ExitCode}");
+
+ if (!System.IO.File.Exists(outputFilePath))
+ throw new Exception("Mono AOT compiler finished successfully but the output file is missing");
+ }
+ }
+
+ private static string DetermineMonoCrossDirName(string platform, IReadOnlyDictionary<string, string> data)
+ {
+ switch (platform)
+ {
+ case OS.Platforms.Windows:
+ case OS.Platforms.UWP:
+ {
+ string arch = data["bits"] == "64" ? "x86_64" : "i686";
+ return $"windows-{arch}";
+ }
+ case OS.Platforms.OSX:
+ {
+ string arch = "x86_64";
+ return $"{platform}-{arch}";
+ }
+ case OS.Platforms.X11:
+ case OS.Platforms.Server:
+ {
+ string arch = data["bits"] == "64" ? "x86_64" : "i686";
+ return $"linux-{arch}";
+ }
+ case OS.Platforms.Haiku:
+ {
+ string arch = data["bits"] == "64" ? "x86_64" : "i686";
+ return $"{platform}-{arch}";
+ }
+ case OS.Platforms.Android:
+ {
+ string abi = data["abi"];
+ return $"{platform}-{abi}";
+ }
+ case OS.Platforms.HTML5:
+ return "wasm-wasm32";
+ default:
+ throw new NotSupportedException();
+ }
+ }
+
+ private static string DetermineToolPrefix(string monoCrossBin)
+ {
+ string exeExt = OS.IsWindows ? ".exe" : string.Empty;
+
+ if (System.IO.File.Exists(Path.Combine(monoCrossBin, $"mono{exeExt}")))
+ return string.Empty;
+
+ if (System.IO.File.Exists(Path.Combine(monoCrossBin, $"mono-sgen{exeExt}" + exeExt)))
+ return string.Empty;
+
+ var files = new DirectoryInfo(monoCrossBin).GetFiles($"*mono{exeExt}" + exeExt, SearchOption.TopDirectoryOnly);
+ if (files.Length > 0)
+ {
+ string fileName = files[0].Name;
+ return fileName.Substring(0, fileName.Length - $"mono{exeExt}".Length);
+ }
+
+ files = new DirectoryInfo(monoCrossBin).GetFiles($"*mono-sgen{exeExt}" + exeExt, SearchOption.TopDirectoryOnly);
+ if (files.Length > 0)
+ {
+ string fileName = files[0].Name;
+ return fileName.Substring(0, fileName.Length - $"mono-sgen{exeExt}".Length);
+ }
+
+ throw new FileNotFoundException($"Cannot find the mono runtime executable in {monoCrossBin}");
+ }
+
+ private static IEnumerable<string> GetEnabledAndroidAbis(string[] features)
+ {
+ var androidAbis = new[]
+ {
+ "armeabi-v7a",
+ "arm64-v8a",
+ "x86",
+ "x86_64"
+ };
+
+ return androidAbis.Where(features.Contains);
+ }
+
+ private static string GetAndroidTriple(string abi)
+ {
+ var abiArchs = new Dictionary<string, string>
+ {
+ ["armeabi-v7a"] = "armv7",
+ ["arm64-v8a"] = "aarch64-v8a",
+ ["x86"] = "i686",
+ ["x86_64"] = "x86_64"
+ };
+
+ string arch = abiArchs[abi];
+
+ return $"{arch}-linux-android";
}
private static bool PlatformHasTemplateDir(string platform)
@@ -194,6 +561,43 @@ namespace GodotTools.Export
return null;
}
+ private static string DeterminePlatformBclDir(string platform)
+ {
+ string templatesDir = Internal.FullTemplatesDir;
+ 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")))
+ platformBclDir = null; // Use the one we're running on
+ }
+
+ return platformBclDir;
+ }
+
+ private static string DeterminePlatformBclProfile(string platform)
+ {
+ switch (platform)
+ {
+ case OS.Platforms.Windows:
+ case OS.Platforms.UWP:
+ case OS.Platforms.OSX:
+ case OS.Platforms.X11:
+ case OS.Platforms.Server:
+ case OS.Platforms.Haiku:
+ return "net_4_x";
+ case OS.Platforms.Android:
+ return "monodroid";
+ case OS.Platforms.HTML5:
+ return "wasm";
+ default:
+ throw new NotSupportedException();
+ }
+ }
+
private static string DataDirName
{
get
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 3ef23761a7..2a5d3de126 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -416,7 +416,7 @@ namespace GodotTools
string settingsHintStr = "Disabled";
- if (OS.IsWindows())
+ if (OS.IsWindows)
{
settingsHintStr += $",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" +
$",Visual Studio Code:{(int) ExternalEditorId.VsCode}";
@@ -427,7 +427,7 @@ namespace GodotTools
$",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" +
$",Visual Studio Code:{(int) ExternalEditorId.VsCode}";
}
- else if (OS.IsUnix())
+ else if (OS.IsUnixLike())
{
settingsHintStr += $",MonoDevelop:{(int) ExternalEditorId.MonoDevelop}" +
$",Visual Studio Code:{(int) ExternalEditorId.VsCode}";
@@ -444,6 +444,7 @@ namespace GodotTools
// Export plugin
var exportPlugin = new ExportPlugin();
AddExportPlugin(exportPlugin);
+ exportPlugin.RegisterExportSettings();
exportPluginWeak = WeakRef(exportPlugin);
BuildManager.Initialize();
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
index 2d9734c5ed..6026c109ad 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
@@ -107,7 +107,7 @@ namespace GodotTools.Ides.MonoDevelop
{EditorId.VisualStudioForMac, "com.microsoft.visual-studio"}
};
}
- else if (OS.IsWindows())
+ else if (OS.IsWindows)
{
ExecutableNames = new Dictionary<EditorId, string>
{
@@ -118,7 +118,7 @@ namespace GodotTools.Ides.MonoDevelop
{EditorId.MonoDevelop, "MonoDevelop.exe"}
};
}
- else if (OS.IsUnix())
+ else if (OS.IsUnixLike())
{
ExecutableNames = new Dictionary<EditorId, string>
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 836c9c11e4..de361ba844 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -52,7 +52,7 @@ namespace GodotTools.Internals
public static void ScriptEditorDebugger_ReloadScripts() => internal_ScriptEditorDebugger_ReloadScripts();
- // Internal Calls
+ #region Internal
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config);
@@ -110,5 +110,7 @@ namespace GodotTools.Internals
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_ScriptEditorDebugger_ReloadScripts();
+
+ #endregion
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
index f0cb8200e5..21ee85f2a9 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
@@ -55,7 +55,7 @@ namespace GodotTools.Utils
return name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
}
- public static bool IsWindows() => IsOS(Names.Windows);
+ public static bool IsWindows => IsOS(Names.Windows);
public static bool IsOSX => IsOS(Names.OSX);
@@ -72,23 +72,23 @@ namespace GodotTools.Utils
public static bool IsHTML5 => IsOS(Names.HTML5);
private static bool? _isUnixCache;
- private static readonly string[] UnixPlatforms = {Names.OSX, Names.X11, Names.Server, Names.Haiku, Names.Android};
+ private static readonly string[] UnixLikePlatforms = {Names.OSX, Names.X11, Names.Server, Names.Haiku, Names.Android};
- public static bool IsUnix()
+ public static bool IsUnixLike()
{
if (_isUnixCache.HasValue)
return _isUnixCache.Value;
string osName = GetPlatformName();
- _isUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase));
+ _isUnixCache = UnixLikePlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase));
return _isUnixCache.Value;
}
- public static char PathSep => IsWindows() ? ';' : ':';
+ public static char PathSep => IsWindows ? ';' : ':';
public static string PathWhich(string name)
{
- string[] windowsExts = IsWindows() ? Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) : null;
+ string[] windowsExts = IsWindows ? Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) : null;
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
var searchDirs = new List<string>();
@@ -102,7 +102,7 @@ namespace GodotTools.Utils
{
string path = Path.Combine(dir, name);
- if (IsWindows() && windowsExts != null)
+ if (IsWindows && windowsExts != null)
{
foreach (var extension in windowsExts)
{
@@ -124,12 +124,14 @@ namespace GodotTools.Utils
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));
}
- ProcessStartInfo startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments))
+ var startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments))
{
RedirectStandardOutput = true,
RedirectStandardError = true,