diff --git a/Source/ThirdParty/nethost/nethost.Build.cs b/Source/ThirdParty/nethost/nethost.Build.cs index 97cab0e54..9de3a8803 100644 --- a/Source/ThirdParty/nethost/nethost.Build.cs +++ b/Source/ThirdParty/nethost/nethost.Build.cs @@ -72,8 +72,7 @@ public class nethost : ThirdPartyModule case TargetPlatform.Switch: case TargetPlatform.PS4: case TargetPlatform.PS5: - options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libnethost.a")); - //options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libhostfxr.a")); + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libmonosgen-2.0.a")); break; case TargetPlatform.Android: options.PublicDefinitions.Add("USE_MONO_DYNAMIC_LIB"); diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs index ccd913bf9..62d980f4e 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs @@ -305,7 +305,6 @@ namespace Flax.Build else if (aotMode == DotNetAOTModes.MonoAOTDynamic || aotMode == DotNetAOTModes.MonoAOTStatic) { var dotnetLibPath = Path.Combine(aotAssembliesPath, "lib/net7.0"); - var monoAssembliesOutputPath = aotMode == DotNetAOTModes.MonoAOTDynamic ? dotnetOutputPath : null; // Build list of assemblies to process (use game assemblies as root to walk over used references from stdlib) var assembliesPaths = new List(); @@ -346,6 +345,10 @@ namespace Flax.Build // Run compilation bool failed = false; + bool validCache = true; + var platformsToolsPath = Path.Combine(Globals.EngineRoot, "Source/Platforms", platform.ToString(), "Binaries/Tools"); + if (!Directory.Exists(platformsToolsPath)) + throw new Exception("Missing platform tools " + platformsToolsPath); var compileAssembly = (string assemblyPath) => { // Determinate whether use debug information for that assembly @@ -357,7 +360,11 @@ namespace Flax.Build } // Skip if output is already generated and is newer than a source assembly - var outputFilePath = assemblyPath + buildPlatform.SharedLibraryFileExtension; + string outputFilePath; + if (aotMode == DotNetAOTModes.MonoAOTDynamic) + outputFilePath = assemblyPath + buildPlatform.SharedLibraryFileExtension; + else + outputFilePath = Path.Combine(Path.GetDirectoryName(assemblyPath), buildPlatform.StaticLibraryFilePrefix + Path.GetFileName(assemblyPath) + buildPlatform.StaticLibraryFileExtension); if (!File.Exists(outputFilePath) || File.GetLastWriteTime(assemblyPath) > File.GetLastWriteTime(outputFilePath)) { if (dotnetAotDebug) @@ -366,33 +373,49 @@ namespace Flax.Build Log.Info(""); Log.Info(""); } - Log.Info(" * " + assemblyPath); var options = new Toolchain.CSharpOptions { + Action = Toolchain.CSharpOptions.ActionTypes.MonoCompile, InputFile = assemblyPath, - AssembliesFolder = aotAssembliesPath, + OutputFile = outputFilePath, + AssembliesPath = aotAssembliesPath, ClassLibraryPath = dotnetLibPath, + PlatformsToolsPath = platformsToolsPath, EnableDebugSymbols = useDebug, EnableToolDebug = dotnetAotDebug, }; + if (!Directory.Exists(options.PlatformsToolsPath)) + throw new Exception("Missing platform tools " + options.PlatformsToolsPath); + Log.Info(" * " + options.OutputFile); if (buildToolchain.CompileCSharp(options)) { Log.Error("Failed to run AOT on assembly " + assemblyPath); failed = true; return; } + validCache = false; } - // Skip if deployed file is already valid - var deployedFilePath = monoAssembliesOutputPath != null ? Path.Combine(monoAssembliesOutputPath, Path.GetFileName(outputFilePath)) : outputFilePath; - if (monoAssembliesOutputPath != null && (!File.Exists(deployedFilePath) || File.GetLastWriteTime(outputFilePath) > File.GetLastWriteTime(deployedFilePath))) + var assemblyFileName = Path.GetFileName(assemblyPath); + // TODO: consider optimizing C# assemblies when doing Mono AOT - maybe we could strip them out of the code and leave just reference/api info for the runtime + if (Configuration.AOTMode == DotNetAOTModes.MonoAOTDynamic) + { + // Skip if deployed file is already valid + var deployedFilePath = Path.Combine(dotnetOutputPath, Path.GetFileName(outputFilePath)); + if (!File.Exists(deployedFilePath) || File.GetLastWriteTime(outputFilePath) > File.GetLastWriteTime(deployedFilePath)) + { + // Copy to the destination folder + Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName)); + Utilities.FileCopy(outputFilePath, deployedFilePath); + if (useDebug && File.Exists(outputFilePath + ".pdb")) + Utilities.FileCopy(outputFilePath + ".pdb", Path.Combine(dotnetOutputPath, Path.GetFileName(outputFilePath + ".pdb"))); + validCache = false; + } + } + else { // Copy to the destination folder - var assemblyFileName = Path.GetFileName(assemblyPath); - Utilities.FileCopy(assemblyPath, Path.Combine(monoAssembliesOutputPath, assemblyFileName)); - Utilities.FileCopy(outputFilePath, deployedFilePath); - if (useDebug && File.Exists(outputFilePath + ".pdb")) - Utilities.FileCopy(outputFilePath + ".pdb", Path.Combine(monoAssembliesOutputPath, Path.GetFileName(outputFilePath + ".pdb"))); + Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName)); } }; if (Configuration.MaxConcurrency > 1 && Configuration.ConcurrencyProcessorScale > 0.0f && !dotnetAotDebug) @@ -408,6 +431,27 @@ namespace Flax.Build } if (failed) throw new Exception($"Failed to run AOT. See log ({Configuration.LogFile})."); + var outputAotLib = Path.Combine(outputPath, buildPlatform.SharedLibraryFilePrefix + "AOT" + buildPlatform.SharedLibraryFileExtension); + if (Configuration.AOTMode == DotNetAOTModes.MonoAOTStatic && (!validCache || !File.Exists(outputAotLib))) + { + // Link static native libraries into a single shared module with whole C# compiled in + var options = new Toolchain.CSharpOptions + { + Action = Toolchain.CSharpOptions.ActionTypes.MonoLink, + InputFiles = assembliesPaths, + OutputFile = outputAotLib, + AssembliesPath = aotAssembliesPath, + ClassLibraryPath = dotnetLibPath, + PlatformsToolsPath = platformsToolsPath, + EnableDebugSymbols = configuration != TargetConfiguration.Release, + EnableToolDebug = dotnetAotDebug, + }; + Log.Info(" * " + options.OutputFile); + if (buildToolchain.CompileCSharp(options)) + { + throw new Exception("Mono AOT failed to link static libraries into a shared module"); + } + } } else { diff --git a/Source/Tools/Flax.Build/Build/Toolchain.cs b/Source/Tools/Flax.Build/Build/Toolchain.cs index ac8ce6255..78bae6a06 100644 --- a/Source/Tools/Flax.Build/Build/Toolchain.cs +++ b/Source/Tools/Flax.Build/Build/Toolchain.cs @@ -139,9 +139,19 @@ namespace Flax.Build /// public struct CSharpOptions { + public enum ActionTypes + { + MonoCompile, + MonoLink, + }; + + public ActionTypes Action; public string InputFile; - public string AssembliesFolder; + public List InputFiles; + public string OutputFile; + public string AssembliesPath; public string ClassLibraryPath; + public string PlatformsToolsPath; public bool EnableDebugSymbols; public bool EnableToolDebug; } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs index e908334ac..064962c35 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs @@ -230,6 +230,8 @@ namespace Flax.Deps.Dependencies // TODO: shared/Microsoft.NETCore.App//System.IO.Compression.Native.dll if (runtimeFlavor == "Mono") { + // PS4 outputs mono into artifacts\obj\mono\PS4.x64.Release\out + // PS4 output AOT compiler into artifacts/obj/mono/PS4.x64.Release/cross/out/bin/mono-aot-cross.exe Utilities.DirectoryCopy(Path.Combine(unpackTemp, "runtimes", hostRuntimeName, "native"), Path.Combine(dstDotnet, "native"), true, true); Utilities.FileDelete(Path.Combine(dstDotnet, "native", privateCorelib)); } @@ -252,11 +254,17 @@ namespace Flax.Deps.Dependencies SetupDirectory(Path.Combine(root, "src", "external"), false); /* - * Mono AOT building for Windows: - * .\build.cmd -c release -runtimeFlavor mono 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 - * + * Mono AOT for Windows: + * .\build.cmd -c release -runtimeFlavor mono -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 + * * Mono AOT cross-compiler for Windows: - * .\build.cmd -c release -runtimeFlavor mono -subset mono /p:BuildMonoAotCrossCompiler=true + * .\build.cmd -c release -runtimeFlavor mono -subset mono /p:BuildMonoAotCrossCompiler=true /p:BuildMonoAOTCrossCompilerOnly=true + * + * Mono AOT for PS4: + * .\build.cmd -os PS4 -a x64 /p:RuntimeOS=ps4 -c release -runtimeFlavor mono -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 + * + * Mono AOT cross-compiler for PS4: + * .\build.cmd -c release -runtimeFlavor mono -subset mono /p:BuildMonoAotCrossCompiler=true /p:BuildMonoAOTCrossCompilerOnly=true /p:TargetOS=PS4 /p:HostOS=windows -cmakeargs "-DCMAKE_CROSSCOMPILING=True" */ foreach (var platform in options.Platforms) diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index 2b9fc3941..4b962caed 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -1028,26 +1028,25 @@ namespace Flax.Build.Platforms /// public override bool CompileCSharp(CSharpOptions options) { - var platformToolsRoot = Path.Combine(Globals.EngineRoot, "Source/Platforms", Platform.Target.ToString(), "Binaries/Tools"); - if (!Directory.Exists(platformToolsRoot)) - throw new Exception("Missing platform tools " + platformToolsRoot); - var aotCompilerPath = Path.Combine(platformToolsRoot, "mono-aot-cross.exe"); + if (options.Action != CSharpOptions.ActionTypes.MonoCompile) + return true; + var aotCompilerPath = Path.Combine(options.PlatformsToolsPath, "mono-aot-cross.exe"); // Setup options var monoAotMode = "full"; - var debugMode = options.EnableDebugSymbols ? "soft-debug" : "nodebug"; - var aotCompilerArgs = $"--aot={monoAotMode},verbose,stats,print-skipped,{debugMode} -O=all"; + 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.AssembliesFolder + ";" + options.ClassLibraryPath; + 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.InputFile}\"", null, platformToolsRoot, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars); + int result = Utilities.Run(aotCompilerPath, $"{aotCompilerArgs} \"{options.InputFile}\"", null, options.PlatformsToolsPath, Utilities.RunOptions.AppMustExist | Utilities.RunOptions.ConsoleLogOutput, envVars); return result != 0; } }