Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 136 additions & 5 deletions modules/mono/build_scripts/mono_configure.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import os
import os.path
import shutil


def is_desktop(platform):
return platform in ["windows", "macos", "linuxbsd"]

Expand All @@ -11,12 +16,138 @@ def module_supports_tools_on(platform):


def configure(env, env_mono):
# is_android = env["platform"] == "android"
# is_web = env["platform"] == "web"
# is_ios = env["platform"] == "ios"
# is_ios_sim = is_ios and env["arch"] in ["x86_32", "x86_64"]

if env.editor_build:
if not module_supports_tools_on(env["platform"]):
raise RuntimeError("This module does not currently support building for this platform for editor builds.")
env_mono.Append(CPPDEFINES=["GD_MONO_HOT_RELOAD"])

if env["platform"] == "web":
rid = get_rid(env["platform"], env["arch"])

this_script_dir = os.path.dirname(os.path.realpath(__file__))
module_dir = os.path.abspath(os.path.join(this_script_dir, os.pardir))
get_runtime_pack_project_path = os.path.join(module_dir, "runtime", "GetRuntimePack")

mono_runtime_copy_path = os.path.join(get_runtime_pack_project_path, "bin", "mono_runtime")
try:
shutil.rmtree(mono_runtime_copy_path)
except FileNotFoundError:
# It's fine if this directory doesn't exist.
pass

import subprocess
exit_code = subprocess.call(
[
"dotnet", "publish",
get_runtime_pack_project_path,
"-r", "browser-wasm",
"--self-contained",
"-c", "Release",
]
)
if exit_code != 0:
raise RuntimeError("Couldn't retrieve Mono runtime pack for '" + rid + "'.")

with open(os.path.join(get_runtime_pack_project_path, "bin", rid, "runtime-pack-dir.txt")) as f:
mono_runtime_path = os.path.join(f.readline().strip(), "runtimes", rid, "native")
shutil.copytree(mono_runtime_path, mono_runtime_copy_path)
mono_runtime_path = mono_runtime_copy_path

# The default Mono runtime uses WASM exceptions.
env.Append(CCFLAGS=["-fwasm-exceptions"])
env.Append(LINKFLAGS=["-fwasm-exceptions"])

env_mono.Prepend(CPPPATH=os.path.join(mono_runtime_path, "include", "mono-2.0"))

env_mono.Append(LIBPATH=[mono_runtime_path])

# Mono libraries.
add_mono_library(env, "libmonosgen-2.0.a", mono_runtime_path)
add_mono_library(env, "libmono-wasm-eh-wasm.a", mono_runtime_path)
add_mono_library(env, "libmono-ee-interp.a", mono_runtime_path)
add_mono_library(env, "libSystem.Native.a", mono_runtime_path)
add_mono_library(env, "libSystem.Globalization.Native.a", mono_runtime_path)
add_mono_library(env, "libSystem.IO.Compression.Native.a", mono_runtime_path)
add_mono_library(env, "wasm-bundled-timezones.a", mono_runtime_path)
add_mono_library(env, "libmono-wasm-simd.a", mono_runtime_path)
add_mono_library(env, "libmono-icall-table.a", mono_runtime_path)
add_mono_library(env, "libicuuc.a", mono_runtime_path)
add_mono_library(env, "libicudata.a", mono_runtime_path)
add_mono_library(env, "libicui18n.a", mono_runtime_path)
add_mono_library(env, "libz.a", mono_runtime_path)

# Mono components.
add_mono_component(env, "debugger", mono_runtime_path)
add_mono_component(env, "diagnostics_tracing", mono_runtime_path)
add_mono_component(env, "hot_reload", mono_runtime_path)
add_mono_component(env, "marshal-ilgen", mono_runtime_path)

# WASM additional sources.
env_thirdparty = env_mono.Clone()
env_thirdparty.disable_warnings()
env_thirdparty.Append(CPPDEFINES=["GEN_PINVOKE"])
if env["dlink_enabled"]:
env_thirdparty.Append(CPPDEFINES=["WASM_SUPPORTS_DLOPEN"])
env_thirdparty.Prepend(CPPPATH=os.path.join(mono_runtime_path, "include", "wasm"))
get_runtime_pack_project_includes = os.path.join(get_runtime_pack_project_path, "obj", "Release", "net9.0", rid, "wasm", "for-publish")
env_thirdparty.Prepend(CPPPATH=os.path.join(get_runtime_pack_project_includes))
env_thirdparty.add_source_files(
env.modules_sources,
[
os.path.join(mono_runtime_path, "src", "runtime.c"),
os.path.join(mono_runtime_path, "src", "driver.c"),
os.path.join(mono_runtime_path, "src", "corebindings.c"),
os.path.join(mono_runtime_path, "src", "pinvoke.c"),
],
)

env.AddJSLibraries([os.path.join(mono_runtime_path, "src", "es6", "dotnet.es6.lib.js")])


"""
Get the .NET runtime identifier for the given Godot platform and architecture names.
"""
def get_rid(platform: str, arch: str):
# Web runtime identifier is always browser-wasm.
if platform == "web":
return "browser-wasm"

# Platform renames.
if platform == "macos":
platform = "osx"
elif platform == "linuxbsd":
platform = "linux"

# Arch renames.
if arch == "x86_32":
arch = "x86"
elif arch == "x86_64":
arch = "x64"
elif arch == "arm32":
arch = "arm"

return platform + "-" + arch


"""
Link mono components statically (use the stubs to load them dynamically at runtime).
See: https://github.com/dotnet/runtime/blob/main/docs/design/mono/components.md
"""
def add_mono_component(env, name: str, mono_runtime_path: str, is_stub: bool = False):
stub_suffix = "-stub" if is_stub else ""
component_filename = "libmono-component-" + name + stub_suffix + "-static.a"
add_mono_library(env, component_filename, mono_runtime_path)


"""
Link mono library archive (.a) statically.
"""
def add_mono_library(env, filename: str, mono_runtime_path: str):
assert(filename.endswith(".a"))
env.Append(
LINKFLAGS=[
"-Wl,-whole-archive",
os.path.join(mono_runtime_path, filename),
"-Wl,-no-whole-archive",
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@
<Link>Sdk\SdkPackageVersions.props</Link>
</None>
<None Include="Sdk\Android.props" Pack="true" PackagePath="Sdk" />
<None Include="Sdk\Android.targets" Pack="true" PackagePath="Sdk" />
<None Include="Sdk\iOSNativeAOT.props" Pack="true" PackagePath="Sdk" />
<None Include="Sdk\iOSNativeAOT.targets" Pack="true" PackagePath="Sdk" />
<None Include="Sdk\Browser.props" Pack="true" PackagePath="Sdk" />
<None Include="Sdk\Browser.targets" Pack="true" PackagePath="Sdk" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
<Project>
<PropertyGroup>
<UseMonoRuntime Condition=" '$(UseMonoRuntime)' == '' and '$(PublishAot)' != 'true' ">true</UseMonoRuntime>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<UseMonoRuntime Condition=" '$(UseMonoRuntime)' == '' and '$(PublishAot)' != 'true' ">true</UseMonoRuntime>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<Project>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project>
<PropertyGroup>
<UseMonoRuntime Condition=" '$(UseMonoRuntime)' == '' and '$(PublishAot)' != 'true' ">true</UseMonoRuntime>

<!-- Default to relinking in ExportDebug and ExportRelease configurations. -->
<WasmBuildNative Condition=" '$(WasmBuildNative)' == '' and '$(Configuration)' == 'ExportDebug' ">true</WasmBuildNative>
<WasmBuildNative Condition=" '$(WasmBuildNative)' == '' and '$(Configuration)' == 'ExportRelease' ">true</WasmBuildNative>
</PropertyGroup>

<!-- Configuration for the Mono WASM runtime. Must match the features enabled on GetRuntimePack.csproj. -->
<PropertyGroup>
<WasmEnableThreads>false</WasmEnableThreads>
<WasmEnableExceptionHandling>true</WasmEnableExceptionHandling>
<WasmEnableSIMD>true</WasmEnableSIMD>
</PropertyGroup>

<!-- Godot doesn't load globalization data so we must enable invariant mode. -->
<PropertyGroup>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

<ItemGroup>
<TrimmerRootAssembly Include="GodotSharp" />
<TrimmerRootAssembly Include="$(TargetName)" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@
<GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants);$(GodotVersionConstants)</GodotDefineConstants>
</PropertyGroup>

<!-- Platform-specific build properties. -->
<Import Project="$(MSBuildThisFileDirectory)\Android.props" Condition=" '$(GodotTargetPlatform)' == 'android' " />
<Import Project="$(MSBuildThisFileDirectory)\iOSNativeAOT.props" Condition=" '$(GodotTargetPlatform)' == 'ios' " />
<Import Project="$(MSBuildThisFileDirectory)\Browser.props" Condition=" '$(GodotTargetPlatform)' == 'web' " />
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
<PackageReference Include="GodotSharpEditor" Version="$(PackageVersion_GodotSharp)" Condition=" '$(Configuration)' == 'Debug' " />
</ItemGroup>

<!-- iOS-specific build targets -->
<!-- Platform-specific build targets. -->
<Import Project="$(MSBuildThisFileDirectory)\Android.targets" Condition=" '$(GodotTargetPlatform)' == 'android' " />
<Import Project="$(MSBuildThisFileDirectory)\iOSNativeAOT.targets" Condition=" '$(GodotTargetPlatform)' == 'ios' " />
<Import Project="$(MSBuildThisFileDirectory)\Browser.targets" Condition=" '$(GodotTargetPlatform)' == 'web' " />

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,6 @@ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long
if (!TryDeterminePlatformFromOSName(osName, out string? platform))
throw new NotSupportedException("Target platform not supported.");

if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS }
.Contains(platform))
{
throw new NotImplementedException("Target platform not yet implemented.");
}

bool useAndroidLinuxBionic = (bool)GetOption("dotnet/android_use_linux_bionic");
PublishConfig publishConfig = new()
{
Expand Down Expand Up @@ -225,6 +219,11 @@ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long
}
}

if (features.Contains("wasm32"))
{
publishConfig.Archs.Add("wasm32");
}

var targets = new List<PublishConfig> { publishConfig };

if (platform == OS.Platforms.iOS)
Expand All @@ -242,7 +241,7 @@ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long

List<string> outputPaths = new();

bool embedBuildResults = ((bool)GetOption("dotnet/embed_build_outputs") || platform == OS.Platforms.Android) && platform != OS.Platforms.MacOS;
bool embedBuildResults = ((bool)GetOption("dotnet/embed_build_outputs") || platform == OS.Platforms.Android || platform == OS.Platforms.Web) && platform != OS.Platforms.MacOS;

var exportedJars = new HashSet<string>();

Expand Down Expand Up @@ -349,6 +348,15 @@ private void _ExportBeginImpl(string[] features, bool isDebug, string path, long
// We get called back for both directories and files, but we only package files for now.
if (isFile)
{
if (path.EndsWith(".c") || path.EndsWith(".h") || path.EndsWith(".ts") || path.EndsWith(".js") || path.EndsWith(".js.map") || path.EndsWith(".js.symbols") || path.EndsWith(".rsp") || path.EndsWith(".wasm"))
{
// Ignore source files and other output files that we don't use in the final build.
// These files may be included by some runtime packs but we don't need them at this
// point. Libraries are provided as `.dll` and/or `.a` archive files (we currently
// don't support Webcil wrapped wasm files).
return;
}

if (embedBuildResults)
{
if (platform == OS.Platforms.Android)
Expand Down Expand Up @@ -510,6 +518,7 @@ private string DetermineRuntimeIdentifierArch(string arch)
"arm64-v8a" => "arm64",
"arm32" => "arm",
"arm64" => "arm64",
"wasm32" => "wasm",
_ => throw new ArgumentOutOfRangeException(nameof(arch), arch, "Unexpected architecture")
};
}
Expand Down
Loading
Loading