// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using System.Diagnostics; using Flax.Build.NativeCpp; using System.Runtime.InteropServices; namespace Flax.Build { /// /// The base class for all platform toolsets. /// public abstract class Platform { private static Platform _buildPlatform; private static Platform[] _platforms; private Dictionary _toolchains; /// /// Gets the current target platform that build tool runs on. /// public static TargetPlatform BuildTargetPlatform { get { if (_buildPlatform == null) { OperatingSystem os = Environment.OSVersion; PlatformID platformId = os.Platform; switch (platformId) { case PlatformID.Win32NT: case PlatformID.Win32S: case PlatformID.Win32Windows: case PlatformID.WinCE: return TargetPlatform.Windows; case PlatformID.Unix: { try { Process p = new Process { StartInfo = { UseShellExecute = false, RedirectStandardOutput = true, FileName = "uname", Arguments = "-s", } }; p.Start(); string uname = p.StandardOutput.ReadToEnd().Trim(); if (uname == "Darwin") return TargetPlatform.Mac; } catch (Exception) { } return TargetPlatform.Linux; } case PlatformID.MacOSX: return TargetPlatform.Mac; default: throw new NotImplementedException(string.Format("Unsupported build platform {0}.", platformId)); } } return _buildPlatform.Target; } } /// /// Gets the current target architecture that build tool runs on. /// public static TargetArchitecture BuildTargetArchitecture { get { var architectureId = RuntimeInformation.ProcessArchitecture; switch (architectureId) { case Architecture.X86: return TargetArchitecture.x86; case Architecture.X64: return TargetArchitecture.x64; case Architecture.Arm: return TargetArchitecture.ARM; case Architecture.Arm64: return TargetArchitecture.ARM64; default: throw new NotImplementedException(string.Format("Unsupported build platform {0}.", architectureId)); } } } /// /// Gets the current platform that build tool runs on. /// public static Platform BuildPlatform { get { if (_buildPlatform == null) { _buildPlatform = GetPlatform(BuildTargetPlatform); } return _buildPlatform; } } /// /// Gets the platform target type. /// public abstract TargetPlatform Target { get; } /// /// Gets a value indicating whether required external SDKs are installed for this platform. /// public abstract bool HasRequiredSDKsInstalled { get; } /// /// Gets a value indicating whether that platform supports shared libraries (dynamic link libraries). /// public abstract bool HasSharedLibrarySupport { get; } /// /// Gets a value indicating whether that platform supports building target into modular libraries (otherwise will force monolithic linking). /// public virtual bool HasModularBuildSupport => true; /// /// Gets a value indicating whether that platform supports using executable file as a reference when linking shared library. Otherwise, platform enforces monolithic linking or separate shared libraries usage. /// public virtual bool HasExecutableFileReferenceSupport => false; /// /// Gets a value indicating whether that platform supports executing native code generated dynamically (JIT), otherwise requires ahead-of-time compilation (AOT). /// public virtual bool HasDynamicCodeExecutionSupport => true; /// /// Gets the executable file extension (including leading dot). /// public abstract string ExecutableFileExtension { get; } /// /// Gets the shared library file extension (including leading dot). /// public abstract string SharedLibraryFileExtension { get; } /// /// Gets the static library file extension (including leading dot). /// public abstract string StaticLibraryFileExtension { get; } /// /// Gets the program database file extension (including leading dot). /// public abstract string ProgramDatabaseFileExtension { get; } /// /// Gets the executable files prefix. /// public virtual string ExecutableFilePrefix => string.Empty; /// /// Gets the shared library files prefix. /// public virtual string SharedLibraryFilePrefix => string.Empty; /// /// Gets the static library files prefix. /// public virtual string StaticLibraryFilePrefix => string.Empty; /// /// Gets the default project format used by the given platform. /// public abstract Projects.ProjectFormat DefaultProjectFormat { get; } /// /// Creates the toolchain for a given architecture. /// /// The architecture. /// The toolchain. protected abstract Toolchain CreateToolchain(TargetArchitecture architecture); /// /// Determines whether this platform can build for the specified platform. /// /// The platform. /// true if this platform can build the specified platform; otherwise, false. public virtual bool CanBuildPlatform(TargetPlatform platform) { return false; } /// /// Gets the path to the output file for the linker. /// /// The original library name. /// The output file type. /// The file name (including prefix, name and extension). public string GetLinkOutputFileName(string name, LinkerOutput output) { switch (output) { case LinkerOutput.Executable: return ExecutableFilePrefix + name + ExecutableFileExtension; case LinkerOutput.SharedLibrary: return SharedLibraryFilePrefix + name + SharedLibraryFileExtension; case LinkerOutput.StaticLibrary: case LinkerOutput.ImportLibrary: return StaticLibraryFilePrefix + name + StaticLibraryFileExtension; default: throw new ArgumentOutOfRangeException(nameof(output), output, null); } } /// /// Creates the build toolchain for a given platform and architecture. /// /// The target platform. /// True if return null platform if it's missing, otherwise will invoke an exception. /// The platform. public static Platform GetPlatform(TargetPlatform targetPlatform, bool nullIfMissing = false) { if (_platforms == null) { using (new ProfileEventScope("GetPlatforms")) _platforms = Builder.BuildTypes.Where(x => x.IsClass && !x.IsAbstract && x.IsSubclassOf(typeof(Platform))).Select(Activator.CreateInstance).Cast().ToArray(); } foreach (var platform in _platforms) { if (platform.Target == targetPlatform) { return platform; } } if (nullIfMissing) return null; throw new Exception(string.Format("Platform {0} is not supported.", targetPlatform)); } /// /// Tries to create the build toolchain for a given architecture. Returns null if platform is not supported. /// /// The target architecture. /// The toolchain. public Toolchain TryGetToolchain(TargetArchitecture targetArchitecture) { Toolchain result = null; if (HasRequiredSDKsInstalled) { try { result = GetToolchain(targetArchitecture); } catch (Exception ex) { Log.Exception(ex); } } return result; } /// /// Creates the build toolchain for a given architecture. /// /// The target architecture. /// The toolchain. public Toolchain GetToolchain(TargetArchitecture targetArchitecture) { if (!HasRequiredSDKsInstalled) throw new Exception(string.Format("Platform {0} has no required SDK installed and cannot be used.", Target)); if (_toolchains == null) _toolchains = new Dictionary(); Toolchain toolchain; if (_toolchains.TryGetValue(targetArchitecture, out toolchain)) return toolchain; toolchain = CreateToolchain(targetArchitecture); _toolchains.Add(targetArchitecture, toolchain); return toolchain; } /// /// Gets path to the current platform binary directory /// public static string GetEditorBinaryDirectory() { var subdir = "Binaries/Editor/"; switch (Platform.BuildTargetPlatform) { case TargetPlatform.Windows: { switch (Platform.BuildTargetArchitecture) { case TargetArchitecture.x64: return subdir + "Win64"; case TargetArchitecture.x86: return subdir + "Win32"; case TargetArchitecture.ARM64: return subdir + "ARM64"; default: throw new NotImplementedException($"{Platform.BuildTargetPlatform}: {Platform.BuildTargetArchitecture}"); } } case TargetPlatform.Linux: return subdir + "Linux"; case TargetPlatform.Mac: return subdir + "Mac"; } throw new NotImplementedException(Platform.BuildTargetPlatform.ToString()); } /// /// Creates the project files generator for the specified project format. /// /// The target platform. /// The target architecture. /// True if the given platform is supported, otherwise false. public static bool IsPlatformSupported(TargetPlatform targetPlatform, TargetArchitecture targetArchitecture) { if (targetArchitecture == TargetArchitecture.AnyCPU) return true; switch (targetPlatform) { case TargetPlatform.Windows: return targetArchitecture == TargetArchitecture.x64 || targetArchitecture == TargetArchitecture.x86 || targetArchitecture == TargetArchitecture.ARM64; case TargetPlatform.XboxScarlett: return targetArchitecture == TargetArchitecture.x64; case TargetPlatform.XboxOne: return targetArchitecture == TargetArchitecture.x64; case TargetPlatform.UWP: return targetArchitecture == TargetArchitecture.x64; case TargetPlatform.Linux: return targetArchitecture == TargetArchitecture.x64; case TargetPlatform.PS4: return targetArchitecture == TargetArchitecture.x64; case TargetPlatform.PS5: return targetArchitecture == TargetArchitecture.x64; case TargetPlatform.Android: return targetArchitecture == TargetArchitecture.ARM64; case TargetPlatform.Switch: return targetArchitecture == TargetArchitecture.ARM64; case TargetPlatform.Mac: return targetArchitecture == TargetArchitecture.ARM64 || targetArchitecture == TargetArchitecture.x64; case TargetPlatform.iOS: return targetArchitecture == TargetArchitecture.ARM64; default: return false; } } } }