|
@@ -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
|