using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Linq; using EpicGames.Core; using EpicGames.UHT.Tables; using EpicGames.UHT.Types; using EpicGames.UHT.Utils; using UnrealSharp.Shared; using UnrealSharpScriptGenerator.Utilities; namespace UnrealSharpScriptGenerator; [UnrealHeaderTool] public static class Program { public static IUhtExportFactory Factory { get; private set; } = null!; public static UHTManifest.Module PluginModule => Factory.PluginModule!; public static string EngineGluePath { get; private set; } = ""; public static string PluginsPath { get; private set; } = ""; public static string ProjectGluePath_LEGACY { get; private set; } = ""; public static string ProjectName => Path.GetFileNameWithoutExtension(Factory.Session.ProjectFile!); public static bool BuildingEditor { get; private set; } public static UhtClass BlueprintFunctionLibrary { get; private set; } = null!; public static string PluginDirectory { get; private set; } = ""; public static string ManagedBinariesPath { get; private set; } = ""; public static string ManagedPath { get; private set; } = ""; public static string ScriptFolder { get; private set; } = ""; public static ImmutableArray PluginDirs { get; private set; } [UhtExporter(Name = "UnrealSharpCore", Description = "Exports C++ to C# code", Options = UhtExporterOptions.Default, ModuleName = "UnrealSharpCore")] private static void Main(IUhtExportFactory factory) { Console.WriteLine("Initializing C# Glue Generator..."); Factory = factory; InitializeStatics(); UhtType? foundType = factory.Session.FindType(null, UhtFindOptions.SourceName | UhtFindOptions.Class, "UBlueprintFunctionLibrary"); if (foundType is not UhtClass blueprintFunctionLibrary) { throw new InvalidOperationException("Failed to find UBlueprintFunctionLibrary class."); } BlueprintFunctionLibrary = blueprintFunctionLibrary; try { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); CSharpExporter.StartExport(); FileExporter.CleanOldExportedFiles(); stopwatch.Stop(); Console.WriteLine($"Export process completed successfully in {stopwatch.Elapsed.TotalSeconds:F2} seconds."); if (CSharpExporter.HasModifiedEngineGlue && BuildingEditor) { Console.WriteLine("Detected modified engine glue. Building UnrealSharp solution..."); DotNetUtilities.BuildSolution(Path.Combine(ManagedPath, "UnrealSharp"), ManagedBinariesPath); } CreateGlueProjects(); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("An error occurred during the export process:"); Console.WriteLine($"Error Message: {ex.Message}"); Console.WriteLine("Stack Trace:"); Console.WriteLine(ex.StackTrace); Console.ResetColor(); } } private static void InitializeStatics() { PluginDirectory = ScriptGeneratorUtilities.TryGetPluginDefine("PLUGIN_PATH"); string projectDirectory = Factory.Session.ProjectDirectory!; ScriptFolder = Path.Combine(projectDirectory, "Script"); PluginsPath = Path.Combine(projectDirectory, "Plugins"); ProjectGluePath_LEGACY = Path.Combine(ScriptFolder, "ProjectGlue"); EngineGluePath = ScriptGeneratorUtilities.TryGetPluginDefine("GENERATED_GLUE_PATH"); ManagedBinariesPath = Path.Combine(PluginDirectory, "Binaries", "Managed"); ManagedPath = Path.Combine(PluginDirectory, "Managed"); BuildingEditor = ScriptGeneratorUtilities.TryGetPluginDefine("BUILDING_EDITOR") == "1"; DirectoryInfo pluginsDir = new DirectoryInfo(PluginsPath); PluginDirs = pluginsDir.GetFiles("*.uplugin", SearchOption.AllDirectories) .Where(x => x.Directory!.GetDirectories("Source").Length != 0) .Select(x => new ProjectDirInfo(Path.GetFileNameWithoutExtension(x.Name), x.DirectoryName!)) .Where(x => x.GlueProjectName != "UnrealSharp") .ToImmutableArray(); } private static void CreateGlueProjects() { bool hasProjectGlue = false; foreach (ProjectDirInfo pluginDir in PluginUtilities.PluginInfo.Values) { if (pluginDir.IsPartOfEngine) continue; if (pluginDir.IsUProject) { hasProjectGlue = true; } TryCreateGlueProject(pluginDir.GlueCsProjPath, pluginDir.GlueProjectName, pluginDir.Dependencies, pluginDir.ProjectRoot); } if (!hasProjectGlue) { UhtSession session = Factory.Session; string projectRoot = session.ProjectDirectory!; string baseName = Path.GetFileNameWithoutExtension(session.ProjectFile!); string projectName = baseName + ".Glue"; string csprojPath = Path.Join(projectRoot, "Script", projectName, projectName + ".csproj"); TryCreateGlueProject(csprojPath, projectName, null, projectRoot); } } private static void TryCreateGlueProject(string csprojPath, string projectName, IEnumerable? dependencyPaths, string projectRoot) { if (!File.Exists(csprojPath)) { string projectDirectory = Path.GetDirectoryName(csprojPath)!; List> arguments = new List> { new("NewProjectName", projectName), new("NewProjectFolder", Path.GetDirectoryName(projectDirectory)!), new("SkipIncludeProjectGlue", "true"), new("SkipSolutionGeneration", "true"), }; arguments.Add(new KeyValuePair("ProjectRoot", projectRoot)); if (!DotNetUtilities.InvokeUSharpBuildTool("GenerateProject", ManagedBinariesPath, ProjectName, PluginDirectory, Factory.Session.ProjectDirectory!, Factory.Session.EngineDirectory!, arguments)) { throw new InvalidOperationException($"Failed to create project file at {csprojPath}"); } Console.WriteLine($"Successfully created project file: {projectName}"); } else { Console.WriteLine($"Project file already exists: {projectName}. Skipping creation."); } AddPluginDependencies(projectName, csprojPath, dependencyPaths); } private static void AddPluginDependencies(string projectName, string projectPath, IEnumerable? dependencies) { List> arguments = new() { new KeyValuePair("ProjectPath", projectPath), }; if (dependencies != null) { foreach (string path in dependencies) { arguments.Add(new KeyValuePair("Dependency", path)); } } if (!DotNetUtilities.InvokeUSharpBuildTool("UpdateProjectDependencies", ManagedBinariesPath, ProjectName, PluginDirectory, Factory.Session.ProjectDirectory!, Factory.Session.EngineDirectory!, arguments)) { throw new InvalidOperationException($"Failed to update project dependencies for {projectPath}"); } Console.WriteLine($"Updated project dependencies for {projectName}"); } }