252
									
								
								Plugins/UnrealSharp/Managed/Shared/DotNetUtilities.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								Plugins/UnrealSharp/Managed/Shared/DotNetUtilities.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,252 @@ | ||||
| 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); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user