Skip to content

Commit 54ba3cf

Browse files
authored
Merge pull request #73257 from RedworkDE/net-android-support
C#: Support exporting for Android
2 parents 3fa8fad + f759cc0 commit 54ba3cf

8 files changed

Lines changed: 101 additions & 847 deletions

File tree

modules/mono/SCsub

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.cpp")
2323
if env["platform"] in ["macos", "ios"]:
2424
env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.mm")
2525
env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.m")
26-
elif env["platform"] == "android":
27-
env_mono.add_source_files(env.modules_sources, "mono_gd/android_mono_config.gen.cpp")
2826

2927
if env.editor_build:
3028
env_mono.add_source_files(env.modules_sources, "editor/*.cpp")

modules/mono/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Prior to .NET Core, we supported these: ["windows", "macos", "linuxbsd", "android", "haiku", "web", "ios"]
22
# Eventually support for each them should be added back (except Haiku if not supported by .NET Core)
3-
supported_platforms = ["windows", "macos", "linuxbsd"]
3+
supported_platforms = ["windows", "macos", "linuxbsd", "android"]
44

55

66
def can_build(env, platform):

modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using System.Collections.Generic;
44
using System.IO;
55
using System.Linq;
6+
using System.Security.Cryptography;
7+
using System.Text;
68
using GodotTools.Build;
79
using GodotTools.Core;
810
using GodotTools.Internals;
@@ -45,6 +47,17 @@ public partial class ExportPlugin : EditorExportPlugin
4547
}
4648
},
4749
{ "default_value", true }
50+
},
51+
new Godot.Collections.Dictionary()
52+
{
53+
{
54+
"option", new Godot.Collections.Dictionary()
55+
{
56+
{ "name", "dotnet/embed_build_outputs" },
57+
{ "type", (int)Variant.Type.Bool }
58+
}
59+
},
60+
{ "default_value", false }
4861
}
4962
};
5063
}
@@ -114,7 +127,7 @@ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long
114127
if (!DeterminePlatformFromFeatures(features, out string platform))
115128
throw new NotSupportedException("Target platform not supported.");
116129

117-
if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS }
130+
if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS, OS.Platforms.Android }
118131
.Contains(platform))
119132
{
120133
throw new NotImplementedException("Target platform not yet implemented.");
@@ -129,15 +142,19 @@ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long
129142
{
130143
archs.Add("x86_64");
131144
}
132-
else if (features.Contains("x86_32"))
145+
if (features.Contains("x86_32"))
133146
{
134147
archs.Add("x86_32");
135148
}
136-
else if (features.Contains("arm64"))
149+
if (features.Contains("arm64"))
137150
{
138151
archs.Add("arm64");
139152
}
140-
else if (features.Contains("universal"))
153+
if (features.Contains("arm32"))
154+
{
155+
archs.Add("arm32");
156+
}
157+
if (features.Contains("universal"))
141158
{
142159
if (platform == OS.Platforms.MacOS)
143160
{
@@ -146,6 +163,8 @@ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long
146163
}
147164
}
148165

166+
bool embedBuildResults = (bool)GetOption("dotnet/embed_build_outputs") || features.Contains("android");
167+
149168
foreach (var arch in archs)
150169
{
151170
string ridOS = DetermineRuntimeIdentifierOS(platform);
@@ -190,17 +209,44 @@ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long
190209
"Publish succeeded but project assembly not found in the output directory");
191210
}
192211

193-
// Add to the exported project shared object list.
212+
var manifest = new StringBuilder();
194213

214+
// Add to the exported project shared object list or packed resources.
195215
foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories))
196216
{
197-
AddSharedObject(file, tags: null,
198-
Path.Join(projectDataDirName,
199-
Path.GetRelativePath(publishOutputTempDir, Path.GetDirectoryName(file))));
217+
if (embedBuildResults)
218+
{
219+
var filePath = SanitizeSlashes(Path.GetRelativePath(publishOutputTempDir, file));
220+
var fileData = File.ReadAllBytes(file);
221+
var hash = Convert.ToBase64String(SHA512.HashData(fileData));
222+
223+
manifest.Append($"{filePath}\t{hash}\n");
224+
225+
AddFile($"res://.godot/mono/publish/{arch}/{filePath}", fileData, false);
226+
}
227+
else
228+
{
229+
AddSharedObject(file, tags: null,
230+
Path.Join(projectDataDirName,
231+
Path.GetRelativePath(publishOutputTempDir, Path.GetDirectoryName(file))));
232+
}
233+
}
234+
235+
if (embedBuildResults)
236+
{
237+
var fileData = Encoding.Default.GetBytes(manifest.ToString());
238+
AddFile($"res://.godot/mono/publish/{arch}/.dotnet-publish-manifest", fileData, false);
200239
}
201240
}
202241
}
203242

243+
private string SanitizeSlashes(string path)
244+
{
245+
if (Path.DirectorySeparatorChar == '\\')
246+
return path.Replace('\\', '/');
247+
return path;
248+
}
249+
204250
private string DetermineRuntimeIdentifierOS(string platform)
205251
=> OS.DotNetOSPlatformMap[platform];
206252

@@ -214,7 +260,7 @@ private string DetermineRuntimeIdentifierArch(string arch)
214260
"x86_64" => "x64",
215261
"armeabi-v7a" => "arm",
216262
"arm64-v8a" => "arm64",
217-
"armv7" => "arm",
263+
"arm32" => "arm",
218264
"arm64" => "arm64",
219265
_ => throw new ArgumentOutOfRangeException(nameof(arch), arch, "Unexpected architecture")
220266
};

modules/mono/godotsharp_dirs.cpp

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -141,19 +141,52 @@ class _GodotSharpDirs {
141141
#else // TOOLS_ENABLED
142142
String arch = Engine::get_singleton()->get_architecture_name();
143143
String appname_safe = path::get_csharp_project_name();
144-
String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
145-
if (!DirAccess::exists(data_dir_root)) {
146-
data_dir_root = exe_dir.path_join("data_Godot_" + arch);
147-
}
144+
String packed_path = "res://.godot/mono/publish/" + arch;
145+
if (DirAccess::exists(packed_path)) {
146+
// The dotnet publish data is packed in the pck/zip.
147+
String data_dir_root = OS::get_singleton()->get_cache_path().path_join("data_" + appname_safe + "_" + arch);
148+
bool has_data = false;
149+
if (!has_data) {
150+
// 1. Try to access the data directly.
151+
String global_packed = ProjectSettings::get_singleton()->globalize_path(packed_path);
152+
if (global_packed.is_absolute_path() && FileAccess::exists(global_packed.path_join(".dotnet-publish-manifest"))) {
153+
data_dir_root = global_packed;
154+
has_data = true;
155+
}
156+
}
157+
if (!has_data) {
158+
// 2. Check if the data was extracted before and is up-to-date.
159+
String packed_manifest = packed_path.path_join(".dotnet-publish-manifest");
160+
String extracted_manifest = data_dir_root.path_join(".dotnet-publish-manifest");
161+
if (FileAccess::exists(packed_manifest) && FileAccess::exists(extracted_manifest)) {
162+
if (FileAccess::get_file_as_bytes(packed_manifest) == FileAccess::get_file_as_bytes(extracted_manifest)) {
163+
has_data = true;
164+
}
165+
}
166+
}
167+
if (!has_data) {
168+
// 3. Extract the data to a temporary location to load from there.
169+
Ref<DirAccess> da = DirAccess::create_for_path(packed_path);
170+
ERR_FAIL_NULL(da);
171+
ERR_FAIL_COND(da->copy_dir(packed_path, data_dir_root) != OK);
172+
}
173+
api_assemblies_dir = data_dir_root;
174+
} else {
175+
// The dotnet publish data is in a directory next to the executable.
176+
String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
177+
if (!DirAccess::exists(data_dir_root)) {
178+
data_dir_root = exe_dir.path_join("data_Godot_" + arch);
179+
}
148180
#ifdef MACOS_ENABLED
149-
if (!DirAccess::exists(data_dir_root)) {
150-
data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch);
151-
}
152-
if (!DirAccess::exists(data_dir_root)) {
153-
data_dir_root = res_dir.path_join("data_Godot_" + arch);
154-
}
181+
if (!DirAccess::exists(data_dir_root)) {
182+
data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch);
183+
}
184+
if (!DirAccess::exists(data_dir_root)) {
185+
data_dir_root = res_dir.path_join("data_Godot_" + arch);
186+
}
155187
#endif
156-
api_assemblies_dir = data_dir_root;
188+
api_assemblies_dir = data_dir_root;
189+
}
157190
#endif
158191
}
159192

modules/mono/mono_gd/android_mono_config.h

Lines changed: 0 additions & 43 deletions
This file was deleted.

modules/mono/mono_gd/gd_mono.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@
5555

5656
// TODO mobile
5757
#if 0
58-
#ifdef ANDROID_ENABLED
59-
#include "support/android_support.h"
60-
#elif defined(IOS_ENABLED)
58+
#ifdef IOS_ENABLED
6159
#include "support/ios_support.h"
6260
#endif
6361
#endif
@@ -554,10 +552,6 @@ GDMono::~GDMono() {
554552
finalizing_scripts_domain = false;
555553
runtime_initialized = false;
556554

557-
#if defined(ANDROID_ENABLED)
558-
gdmono::android::support::cleanup();
559-
#endif
560-
561555
singleton = nullptr;
562556
}
563557

0 commit comments

Comments
 (0)