332 lines
13 KiB
C#
332 lines
13 KiB
C#
// Copyright (c) 2012-2023 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
|
|
{
|
|
/// <summary>
|
|
/// The base class for all platform toolsets.
|
|
/// </summary>
|
|
public abstract class Platform
|
|
{
|
|
private static Platform _buildPlatform;
|
|
private static Platform[] _platforms;
|
|
private Dictionary<TargetArchitecture, Toolchain> _toolchains;
|
|
|
|
/// <summary>
|
|
/// Gets the current target platform that build tool runs on.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the current target architecture that build tool runs on.
|
|
/// </summary>
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the current platform that build tool runs on.
|
|
/// </summary>
|
|
public static Platform BuildPlatform
|
|
{
|
|
get
|
|
{
|
|
if (_buildPlatform == null)
|
|
{
|
|
_buildPlatform = GetPlatform(BuildTargetPlatform);
|
|
}
|
|
return _buildPlatform;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the platform target type.
|
|
/// </summary>
|
|
public abstract TargetPlatform Target { get; }
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether required external SDKs are installed for this platform.
|
|
/// </summary>
|
|
public abstract bool HasRequiredSDKsInstalled { get; }
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether that platform supports shared libraries (dynamic link libraries).
|
|
/// </summary>
|
|
public abstract bool HasSharedLibrarySupport { get; }
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether that platform supports building target into modular libraries (otherwise will force monolithic linking).
|
|
/// </summary>
|
|
public virtual bool HasModularBuildSupport => true;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public virtual bool HasExecutableFileReferenceSupport => false;
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether that platform supports executing native code generated dynamically (JIT), otherwise requires ahead-of-time compilation (AOT).
|
|
/// </summary>
|
|
public virtual bool HasDynamicCodeExecutionSupport => true;
|
|
|
|
/// <summary>
|
|
/// Gets the executable file extension (including leading dot).
|
|
/// </summary>
|
|
public abstract string ExecutableFileExtension { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the shared library file extension (including leading dot).
|
|
/// </summary>
|
|
public abstract string SharedLibraryFileExtension { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the static library file extension (including leading dot).
|
|
/// </summary>
|
|
public abstract string StaticLibraryFileExtension { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the program database file extension (including leading dot).
|
|
/// </summary>
|
|
public abstract string ProgramDatabaseFileExtension { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the executable files prefix.
|
|
/// </summary>
|
|
public virtual string ExecutableFilePrefix => string.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the shared library files prefix.
|
|
/// </summary>
|
|
public virtual string SharedLibraryFilePrefix => string.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the static library files prefix.
|
|
/// </summary>
|
|
public virtual string StaticLibraryFilePrefix => string.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the default project format used by the given platform.
|
|
/// </summary>
|
|
public abstract Projects.ProjectFormat DefaultProjectFormat { get; }
|
|
|
|
/// <summary>
|
|
/// Creates the toolchain for a given architecture.
|
|
/// </summary>
|
|
/// <param name="architecture">The architecture.</param>
|
|
/// <returns>The toolchain.</returns>
|
|
protected abstract Toolchain CreateToolchain(TargetArchitecture architecture);
|
|
|
|
/// <summary>
|
|
/// Determines whether this platform can build for the specified platform.
|
|
/// </summary>
|
|
/// <param name="platform">The platform.</param>
|
|
/// <returns><c>true</c> if this platform can build the specified platform; otherwise, <c>false</c>.</returns>
|
|
public virtual bool CanBuildPlatform(TargetPlatform platform)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the path to the output file for the linker.
|
|
/// </summary>
|
|
/// <param name="name">The original library name.</param>
|
|
/// <param name="output">The output file type.</param>
|
|
/// <returns>The file name (including prefix, name and extension).</returns>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the build toolchain for a given platform and architecture.
|
|
/// </summary>
|
|
/// <param name="targetPlatform">The target platform.</param>
|
|
/// <param name="nullIfMissing">True if return null platform if it's missing, otherwise will invoke an exception.</param>
|
|
/// <returns>The toolchain.</returns>
|
|
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<Platform>().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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to create the build toolchain for a given architecture. Returns null if platform is not supported.
|
|
/// </summary>
|
|
/// <param name="targetArchitecture">The target architecture.</param>
|
|
/// <returns>The toolchain.</returns>
|
|
public Toolchain TryGetToolchain(TargetArchitecture targetArchitecture)
|
|
{
|
|
Toolchain result = null;
|
|
if (HasRequiredSDKsInstalled)
|
|
{
|
|
try
|
|
{
|
|
result = GetToolchain(targetArchitecture);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Exception(ex);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the build toolchain for a given architecture.
|
|
/// </summary>
|
|
/// <param name="targetArchitecture">The target architecture.</param>
|
|
/// <returns>The toolchain.</returns>
|
|
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<TargetArchitecture, Toolchain>();
|
|
|
|
Toolchain toolchain;
|
|
if (_toolchains.TryGetValue(targetArchitecture, out toolchain))
|
|
return toolchain;
|
|
|
|
toolchain = CreateToolchain(targetArchitecture);
|
|
_toolchains.Add(targetArchitecture, toolchain);
|
|
|
|
return toolchain;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets path to the current platform binary directory
|
|
/// </summary>
|
|
public static string GetEditorBinaryDirectory()
|
|
{
|
|
var subdir = "Binaries/Editor/";
|
|
switch (Platform.BuildTargetPlatform)
|
|
{
|
|
case TargetPlatform.Windows:
|
|
return subdir + "Win64";
|
|
case TargetPlatform.Linux:
|
|
return subdir + "Linux";
|
|
case TargetPlatform.Mac:
|
|
return subdir + "Mac";
|
|
}
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the project files generator for the specified project format.
|
|
/// </summary>
|
|
/// <param name="targetPlatform">The target platform.</param>
|
|
/// <param name="targetArchitecture">The target architecture.</param>
|
|
/// <returns>True if the given platform is supported, otherwise false.</returns>
|
|
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;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|