470 lines
21 KiB
C#
470 lines
21 KiB
C#
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using Flax.Build;
|
|
using Flax.Build.Platforms;
|
|
using Flax.Build.Projects.VisualStudio;
|
|
using Flax.Deploy;
|
|
|
|
namespace Flax.Deps
|
|
{
|
|
/// <summary>
|
|
/// Represents a single dependency package required by the engine to be pre-build.
|
|
/// </summary>
|
|
abstract class Dependency
|
|
{
|
|
/// <summary>
|
|
/// The options.
|
|
/// </summary>
|
|
public class BuildOptions
|
|
{
|
|
/// <summary>
|
|
/// The intermediate folder for the deps to be build inside it.
|
|
/// </summary>
|
|
public string IntermediateFolder;
|
|
|
|
/// <summary>
|
|
/// The output folder for the platform dependencies.
|
|
/// </summary>
|
|
public string PlatformsFolder;
|
|
|
|
/// <summary>
|
|
/// The ThirdParty header files folder for the deps.
|
|
/// </summary>
|
|
public string ThirdPartyFolder;
|
|
|
|
/// <summary>
|
|
/// The target platforms to build dependency for (contains only platforms supported by the dependency itself).
|
|
/// </summary>
|
|
public TargetPlatform[] Platforms;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the build platform.
|
|
/// </summary>
|
|
protected static TargetPlatform BuildPlatform => Platform.BuildPlatform.Target;
|
|
|
|
/// <summary>
|
|
/// Gets the platforms list supported by this dependency to build on the current build platform (based on <see cref="Platform.BuildPlatform"/>).
|
|
/// </summary>
|
|
public abstract TargetPlatform[] Platforms { get; }
|
|
|
|
/// <summary>
|
|
/// True if build dependency by default, otherwise only when explicitly specified via command line.
|
|
/// </summary>
|
|
public virtual bool BuildByDefault => true;
|
|
|
|
/// <summary>
|
|
/// Builds the dependency package using the specified options.
|
|
/// </summary>
|
|
/// <param name="options">The options.</param>
|
|
public abstract void Build(BuildOptions options);
|
|
|
|
/// <summary>
|
|
/// Gets the dependency third-party packages binaries folder.
|
|
/// </summary>
|
|
/// <param name="options">The options.</param>
|
|
/// <param name="platform">The target platform.</param>
|
|
/// <param name="architecture">The target architecture.</param>
|
|
/// <param name="createIfMissing">Auto-create directory if it's missing.</param>
|
|
/// <returns>The absolute path to the deps folder for the given platform and architecture configuration.</returns>
|
|
public static string GetThirdPartyFolder(BuildOptions options, TargetPlatform platform, TargetArchitecture architecture, bool createIfMissing = true)
|
|
{
|
|
var path = Path.Combine(options.PlatformsFolder, platform.ToString(), "Binaries", "ThirdParty", architecture.ToString());
|
|
if (createIfMissing && !Directory.Exists(path))
|
|
Directory.CreateDirectory(path);
|
|
return path;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dependency packages binaries folder.
|
|
/// </summary>
|
|
/// <param name="options">The options.</param>
|
|
/// <param name="platform">The target platform.</param>
|
|
/// <returns>The absolute path to the deps folder for the given platform and architecture configuration.</returns>
|
|
public static string GetBinariesFolder(BuildOptions options, TargetPlatform platform)
|
|
{
|
|
return Path.Combine(options.PlatformsFolder, platform.ToString(), "Binaries");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Setups the directory.
|
|
/// </summary>
|
|
/// <param name="path">The path.</param>
|
|
/// <param name="forceEmpty">If set to <c>true</c> the directory will be cleared if not empty.</param>
|
|
public static void SetupDirectory(string path, bool forceEmpty)
|
|
{
|
|
if (Directory.Exists(path))
|
|
{
|
|
if (forceEmpty)
|
|
{
|
|
Utilities.DirectoryDelete(path);
|
|
Directory.CreateDirectory(path);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Directory.CreateDirectory(path);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clones the directory.
|
|
/// </summary>
|
|
/// <param name="src">The source folder path.</param>
|
|
/// <param name="dst">The destination folder path.</param>
|
|
public static void CloneDirectory(string src, string dst)
|
|
{
|
|
if (Directory.Exists(dst))
|
|
Utilities.DirectoryDelete(dst);
|
|
Utilities.DirectoryCopy(src, dst);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clones the git repository from the remote url (full repository).
|
|
/// </summary>
|
|
/// <param name="path">The local path for close.</param>
|
|
/// <param name="url">The remote url.</param>
|
|
/// <param name="commit">The commit to checkout.</param>
|
|
/// <param name="args">The custom arguments to add to the clone command.</param>
|
|
/// <param name="submodules">True if initialize submodules of the repository (recursive).</param>
|
|
public static void CloneGitRepo(string path, string url, string commit = null, string args = null, bool submodules = false)
|
|
{
|
|
if (!Directory.Exists(Path.Combine(path, Path.GetFileNameWithoutExtension(url), ".git")))
|
|
{
|
|
string cmdLine = string.Format("clone \"{0}\" \"{1}\"", url, path);
|
|
if (args != null)
|
|
cmdLine += " " + args;
|
|
if (submodules)
|
|
cmdLine += " --recurse-submodules";
|
|
|
|
Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.DefaultTool);
|
|
if (submodules)
|
|
Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.DefaultTool);
|
|
}
|
|
if (commit != null)
|
|
Utilities.Run("git", string.Format("reset --hard {0}", commit), null, null, Utilities.RunOptions.DefaultTool);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clones the git repository from the remote url.
|
|
/// </summary>
|
|
/// <param name="path">The local path for close.</param>
|
|
/// <param name="url">The remote url.</param>
|
|
/// <param name="args">The custom arguments to add to the clone command.</param>
|
|
/// <param name="submodules">True if initialize submodules of the repository (recursive).</param>
|
|
public static void CloneGitRepoFast(string path, string url, string args = null, bool submodules = false)
|
|
{
|
|
if (!Directory.Exists(Path.Combine(path, Path.GetFileNameWithoutExtension(url), ".git")))
|
|
{
|
|
string cmdLine = string.Format("clone \"{0}\" \"{1}\" --depth 1", url, path);
|
|
if (args != null)
|
|
cmdLine += " " + args;
|
|
if (submodules)
|
|
cmdLine += " --recurse-submodules";
|
|
|
|
Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.DefaultTool);
|
|
if (submodules)
|
|
Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.DefaultTool);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clones the git repository from the remote url (clones a single branch).
|
|
/// </summary>
|
|
/// <param name="path">The local path for close.</param>
|
|
/// <param name="url">The remote url.</param>
|
|
/// <param name="branch">The name of the branch to checkout.</param>
|
|
/// <param name="commit">The commit to checkout.</param>
|
|
/// <param name="args">The custom arguments to add to the clone command.</param>
|
|
/// <param name="submodules">True if initialize submodules of the repository (recursive).</param>
|
|
public static void CloneGitRepoSingleBranch(string path, string url, string branch, string commit = null, string args = null, bool submodules = false)
|
|
{
|
|
if (!Directory.Exists(Path.Combine(path, ".git")))
|
|
{
|
|
string cmdLine = string.Format("clone --single-branch --branch {2} \"{0}\" \"{1}\"", url, path, branch);
|
|
if (commit == null)
|
|
cmdLine += " --depth 1";
|
|
if (args != null)
|
|
cmdLine += " " + args;
|
|
if (submodules)
|
|
cmdLine += " --recurse-submodules";
|
|
|
|
Utilities.Run("git", cmdLine, null, null, Utilities.RunOptions.DefaultTool);
|
|
if (submodules)
|
|
Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.DefaultTool);
|
|
}
|
|
|
|
if (commit != null)
|
|
{
|
|
Utilities.Run("git", string.Format("reset --hard {0}", commit), null, path, Utilities.RunOptions.DefaultTool);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes the current branch to the given branch in the git repository (resets all the local changes). Does not perform pull command to update branch with remote repo.
|
|
/// </summary>
|
|
/// <param name="path">The local path that contains git repository.</param>
|
|
/// <param name="branch">The name of the branch to checkout.</param>
|
|
/// <param name="commit">The commit to checkout.</param>
|
|
/// <param name="args">The custom arguments to add to the clone command.</param>
|
|
/// <param name="submodules">True if initialize submodules of the repository (recursive).</param>
|
|
public static void GitCheckout(string path, string branch, string commit = null, string args = null, bool submodules = false)
|
|
{
|
|
string cmdLine = string.Format("checkout -B {0} origin/{0}", branch);
|
|
if (args != null)
|
|
cmdLine += " " + args;
|
|
if (submodules)
|
|
cmdLine += " --recurse-submodules";
|
|
|
|
Utilities.Run("git", cmdLine, null, path, Utilities.RunOptions.DefaultTool);
|
|
if (submodules)
|
|
Utilities.Run("git", "submodule update --init --recursive", null, null, Utilities.RunOptions.DefaultTool);
|
|
|
|
if (commit != null)
|
|
{
|
|
Utilities.Run("git", string.Format("reset --hard {0}", commit), null, path, Utilities.RunOptions.DefaultTool);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets all the local changes.
|
|
/// </summary>
|
|
/// <param name="path">The local path that contains git repository.</param>
|
|
public static void GitResetLocalChanges(string path)
|
|
{
|
|
Utilities.Run("git", "reset --hard", null, path, Utilities.RunOptions.DefaultTool);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds the cmake project.
|
|
/// </summary>
|
|
/// <param name="path">The path.</param>
|
|
/// <param name="envVars">Custom environment variables to pass to the child process.</param>
|
|
public static void BuildCmake(string path, Dictionary<string, string> envVars = null)
|
|
{
|
|
Utilities.Run("cmake", "--build . --config Release", null, path, Utilities.RunOptions.DefaultTool, envVars);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs the cmake tool.
|
|
/// </summary>
|
|
/// <param name="path">The path.</param>
|
|
/// <param name="platform">The output platform.</param>
|
|
/// <param name="architecture">The output architecture.</param>
|
|
/// <param name="customArgs">The custom arguments for the CMake.</param>
|
|
/// <param name="envVars">Custom environment variables to pass to the child process.</param>
|
|
public static void RunCmake(string path, TargetPlatform platform, TargetArchitecture architecture, string customArgs = null, Dictionary<string, string> envVars = null)
|
|
{
|
|
string cmdLine;
|
|
switch (platform)
|
|
{
|
|
case TargetPlatform.Windows:
|
|
case TargetPlatform.XboxOne:
|
|
case TargetPlatform.XboxScarlett:
|
|
case TargetPlatform.UWP:
|
|
{
|
|
string arch;
|
|
switch (architecture)
|
|
{
|
|
case TargetArchitecture.x86:
|
|
arch = "Win32";
|
|
break;
|
|
case TargetArchitecture.x64:
|
|
arch = "x64";
|
|
break;
|
|
case TargetArchitecture.ARM:
|
|
arch = "ARM";
|
|
break;
|
|
case TargetArchitecture.ARM64:
|
|
arch = "ARM64";
|
|
break;
|
|
default: throw new InvalidArchitectureException(architecture);
|
|
}
|
|
cmdLine = string.Format("CMakeLists.txt -G \"Visual Studio 17 2022\" -A {0}", arch);
|
|
break;
|
|
}
|
|
case TargetPlatform.PS4:
|
|
cmdLine = "CMakeLists.txt -DCMAKE_GENERATOR_PLATFORM=ORBIS -G \"Visual Studio 15 2017\"";
|
|
break;
|
|
case TargetPlatform.PS5:
|
|
cmdLine = "CMakeLists.txt -DCMAKE_GENERATOR_PLATFORM=PROSPERO -G \"Visual Studio 16 2019\"";
|
|
break;
|
|
case TargetPlatform.Linux:
|
|
cmdLine = "CMakeLists.txt";
|
|
break;
|
|
case TargetPlatform.Switch:
|
|
cmdLine = string.Format("-DCMAKE_TOOLCHAIN_FILE=\"{1}\\Source\\Platforms\\Switch\\Binaries\\Data\\Switch.cmake\" -G \"NMake Makefiles\" -DCMAKE_MAKE_PROGRAM=\"{0}..\\..\\VC\\bin\\nmake.exe\"", Environment.GetEnvironmentVariable("VS140COMNTOOLS"), Globals.EngineRoot);
|
|
break;
|
|
case TargetPlatform.Android:
|
|
{
|
|
var ndk = AndroidNdk.Instance.RootPath;
|
|
var abi = AndroidToolchain.GetAbiName(architecture);
|
|
var hostName = AndroidSdk.GetHostName();
|
|
cmdLine = string.Format("-DCMAKE_TOOLCHAIN_FILE=\"{0}/build/cmake/android.toolchain.cmake\" -DANDROID_NDK=\"{0}\" -DANDROID_STL=c++_shared -DANDROID_ABI={1} -DANDROID_PLATFORM=android-{2} -G \"MinGW Makefiles\" -DCMAKE_MAKE_PROGRAM=\"{0}/prebuilt/{3}/bin/make.exe\"", ndk, abi, Configuration.AndroidPlatformApi, hostName);
|
|
break;
|
|
}
|
|
case TargetPlatform.Mac:
|
|
{
|
|
var arch = GetAppleArchName(architecture);
|
|
cmdLine = string.Format("CMakeLists.txt -DCMAKE_OSX_DEPLOYMENT_TARGET=\"{0}\" -DCMAKE_OSX_ARCHITECTURES={1}", Configuration.MacOSXMinVer, arch);
|
|
break;
|
|
}
|
|
case TargetPlatform.iOS:
|
|
{
|
|
var arch = GetAppleArchName(architecture);
|
|
cmdLine = string.Format("CMakeLists.txt -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=\"{0}\" -DCMAKE_OSX_ARCHITECTURES={1}", Configuration.iOSMinVer, arch);
|
|
break;
|
|
}
|
|
default: throw new InvalidPlatformException(platform);
|
|
}
|
|
|
|
if (customArgs != null)
|
|
cmdLine += " " + customArgs;
|
|
|
|
Utilities.Run("cmake", cmdLine, null, path, Utilities.RunOptions.DefaultTool, envVars);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the Apple architecture name (for toolchain).
|
|
/// </summary>
|
|
public static string GetAppleArchName(TargetArchitecture architecture)
|
|
{
|
|
string arch;
|
|
switch (architecture)
|
|
{
|
|
case TargetArchitecture.x86:
|
|
arch = "i386";
|
|
break;
|
|
case TargetArchitecture.x64:
|
|
arch = "x86_64";
|
|
break;
|
|
case TargetArchitecture.ARM:
|
|
arch = "arm";
|
|
break;
|
|
case TargetArchitecture.ARM64:
|
|
arch = "arm64";
|
|
break;
|
|
default: throw new InvalidArchitectureException(architecture);
|
|
}
|
|
return arch;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs the bash script via Cygwin tool (native bash on platforms other than Windows).
|
|
/// </summary>
|
|
/// <param name="path">The path.</param>
|
|
/// <param name="workspace">The workspace folder.</param>
|
|
public static void RunCygwin(string path, string workspace = null)
|
|
{
|
|
RunBash(path, string.Empty, workspace);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs the bash script (executes natively on Unix platforms, uses Cygwin on Windows).
|
|
/// </summary>
|
|
/// <param name="path">The script or command path.</param>
|
|
/// <param name="args">The arguments.</param>
|
|
/// <param name="workspace">The workspace folder.</param>
|
|
/// <param name="envVars">Custom environment variables to pass to the child process.</param>
|
|
public static void RunBash(string path, string args = null, string workspace = null, Dictionary<string, string> envVars = null)
|
|
{
|
|
switch (BuildPlatform)
|
|
{
|
|
case TargetPlatform.Windows:
|
|
{
|
|
// Find Cygwin
|
|
var cygwinFolder = Environment.GetEnvironmentVariable("CYGWIN");
|
|
if (string.IsNullOrEmpty(cygwinFolder) || !Directory.Exists(cygwinFolder))
|
|
{
|
|
cygwinFolder = "C:\\cygwin";
|
|
if (!Directory.Exists(cygwinFolder))
|
|
{
|
|
cygwinFolder = "C:\\cygwin64";
|
|
if (!Directory.Exists(cygwinFolder))
|
|
throw new Exception("Missing Cygwin. Install Cygwin64 to C:\\cygwin or set CYGWIN env variable to install location folder.");
|
|
}
|
|
}
|
|
var cygwinBinFolder = Path.Combine(cygwinFolder, "bin");
|
|
|
|
// Ensure that Cygwin binaries folder is in a PATH
|
|
string envPath = null;
|
|
envVars?.TryGetValue("PATH", out envPath);
|
|
if (envPath == null || envPath.IndexOf(cygwinBinFolder, StringComparison.OrdinalIgnoreCase) == -1)
|
|
{
|
|
if (envVars == null)
|
|
envVars = new Dictionary<string, string>();
|
|
envVars["PATH"] = cygwinBinFolder;
|
|
if (envPath != null)
|
|
envVars["PATH"] += ";" + envPath;
|
|
}
|
|
|
|
// Get the executable file path
|
|
if (path.EndsWith(".sh", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Bash script
|
|
if (args == null)
|
|
args = path.Replace('\\', '/');
|
|
else
|
|
args = path.Replace('\\', '/') + " " + args;
|
|
path = Path.Combine(cygwinBinFolder, "bash.exe");
|
|
}
|
|
else if (File.Exists(Path.Combine(cygwinBinFolder, path + ".exe")))
|
|
{
|
|
// Tool (eg. make)
|
|
path = Path.Combine(cygwinBinFolder, path + ".exe");
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("Cannot execute command " + path + " with args " + args);
|
|
}
|
|
break;
|
|
}
|
|
case TargetPlatform.Linux:
|
|
case TargetPlatform.Mac: break;
|
|
default: throw new InvalidPlatformException(BuildPlatform);
|
|
}
|
|
Utilities.Run(path, args, null, workspace, Utilities.RunOptions.ThrowExceptionOnError, envVars);
|
|
}
|
|
|
|
internal bool GetMsBuildForPlatform(TargetPlatform targetPlatform, out VisualStudioVersion vsVersion, out string msBuildPath)
|
|
{
|
|
// Some consoles don't support the latest Visual Studio 2022
|
|
vsVersion = VisualStudioVersion.VisualStudio2022;
|
|
switch (targetPlatform)
|
|
{
|
|
case TargetPlatform.PS4:
|
|
vsVersion = VisualStudioVersion.VisualStudio2017;
|
|
break;
|
|
case TargetPlatform.PS5:
|
|
case TargetPlatform.Switch:
|
|
vsVersion = VisualStudioVersion.VisualStudio2019;
|
|
break;
|
|
}
|
|
if (vsVersion != VisualStudioVersion.VisualStudio2022)
|
|
{
|
|
var visualStudioInstances = VisualStudioInstance.GetInstances();
|
|
foreach (var visualStudioInstance in visualStudioInstances)
|
|
{
|
|
if (visualStudioInstance.Version <= vsVersion)
|
|
{
|
|
var toolPath = Path.Combine(visualStudioInstance.Path, "MSBuild\\Current\\Bin\\MSBuild.exe");
|
|
if (!File.Exists(toolPath))
|
|
toolPath = Path.Combine(visualStudioInstance.Path, "MSBuild\\15.0\\Bin\\MSBuild.exe");
|
|
if (File.Exists(toolPath))
|
|
{
|
|
vsVersion = visualStudioInstance.Version;
|
|
msBuildPath = toolPath;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
msBuildPath = VCEnvironment.MSBuildPath;
|
|
return false;
|
|
}
|
|
}
|
|
}
|