253 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			253 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Collections.ObjectModel;
 | |
| using System.Diagnostics;
 | |
| using System.IO;
 | |
| using System.Text;
 | |
| 
 | |
| namespace UnrealSharp.Shared;
 | |
| 
 | |
| public static class DotNetUtilities
 | |
| {
 | |
| 	public const string DOTNET_MAJOR_VERSION = "9.0";
 | |
| 	public const string DOTNET_MAJOR_VERSION_DISPLAY = "net" + DOTNET_MAJOR_VERSION;
 | |
| 
 | |
|     public static string FindDotNetExecutable()
 | |
|     {
 | |
| 		const string DOTNET_WIN = "dotnet.exe";
 | |
| 		const string DOTNET_UNIX = "dotnet";
 | |
| 
 | |
| 		var dotnetExe = OperatingSystem.IsWindows() ? DOTNET_WIN : DOTNET_UNIX;
 | |
| 
 | |
|     	var pathVariable = Environment.GetEnvironmentVariable("PATH");
 | |
| 
 | |
|     	if (pathVariable == null)
 | |
|     	{
 | |
|     		throw new Exception($"Couldn't find {dotnetExe}!");
 | |
|     	}
 | |
| 
 | |
|     	var paths = pathVariable.Split(Path.PathSeparator);
 | |
| 
 | |
|     	foreach (var path in paths)
 | |
|     	{
 | |
|     		// This is a hack to avoid using the dotnet.exe from the Unreal Engine installation directory.
 | |
|     		// Can't use the dotnet.exe from the Unreal Engine installation directory because it's .NET 6.0
 | |
|     		if (!path.Contains(@"\dotnet\"))
 | |
|     		{
 | |
|     			continue;
 | |
|     		}
 | |
| 
 | |
|     		var dotnetExePath = Path.Combine(path, dotnetExe);
 | |
| 
 | |
|     		if (File.Exists(dotnetExePath))
 | |
|     		{
 | |
|     			return dotnetExePath;
 | |
|     		}
 | |
|     	}
 | |
| 
 | |
|     	if ( OperatingSystem.IsMacOS() ) {
 | |
| 			if ( File.Exists( "/usr/local/share/dotnet/dotnet" ) ) {
 | |
| 				return "/usr/local/share/dotnet/dotnet";
 | |
| 			}
 | |
| 			if ( File.Exists( "/opt/homebrew/bin/dotnet" ) ) {
 | |
| 				return "/opt/homebrew/bin/dotnet";
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		throw new Exception($"Couldn't find {dotnetExe} in PATH!");
 | |
|     }
 | |
| 
 | |
|     public static string GetLatestDotNetSdkPath()
 | |
|     {
 | |
| 	    string dotNetExecutable = FindDotNetExecutable();
 | |
| 	    string dotNetExecutableDirectory = Path.GetDirectoryName(dotNetExecutable)!;
 | |
| 	    string dotNetSdkDirectory = Path.Combine(dotNetExecutableDirectory!, "sdk");
 | |
| 
 | |
| 	    string[] folderPaths = Directory.GetDirectories(dotNetSdkDirectory);
 | |
| 
 | |
| 	    string highestVersion = "0.0.0";
 | |
| 
 | |
| 	    foreach (string folderPath in folderPaths)
 | |
| 	    {
 | |
| 		    string folderName = Path.GetFileName(folderPath);
 | |
| 
 | |
| 		    if (string.IsNullOrEmpty(folderName) || !char.IsDigit(folderName[0]))
 | |
| 		    {
 | |
| 			    continue;
 | |
| 		    }
 | |
| 
 | |
| 		    if (string.Compare(folderName, highestVersion, StringComparison.Ordinal) > 0)
 | |
| 		    {
 | |
| 			    highestVersion = folderName;
 | |
| 		    }
 | |
| 	    }
 | |
| 
 | |
| 	    if (highestVersion == "0.0.0")
 | |
| 	    {
 | |
| 		    throw new Exception("Failed to find the latest .NET SDK version.");
 | |
| 	    }
 | |
| 
 | |
| 	    if (!highestVersion.StartsWith(DOTNET_MAJOR_VERSION))
 | |
| 	    {
 | |
| 		    throw new Exception($"Failed to find the latest .NET SDK version. Expected version to start with {DOTNET_MAJOR_VERSION} but found: {highestVersion}");
 | |
| 	    }
 | |
| 
 | |
| 	    return Path.Combine(dotNetSdkDirectory, highestVersion);
 | |
|     }
 | |
| 
 | |
|     public static void BuildSolution(string projectRootDirectory, string managedBinariesPath)
 | |
|     {
 | |
|     	if (!Directory.Exists(projectRootDirectory))
 | |
|     	{
 | |
|     		throw new Exception($"Couldn't find project root directory: {projectRootDirectory}");
 | |
|     	}
 | |
| 
 | |
| 	    if (!Directory.Exists(managedBinariesPath))
 | |
| 	    {
 | |
| 		    Directory.CreateDirectory(managedBinariesPath);
 | |
| 	    }
 | |
| 
 | |
|     	Collection<string> arguments = new Collection<string>
 | |
| 		{
 | |
| 			"publish",
 | |
| 			$"-p:PublishDir=\"{managedBinariesPath}\""
 | |
| 		};
 | |
| 
 | |
| 	    InvokeDotNet(arguments, projectRootDirectory);
 | |
|     }
 | |
| 
 | |
|     public static bool InvokeDotNet(Collection<string> arguments, string? workingDirectory = null)
 | |
|     {
 | |
| 	    string dotnetPath = FindDotNetExecutable();
 | |
| 
 | |
| 	    var startInfo = new ProcessStartInfo
 | |
| 	    {
 | |
| 		    FileName = dotnetPath,
 | |
| 		    RedirectStandardOutput = true,
 | |
| 		    RedirectStandardError = true
 | |
| 	    };
 | |
| 
 | |
| 	    foreach (string argument in arguments)
 | |
| 	    {
 | |
| 		    startInfo.ArgumentList.Add(argument);
 | |
| 	    }
 | |
| 
 | |
| 	    if (workingDirectory != null)
 | |
| 	    {
 | |
| 		    startInfo.WorkingDirectory = workingDirectory;
 | |
| 	    }
 | |
| 
 | |
| 	    // Set the MSBuild environment variables to the latest .NET SDK that U# supports.
 | |
| 	    // Otherwise, we'll use the .NET SDK that comes with the Unreal Engine.
 | |
| 	    {
 | |
| 		    string latestDotNetSdkPath = GetLatestDotNetSdkPath();
 | |
| 		    startInfo.Environment["MSBuildExtensionsPath"] = latestDotNetSdkPath;
 | |
| 		    startInfo.Environment["MSBUILD_EXE_PATH"] = $@"{latestDotNetSdkPath}\MSBuild.dll";
 | |
| 		    startInfo.Environment["MSBuildSDKsPath"] = $@"{latestDotNetSdkPath}\Sdks";
 | |
| 	    }
 | |
| 
 | |
| 	    using Process process = new Process();
 | |
| 	    process.StartInfo = startInfo;
 | |
| 
 | |
| 	    try
 | |
| 	    {
 | |
| 		    StringBuilder outputBuilder = new StringBuilder();
 | |
| 		    process.OutputDataReceived += (sender, e) =>
 | |
| 		    {
 | |
| 			    if (e.Data != null)
 | |
| 			    {
 | |
| 				    outputBuilder.AppendLine(e.Data);
 | |
| 			    }
 | |
| 		    };
 | |
|             
 | |
| 		    process.ErrorDataReceived += (sender, e) =>
 | |
| 		    {
 | |
| 			    if (e.Data != null)
 | |
| 			    {
 | |
| 				    outputBuilder.AppendLine(e.Data);
 | |
| 			    }
 | |
| 		    };
 | |
|             
 | |
| 		    if (!process.Start())
 | |
| 		    {
 | |
| 			    throw new Exception("Failed to start process");
 | |
| 		    }
 | |
|             
 | |
| 		    process.BeginErrorReadLine();
 | |
| 		    process.BeginOutputReadLine();
 | |
| 		    process.WaitForExit();
 | |
| 
 | |
| 		    if (process.ExitCode != 0)
 | |
| 		    {
 | |
| 			    string errorMessage = outputBuilder.ToString();
 | |
| 			    
 | |
| 			    if (string.IsNullOrEmpty(errorMessage))
 | |
| 			    {
 | |
| 				    errorMessage = "Process exited with non-zero exit code but no output was captured.";
 | |
| 			    }
 | |
| 			    
 | |
| 			    throw new Exception($"Process failed with exit code {process.ExitCode}: {errorMessage}");
 | |
| 		    }
 | |
| 	    }
 | |
| 	    catch (Exception ex)
 | |
| 	    {
 | |
| 		    Console.WriteLine($"An error occurred: {ex.Message}");
 | |
| 		    return false;
 | |
| 	    }
 | |
| 	    
 | |
| 	    return true;
 | |
|     }
 | |
| 
 | |
|     public static bool InvokeUSharpBuildTool(string action,
 | |
| 	    string managedBinariesPath,
 | |
| 	    string projectName,
 | |
| 	    string pluginDirectory,
 | |
| 	    string projectDirectory,
 | |
| 	    string engineDirectory,
 | |
| 	    IEnumerable<KeyValuePair<string, string>>? additionalArguments = null)
 | |
|     {
 | |
| 	    string dotNetExe = FindDotNetExecutable();
 | |
| 	    string unrealSharpBuildToolPath = Path.Combine(managedBinariesPath, "UnrealSharpBuildTool.dll");
 | |
| 
 | |
| 	    if (!File.Exists(unrealSharpBuildToolPath))
 | |
| 	    {
 | |
| 		    throw new Exception($"Failed to find UnrealSharpBuildTool.dll at: {unrealSharpBuildToolPath}");
 | |
| 	    }
 | |
| 
 | |
| 	    Collection<string> arguments = new Collection<string>
 | |
| 	    {
 | |
| 		    unrealSharpBuildToolPath,
 | |
| 
 | |
| 		    "--Action",
 | |
| 		    action,
 | |
| 
 | |
| 		    "--EngineDirectory",
 | |
| 		    $"{engineDirectory}",
 | |
| 
 | |
| 		    "--ProjectDirectory",
 | |
| 		    $"{projectDirectory}",
 | |
| 
 | |
| 		    "--ProjectName",
 | |
| 		    projectName,
 | |
| 
 | |
| 		    "--PluginDirectory",
 | |
| 		    $"{pluginDirectory}",
 | |
| 
 | |
| 		    "--DotNetPath",
 | |
| 		    $"{dotNetExe}"
 | |
| 	    };
 | |
| 
 | |
| 	    if (additionalArguments != null)
 | |
| 	    {
 | |
| 		    arguments.Add("--AdditionalArgs");
 | |
| 
 | |
| 		    foreach (KeyValuePair<string, string> argument in additionalArguments)
 | |
| 		    {
 | |
| 			    arguments.Add($"{argument.Key}={argument.Value}");
 | |
| 		    }
 | |
| 	    }
 | |
| 
 | |
| 	    return InvokeDotNet(arguments);
 | |
|     }
 | |
| }
 |