Wednesday, August 30, 2023

Re: godot - diff to build with C#/mono support

1-week *ping*

On Thu, Aug 24, 2023 at 11:47:06PM -0400, Thomas Frohwein wrote:
> Hi,
>
> This is a bigger diff that (finally) enables the mono module for Godot
> and allows running Godot projects with C# instead of Godot's own
> GDScript language in the editor. As this has been a bigger endeavor,
> I'm structuring this email a little more.
>
> Background
> ----------
>
> Upstream has a convoluted process for building Godot with mono [1],
> basically in order to generate the C# glue. This process never
> worked in ports tree, in part because a full godot binary needs to be
> built first, then invoked with --generate-mono-glue which seems to
> require openting an X11 window as part of the process.
>
> Methods
> -------
>
> I managed to build godot with mono module enabled by using pre-generated
> mono glue code (see distfiles) that is plugged in. The reason why
> upstream doesn't provide the generated code is likely that they want to
> allow for systems with different mono and/or dotnet versions which
> might generate different results.
>
> This also needs devel/msbuild, and a number of patches to teach Godot
> where to find the files that it expects to be relative to its binary
> which might be in any user directory.
>
> Results
> -------
>
> The port becomes a bit more complex. If built with mono, both bin/godot
> and bin/godot-tools need to find the C# assemblies (mostly in the
> GodotSharp directory) in order to launch. Therefore, I split the
> assemblies out into a third package godot-sharp, meant to supply those
> dependencies to -main and -tools.
>
> Non-C# projects including commercially distributed games based on
> GDScript should continue to run as before (tested by me with Brotato).
> I haven't found an example of a commercial game using C# with Godot to
> test this with.
>
> The editor invokes msbuild for C# projects before running them. I
> tested this with 2 of the demo projects for Godot 3.5 from [2],
> "Dodge the Creeps with C#" and "2.5D Demo with C#". They work, but a
> 1-line modification of a file in the user's nuget directory is needed
> which I added to README-tools. This is likely because
> TargetFrameworkFallbackSearchPaths isn't supported by our mono or
> msbuild at this point. With this small modification, I can build and
> run the Godot C# demo projects as expected.
>
> The addition of the library suffix for libmonosgen-2.0 in
> mono_configure.py is a bit fragile, as it may need manual updating
> should there be a bump in the mono library. I haven't figured out a
> better approach so far.
>
> Summary
> -------
>
> This adds C#/mono support, but this will likely some more refinement.
>
> This effort deals with some weirdnesses that arise from how Godot games
> are generally distributed, which is similar to other portable game
> development and distribution frameworks/engines, like FNA, MonoGame,
> Unity, LibGDX etc. The core game logic and the assets exist in platform
> independent code. The developer ships the game with that plus all the
> platform-specific native binaries and libraries together. It seems that
> all the officially supported operating systems (Windows, Mac, Linux)
> adhere to being able to run such binaries and libraries even if they
> were compiled 10 years ago.
>
> As we don't do that on OpenBSD, things like file locations need to be
> adjusted for a system install, as opposed to the locally contained
> approach. For an eventual godot4 port, it looks like mono won't do
> anymore and an eventual port of dotnet would be needed.
>
> In the future, we might be able with some more refinement to get rid of
> at least some of the files in pkg/PLIST-sharp by pointing godot to the
> correct location of the mono and msbuild files.
>
> The best way forward is probably to try to work with upstream on a new
> install and runtime approach that uses binary and libraries installed in
> conventional system directories. I have plans to propose such an scons
> modality upstream after familiarizing myself with where Godot4 is at now.
>
> comments and ok's are welcome; given the complexity there might still
> be benefit in adding this early to the port and working out some
> refinements in-tree, with also more opportunity to hear of
> unanticipated bugs.
>
> [1] https://docs.godotengine.org/en/3.5/development/compiling/compiling_with_mono.html
> [2] https://github.com/godotengine/godot-demo-projects

Index: Makefile
===================================================================
RCS file: /cvs/ports/games/godot/Makefile,v
retrieving revision 1.49
diff -u -p -r1.49 Makefile
--- Makefile 14 Aug 2023 12:40:50 -0000 1.49
+++ Makefile 25 Aug 2023 02:44:30 -0000
@@ -2,12 +2,19 @@ BROKEN-powerpc = fails at runtime, the U

COMMENT-main = 2D and 3D game engine
COMMENT-tools= 2D and 3D game engine (with tools)
+COMMENT-sharp= glue for mono/C# module of Godot
+
+ONLY_FOR_ARCHS = ${MONO_ARCHS}
+
+# WXNEEDED and NOBTCFI needed for module_mono_enabled=yes
+USE_WXNEEDED = Yes
+USE_NOBTCFI = Yes

V = 3.5.2
GODOTSTEAM_V = v3.20
DISTNAME = godot-${V}-stable
PKGNAME = godot-${V}
-REVISION = 5
+REVISION = 6

CATEGORIES = games

@@ -18,7 +25,7 @@ MAINTAINER = Omar Polo <op@openbsd.org>
# MIT
PERMIT_PACKAGE = Yes

-MULTI_PACKAGES = -main -tools
+MULTI_PACKAGES = -main -tools -sharp

WANTLIB += ${COMPILER_LIBCXX} BulletCollision BulletDynamics BulletSoftBody
WANTLIB += GL LinearMath X11 X11-xcb Xau Xcursor Xdmcp Xext Xfixes
@@ -28,23 +35,30 @@ WANTLIB += opusfile pcre2-32 sharpyuv sn
WANTLIB += usbhid vorbis vorbisfile vpx webp xcb xcb-dri2 xcb-glx
WANTLIB += zstd

-WANTLIB-main = ${WANTLIB} Xss
-WANTLIB-tools = ${WANTLIB}
+# mono-native is dlopened when using the mono/C#/godot-sharp parts
+WANTLIB-main = ${WANTLIB} Xss mono-native
+WANTLIB-tools = ${WANTLIB} mono-native
+
+# needed by files in share/godot/GodotSharp/Mono/lib/
+WANTLIB-sharp = execinfo m pthread z

# C++14
COMPILER = base-clang ports-gcc

MASTER_SITES = https://downloads.tuxfamily.org/godotengine/${V}/
MASTER_SITES0 = https://github.com/CoaguCo-Industries/GodotSteam/archive/refs/tags/
+MASTER_SITES1
= https://thfr.info/distfiles/
DISTFILES = ${DISTNAME}${EXTRACT_SUFX} \
- ${GODOTSTEAM_V}.tar.gz:0
+ ${GODOTSTEAM_V}.tar.gz:0 \
+ godot-${V}-mono-glue.tar.gz:1 \
+ godot-${V}-nuget-packages.tar.xz:1
EXTRACT_SUFX = .tar.xz
DIST_SUBDIR = ${PKGNAME}

MODULES = devel/scons

-# Building with module_mono_enabled requires msbuild and to fix the
-# sharedlib_ext in modules/mono/config.py to '.so.1.0'
+MODSCONS_ENV += HOME=${NUGETHOME} \
+ NUGET_PACKAGES=${NUGETHOME}/packages
MODSCONS_FLAGS = CC="${CC}" \
CXX="${CXX}" \
CFLAGS="${CFLAGS}" \
@@ -64,6 +78,9 @@ MODSCONS_FLAGS = CC="${CC}" \
builtin_zlib=no \
builtin_zstd=no \
custom_modules=${WRKSRC}/godotsteam \
+ module_mono_enabled=yes \
+ mono_prefix=${LOCALBASE} \
+ mono_static=yes \
progress=no \
pulseaudio=no \
verbose=yes \
@@ -80,6 +97,8 @@ MODSCONS_FLAGS += builtin_freetype=yes
MODSCONS_FLAGS += module_raycast_enabled="false"
.endif

+BUILD_DEPENDS = devel/msbuild
+
LIB_DEPENDS = archivers/zstd \
audio/libvorbis \
audio/musepack \
@@ -89,12 +108,18 @@ LIB_DEPENDS = archivers/zstd \
devel/pcre2 \
games/goldberg_emulator \
graphics/libwebp \
+ lang/mono \
multimedia/libtheora \
multimedia/libvpx \
net/enet \
security/polarssl
+LIB_DEPENDS-sharp =

-RUN_DEPENDS-tools = devel/desktop-file-utils
+RUN_DEPENDS-sharp =
+RUN_DEPENDS-main = games/godot,-sharp
+RUN_DEPENDS-tools = devel/desktop-file-utils \
+ devel/msbuild \
+ games/godot,-sharp

DEBUG_PACKAGES = ${BUILD_PACKAGES}
NO_TEST = Yes
@@ -116,30 +141,55 @@ WANTLIB += atomic
.endif

CFLAGS += -I${LOCALBASE}/include/goldberg_emulator
+CXXFLAGS += -I${LOCALBASE}/include/goldberg_emulator
+
+GLUEDIR = ${WRKDIR}/godot-${V}-mono-glue
+NUGETHOME = ${WRKDIR}/nugethome
+SUBST_VARS += NUGETHOME

-# copy over to allow patching GodotSteam
post-extract:
+ @# install backends from FILESDIR
cp -R ${FILESDIR}/sndio ${WRKDIST}/drivers
cp ${FILESDIR}/ujoy/joypad_openbsd.{cpp,h} \
${WRKDIST}/platform/x11/
+ @# move GodotSteam into WRKSRC to allow patching
mv ${WRKDIR}/GodotSteam-${GODOTSTEAM_V:S/v//} ${WRKSRC}/godotsteam
+ @# set up nuget package location
+ mkdir -p ${NUGETHOME}
+ mv ${WRKDIR}/godot-${V}-nuget-packages ${NUGETHOME}/packages
+ @# move pre-generated mono glue into place
+ mv ${GLUEDIR}/mono_glue.gen.cpp ${WRKSRC}/modules/mono/glue/
+ mv ${GLUEDIR}/GodotSharp/GodotSharp/Generated \
+ ${WRKSRC}/modules/mono/glue/GodotSharp/GodotSharp/
+ mv ${GLUEDIR}/GodotSharp/GodotSharpEditor/Generated \
+ ${WRKSRC}/modules/mono/glue/GodotSharp/GodotSharpEditor/

pre-configure:
${SUBST_CMD} ${WRKSRC}/misc/dist/linux/*.desktop
+ ${SUBST_CMD} ${WRKSRC}/modules/mono/build_scripts/solution_builder.py
+ ${SUBST_CMD} ${WRKSRC}/modules/mono/godotsharp_dirs.cpp

do-build:
+ @# Export Template build
@${MODSCONS_BUILD_TARGET} tools=no target=release \
CXXFLAGS="${CXXFLAGS} -Wno-deprecated-register -DSUSPEND_SCREENSAVER=1" \
LINKFLAGS="${LDFLAGS} -lintl -lmpcdec -lusbhid -lXss"
+ @# Editor build
@${MODSCONS_BUILD_TARGET} tools=yes target=release_debug \
CXXFLAGS="${CXXFLAGS} -Wno-deprecated-register" \
LINKFLAGS="${LDFLAGS} -lintl -lmpcdec -lusbhid"

do-install:
- ${INSTALL_PROGRAM} ${WRKBUILD}/bin/godot.x11.opt.${BINSUFFIX} \
+ @# Export Template files
+ @${INSTALL_PROGRAM} ${WRKBUILD}/bin/godot.x11.opt.${BINSUFFIX}.mono \
${PREFIX}/bin/godot
- ${INSTALL_PROGRAM} ${WRKBUILD}/bin/godot.x11.opt.tools.${BINSUFFIX} \
+ @# Editor files
+ ${INSTALL_PROGRAM} ${WRKBUILD}/bin/godot.x11.opt.tools.${BINSUFFIX}.mono \
${PREFIX}/bin/godot-tools
+ @# GodotSharp files
+ ${INSTALL_DATA_DIR} ${PREFIX}/share/godot
+ cp -R ${WRKBUILD}/bin/GodotSharp ${PREFIX}/share/godot/
+ @# Rest: man pages, icons, desktop integration
${INSTALL_MAN_DIR} ${PREFIX}/man/man6
${INSTALL_MAN} ${WRKSRC}/misc/dist/linux/godot.6 \
${PREFIX}/man/man6
Index: distinfo
===================================================================
RCS file: /cvs/ports/games/godot/distinfo,v
retrieving revision 1.20
diff -u -p -r1.20 distinfo
--- distinfo 11 Aug 2023 12:36:10 -0000 1.20
+++ distinfo 25 Aug 2023 02:44:30 -0000
@@ -1,4 +1,8 @@
+SHA256 (godot-3.5.2/godot-3.5.2-mono-glue.tar.gz) = ezUneQt2JzlsQPF4FvSCcNtJiYbdb3NEY4Lvjj/gz64=
+SHA256 (godot-3.5.2/godot-3.5.2-nuget-packages.tar.xz) = RXlTjB1IgjZEP2ZJY05xoGQv8XTRKHxwPaMmiNdduQE=
SHA256 (godot-3.5.2/godot-3.5.2-stable.tar.xz) = tDodaynqJby6+86/e6CEMmGqEjfic5SUU4oiANjHvmI=
SHA256 (godot-3.5.2/v3.20.tar.gz) = FoAl3iZ+1CWwIPXwJZ9S7q3QMn//RslYT06tXkc38QU=
+SIZE (godot-3.5.2/godot-3.5.2-mono-glue.tar.gz) = 1094618
+SIZE (godot-3.5.2/godot-3.5.2-nuget-packages.tar.xz) = 55742184
SIZE (godot-3.5.2/godot-3.5.2-stable.tar.xz) = 24047432
SIZE (godot-3.5.2/v3.20.tar.gz) = 253832
Index: patches/patch-modules_mono_build_scripts_mono_configure_py
===================================================================
RCS file: patches/patch-modules_mono_build_scripts_mono_configure_py
diff -N patches/patch-modules_mono_build_scripts_mono_configure_py
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-modules_mono_build_scripts_mono_configure_py 25 Aug 2023 02:44:30 -0000
@@ -0,0 +1,51 @@
+include a suffix argument for find_file_in_dir
+use suffix argument to look for libmonosgen-2.0.so.1.0
+disable librt/libdl
+
+Index: modules/mono/build_scripts/mono_configure.py
+--- modules/mono/build_scripts/mono_configure.py.orig
++++ modules/mono/build_scripts/mono_configure.py
+@@ -31,15 +31,16 @@ def find_name_in_dir_files(directory, names, prefixes=
+ return ""
+
+
+-def find_file_in_dir(directory, names, prefixes=[""], extensions=[""]):
+- for extension in extensions:
+- if extension and not extension.startswith("."):
+- extension = "." + extension
+- for prefix in prefixes:
+- for curname in names:
+- filename = prefix + curname + extension
+- if os.path.isfile(os.path.join(directory, filename)):
+- return filename
++def find_file_in_dir(directory, names, prefixes=[""], extensions=[""], suffix=[""]):
++ for sufx in suffix:
++ for extension in extensions:
++ if extension and not extension.startswith("."):
++ extension = "." + extension
++ for prefix in prefixes:
++ for curname in names:
++ filename = prefix + curname + extension + sufx
++ if os.path.isfile(os.path.join(directory, filename)):
++ return filename
+ return ""
+
+
+@@ -335,7 +336,7 @@ def configure(env, env_mono):
+ elif is_javascript:
+ env.Append(LIBS=["m", "rt", "dl", "pthread"])
+ else:
+- env.Append(LIBS=["m", "rt", "dl", "pthread"])
++ env.Append(LIBS=["m", "pthread"])
+
+ if not mono_static:
+ mono_so_file = find_file_in_dir(
+@@ -358,7 +359,7 @@ def configure(env, env_mono):
+ tmpenv.ParseConfig("pkg-config monosgen-2 --libs-only-L")
+
+ for hint_dir in tmpenv["LIBPATH"]:
+- file_found = find_file_in_dir(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext])
++ file_found = find_file_in_dir(hint_dir, mono_lib_names, prefixes=["lib"], extensions=[sharedlib_ext], suffix=[".1.0"])
+ if file_found:
+ mono_lib_path = hint_dir
+ mono_so_file = file_found
Index: patches/patch-modules_mono_build_scripts_solution_builder_py
===================================================================
RCS file: patches/patch-modules_mono_build_scripts_solution_builder_py
diff -N patches/patch-modules_mono_build_scripts_solution_builder_py
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-modules_mono_build_scripts_solution_builder_py 25 Aug 2023 02:44:30 -0000
@@ -0,0 +1,28 @@
+point msbuild to the prepared nuget packages dir
+supply NUGETHOME as home dir
+
+Index: modules/mono/build_scripts/solution_builder.py
+--- modules/mono/build_scripts/solution_builder.py.orig
++++ modules/mono/build_scripts/solution_builder.py
+@@ -89,11 +89,10 @@ def run_command(command, args, env_override=None, name
+ def cmd_args_to_str(cmd_args):
+ return " ".join([arg if not " " in arg else '"%s"' % arg for arg in cmd_args])
+
+- args = [command] + args
++ args = ["env", "HOME=${NUGETHOME}"] + [command] + args
+
+ if name is None:
+ name = os.path.basename(command)
+-
+ if verbose:
+ print("Running '%s': %s" % (name, cmd_args_to_str(args)))
+
+@@ -142,7 +141,7 @@ def build_solution(env, solution_path, build_config, e
+
+ # Build solution
+
+- msbuild_args += [solution_path, "/restore", "/t:Build", "/p:Configuration=" + build_config]
++ msbuild_args += [solution_path, "/restore", "/p:RestorePackagesPath=${NUGETHOME}/packages", "/t:Build", "/p:Configuration=" + build_config]
+ msbuild_args += extra_msbuild_args
+
+ run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name="msbuild")
Index: patches/patch-modules_mono_csharp_script_cpp
===================================================================
RCS file: patches/patch-modules_mono_csharp_script_cpp
diff -N patches/patch-modules_mono_csharp_script_cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-modules_mono_csharp_script_cpp 25 Aug 2023 02:44:30 -0000
@@ -0,0 +1,28 @@
+fix error: cannot initialize return object of type 'bool' with
+an rvalue of type 'nullptr_t'
+
+Index: modules/mono/csharp_script.cpp
+--- modules/mono/csharp_script.cpp.orig
++++ modules/mono/csharp_script.cpp
+@@ -2310,7 +2310,7 @@ bool CSharpScript::_update_exports(PlaceHolderScriptIn
+
+ GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), 0);
+
+- ERR_FAIL_NULL_V_MSG(ctor, NULL,
++ ERR_FAIL_NULL_V_MSG(ctor, false,
+ "Cannot construct temporary MonoObject because the class does not define a parameterless constructor: '" + get_path() + "'.");
+
+ MonoException *ctor_exc = NULL;
+@@ -2891,10 +2891,10 @@ bool CSharpScript::can_instance() const {
+ if (extra_cond && !script_class) {
+ if (GDMono::get_singleton()->get_project_assembly() == NULL) {
+ // The project assembly is not loaded
+- ERR_FAIL_V_MSG(NULL, "Cannot instance script because the project assembly is not loaded. Script: '" + get_path() + "'.");
++ ERR_FAIL_V_MSG(false, "Cannot instance script because the project assembly is not loaded. Script: '" + get_path() + "'.");
+ } else {
+ // The project assembly is loaded, but the class could not found
+- ERR_FAIL_V_MSG(NULL, "Cannot instance script because the class '" + name + "' could not be found. Script: '" + get_path() + "'.");
++ ERR_FAIL_V_MSG(false, "Cannot instance script because the class '" + name + "' could not be found. Script: '" + get_path() + "'.");
+ }
+ }
+
Index: patches/patch-modules_mono_glue_GodotSharp_GodotSharp_GodotSharp_csproj
===================================================================
RCS file: patches/patch-modules_mono_glue_GodotSharp_GodotSharp_GodotSharp_csproj
diff -N patches/patch-modules_mono_glue_GodotSharp_GodotSharp_GodotSharp_csproj
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-modules_mono_glue_GodotSharp_GodotSharp_GodotSharp_csproj 25 Aug 2023 02:44:30 -0000
@@ -0,0 +1,13 @@
+needs LangVersion 7.1 for 'default literal'; otherwise error CS8107
+
+Index: modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+--- modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj.orig
++++ modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+@@ -7,6 +7,7 @@
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile>
+ <EnableDefaultItems>false</EnableDefaultItems>
++ <LangVersion>7.1</LangVersion>
+ </PropertyGroup>
+ <PropertyGroup>
+ <DefineConstants>$(DefineConstants);GODOT</DefineConstants>
Index: patches/patch-modules_mono_godotsharp_dirs_cpp
===================================================================
RCS file: patches/patch-modules_mono_godotsharp_dirs_cpp
diff -N patches/patch-modules_mono_godotsharp_dirs_cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-modules_mono_godotsharp_dirs_cpp 25 Aug 2023 02:44:30 -0000
@@ -0,0 +1,93 @@
+fix paths for our mono and godot install dirs
+make data_editor_prebuilt_api_dir() available also when !TOOLS_ENABLED
+
+Index: modules/mono/godotsharp_dirs.cpp
+--- modules/mono/godotsharp_dirs.cpp.orig
++++ modules/mono/godotsharp_dirs.cpp
+@@ -97,6 +97,8 @@ class _GodotSharpDirs { (public)
+ String res_temp_assemblies_dir;
+ String mono_user_dir;
+ String mono_logs_dir;
++ String data_editor_tools_dir;
++ String data_editor_prebuilt_api_dir;
+
+ #ifdef TOOLS_ENABLED
+ String mono_solutions_dir;
+@@ -106,8 +108,6 @@ class _GodotSharpDirs { (public)
+ String sln_filepath;
+ String csproj_filepath;
+
+- String data_editor_tools_dir;
+- String data_editor_prebuilt_api_dir;
+ #else
+ // Equivalent of res_assemblies_dir, but in the data directory rather than in 'res://'.
+ // Only defined on export templates. Used when exporting assemblies outside of PCKs.
+@@ -177,20 +177,20 @@ class _GodotSharpDirs { (public)
+

No comments:

Post a Comment