diff --git a/Source/ThirdParty/nethost/nethost.Build.cs b/Source/ThirdParty/nethost/nethost.Build.cs index b69035185..383153477 100644 --- a/Source/ThirdParty/nethost/nethost.Build.cs +++ b/Source/ThirdParty/nethost/nethost.Build.cs @@ -44,44 +44,9 @@ public class nethost : ThirdPartyModule options.SourceFiles.Clear(); - string arch = "x64"; //options.Architecture == TargetArchitecture.x64 ? "x64" : "x86"; - - string dotnetPath, dotnetVersion; - string appHostRuntimePath; - string os; - string[] dotnetVersions; - - // Pick DotNet SDK - if (options.Platform.Target == TargetPlatform.Windows) - { - os = $"win-{arch}"; -#pragma warning disable CA1416 - using RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); - using RegistryKey hostKey = baseKey.OpenSubKey(@$"SOFTWARE\dotnet\Setup\InstalledVersions\{arch}\sharedhost"); - dotnetPath = (string)hostKey.GetValue("Path"); - using RegistryKey runtimeKey = baseKey.OpenSubKey(@$"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sharedfx\Microsoft.NETCore.App"); - dotnetVersions = runtimeKey.GetValueNames(); -#pragma warning restore CA1416 - } - else if (options.Platform.Target == TargetPlatform.Linux) - { - // TODO: Support /etc/dotnet/install_location - dotnetPath = "/usr/share/dotnet/"; - os = $"linux-{arch}"; - dotnetVersions = Directory.GetDirectories($"{dotnetPath}host/fxr/").Select(x => Path.GetFileName(x)).ToArray(); - } - else - throw new InvalidPlatformException(options.Platform.Target); - - // Pick SDK version - dotnetVersion = dotnetVersions.OrderByDescending(ParseVersion).FirstOrDefault(); - if (string.IsNullOrEmpty(dotnetPath)) - dotnetPath = Environment.GetEnvironmentVariable("DOTNET_ROOT"); - if (string.IsNullOrEmpty(dotnetPath) || string.IsNullOrEmpty(dotnetVersion)) - throw new Exception("Failed to find dotnet installation"); - int majorVersion = int.Parse(dotnetVersion.Substring(0, dotnetVersion.IndexOf("."))); - if (majorVersion < 7) - throw new Exception($"Unsupported dotnet version found, minimum version required is .NET 7 (found {dotnetVersion})"); + var dotnetSdk = DotNetSdk.Instance; + if (!dotnetSdk.IsValid) + throw new Exception($"Missing NET SDK {DotNetSdk.MinimumVersion}."); // Setup build configuration switch (options.Platform.Target) @@ -90,9 +55,8 @@ public class nethost : ThirdPartyModule case TargetPlatform.XboxOne: case TargetPlatform.XboxScarlett: case TargetPlatform.UWP: - appHostRuntimePath = string.Format("{0}packs\\Microsoft.NETCore.App.Host.{1}\\{2}\\runtimes\\{1}\\native", dotnetPath, os, dotnetVersion); - options.OutputFiles.Add(Path.Combine(appHostRuntimePath, "nethost.lib")); - options.DependencyFiles.Add(Path.Combine(appHostRuntimePath, "nethost.dll")); + options.OutputFiles.Add(Path.Combine(dotnetSdk.HostRootPath, "nethost.lib")); + options.DependencyFiles.Add(Path.Combine(dotnetSdk.HostRootPath, "nethost.dll")); break; case TargetPlatform.Linux: case TargetPlatform.Android: @@ -100,14 +64,13 @@ public class nethost : ThirdPartyModule case TargetPlatform.PS4: case TargetPlatform.PS5: case TargetPlatform.Mac: - appHostRuntimePath = string.Format("{0}packs/Microsoft.NETCore.App.Host.{1}/{2}/runtimes/{1}/native", dotnetPath, os, dotnetVersion); - options.OutputFiles.Add(Path.Combine(appHostRuntimePath, "libnethost.a")); - options.DependencyFiles.Add(Path.Combine(appHostRuntimePath, "libnethost.so")); + options.OutputFiles.Add(Path.Combine(dotnetSdk.HostRootPath, "libnethost.a")); + options.DependencyFiles.Add(Path.Combine(dotnetSdk.HostRootPath, "libnethost.so")); break; default: throw new InvalidPlatformException(options.Platform.Target); } - options.PublicIncludePaths.Add(appHostRuntimePath); + options.PublicIncludePaths.Add(dotnetSdk.HostRootPath); 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/Builder.DotNet.cs b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs index 7c63cfba9..1ddc390f4 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs @@ -5,9 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Flax.Build.Graph; -using Flax.Build.Platforms; using Flax.Deploy; -using Microsoft.Win32; namespace Flax.Build { @@ -148,26 +146,12 @@ namespace Flax.Build private static void BuildDotNet(TaskGraph graph, BuildData buildData, NativeCpp.BuildOptions buildOptions, string name, List sourceFiles, HashSet fileReferences = null, IGrouping binaryModule = null) { -#if USE_NETCORE - static Version ParseVersion(string version) - { - // Give precedence to final releases over release candidate / beta releases - int rev = 9999; - if (version.Contains("-")) // e.g. 7.0.0-rc.2.22472.3 - { - version = version.Substring(0, version.IndexOf("-")); - rev = 0; - } - Version ver = new Version(version); - return new Version(ver.Major, ver.Minor, ver.Build, rev); - } -#endif // Setup build options var buildPlatform = Platform.BuildTargetPlatform; var outputPath = Path.GetDirectoryName(buildData.Target.GetOutputFilePath(buildOptions)); var outputFile = Path.Combine(outputPath, name + ".dll"); var outputDocFile = Path.Combine(outputPath, name + ".xml"); - string monoRoot, monoPath, cscPath, referenceAssemblies, referenceAnalyzers; + string monoRoot, monoPath = null, cscPath, referenceAssemblies, referenceAnalyzers; switch (buildPlatform) { case TargetPlatform.Windows: @@ -175,28 +159,18 @@ namespace Flax.Build monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Windows", "Mono"); // Prefer installed Roslyn C# compiler over Mono one - monoPath = null; cscPath = Path.Combine(Path.GetDirectoryName(VCEnvironment.MSBuildPath), "Roslyn", "csc.exe"); #if USE_NETCORE - // dotnet - if (WindowsPlatformBase.TryReadDirRegistryKey(@"HKEY_LOCAL_MACHINE\SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedhost", "Path", out string dotnetPath)) + var dotnetSdk = DotNetSdk.Instance; + if (dotnetSdk.IsValid) { -#pragma warning disable CA1416 - string arch = "x64"; - using RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); - using RegistryKey sdkVersionsKey = baseKey.OpenSubKey($@"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sdk"); - using RegistryKey sharedfxVersionsKey = baseKey.OpenSubKey($@"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sharedfx\Microsoft.NETCore.App"); - using RegistryKey hostfxrKey = baseKey.OpenSubKey($@"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\hostfxr"); - - string dotnetSdkVersion = sdkVersionsKey.GetValueNames().OrderByDescending(x => ParseVersion(x)).FirstOrDefault(); - string dotnetSharedfxVersion = sharedfxVersionsKey.GetValueNames().OrderByDescending(x => ParseVersion(x)).FirstOrDefault(); -#pragma warning restore CA1416 - cscPath = @$"{dotnetPath}sdk\{dotnetSdkVersion}\Roslyn\bincore\csc.dll"; - referenceAssemblies = @$"{dotnetPath}shared\Microsoft.NETCore.App\{dotnetSharedfxVersion}\"; - referenceAnalyzers = @$"{dotnetPath}packs\Microsoft.NETCore.App.Ref\{dotnetSharedfxVersion}\analyzers\dotnet\cs\"; + // Use dotnet + cscPath = @$"{dotnetSdk.RootPath}sdk\{dotnetSdk.VersionName}\Roslyn\bincore\csc.dll"; + referenceAssemblies = @$"{dotnetSdk.RootPath}shared\Microsoft.NETCore.App\{dotnetSdk.RuntimeVersionName}\"; + referenceAnalyzers = @$"{dotnetSdk.RootPath}packs\Microsoft.NETCore.App.Ref\{dotnetSdk.RuntimeVersionName}\analyzers\dotnet\cs\"; } - else //if (!File.Exists(cscPath)) + else #endif { // Fallback to Mono binaries @@ -210,24 +184,13 @@ namespace Flax.Build case TargetPlatform.Linux: { #if USE_NETCORE - // TODO: Support /etc/dotnet/install_location - string dotnetPath = "/usr/share/dotnet/"; - string arch = "x64"; - string os = $"linux-{arch}"; - monoPath = null; - - string[] sharedfxVersions = Directory.GetDirectories($"{dotnetPath}shared/Microsoft.NETCore.App/").Select(x => Path.GetFileName(x)).ToArray(); - string dotnetSharedfxVersion = sharedfxVersions.OrderByDescending(x => ParseVersion(x)).FirstOrDefault(); - - string[] sdkVersions = Directory.GetDirectories($"{dotnetPath}sdk/").Select(x => Path.GetFileName(x)).ToArray(); - string dotnetSdkVersion = sdkVersions.OrderByDescending(x => ParseVersion(x)).FirstOrDefault(); - - int majorVersion = int.Parse(dotnetSdkVersion.Substring(0, dotnetSdkVersion.IndexOf("."))); - if (majorVersion >= 7) + var dotnetSdk = DotNetSdk.Instance; + if (dotnetSdk.IsValid) { - cscPath = @$"{dotnetPath}sdk/{dotnetSdkVersion}/Roslyn/bincore/csc.dll"; - referenceAssemblies = @$"{dotnetPath}shared/Microsoft.NETCore.App/{dotnetSharedfxVersion}/"; - referenceAnalyzers = @$"{dotnetPath}packs/Microsoft.NETCore.App.Ref/{dotnetSharedfxVersion}/analyzers/dotnet/cs/"; + // Use dotnet + cscPath = @$"{dotnetSdk.RootPath}sdk/{dotnetSdk.VersionName}/Roslyn/bincore/csc.dll"; + referenceAssemblies = @$"{dotnetSdk.RootPath}shared/Microsoft.NETCore.App/{dotnetSdk.RuntimeVersionName}/"; + referenceAnalyzers = @$"{dotnetSdk.RootPath}packs/Microsoft.NETCore.App.Ref/{dotnetSdk.RuntimeVersionName}/analyzers/dotnet/cs/"; } else #endif diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs new file mode 100644 index 000000000..e27526369 --- /dev/null +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -0,0 +1,158 @@ +// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. + +using System; +using System.IO; +using System.Linq; +using Microsoft.Win32; + +namespace Flax.Build +{ + /// + /// The DotNet SDK. + /// + public sealed class DotNetSdk : Sdk + { + /// + /// The singleton instance. + /// + public static readonly DotNetSdk Instance = new DotNetSdk(); + + /// + /// The minimum SDK version. + /// + public static Version MinimumVersion => new Version(7, 0); + + /// + public override TargetPlatform[] Platforms + { + get + { + return new[] + { + TargetPlatform.Windows, + TargetPlatform.Linux, + }; + } + } + + /// + /// Dotnet SDK version (eg. 7.0). + /// + public readonly string VersionName; + + /// + /// Dotnet Shared FX library version (eg. 7.0). + /// + 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. + /// + public DotNetSdk() + { + var platform = Platform.BuildTargetPlatform; + var architecture = Platform.BuildTargetArchitecture; + if (!Platforms.Contains(platform)) + return; + + // Find system-installed SDK + string dotnetPath; + string os, arch; + string[] dotnetSdkVersions, dotnetRuntimeVersions; + switch (architecture) + { + case TargetArchitecture.x86: + arch = "x86"; + break; + case TargetArchitecture.x64: + arch = "x64"; + break; + case TargetArchitecture.ARM: + arch = "arm"; + break; + case TargetArchitecture.ARM64: + arch = "arm64"; + break; + default: + throw new InvalidArchitectureException(architecture); + } + switch (platform) + { + case TargetPlatform.Windows: + { + os = $"win-{arch}"; +#pragma warning disable CA1416 + using RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); + using RegistryKey sdkVersionsKey = baseKey.OpenSubKey($@"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sdk"); + using RegistryKey runtimeKey = baseKey.OpenSubKey(@$"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sharedfx\Microsoft.NETCore.App"); + using RegistryKey hostKey = baseKey.OpenSubKey(@$"SOFTWARE\dotnet\Setup\InstalledVersions\{arch}\sharedhost"); + dotnetPath = (string)hostKey.GetValue("Path"); + dotnetSdkVersions = sdkVersionsKey.GetValueNames(); + dotnetRuntimeVersions = runtimeKey.GetValueNames(); +#pragma warning restore CA1416 + break; + } + case TargetPlatform.Linux: + { + // TODO: Support /etc/dotnet/install_location + os = $"linux-{arch}"; + dotnetPath = "/usr/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); + } + + // Pick SDK version + string dotnetSdkVersion = dotnetSdkVersions.OrderByDescending(ParseVersion).FirstOrDefault(); + string dotnetRuntimeVersion = dotnetRuntimeVersions.OrderByDescending(ParseVersion).FirstOrDefault(); + if (string.IsNullOrEmpty(dotnetSdkVersion)) + dotnetSdkVersion = Environment.GetEnvironmentVariable("DOTNET_ROOT"); + if (string.IsNullOrEmpty(dotnetPath) || string.IsNullOrEmpty(dotnetSdkVersion) || string.IsNullOrEmpty(dotnetRuntimeVersion)) + { + Log.Warning("Missing .NET SDK"); + return; + } + int majorVersion = int.Parse(dotnetSdkVersion.Substring(0, dotnetSdkVersion.IndexOf("."))); + if (majorVersion < MinimumVersion.Major) + { + Log.Warning($"Unsupported .NET SDK {dotnetSdkVersion} version found. Minimum version required is .NET {MinimumVersion}."); + return; + } + HostRootPath = Path.Combine(dotnetPath, $"packs/Microsoft.NETCore.App.Host.{os}/{dotnetRuntimeVersion}/runtimes/{os}/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; + Log.Verbose($"Found .NET SDK {VersionName} (runtime {RuntimeVersionName}) at {RootPath}"); + } + + private static Version ParseVersion(string version) + { + // Give precedence to final releases over release candidate / beta releases + int rev = 9999; + if (version.Contains("-")) // e.g. 7.0.0-rc.2.22472.3 + { + version = version.Substring(0, version.IndexOf("-")); + rev = 0; + } + Version ver = new Version(version); + return new Version(ver.Major, ver.Minor, ver.Build, rev); + } + } +}