| @ -0,0 +1,242 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using System.Threading; | ||||
| using EpicGames.UHT.Types; | ||||
|  | ||||
| namespace UnrealSharpScriptGenerator.Utilities; | ||||
|  | ||||
| public readonly struct ProjectDirInfo | ||||
| { | ||||
|     private readonly string _projectName; | ||||
|     private readonly string _projectDirectory; | ||||
|     public HashSet<string>? Dependencies { get; } | ||||
|  | ||||
|     public ProjectDirInfo(string projectName, string projectDirectory, HashSet<string>? dependencies = null) | ||||
|     { | ||||
|         _projectName = projectName; | ||||
|         _projectDirectory = projectDirectory; | ||||
|         Dependencies = dependencies; | ||||
|     } | ||||
|      | ||||
|     public string GlueProjectName => $"{_projectName}.Glue"; | ||||
|     public string GlueProjectName_LEGACY => $"{_projectName}.PluginGlue"; | ||||
|     public string GlueProjectFile => $"{GlueProjectName}.csproj"; | ||||
|      | ||||
|     public string ScriptDirectory => Path.Combine(_projectDirectory, "Script"); | ||||
|      | ||||
|     public string GlueCsProjPath => Path.Combine(GlueProjectDirectory, GlueProjectFile); | ||||
|  | ||||
|     public bool IsUProject => _projectDirectory.EndsWith(".uproject", StringComparison.OrdinalIgnoreCase); | ||||
|      | ||||
|     public bool IsPartOfEngine => _projectName == "Engine"; | ||||
|      | ||||
|     public string GlueProjectDirectory => Path.Combine(ScriptDirectory, GlueProjectName); | ||||
|     public string GlueProjectDirectory_LEGACY => Path.Combine(ScriptDirectory, GlueProjectName_LEGACY); | ||||
|      | ||||
|     public string ProjectRoot => _projectDirectory; | ||||
| } | ||||
|  | ||||
| public static class FileExporter | ||||
| { | ||||
|     private static readonly ReaderWriterLockSlim ReadWriteLock = new(); | ||||
|  | ||||
|     private static readonly List<string> ChangedFiles = new(); | ||||
|     private static readonly List<string> UnchangedFiles = new(); | ||||
|  | ||||
|     public static void SaveGlueToDisk(UhtType type, GeneratorStringBuilder stringBuilder) | ||||
|     { | ||||
|         string directory = GetDirectoryPath(type.Package); | ||||
|         SaveGlueToDisk(type.Package, directory, type.EngineName, stringBuilder.ToString()); | ||||
|     } | ||||
|  | ||||
|     public static string GetFilePath(string typeName, string directory) | ||||
|     { | ||||
|         return Path.Combine(directory, $"{typeName}.generated.cs"); | ||||
|     } | ||||
|  | ||||
|     public static void SaveGlueToDisk(UhtPackage package, string directory, string typeName, string text) | ||||
|     { | ||||
|         string absoluteFilePath = GetFilePath(typeName, directory); | ||||
|         bool directoryExists = Directory.Exists(directory); | ||||
|         bool glueExists = File.Exists(absoluteFilePath); | ||||
|  | ||||
|         ReadWriteLock.EnterWriteLock(); | ||||
|         try | ||||
|         { | ||||
|             bool matchingGlue = glueExists && File.ReadAllText(absoluteFilePath) == text; | ||||
|  | ||||
|             // If the directory exists and the file exists with the same text, we can return early | ||||
|             if (directoryExists && matchingGlue) | ||||
|             { | ||||
|                 UnchangedFiles.Add(absoluteFilePath); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (!directoryExists) | ||||
|             { | ||||
|                 Directory.CreateDirectory(directory); | ||||
|             } | ||||
|  | ||||
|             File.WriteAllText(absoluteFilePath, text); | ||||
|             ChangedFiles.Add(absoluteFilePath); | ||||
|  | ||||
|             if (package.IsPartOfEngine()) | ||||
|             { | ||||
|                 CSharpExporter.HasModifiedEngineGlue = true; | ||||
|             } | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             ReadWriteLock.ExitWriteLock(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static void AddUnchangedType(UhtType type) | ||||
|     { | ||||
|         string directory = GetDirectoryPath(type.Package); | ||||
|         string filePath = GetFilePath(type.EngineName, directory); | ||||
|         UnchangedFiles.Add(filePath); | ||||
|  | ||||
|         if (type is UhtStruct uhtStruct && uhtStruct.Functions.Any(f => f.HasMetadata("ExtensionMethod"))) | ||||
|         { | ||||
|             UnchangedFiles.Add(GetFilePath($"{type.EngineName}_Extensions", directory)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static string GetDirectoryPath(UhtPackage package) | ||||
|     { | ||||
|         if (package == null) | ||||
|         { | ||||
|             throw new InvalidOperationException("Package is null"); | ||||
|         } | ||||
|  | ||||
|         string rootPath = GetGluePath(package); | ||||
|         return Path.Combine(rootPath, package.GetShortName()); | ||||
|     } | ||||
|  | ||||
|     public static string GetGluePath(UhtPackage package) | ||||
|     { | ||||
|         ProjectDirInfo projectDirInfo = package.FindOrAddProjectInfo(); | ||||
|         return projectDirInfo.GlueProjectDirectory; | ||||
|     } | ||||
|  | ||||
|     public static void CleanOldExportedFiles() | ||||
|     { | ||||
|         Console.WriteLine("Cleaning up old generated C# glue files..."); | ||||
|         CleanFilesInDirectories(Program.EngineGluePath); | ||||
|         CleanFilesInDirectories(Program.ProjectGluePath_LEGACY, true); | ||||
|          | ||||
|         foreach (ProjectDirInfo pluginDirectory in Program.PluginDirs) | ||||
|         { | ||||
|             CleanFilesInDirectories(pluginDirectory.GlueProjectDirectory, true); | ||||
|             CleanFilesInDirectories(pluginDirectory.GlueProjectDirectory_LEGACY, true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static void CleanModuleFolders() | ||||
|     { | ||||
|         CleanGeneratedFolder(Program.EngineGluePath); | ||||
|         CleanGeneratedFolder(Program.ProjectGluePath_LEGACY); | ||||
|          | ||||
|         foreach (ProjectDirInfo pluginDirectory in Program.PluginDirs) | ||||
|         { | ||||
|             CleanGeneratedFolder(pluginDirectory.GlueProjectDirectory); | ||||
|             CleanGeneratedFolder(pluginDirectory.GlueProjectDirectory_LEGACY); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     public static void CleanGeneratedFolder(string path) | ||||
|     { | ||||
|         if (!Directory.Exists(path)) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         HashSet<string> ignoredDirectories = GetIgnoredDirectories(path); | ||||
|  | ||||
|         // TODO: Move runtime glue to a separate csproj. So we can fully clean the ProjectGlue folder. | ||||
|         // Below is a temporary solution to not delete runtime glue that can cause compilation errors on editor startup, | ||||
|         // and avoid having to restore nuget packages. | ||||
|         string[] directories = Directory.GetDirectories(path); | ||||
|         foreach (string directory in directories) | ||||
|         { | ||||
|             if (IsIntermediateDirectory(directory) || ignoredDirectories.Contains(Path.GetRelativePath(path, directory))) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             Directory.Delete(directory, true); | ||||
|         } | ||||
|     } | ||||
|     private static HashSet<string> GetIgnoredDirectories(string path) | ||||
|     { | ||||
|         string glueIgnoreFileName = Path.Combine(path, ".glueignore"); | ||||
|         if (!File.Exists(glueIgnoreFileName)) | ||||
|         { | ||||
|             return new HashSet<string>(StringComparer.OrdinalIgnoreCase); | ||||
|         } | ||||
|  | ||||
|         HashSet<string> ignoredDirectories = new HashSet<string>(StringComparer.OrdinalIgnoreCase); | ||||
|         using StreamReader fileInput = File.OpenText(glueIgnoreFileName); | ||||
|         while (!fileInput.EndOfStream) | ||||
|         { | ||||
|             string? line = fileInput.ReadLine(); | ||||
|             if (string.IsNullOrWhiteSpace(line)) continue; | ||||
|  | ||||
|             ignoredDirectories.Add(line.Trim()); | ||||
|         } | ||||
|         return ignoredDirectories; | ||||
|     } | ||||
|  | ||||
|     private static void CleanFilesInDirectories(string path, bool recursive = false) | ||||
|     { | ||||
|         if (!Directory.Exists(path)) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         string[] directories = Directory.GetDirectories(path); | ||||
|         HashSet<string> ignoredDirectories = GetIgnoredDirectories(path); | ||||
|  | ||||
|         foreach (var directory in directories) | ||||
|         { | ||||
|             if (ignoredDirectories.Contains(Path.GetRelativePath(path, directory))) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             string moduleName = Path.GetFileName(directory); | ||||
|             if (!CSharpExporter.HasBeenExported(moduleName)) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             int removedFiles = 0; | ||||
|             string[] files = Directory.GetFiles(directory); | ||||
|  | ||||
|             foreach (var file in files) | ||||
|             { | ||||
|                 if (ChangedFiles.Contains(file) || UnchangedFiles.Contains(file)) | ||||
|                 { | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 File.Delete(file); | ||||
|                 removedFiles++; | ||||
|             } | ||||
|  | ||||
|             if (removedFiles == files.Length) | ||||
|             { | ||||
|                 Directory.Delete(directory, recursive); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static bool IsIntermediateDirectory(string path) | ||||
|     { | ||||
|         string directoryName = Path.GetFileName(path); | ||||
|         return directoryName is "obj" or "bin" or "Properties"; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user