diff --git a/Source/Editor/Cooker/Platform/Web/WebPlatformTools.cpp b/Source/Editor/Cooker/Platform/Web/WebPlatformTools.cpp index db822bec2..ebd46dd22 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::None;//DotNetAOTModes::MonoAOTStatic;//DotNetAOTModes::NoDotnet; + return DotNetAOTModes::MonoAOTStatic;// DotNetAOTModes::None;//DotNetAOTModes::MonoAOTStatic;//DotNetAOTModes::NoDotnet; } PixelFormat WebPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) @@ -91,8 +91,9 @@ bool WebPlatformTools::OnPostProcess(CookingData& data) // Move .wasm assemblies into the data files in order for dlopen to work (blocking) { - Array files; + Array files, files2; FileSystem::DirectoryGetFiles(files, data.OriginalOutputPath, TEXT("*.wasm"), DirectorySearchOption::AllDirectories); + FileSystem::DirectoryGetFiles(files, data.OriginalOutputPath, TEXT("*.so"), DirectorySearchOption::AllDirectories); StringView gameWasm = StringUtils::GetFileNameWithoutExtension(gameJs); for (const String& file : files) { @@ -100,6 +101,12 @@ bool WebPlatformTools::OnPostProcess(CookingData& data) continue; // Skip the main game module FileSystem::MoveFile(data.DataOutputPath / StringUtils::GetFileName(file), file, true); } + for (const String& file : files2) + { + if (StringUtils::GetFileNameWithoutExtension(file) == gameWasm) + continue; // Skip the main game module + FileSystem::MoveFile(data.DataOutputPath / StringUtils::GetFileName(file), file, true); + } } // Pack data files into a single file using Emscripten's file_packager tool diff --git a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp index 30d3fdcbc..4f50bb2b4 100644 --- a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp +++ b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp @@ -37,8 +37,8 @@ void PrecompileAssembliesStep::OnBuildStarted(CookingData& data) if (cachedData != aotModeCacheValue) { LOG(Info, "AOT cache invalidation"); - FileSystem::DeleteDirectory(data.ManagedCodeOutputPath); // Remove AOT cache - FileSystem::DeleteDirectory(data.DataOutputPath / TEXT("Dotnet")); // Remove deployed Dotnet libs (be sure to remove any leftovers from previous build) + //FileSystem::DeleteDirectory(data.ManagedCodeOutputPath); // Remove AOT cache + //FileSystem::DeleteDirectory(data.DataOutputPath / TEXT("Dotnet")); // Remove deployed Dotnet libs (be sure to remove any leftovers from previous build) } } if (!FileSystem::DirectoryExists(data.ManagedCodeOutputPath)) diff --git a/Source/Engine/Platform/Web/WebDefines.h b/Source/Engine/Platform/Web/WebDefines.h index 9db34135a..9ef4fc941 100644 --- a/Source/Engine/Platform/Web/WebDefines.h +++ b/Source/Engine/Platform/Web/WebDefines.h @@ -31,6 +31,6 @@ // Use AOT for Mono #define USE_MONO_AOT 1 -#define USE_MONO_AOT_MODE MONO_AOT_MODE_FULL +#define USE_MONO_AOT_MODE MONO_AOT_MODE_INTERP #endif diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index e9badc989..eb00bf2af 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -311,7 +311,6 @@ void MCore::UnloadDomain(const StringAnsi& domainName) bool MCore::LoadEngine() { - LOG(Info, "DotNet MCore::LoadEngine"); PROFILE_CPU(); PROFILE_MEM(ScriptingCSharp); @@ -1788,7 +1787,6 @@ 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)) @@ -2063,10 +2061,10 @@ static MonoAssembly* OnMonoAssemblyLoad(const char* aname) String fileName = name; if (!name.EndsWith(TEXT(".dll")) && !name.EndsWith(TEXT(".exe"))) fileName += TEXT(".dll"); - String path = Globals::ProjectFolder / String(TEXT("/Dotnet/")) / fileName; + String path = Globals::ProjectFolder / String(TEXT("Dotnet/")) / fileName; if (!FileSystem::FileExists(path)) { - path = Globals::ProjectFolder / String(TEXT("/Dotnet/shared/Microsoft.NETCore.App/")) / fileName; + path = Globals::ProjectFolder / String(TEXT("Dotnet/shared/Microsoft.NETCore.App/")) / fileName; if (!FileSystem::FileExists(path)) { path = fileName; @@ -2219,7 +2217,6 @@ 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")); @@ -2238,8 +2235,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); + mono_jit_set_aot_mode(MONO_AOT_MODE_FULL); #endif // Platform-specific setup @@ -2249,7 +2245,6 @@ 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(); @@ -2281,7 +2276,6 @@ bool InitHostfxr() #endif // Setup debugger - LOG(Info, "InitHostfxr2 setup debugger"); { int32 debuggerLogLevel = 0; if (CommandLine::Options.MonoLog.IsTrue() || DOTNET_HOST_MONO_DEBUG) @@ -2328,7 +2322,6 @@ 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); @@ -2362,18 +2355,15 @@ 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 a7b3d4e36..06392f932 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -464,7 +464,6 @@ 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 d1e9d6c56..1ddaeae8e 100644 --- a/Source/Engine/Scripting/Runtime/None.cpp +++ b/Source/Engine/Scripting/Runtime/None.cpp @@ -47,7 +47,6 @@ 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/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs index 4a1776a69..58b3088df 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs @@ -66,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(); } @@ -387,7 +387,7 @@ namespace Flax.Build buildToolchain.CompileCSharp(ref options); platformToolsPath = options.PlatformToolsPath; } - if (!wasm) + //if (!wasm) { // Check if core library has been modified var options = new Toolchain.CSharpOptions @@ -403,12 +403,28 @@ namespace Flax.Build }; buildToolchain.CompileCSharp(ref options); coreLibDirty = File.GetLastWriteTime(options.InputFiles[0]) > File.GetLastWriteTime(options.OutputFiles[0]); + Log.Info("corelibdirty:" + coreLibDirty + ", " + options.OutputFiles[0]); } if (!Directory.Exists(platformToolsPath)) throw new Exception("Missing platform tools " + platformToolsPath); Log.Info("Platform tools found in: " + platformToolsPath); + + // Workaround for unknown command "rm" when running on Windows + Utilities.WriteFileIfChanged(Path.Combine(aotAssembliesPath, "rm.bat"), + """ + @echo off + setlocal enabledelayedexpansion + set command=%* + set command=!command:~3! + del /F /Q /S %command% + rmdir /S /Q %command% + """ + ); + var compileAssembly = (string assemblyPath) => { + //if (!assemblyPath.Contains("Newtonsoft")) + // return; // Determinate whether use debug information for that assembly var useDebug = configuration != TargetConfiguration.Release; if (!dotnetAotDebug && !inputFiles.Contains(assemblyPath)) @@ -497,7 +513,7 @@ namespace Flax.Build }; Log.Info("AOT: " + string.Join(", ", assembliesPaths)); Log.Info(""); - if (false && Configuration.MaxConcurrency > 1 && Configuration.ConcurrencyProcessorScale > 0.0f && !dotnetAotDebug) + if (Configuration.MaxConcurrency > 1 && Configuration.ConcurrencyProcessorScale > 0.0f && !dotnetAotDebug) { // Multi-threaded System.Threading.Tasks.Parallel.ForEach(assembliesPaths, compileAssembly); diff --git a/Source/Tools/Flax.Build/Platforms/Web/WebPlatform.cs b/Source/Tools/Flax.Build/Platforms/Web/WebPlatform.cs index 9f184d472..f664c633c 100644 --- a/Source/Tools/Flax.Build/Platforms/Web/WebPlatform.cs +++ b/Source/Tools/Flax.Build/Platforms/Web/WebPlatform.cs @@ -32,7 +32,7 @@ namespace Flax.Build.Platforms public override string ExecutableFileExtension => ".html"; /// - public override string SharedLibraryFileExtension => ".wasm"; + public override string SharedLibraryFileExtension => ".so";//".wasm"; /// public override string StaticLibraryFileExtension => ".a"; diff --git a/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs b/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs index 117874710..10a4957fe 100644 --- a/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs @@ -245,7 +245,7 @@ namespace Flax.Build.Platforms { switch (options.Action) { - case CSharpOptions.ActionTypes.GetOutputFiles: + /*case CSharpOptions.ActionTypes.GetOutputFiles: { foreach (var inputFile in options.InputFiles) { @@ -264,7 +264,7 @@ namespace Flax.Build.Platforms options.OutputFiles.Add(assemblyPath); } return false; - } + }*/ case CSharpOptions.ActionTypes.GetPlatformTools: { string arch = Platform.BuildTargetArchitecture switch @@ -286,6 +286,120 @@ namespace Flax.Build.Platforms 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.MonoLink: + { + // Setup arguments + var args = new List(); + //args.AddRange(options.LinkEnv.CustomArgs); + { + args.Add(string.Format("-o \"{0}\"", options.OutputFiles[0].Replace('\\', '/'))); + + //AddSharedArgs(args, options, false, false); + + // Setup memory + var initialMemory = Configuration.WebInitialMemory; + //if (options.CompileEnv.Sanitizers.HasFlag(Sanitizer.Address)) + // initialMemory = Math.Max(initialMemory, 64); // Address Sanitizer needs more memory + /*args.Add($"-sINITIAL_MEMORY={initialMemory}MB"); + args.Add("-sSTACK_SIZE=4MB"); + args.Add("-sALLOW_MEMORY_GROWTH=1"); + + // Setup file access (Game Cooker packs files with file_packager tool) + args.Add("-sFORCE_FILESYSTEM");*/ + //args.Add("-sLZ4"); + + // https://emscripten.org/docs/compiling/Dynamic-Linking.html#dynamic-linking + // TODO: use -sMAIN_MODULE=2 and -sSIDE_MODULE=2 to strip unused code (mark public APIs with EMSCRIPTEN_KEEPALIVE) + /*if (options.LinkEnv.Output == LinkerOutput.Executable) + { + args.Add("-sMAIN_MODULE"); + args.Add("-sEXPORT_ALL"); + } + else*/ + { + args.Add("-sSIDE_MODULE"); + } + } + + args.Add("-Wl,--allow-multiple-definition"); // Multiple pthread-related definitions in dotnet runtime + + args.Add("-Wl,--start-group"); + + // Input libraries + var libraryPaths = new HashSet(); + var dynamicLibExt = Platform.SharedLibraryFileExtension; + var executableExt = Platform.ExecutableFileExtension; + foreach (var library in options.InputFiles) + { + /*var dir = Path.GetDirectoryName(library); + var ext = Path.GetExtension(library); + if (library.StartsWith("--use-port=")) + { + // Ports (https://emscripten.org/docs/compiling/Building-Projects.html#emscripten-ports) + args.Add(library); + } + else if (string.IsNullOrEmpty(dir)) + { + args.Add(string.Format("\"-l{0}\"", library)); + } + else if (ext == executableExt) + { + // Skip executable + } + else if (ext == dynamicLibExt) + { + // Link against dynamic library + //task.PrerequisiteFiles.Add(library); + libraryPaths.Add(dir); + args.Add(string.Format("\"-l{0}\"", GetLibName(library))); + } + else + { + //task.PrerequisiteFiles.Add(library); + args.Add(string.Format("\"-l{0}\"", GetLibName(library))); + libraryPaths.Add(dir); // FIXME + }*/ + args.Add(library.Replace(Path.DirectorySeparatorChar, '/') + Platform.StaticLibraryFileExtension); + } + + // Input files (link static libraries last) + /*task.PrerequisiteFiles.AddRange(options.LinkEnv.InputFiles); + foreach (var file in options.LinkEnv.InputFiles.Where(x => !x.EndsWith(".a")).Concat(options.LinkEnv.InputFiles.Where(x => x.EndsWith(".a")))) + { + args.Add(string.Format("\"{0}\"", file.Replace('\\', '/'))); + } + + // Additional lib paths + libraryPaths.AddRange(options.LinkEnv.LibraryPaths); + foreach (var path in libraryPaths) + { + args.Add(string.Format("-L\"{0}\"", path.Replace('\\', '/'))); + }*/ + + args.Add("-Wl,--end-group"); + + // Use a response file (it can contain any commands that you would specify on the command line) + bool useResponseFile = true; + string responseFile = null; + if (useResponseFile) + { + responseFile = Path.Combine(options.AssembliesPath, Path.GetFileName(options.OutputFiles[0]) + ".response"); + //task.PrerequisiteFiles.Add(responseFile); + Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args)); + args.Clear(); + args.Add(string.Format("@\"{0}\"", responseFile)); + } + + //task.WorkingDirectory = options.WorkingDirectory; + //task.CommandPath = _compilerPath; + //task.CommandArguments = string.Join(" ", args); + + int result = Utilities.Run(NativeCompilerPath, $"{string.Join(" ", args)}", null, Path.GetDirectoryName(options.OutputFiles[0]), Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput); + if (result != 0) + return true; + + return false; + } case CSharpOptions.ActionTypes.MonoCompile: { string binaryExtension = Platform.BuildPlatform.Target == TargetPlatform.Windows ? ".exe" : ""; @@ -298,11 +412,11 @@ namespace Flax.Build.Platforms var outputFileDylib = options.OutputFiles[0]; var inputFileFolder = Path.GetDirectoryName(inputFile); - if (Path.GetFileNameWithoutExtension(inputFile) == "System.Private.CoreLib") + /*if (Path.GetFileNameWithoutExtension(inputFile) == "System.Private.CoreLib") { // Pre-compiled, skip return false; - } + }*/ string outputFile; { @@ -320,15 +434,24 @@ namespace Flax.Build.Platforms var llvmPath = "\"C:\\Program Files\\LLVM\\bin\"";//Path.Combine(EmscriptenSdk.Instance.EmscriptenPath, "bin"); var monoAotMode = "full"; + if (useLLVM) + monoAotMode = "llvmonly"; + //if ("mscorlib.dll" == "") + // monoAotMode = "interp"; + 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={monoAotMode},asmonly,verbose,stats,print-skipped,{monoDebugMode}{(useLLVM ? $",llvm-path={llvmPath},llvm-outfile=" + Path.GetFileName(outputFile) : "")} -O=float32"; + + //aotCompilerArgs += " --verbose"; + //aotCompilerArgs += $" --aot-path=\"{inputFileFolder}\""; //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; + envVars["MONO_PATH"] = options.ClassLibraryPath.Replace('/', Path.DirectorySeparatorChar) + Path.PathSeparator + options.AssembliesPath.Replace('/', Path.DirectorySeparatorChar); + Log.Info("MONO_PATH: " + envVars["MONO_PATH"]); if (options.EnableToolDebug) { envVars["MONO_LOG_LEVEL"] = "debug"; diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index 8d581d986..5e27c391d 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -500,6 +500,9 @@ namespace Flax.Build.Platforms // List Include Files //commonArgs.Add("/showIncludes"); + commonArgs.Add("/permissive-"); + commonArgs.Add("/Zc:externC-"); + // Code Analysis commonArgs.Add("/analyze-");