Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Mono/C# project exporting #15615

Closed
mhilbrunner opened this Issue Jan 12, 2018 · 21 comments

Comments

Projects
None yet
9 participants
@mhilbrunner
Copy link
Member

mhilbrunner commented Jan 12, 2018

Known issue, but it seems we have no issue yet to track it, if I haven't missed something.

@mhilbrunner mhilbrunner added this to the 3.0 milestone Jan 12, 2018

@NathanWarden

This comment has been minimized.

Copy link
Contributor

NathanWarden commented Jan 12, 2018

Thanks, I was wondering if there was an issue for it too, but never found one either :)

@akien-mga

This comment has been minimized.

Copy link
Member

akien-mga commented Jan 18, 2018

Sadly it's too late for 3.0 now 😿

So 3.0 will be released with Mono export support, which will be added in a later maintenance release (likely 3.0.1, and of course 3.1 too), so C# users won't be able to deploy to production with 3.0.
3.0.1 should arrive faster than it takes to make a production-ready game though, so it's not too bad..

@akien-mga akien-mga modified the milestones: 3.0, 3.1 Jan 18, 2018

@mhilbrunner

This comment has been minimized.

Copy link
Member Author

mhilbrunner commented Jan 19, 2018

Yeah, but will make some things like doing Game Jams with Godot 3.0 in C# hard. Let's hope for a fast 3.0.1 arrival :)

@akien-mga akien-mga changed the title Mono/C# exporting does not yet work Implement Mono/C# project exporting Jan 22, 2018

@bruvzg

This comment has been minimized.

Copy link
Contributor

bruvzg commented Jan 22, 2018

Copy/paste from #15972 with some updates.


mono_set_dirs works fine with local mscorlib.dll and assemblies.

Tested on:

  • macOS 10.13.2 (17C205),
  • Debian GNU/Linux buster/sid (4.14.0-3-amd64),
  • Windows 10 (1709 build 16299.192)

Mono 5.4.1.7 (2017-06/e66d9abbb27), with DLLs from macOS Mono installation on all platforms.

Editor runs with mscorlib.dll alone, precompiled Mono "Hello world" project runs with mscorlib.dll and some of System*.dll (see "File tree" below).

File tree

mono_lib

Patch (Updated for Windows version)
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 6c07c90f7..e27cd03d8 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -155,25 +155,7 @@ void GDMono::initialize() {
 	mono_trace_set_printerr_handler(gdmono_MonoPrintCallback);
 #endif
 
-#ifdef WINDOWS_ENABLED
-	mono_reg_info = MonoRegUtils::find_mono();
-
-	CharString assembly_dir;
-	CharString config_dir;
-
-	if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
-		assembly_dir = mono_reg_info.assembly_dir.utf8();
-	}
-
-	if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
-		config_dir = mono_reg_info.config_dir.utf8();
-	}
-
-	mono_set_dirs(assembly_dir.length() ? assembly_dir.get_data() : NULL,
-			config_dir.length() ? config_dir.get_data() : NULL);
-#else
-	mono_set_dirs(NULL, NULL);
-#endif
+	mono_set_dirs(OS::get_singleton()->get_executable_path().get_base_dir().utf8().get_data(), OS::get_singleton()->get_executable_path().get_base_dir().utf8().get_data());
 
 	GDMonoAssembly::initialize();
 

Mono embedding documentation:
http://www.mono-project.com/docs/advanced/embedding/#configuring-the-runtime

For some reason static Mono support is disabled on Windows (in modules/mono/config.py). Mono distribution for Windows provides static library: libmono-static-sgen.lib. Why?

@neikeq

This comment has been minimized.

Copy link
Member

neikeq commented Jan 22, 2018

The ideal would be to export mscorlib in the PCK file, but if it proves not to be possible (it seems mscorlib is hard-coded to be loaded from the assemblies path), then that will be the only option.

For some reason static Mono support is disabled on Windows (in modules/mono/config.py). Mono distribution for Windows provides static library: libmono-static-sgen.lib. Why?

You can't link mono statically on Windows.

@bruvzg

This comment has been minimized.

Copy link
Contributor

bruvzg commented Jan 22, 2018

There is preload hook in mscorlib loading code assembly.c#L3479 and it is triggering GDMonoAssembly::_preload_hook gd_mono_assembly.cpp#L94.

@NathanWarden

This comment has been minimized.

Copy link
Contributor

NathanWarden commented Jan 22, 2018

I think whatever is the properly documented and most straightforward way to do it makes the most sense.

The wording isn't quite clear, but it looks like Apple might need to code sign the assemblies, which wouldn't be possible with a pck file, but I may be reading that wrong. Read the third paragraph in the section named "App code signing".
https://www.apple.com/business/docs/iOS_Security_Guide.pdf?platform=hootsuite

If not including it in the pack file poses a problem down the road it could always be changed in the future.

My opinion is to just leave it as a folder of assemblies for now.

I'm not sure if it's relevant, but while Unity packs up resources into packed files it doesn't pack up the DLLs with the rest of the assets either. The assemblies are loosely stored in their own directory. I'm going to probably take a look and see how other game engines do this as well. I'm going to make a wild guess that they all do it this same way :)

@bruvzg

This comment has been minimized.

Copy link
Contributor

bruvzg commented Jan 22, 2018

IIRC Apple disallow loading any code at runtime, Xamarin.iOS precompile all assemblies into static native libraries, with tons of limitations.

@redxdev

This comment has been minimized.

Copy link

redxdev commented Jan 22, 2018

Mono supports AOT compilation of assemblies. Xamarin.iOS uses that afaik, but of course testing will need to be done, and I don't know what kind of limitations there are with said feature.

@bruvzg

This comment has been minimized.

@bruvzg

This comment has been minimized.

Copy link
Contributor

bruvzg commented Jan 23, 2018

Here's POC for loading mscorlib.dll and System*.dll form memory buffer inside preload hook (buffer is filled from hardcoded files), and it's probably overcomplicated but working.

@bruvzg

This comment has been minimized.

Copy link
Contributor

bruvzg commented Jan 23, 2018

Better and more compact variant for loading mscorlib.dll and all assemblies form res://

diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index ba56ed6..caa5799 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -96,6 +96,7 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
 	(void)user_data; // UNUSED
 
 	if (search_dirs.empty()) {
+		search_dirs.push_back("res://");
 #ifdef TOOLS_DOMAIN
 		search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
 #endif
@@ -116,14 +117,38 @@ MonoAssembly *GDMonoAssembly::_preload_hook(MonoAssemblyName *aname, char **asse
 		}
 	}
 
+	String name = mono_assembly_name_get_name(aname);
+
+	bool has_extension = name.ends_with(".dll");
+	name = has_extension ? name.get_basename() : name;
+	if (name == "mscorlib") {
+		if (FileAccess::exists("res://mscorlib.dll")) {
+			OS::get_singleton()->print(String("Mono: Preloading buildin \"mscorlib\"\n").utf8());
+			MonoAssembly *assembly = _load_assembly_from("mscorlib", "res://mscorlib.dll");
+			if (assembly != NULL) {
+				return assembly;
+			}
+		} else {
+			OS::get_singleton()->print(String("Mono: No buildin \"mscorlib\" found\n").utf8());
+		}
+	}
+
 	return NULL;
 }
 
 MonoAssembly *GDMonoAssembly::_load_assembly_from(const String &p_name, const String &p_path) {
 
-	GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
-
 	MonoDomain *domain = mono_domain_get();
+	GDMonoAssembly **stored_assembly = GDMono::get_singleton()->get_loaded_assembly(p_name);
+
+	if (stored_assembly != NULL) {
+		if (OS::get_singleton()->is_stdout_verbose())
+			OS::get_singleton()->print(String("Mono: Assembly " + p_name + " already loaded\n").utf8());
+
+		return (*stored_assembly)->get_assembly();
+	}
+
+	GDMonoAssembly *assembly = memnew(GDMonoAssembly(p_name, p_path));
 
 	Error err = assembly->load(domain);
@NathanWarden

This comment has been minimized.

Copy link
Contributor

NathanWarden commented Jan 23, 2018

@bruvzg You should make a PR of your work :)

@Benjamin-Dobell

This comment has been minimized.

Copy link
Contributor

Benjamin-Dobell commented Feb 2, 2018

Unity has IL2CPP -> emscripten, what's the strategy for getting C# scripts working on the web?

@bruvzg

This comment has been minimized.

Copy link
Contributor

bruvzg commented Feb 2, 2018

There are some IL > LLVM > WASM experiments (Mono and WebAssembly), but it's only proof-of-concept and not ready for production.

Calinou referenced this issue Feb 8, 2018

Bump version to 3.1-dev
On the road again!
@Ertain

This comment has been minimized.

Copy link
Contributor

Ertain commented Mar 30, 2018

Has there been any progression with exporting a project with a C# assembly? For example, if I have a project with some GDscripts and C# scripts, along with an assembly, I can't export it properly. When I try to run the game, it gives an error about the C# script.

@neikeq

This comment has been minimized.

Copy link
Member

neikeq commented Mar 30, 2018

@Ertain The logic is already there on the export plugin side, but the two missing points I mention in #16920 are holding it for now. Sadly, @reduz was pretty busy preparing for GDC, so we couldn't talk this yet.

However, if you are willing to do some manual work, you can already export your game (Export PCK-only is not yet working though). You only need to replace the exported binary with one you build yourself with the mono module enabled after exporting.

@Ertain

This comment has been minimized.

Copy link
Contributor

Ertain commented Mar 30, 2018

@neikeq Do you mean exporting in a zip archive? I've tried that; it doesn't work. When I export a simple project (that consists of a GDscript, a C# script, and a *.DLL assembly), then put in an executable in the directory (an executable that already has Mono), and try to run it, the program can't find the assembly, and it gives an error.

Edit: for completeness, here's the sample project to which I was referring. Note that this was written on Linux Mint, so it may be difficult to import.

@Ertain

This comment has been minimized.

Copy link
Contributor

Ertain commented Apr 2, 2018

@neikeq I also tried placing the "GodotSharpTools.dll" and "libmonosgen-2.0.so" files in the exported directory. But that didn't work, either.

@KellyThomas

This comment has been minimized.

Copy link
Contributor

KellyThomas commented Jul 17, 2018

@mhilbrunner Now that we have exports for desktop platforms it might be useful to update the description for this issue.

@mhilbrunner

This comment has been minimized.

Copy link
Member Author

mhilbrunner commented Jul 17, 2018

@KellyThomas Good point. I'll actually close this one for now, exporting for other platforms should probably be either milestones in a rodmap somewhere or new, seperate issues.

Here's to being able to export C# projects! Thanks for the great work, neikeq. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.