diff --git a/.github/workflows/build_mac.yml b/.github/workflows/build_mac.yml index e78e04a4c..d5d8def1f 100644 --- a/.github/workflows/build_mac.yml +++ b/.github/workflows/build_mac.yml @@ -5,7 +5,6 @@ jobs: # Editor editor-mac: - if: ${{ false }} name: Editor (Mac, Development x64) runs-on: "macos-latest" steps: @@ -30,7 +29,6 @@ jobs: # Game game-mac: - if: ${{ false }} name: Game (Mac, Release x64) runs-on: "macos-latest" steps: diff --git a/Source/Engine/Scripting/DotNet/CoreCLR.cpp b/Source/Engine/Scripting/DotNet/CoreCLR.cpp index adbc7fb5f..e0e0a768d 100644 --- a/Source/Engine/Scripting/DotNet/CoreCLR.cpp +++ b/Source/Engine/Scripting/DotNet/CoreCLR.cpp @@ -59,7 +59,13 @@ bool CoreCLR::InitHostfxr(const String& configPath, const String& libraryPath) const String path(hostfxrPath); LOG(Info, "Found hostfxr in {0}", path); + // Get API from hostfxr library void* hostfxr = Platform::LoadLibrary(path.Get()); + if (hostfxr == nullptr) + { + LOG(Error, "Failed to setup hostfxr API ({0})", path); + return true; + } hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)Platform::GetProcAddress(hostfxr, "hostfxr_initialize_for_runtime_config"); hostfxr_initialize_for_dotnet_command_line = (hostfxr_initialize_for_dotnet_command_line_fn)Platform::GetProcAddress(hostfxr, "hostfxr_initialize_for_dotnet_command_line"); hostfxr_get_runtime_delegate = (hostfxr_get_runtime_delegate_fn)Platform::GetProcAddress(hostfxr, "hostfxr_get_runtime_delegate"); @@ -69,7 +75,7 @@ bool CoreCLR::InitHostfxr(const String& configPath, const String& libraryPath) hostfxr_run_app = (hostfxr_run_app_fn)Platform::GetProcAddress(hostfxr, "hostfxr_run_app"); if (!hostfxr_get_runtime_delegate || !hostfxr_run_app) { - LOG(Error, "Failed to setup hostfxr API"); + LOG(Error, "Failed to setup hostfxr API ({0})", path); return true; } diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.cpp b/Source/Engine/Scripting/ManagedCLR/MCore.cpp index a96eb5bff..09be5da67 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MCore.cpp @@ -119,6 +119,7 @@ void MCore::UnloadDomain(const MString& domainName) #if USE_NETCORE bool MCore::LoadEngine() { + PROFILE_CPU(); const String csharpLibraryPath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll"); const String csharpRuntimeConfigPath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.runtimeconfig.json"); if (!FileSystem::FileExists(csharpLibraryPath)) @@ -152,6 +153,9 @@ bool MCore::LoadEngine() void MCore::UnloadEngine() { + if (!MRootDomain) + return; + PROFILE_CPU(); CoreCLR::CallStaticMethodByName(TEXT("Exit")); MDomains.ClearDelete(); MRootDomain = nullptr; diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 2f2fd7610..553cb6f88 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -140,7 +140,7 @@ bool ScriptingService::Init() // Initialize managed runtime if (MCore::LoadEngine()) { - LOG(Fatal, "Mono initialization failed."); + LOG(Fatal, "C# runtime initialization failed."); return true; } diff --git a/Source/ThirdParty/nethost/nethost.Build.cs b/Source/ThirdParty/nethost/nethost.Build.cs index a33790d08..048a8ee12 100644 --- a/Source/ThirdParty/nethost/nethost.Build.cs +++ b/Source/ThirdParty/nethost/nethost.Build.cs @@ -1,7 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System.IO; using System; +using System.IO; using Flax.Build; using Flax.Build.NativeCpp; @@ -29,9 +29,12 @@ public class nethost : ThirdPartyModule options.SourceFiles.Clear(); + // Get .NET SDK runtime host var dotnetSdk = DotNetSdk.Instance; if (!dotnetSdk.IsValid) throw new Exception($"Missing NET SDK {DotNetSdk.MinimumVersion}."); + if (!dotnetSdk.GetHostRuntime(options.Platform.Target, options.Architecture, out var hostRuntimePath)) + throw new Exception($"Missing NET SDK runtime for {options.Platform.Target} {options.Architecture}."); // Setup build configuration switch (options.Platform.Target) @@ -40,22 +43,25 @@ public class nethost : ThirdPartyModule case TargetPlatform.XboxOne: case TargetPlatform.XboxScarlett: case TargetPlatform.UWP: - options.OutputFiles.Add(Path.Combine(dotnetSdk.HostRootPath, "nethost.lib")); - options.DependencyFiles.Add(Path.Combine(dotnetSdk.HostRootPath, "nethost.dll")); + options.OutputFiles.Add(Path.Combine(hostRuntimePath, "nethost.lib")); + options.DependencyFiles.Add(Path.Combine(hostRuntimePath, "nethost.dll")); break; case TargetPlatform.Linux: case TargetPlatform.Android: case TargetPlatform.Switch: case TargetPlatform.PS4: case TargetPlatform.PS5: + options.OutputFiles.Add(Path.Combine(hostRuntimePath, "libnethost.a")); + options.DependencyFiles.Add(Path.Combine(hostRuntimePath, "libnethost.so")); + break; case TargetPlatform.Mac: - options.OutputFiles.Add(Path.Combine(dotnetSdk.HostRootPath, "libnethost.a")); - options.DependencyFiles.Add(Path.Combine(dotnetSdk.HostRootPath, "libnethost.so")); + options.OutputFiles.Add(Path.Combine(hostRuntimePath, "libnethost.a")); + options.DependencyFiles.Add(Path.Combine(hostRuntimePath, "libnethost.dylib")); break; default: throw new InvalidPlatformException(options.Platform.Target); } - options.PublicIncludePaths.Add(dotnetSdk.HostRootPath); + options.PublicIncludePaths.Add(hostRuntimePath); options.ScriptingAPI.Defines.Add("USE_NETCORE"); options.DependencyFiles.Add(Path.Combine(FolderPath, "FlaxEngine.CSharp.runtimeconfig.json")); } diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index 35cce8d98..381e3248c 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -3,6 +3,7 @@ using System; using System.IO; using System.Linq; +using System.Collections.Generic; using Microsoft.Win32; namespace Flax.Build @@ -12,6 +13,8 @@ namespace Flax.Build /// public sealed class DotNetSdk : Sdk { + private Dictionary, string> _hostRuntimes = new(); + /// /// The singleton instance. /// @@ -31,6 +34,7 @@ namespace Flax.Build { TargetPlatform.Windows, TargetPlatform.Linux, + TargetPlatform.Mac, }; } } @@ -45,11 +49,6 @@ namespace Flax.Build /// public readonly string RuntimeVersionName; - /// - /// Dotnet hosting library files path (eg. /packs/Microsoft.NETCore.App.Host.//runtimes/-/native). - /// - public readonly string HostRootPath; - /// /// Initializes a new instance of the class. /// @@ -114,6 +113,15 @@ namespace Flax.Build dotnetRuntimeVersions = Directory.GetDirectories($"{dotnetPath}shared/Microsoft.NETCore.App/").Select(Path.GetFileName).ToArray(); break; } + case TargetPlatform.Mac: + { + rid = $"osx-{arch}"; + ridFallback = ""; + dotnetPath = "/usr/local/share/dotnet/"; + dotnetSdkVersions = Directory.GetDirectories($"{dotnetPath}sdk/").Select(Path.GetFileName).ToArray(); + dotnetRuntimeVersions = Directory.GetDirectories($"{dotnetPath}shared/Microsoft.NETCore.App/").Select(Path.GetFileName).ToArray(); + break; + } default: throw new InvalidPlatformException(platform); } @@ -134,22 +142,49 @@ namespace Flax.Build Log.Warning($"Unsupported .NET SDK {dotnetSdkVersion} version found. Minimum version required is .NET {MinimumVersion}."); return; } - HostRootPath = Path.Combine(dotnetPath, $"packs/Microsoft.NETCore.App.Host.{rid}/{dotnetRuntimeVersion}/runtimes/{rid}/native"); - if (!string.IsNullOrEmpty(ridFallback) && !Directory.Exists(HostRootPath)) - HostRootPath = Path.Combine(dotnetPath, $"packs/Microsoft.NETCore.App.Host.{ridFallback}/{dotnetRuntimeVersion}/runtimes/{ridFallback}/native"); - if (!Directory.Exists(HostRootPath)) - { - Log.Warning($"Missing .NET SDK host runtime path in {HostRootPath}."); - return; - } - - // Found RootPath = dotnetPath; - IsValid = true; Version = ParseVersion(dotnetSdkVersion); VersionName = dotnetSdkVersion; RuntimeVersionName = dotnetRuntimeVersion; + + // Pick SDK runtime + if (!TryAddHostRuntime(platform, architecture, rid) && !TryAddHostRuntime(platform, architecture, ridFallback)) + { + var path = Path.Combine(RootPath, $"packs/Microsoft.NETCore.App.Host.{rid}/{RuntimeVersionName}/runtimes/{rid}/native"); + Log.Warning($"Missing .NET SDK host runtime for {platform} {architecture} ({path})."); + return; + } + TryAddHostRuntime(TargetPlatform.Windows, TargetArchitecture.x86, "win-x86"); + TryAddHostRuntime(TargetPlatform.Windows, TargetArchitecture.x64, "win-x64"); + TryAddHostRuntime(TargetPlatform.Windows, TargetArchitecture.ARM64, "win-arm64"); + TryAddHostRuntime(TargetPlatform.Mac, TargetArchitecture.x64, "osx-x64"); + TryAddHostRuntime(TargetPlatform.Mac, TargetArchitecture.ARM64, "osx-arm64"); + + // Found + IsValid = true; Log.Verbose($"Found .NET SDK {VersionName} (runtime {RuntimeVersionName}) at {RootPath}"); + foreach (var e in _hostRuntimes) + Log.Verbose($" - Host Runtime for {e.Key.Key} {e.Key.Value}"); + } + + /// + /// Gets the path to runtime host contents folder for a given target platform and architecure. + /// In format: /packs/Microsoft.NETCore.App.Host.//runtimes/-/native + /// + public bool GetHostRuntime(TargetPlatform platform, TargetArchitecture arch, out string path) + { + return _hostRuntimes.TryGetValue(new KeyValuePair(platform, arch), out path); + } + + private bool TryAddHostRuntime(TargetPlatform platform, TargetArchitecture arch, string rid) + { + if (string.IsNullOrEmpty(rid)) + return false; + var path = Path.Combine(RootPath, $"packs/Microsoft.NETCore.App.Host.{rid}/{RuntimeVersionName}/runtimes/{rid}/native"); + var exists = Directory.Exists(path); + if (exists) + _hostRuntimes[new KeyValuePair(platform, arch)] = path; + return exists; } private static Version ParseVersion(string version)