diff --git a/Source/Editor/Cooker/Platform/Web/WebPlatformTools.cpp b/Source/Editor/Cooker/Platform/Web/WebPlatformTools.cpp index dfb0b5403..db822bec2 100644 --- a/Source/Editor/Cooker/Platform/Web/WebPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Web/WebPlatformTools.cpp @@ -38,7 +38,7 @@ ArchitectureType WebPlatformTools::GetArchitecture() const DotNetAOTModes WebPlatformTools::UseAOT() const { - return DotNetAOTModes::NoDotnet; + return DotNetAOTModes::None;//DotNetAOTModes::MonoAOTStatic;//DotNetAOTModes::NoDotnet; } PixelFormat WebPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) diff --git a/Source/Engine/Graphics/Graphics.Build.cs b/Source/Engine/Graphics/Graphics.Build.cs index 015e2c01d..ec054528b 100644 --- a/Source/Engine/Graphics/Graphics.Build.cs +++ b/Source/Engine/Graphics/Graphics.Build.cs @@ -97,7 +97,8 @@ public class Graphics : EngineModule Log.WarningOnce(string.Format("Building for {0} without Vulkan rendering backend (Vulkan SDK is missing)", options.Platform.Target), ref _logMissingVulkanSDK); break; case TargetPlatform.Web: - options.PrivateDependencies.Add("GraphicsDeviceWebGPU"); + options.PrivateDependencies.Add("GraphicsDeviceNull"); + //options.PrivateDependencies.Add("GraphicsDeviceWebGPU"); break; default: throw new InvalidPlatformException(options.Platform.Target); } diff --git a/Source/Engine/Physics/CollisionData.cs b/Source/Engine/Physics/CollisionData.cs index 8bb9260d1..278a99e0c 100644 --- a/Source/Engine/Physics/CollisionData.cs +++ b/Source/Engine/Physics/CollisionData.cs @@ -6,6 +6,7 @@ namespace FlaxEngine { partial class CollisionData { +#if false /// /// Cooks the mesh collision data and updates the virtual asset. action cannot be performed on a main thread. /// [Deprecated on 16.06.2022, expires on 16.06.2024] @@ -68,5 +69,6 @@ namespace FlaxEngine for (int i = 0; i < tmp.Length; i++) vertexBuffer[i] = tmp[i]; } +#endif } } diff --git a/Source/Engine/Platform/Web/WebDefines.h b/Source/Engine/Platform/Web/WebDefines.h index d5511d1f7..9db34135a 100644 --- a/Source/Engine/Platform/Web/WebDefines.h +++ b/Source/Engine/Platform/Web/WebDefines.h @@ -29,4 +29,8 @@ #define GPU_ENABLE_ASYNC_RESOURCES_CREATION 0 #endif +// Use AOT for Mono +#define USE_MONO_AOT 1 +#define USE_MONO_AOT_MODE MONO_AOT_MODE_FULL + #endif diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 4be0ce1a1..e9badc989 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -51,7 +51,7 @@ #include #include typedef char char_t; -#define DOTNET_HOST_MONO_DEBUG 0 +#define DOTNET_HOST_MONO_DEBUG 1 #ifdef USE_MONO_AOT_MODULE void* MonoAotModuleHandle = nullptr; #endif @@ -270,6 +270,36 @@ struct NativePropertyDefinitions MMethodAttributes setterAttributes; }; +#if PLATFORM_WEB + +extern "C" +{ + DLLEXPORT void schedule_background_exec() + { + } + + DLLEXPORT int mono_wasm_process_current_pid() + { + return -1; + } + + DLLEXPORT void mono_wasm_schedule_timer(int shortestDueTimeMs) + { + } + + DLLEXPORT int mono_jiterp_parse_option(const char* option) + { + return -1; + } + + DLLEXPORT int mono_wasm_browser_entropy(uint8_t* buffer, int32_t bufferLength) + { + return 0; + } +} + +#endif + MDomain* MCore::CreateDomain(const StringAnsi& domainName) { return nullptr; @@ -281,6 +311,7 @@ void MCore::UnloadDomain(const StringAnsi& domainName) bool MCore::LoadEngine() { + LOG(Info, "DotNet MCore::LoadEngine"); PROFILE_CPU(); PROFILE_MEM(ScriptingCSharp); @@ -1757,6 +1788,7 @@ hostfxr_run_app_fn hostfxr_run_app; bool InitHostfxr() { + LOG(Info, "InitHostfxr"); const ::String csharpLibraryPath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll"); const ::String csharpRuntimeConfigPath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.runtimeconfig.json"); if (!FileSystem::FileExists(csharpLibraryPath)) @@ -2187,6 +2219,7 @@ bool MonoAotPreloadTask::Run() bool InitHostfxr() { + LOG(Info, "InitHostfxr2"); #if DOTNET_HOST_MONO_DEBUG // Enable detailed Mono logging Platform::SetEnvironmentVariable(TEXT("MONO_LOG_LEVEL"), TEXT("debug")); @@ -2205,6 +2238,7 @@ bool InitHostfxr() #if defined(USE_MONO_AOT_MODE) // Enable AOT mode (per-platform) + LOG(Info, "InitHostfxr mono_jit_set_aot_mode"); mono_jit_set_aot_mode(USE_MONO_AOT_MODE); #endif @@ -2215,6 +2249,7 @@ bool InitHostfxr() #endif #ifdef USE_MONO_AOT_MODULE + LOG(Info, "InitHostfxr USE_MONO_AOT_MODULE"); // Wait for AOT module preloading while (Platform::AtomicRead(&MonoAotPreloadServiceInstance.Ready) == 0) Platform::Yield(); @@ -2246,6 +2281,7 @@ bool InitHostfxr() #endif // Setup debugger + LOG(Info, "InitHostfxr2 setup debugger"); { int32 debuggerLogLevel = 0; if (CommandLine::Options.MonoLog.IsTrue() || DOTNET_HOST_MONO_DEBUG) @@ -2292,6 +2328,7 @@ bool InitHostfxr() #endif } + LOG(Info, "InitHostfxr2 set log handler"); // Connect to mono engine callback system mono_trace_set_log_handler(OnLogCallback, nullptr); mono_trace_set_print_handler(OnPrintCallback); @@ -2325,15 +2362,18 @@ bool InitHostfxr() #else const char* monoVersion = ""; // ignored #endif + LOG(Info, "InitHostfxr2 mono_jit_init_version"); MonoDomainHandle = mono_jit_init_version("Flax", monoVersion); if (!MonoDomainHandle) { LOG(Fatal, "Failed to initialize Mono."); return true; } + LOG(Info, "InitHostfxr2 mono_gc_init_finalizer_thread"); mono_gc_init_finalizer_thread(); // Log info + LOG(Info, "InitHostfxr2 mono_get_runtime_build_info"); char* buildInfo = mono_get_runtime_build_info(); LOG(Info, "Mono runtime version: {0}", String(buildInfo)); mono_free(buildInfo); diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp index 06392f932..a7b3d4e36 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -464,6 +464,7 @@ static void* OnMonoLinuxDlSym(void* handle, const char* name, char** err, void* bool MCore::LoadEngine() { + LOG(Info, "Mono MCore::LoadEngine"); PROFILE_CPU(); ASSERT(Globals::MonoPath.IsANSI()); diff --git a/Source/Engine/Scripting/Runtime/None.cpp b/Source/Engine/Scripting/Runtime/None.cpp index 1ddaeae8e..d1e9d6c56 100644 --- a/Source/Engine/Scripting/Runtime/None.cpp +++ b/Source/Engine/Scripting/Runtime/None.cpp @@ -47,6 +47,7 @@ void MCore::UnloadDomain(const StringAnsi& domainName) bool MCore::LoadEngine() { + LOG(Info, "None MCore::LoadEngine"); MRootDomain = New("Root"); MDomains.Add(MRootDomain); return false; diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 9b8439528..b60defd4a 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -176,16 +176,19 @@ void onEngineUnloading(MAssembly* assembly); bool ScriptingService::Init() { + LOG(Info, "ScriptingService::Init"); PROFILE_MEM(Scripting); Stopwatch stopwatch; // Initialize managed runtime + LOG(Info, "ScriptingService::Init MCore::LoadEngine"); if (MCore::LoadEngine()) { LOG(Fatal, "C# runtime initialization failed."); return true; } + LOG(Info, "ScriptingService::Init MCore::CreateScriptingAssemblyLoadContext"); MCore::CreateScriptingAssemblyLoadContext(); // Cache root domain diff --git a/Source/ThirdParty/SDL/SDL.Build.cs b/Source/ThirdParty/SDL/SDL.Build.cs index a810555bb..ea5f5c373 100644 --- a/Source/ThirdParty/SDL/SDL.Build.cs +++ b/Source/ThirdParty/SDL/SDL.Build.cs @@ -49,7 +49,8 @@ public class SDL : DepsModule options.OutputFiles.Add(Path.Combine(depsRoot, "libSDL3.a")); break; case TargetPlatform.Web: - options.OutputFiles.Add("--use-port=sdl3"); + //options.OutputFiles.Add("--use-port=sdl3"); + options.OutputFiles.Add(Path.Combine(depsRoot, "libSDL3.a")); break; default: throw new InvalidPlatformException(options.Platform.Target); } diff --git a/Source/ThirdParty/nethost/nethost.Build.cs b/Source/ThirdParty/nethost/nethost.Build.cs index 2910e401d..e9ac30756 100644 --- a/Source/ThirdParty/nethost/nethost.Build.cs +++ b/Source/ThirdParty/nethost/nethost.Build.cs @@ -104,6 +104,47 @@ public class nethost : ThirdPartyModule options.PublicDefinitions.Add("USE_MONO_DYNAMIC_LIB"); options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.dylib")); options.Libraries.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.dylib")); + break; + case TargetPlatform.Web: + //options.PublicDefinitions.Add("USE_MONO_DYNAMIC_LIB"); + options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.a")); + options.Libraries.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.a")); + + foreach (var lib in new string[] { + //"libz.a", + "libSystem.Native.a", + //"libSystem.Globalization.Native.a", + //"libSystem.IO.Compression.Native.a", + "libmono-component-debugger-stub-static.a",//"libmono-component-debugger-static.a", + "libmono-component-hot_reload-stub-static.a",//"libmono-component-hot_reload-static.a", + "libmono-component-marshal-ilgen-stub-static.a",//"libmono-component-marshal-ilgen-static.a", + "libmono-component-diagnostics_tracing-stub-static.a", + //"libmono-profiler-browser.a", + "libmono-wasm-eh-js.a", + //"libicuuc.a", + //"libmono-icall-table.a", + /*"libmono-wasm-nosimd.a", + "libSystem.Native.a", + "libSystem.IO.Compression.Native.a", + //"libmono-wasm-eh-wasm.a", + //"libmono-ee-interp.a", + "libicuuc.a", + "libicui18n.a", + "libicudata.a", + "libbrotlienc.a", + "libbrotlidec.a", + "libbrotlicommon.a",*/ + //"dotnet.native.wasm", + }) + { + options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, lib)); + options.Libraries.Add(Path.Combine(hostRuntime.Path, lib)); + } + + //options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "dotnet.native.wasm")); + //options.LinkEnv.InputFiles.Add(Path.Combine(hostRuntime.Path, "dotnet.native.wasm")); + //options.SourceFiles.Add(Path.Combine(hostRuntime.Path, "dotnet.native.js")); + break; default: throw new InvalidPlatformException(options.Platform.Target); } diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs index c9db8fd64..4a1776a69 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs @@ -24,6 +24,11 @@ namespace Flax.Build /// ILC, + /// + /// + /// + WebCIL, + /// /// Use Mono AOT to cross-compile all used C# assemblies into native platform shared libraries. /// @@ -61,7 +66,7 @@ namespace Flax.Build public static void RunDotNetAOT() { Log.Info("Running .NET AOT in mode " + AOTMode); - //Configuration.CustomDefines.Add("DOTNET_AOT_DEBUG"); + Configuration.CustomDefines.Add("DOTNET_AOT_DEBUG"); DotNetAOT.RunAOT(); } @@ -318,6 +323,8 @@ namespace Flax.Build var dotnetLibPath = Utilities.NormalizePath(Path.GetDirectoryName(coreLibPaths[0])); Log.Info("Class library found in: " + dotnetLibPath); + bool wasm = true; + // Build list of assemblies to process (use game assemblies as root to walk over used references from stdlib) var assembliesPaths = new List(); if (Configuration.SkipUnusedDotnetLibsPackaging) @@ -354,6 +361,16 @@ namespace Flax.Build assembliesPaths.AddRange(stdLibFiles); assembliesPaths.AddRange(inputFiles); } + if (!wasm) + { + List assembliesPaths2 = new(); + foreach (var assemblyPath in assembliesPaths) + { + if (!assemblyPath.StartsWith(dotnetLibPath)) + assembliesPaths2.Add(assemblyPath); + } + assembliesPaths = assembliesPaths2; + } // Run compilation bool failed = false, validCache = true, coreLibDirty = false; @@ -370,6 +387,7 @@ namespace Flax.Build buildToolchain.CompileCSharp(ref options); platformToolsPath = options.PlatformToolsPath; } + if (!wasm) { // Check if core library has been modified var options = new Toolchain.CSharpOptions @@ -411,6 +429,13 @@ namespace Flax.Build EnableDebugSymbols = useDebug, EnableToolDebug = dotnetAotDebug, }; + + if (true) + { + //if (Path.GetFileNameWithoutExtension(assemblyPath) == "System.Private.CoreLib") + + } + buildToolchain.CompileCSharp(ref options); // Skip if output is already generated and is newer than a source assembly @@ -470,7 +495,9 @@ namespace Flax.Build Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName), Utilities.CopyMode.OverrideIfNewer); } }; - if (Configuration.MaxConcurrency > 1 && Configuration.ConcurrencyProcessorScale > 0.0f && !dotnetAotDebug) + Log.Info("AOT: " + string.Join(", ", assembliesPaths)); + Log.Info(""); + if (false && Configuration.MaxConcurrency > 1 && Configuration.ConcurrencyProcessorScale > 0.0f && !dotnetAotDebug) { // Multi-threaded System.Threading.Tasks.Parallel.ForEach(assembliesPaths, compileAssembly); @@ -479,7 +506,14 @@ namespace Flax.Build { // Single-threaded foreach (var assemblyPath in assembliesPaths) + { + Log.Info(""); + Log.Warning(" " + assemblyPath); + Log.Info(""); compileAssembly(assemblyPath); + if (failed) + break; + } } if (failed) throw new Exception($"Failed to run AOT. See log ({Configuration.LogFile})."); diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index de0fa755c..4c3309d37 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -148,6 +148,7 @@ namespace Flax.Build TargetPlatform.Windows, TargetPlatform.Linux, TargetPlatform.Mac, + TargetPlatform.Web, }; } } @@ -356,6 +357,9 @@ namespace Flax.Build TryAddHostRuntime(TargetPlatform.Android, TargetArchitecture.ARM64, "android-arm64", "Runtime.Mono"); TryAddHostRuntime(TargetPlatform.iOS, TargetArchitecture.ARM, "ios-arm64", "Runtime.Mono"); TryAddHostRuntime(TargetPlatform.iOS, TargetArchitecture.ARM64, "ios-arm64", "Runtime.Mono"); + TryAddHostRuntime(TargetPlatform.Web, TargetArchitecture.x86, "browser-wasm", "Runtime.Mono"); + //TryAddHostRuntime(TargetPlatform.Web, TargetArchitecture.x86, "browser-wasm", "Runtime.Mono.multithread"); + //TryAddHostRuntime(TargetPlatform.Web, TargetArchitecture.x86, "browser-wasm", "Runtime.AOT.win-x64.Cross"); // Found IsValid = true; @@ -410,10 +414,16 @@ namespace Flax.Build case TargetPlatform.iOS: result = "ios"; break; + case TargetPlatform.Web: + result = "browser"; + break; default: throw new InvalidPlatformException(platform); } switch (architecture) { + case TargetArchitecture.x86 when platform == TargetPlatform.Web: + result += "-wasm"; + break; case TargetArchitecture.x86: result += "-x86"; break; diff --git a/Source/Tools/Flax.Build/Build/Target.cs b/Source/Tools/Flax.Build/Build/Target.cs index 257cff587..afd2f353d 100644 --- a/Source/Tools/Flax.Build/Build/Target.cs +++ b/Source/Tools/Flax.Build/Build/Target.cs @@ -276,6 +276,7 @@ namespace Flax.Build case TargetPlatform.Linux: case TargetPlatform.iOS: case TargetPlatform.Mac: + case TargetPlatform.Web: // TODO: try to disable this on more platforms! (eg. LocalizationService::OnLocalizationChanged uses it) options.CompileEnv.EnableExceptions = true; break; diff --git a/Source/Tools/Flax.Build/Configuration.cs b/Source/Tools/Flax.Build/Configuration.cs index 9f2796767..269b8e047 100644 --- a/Source/Tools/Flax.Build/Configuration.cs +++ b/Source/Tools/Flax.Build/Configuration.cs @@ -296,8 +296,8 @@ namespace Flax.Build public static bool WithCSharp(NativeCpp.BuildOptions options) { - if (options.Platform.Target == TargetPlatform.Web) - return false; // TODO: implement .NET for WebAssembly + //if (options.Platform.Target == TargetPlatform.Web) + // return false; // TODO: implement .NET for WebAssembly return UseCSharp || options.Target.IsEditor; } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/SDL.cs b/Source/Tools/Flax.Build/Deps/Dependencies/SDL.cs index 830b43500..ff7d34cca 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/SDL.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/SDL.cs @@ -123,6 +123,7 @@ namespace Flax.Deps.Dependencies } case TargetPlatform.Linux: case TargetPlatform.Mac: + case TargetPlatform.Web: { var buildDir = Path.Combine(root, "build-" + architecture.ToString()); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs index f67244c9b..29a8ff009 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs @@ -114,6 +114,7 @@ namespace Flax.Deps.Dependencies case TargetPlatform.PS4: case TargetPlatform.PS5: case TargetPlatform.Switch: + case TargetPlatform.Web: runtimeFlavor = "Mono"; setupVersion = true; buildMonoAotCross = true; @@ -128,6 +129,9 @@ namespace Flax.Deps.Dependencies case TargetPlatform.Switch: os = "switch"; break; + case TargetPlatform.Web: + os = "browser"; + break; default: throw new InvalidPlatformException(targetPlatform); } buildArgs = $" /p:RuntimeOS={os} -subset mono+libs -cmakeargs \"-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1\" /p:FeaturePerfTracing=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false"; diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index 2b1de06ff..6f8add0a5 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -447,6 +447,7 @@ namespace Flax.Deps public static void RunCmake(string path, TargetPlatform platform, TargetArchitecture architecture, string customArgs = null, Dictionary envVars = null) { string cmdLine; + string cmakeBinary = "cmake"; switch (platform) { case TargetPlatform.Windows: @@ -516,13 +517,19 @@ namespace Flax.Deps cmdLine = string.Format("CMakeLists.txt -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=\"{0}\" -DCMAKE_OSX_ARCHITECTURES={1}", Configuration.iOSMinVer, arch); break; } + case TargetPlatform.Web: + { + cmakeBinary = "emcmake.bat"; + cmdLine = "cmake CMakeLists.txt"; + break; + } default: throw new InvalidPlatformException(platform); } if (customArgs != null) cmdLine += " " + customArgs; - Utilities.Run("cmake", cmdLine, null, path, Utilities.RunOptions.DefaultTool, envVars); + Utilities.Run(cmakeBinary, cmdLine, null, path, Utilities.RunOptions.DefaultTool, envVars); } /// diff --git a/Source/Tools/Flax.Build/Platforms/Web/EmscriptenSdk.cs b/Source/Tools/Flax.Build/Platforms/Web/EmscriptenSdk.cs index 946fc0c87..b9169cafa 100644 --- a/Source/Tools/Flax.Build/Platforms/Web/EmscriptenSdk.cs +++ b/Source/Tools/Flax.Build/Platforms/Web/EmscriptenSdk.cs @@ -64,7 +64,7 @@ namespace Flax.Build.Platforms versionStr = versionStr.Substring(1, versionStr.Length - 2); Version = new Version(versionStr); - var minVersion = new Version(4, 0); + var minVersion = new Version(3, 1); if (Version < minVersion) { Log.Error(string.Format("Unsupported Emscripten SDK version {0}. Minimum supported is {1}.", Version, minVersion)); diff --git a/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs b/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs index 115c67fed..117874710 100644 --- a/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs @@ -100,6 +100,8 @@ namespace Flax.Build.Platforms private void AddSharedArgs(List args, BuildOptions options, bool debugInformation, bool optimization) { + //args.Add("-pthread"); + if (debugInformation) args.Add("-g2"); else @@ -132,7 +134,7 @@ namespace Flax.Build.Platforms if (options.LinkEnv.LinkTimeCodeGeneration) args.Add("-flto"); - if (options.LinkEnv.Output == LinkerOutput.SharedLibrary) + //if (options.LinkEnv.Output == LinkerOutput.SharedLibrary) args.Add("-fPIC"); var sanitizers = options.CompileEnv.Sanitizers; @@ -158,8 +160,8 @@ namespace Flax.Build.Platforms AddSharedArgs(commonArgs, options, options.CompileEnv.DebugInformation, options.CompileEnv.Optimization); // Hack to pull SDL3 port via emcc - if (options.CompileEnv.PreprocessorDefinitions.Contains("PLATFORM_SDL")) - commonArgs.Add("--use-port=sdl3"); + //if (options.CompileEnv.PreprocessorDefinitions.Contains("PLATFORM_SDL")) + // commonArgs.Add("--use-port=sdl3"); } // Add preprocessor definitions @@ -239,6 +241,168 @@ namespace Flax.Build.Platforms return output; } + public override bool CompileCSharp(ref CSharpOptions options) + { + switch (options.Action) + { + case CSharpOptions.ActionTypes.GetOutputFiles: + { + foreach (var inputFile in options.InputFiles) + { + string assemblyPath; + if (Configuration.AOTMode == DotNetAOTModes.MonoAOTDynamic) + assemblyPath = inputFile + Platform.SharedLibraryFileExtension; + else + assemblyPath = Path.Combine(Path.GetDirectoryName(inputFile), Platform.StaticLibraryFilePrefix + Path.GetFileName(inputFile) + Platform.StaticLibraryFileExtension); + + if (Path.GetFileNameWithoutExtension(inputFile) == "System.Private.CoreLib") + { + // Use pre-compiled binaries + DotNetSdk.Instance.GetHostRuntime(TargetPlatform.Web, TargetArchitecture.x86, out var hostRuntime); + assemblyPath = Path.Combine(hostRuntime.Path, "libmonosgen-2.0.a"); + } + options.OutputFiles.Add(assemblyPath); + } + return false; + } + case CSharpOptions.ActionTypes.GetPlatformTools: + { + string arch = Platform.BuildTargetArchitecture switch + { + TargetArchitecture.x64 => "x64", + TargetArchitecture.ARM64 => "arm64", + _ => throw new PlatformNotSupportedException(Platform.BuildTargetArchitecture.ToString()), + }; + string os; + switch (Platform.BuildPlatform.Target) + { + case TargetPlatform.Windows: + os = "win"; + break; + default: + throw new PlatformNotSupportedException(Platform.BuildPlatform.Target.ToString()); + } + + options.PlatformToolsPath = Path.Combine(DotNetSdk.SelectVersionFolder(Path.Combine(DotNetSdk.Instance.RootPath, $"packs/Microsoft.NETCore.App.Runtime.AOT.{os}-{arch}.Cross.browser-wasm")), "tools"); + return false; + } + case CSharpOptions.ActionTypes.MonoCompile: + { + string binaryExtension = Platform.BuildPlatform.Target == TargetPlatform.Windows ? ".exe" : ""; + var aotCompilerPath = Path.Combine(options.PlatformToolsPath, "mono-aot-cross") + binaryExtension; + //var clangPath = Path.Combine(ToolchainPath, "usr/bin/clang"); + + var inputFile = options.InputFiles[0]; + var inputFileAsm = inputFile + ".s"; + var inputFileObj = inputFile + ".o"; + var outputFileDylib = options.OutputFiles[0]; + var inputFileFolder = Path.GetDirectoryName(inputFile); + + if (Path.GetFileNameWithoutExtension(inputFile) == "System.Private.CoreLib") + { + // Pre-compiled, skip + return false; + } + + string outputFile; + { + if (Configuration.AOTMode == DotNetAOTModes.MonoAOTDynamic) + outputFile = inputFile + Platform.SharedLibraryFileExtension; + else + outputFile = Path.Combine(Path.GetDirectoryName(inputFile), Platform.StaticLibraryFilePrefix + Path.GetFileName(inputFile) + Platform.StaticLibraryFileExtension); + } + + // Setup options + bool debugSymbols = options.EnableDebugSymbols; + bool useLLVM = true; + //if (useLLVM) + debugSymbols = false; + var llvmPath = "\"C:\\Program Files\\LLVM\\bin\"";//Path.Combine(EmscriptenSdk.Instance.EmscriptenPath, "bin"); + + var monoAotMode = "full"; + var monoDebugMode = debugSymbols ? "soft-debug" : "nodebug"; + var aotCompilerArgs = $"--verbose{(useLLVM ? " --llvm" : "")} --aot={monoAotMode},llvmonly,verbose,stats,print-skipped,{monoDebugMode}{(useLLVM ? ",llvm-outfile="+Path.GetFileName(outputFile) : "")} -O=float32"; + //var aotCompilerArgs = $"{(useLLVM ? " --llvm" : "")} --aot=static,llvmonly,verbose,stats,print-skipped,llvm-path={llvmPath},{monoDebugMode}{(useLLVM ? ",llvm-outfile=" + Path.GetFileName(outputFile) : "")} -O=float32"; + //var aotCompilerArgs = $"{(useLLVM ? " --llvm" : "")} --aot=static,llvmonly,verbose,stats,print-skipped,{monoDebugMode}{(useLLVM ? ",llvm-outfile=" + Path.GetFileName(outputFile) : "")} -O=float32"; + //if (debugSymbols || options.EnableToolDebug) + // aotCompilerArgs = "--debug " + aotCompilerArgs; + + var envVars = new Dictionary(); + envVars["MONO_PATH"] = options.AssembliesPath + ":" + options.ClassLibraryPath; + if (options.EnableToolDebug) + { + envVars["MONO_LOG_LEVEL"] = "debug"; + } + + //envVars["MONO_DEBUG"] = "gen-seq-points"; + + + // Run cross-compiler compiler (outputs assembly code) + int result = Utilities.Run(aotCompilerPath, $"{aotCompilerArgs} \"{inputFile}\"", null, inputFileFolder, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars); + if (result != 0) + return true; + + // Get build args for iOS + /*var clangArgs = new List(); + AddArgsCommon(null, clangArgs); + var clangArgsText = string.Join(" ", clangArgs); + + // Build object file + result = Utilities.Run(clangPath, $"\"{inputFileAsm}\" -c -o \"{inputFileObj}\" " + clangArgsText, null, inputFileFolder, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars); + if (result != 0) + return true; + + // Build dylib file + result = Utilities.Run(clangPath, $"\"{inputFileObj}\" -dynamiclib -fPIC -o \"{outputFileDylib}\" " + clangArgsText, null, inputFileFolder, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars); + if (result != 0) + return true; + + // Clean intermediate results + File.Delete(inputFileAsm); + File.Delete(inputFileObj); + + // Fix rpath id + result = Utilities.Run("install_name_tool", $"-id \"@rpath/{Path.GetFileName(outputFileDylib)}\" \"{outputFileDylib}\"", null, inputFileFolder, Utilities.RunOptions.ConsoleLogOutput, envVars); + if (result != 0) + return true;*/ + + return false; + } + } + return base.CompileCSharp(ref options); + } +#if false + /// + public override bool CompileCSharp(ref CSharpOptions options) + { + switch (options.Action) + { + case CSharpOptions.ActionTypes.MonoCompile: + { + var aotCompilerPath = Path.Combine(options.PlatformToolsPath, "mono-aot-cross.exe"); + + // Setup options + var monoAotMode = "full"; + var monoDebugMode = options.EnableDebugSymbols ? "soft-debug" : "nodebug"; + var aotCompilerArgs = $"--aot={monoAotMode},verbose,stats,print-skipped,{monoDebugMode} -O=all"; + if (options.EnableDebugSymbols || options.EnableToolDebug) + aotCompilerArgs = "--debug " + aotCompilerArgs; + var envVars = new Dictionary(); + envVars["MONO_PATH"] = options.AssembliesPath + ";" + options.ClassLibraryPath; + if (options.EnableToolDebug) + { + envVars["MONO_LOG_LEVEL"] = "debug"; + } + + // Run cross-compiler compiler + int result = Utilities.Run(aotCompilerPath, $"{aotCompilerArgs} \"{options.InputFiles[0]}\"", null, ""/*options.PlatformToolsPath*/, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars); + return result != 0; + } + } + return base.CompileCSharp(ref options); + } +#endif + private Task CreateBinary(TaskGraph graph, BuildOptions options, string outputFilePath) { var task = graph.Add(); @@ -276,6 +440,8 @@ namespace Flax.Build.Platforms } } + args.Add("-Wl,--allow-multiple-definition"); // Multiple pthread-related definitions in dotnet runtime + args.Add("-Wl,--start-group"); // Input libraries @@ -310,6 +476,7 @@ namespace Flax.Build.Platforms { task.PrerequisiteFiles.Add(library); args.Add(string.Format("\"-l{0}\"", GetLibName(library))); + libraryPaths.Add(dir); // FIXME } } diff --git a/Source/Tools/Flax.Build/Program.cs b/Source/Tools/Flax.Build/Program.cs index 711ac5ccb..6ca0fdff3 100644 --- a/Source/Tools/Flax.Build/Program.cs +++ b/Source/Tools/Flax.Build/Program.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.Net; +using System.Reflection; using System.Threading; using Flax.Deploy; using Flax.Deps; @@ -173,6 +174,10 @@ namespace Flax.Build } catch (Exception ex) { + // Skip the exception thrown from failed method invocation + if (ex is TargetInvocationException && ex.InnerException != null) + ex = ex.InnerException; + // Ignore exception logging for build errors if (!(ex is BuildException)) Log.Exception(ex);