From 6e41c2579c576e6a070618229d4919754a6c87dc Mon Sep 17 00:00:00 2001 From: max Date: Sun, 21 Jul 2024 04:38:31 +0200 Subject: [PATCH] started working on compiler project the idea of the compiler project is to have a tool that generates and compiles the solution + csproj files for the project. This is then used by the editor or via the command line. --- Nerfed.Compiler/CSProject.cs | 62 +++++++ Nerfed.Compiler/Compiler.cs | 229 +++++++++++++++++++++++++ Nerfed.Compiler/Nerfed.Compiler.csproj | 26 +++ Nerfed.Compiler/Program.cs | 10 ++ Nerfed.Compiler/Project.cs | 52 ++++++ Nerfed.sln | 48 +++--- 6 files changed, 407 insertions(+), 20 deletions(-) create mode 100644 Nerfed.Compiler/CSProject.cs create mode 100644 Nerfed.Compiler/Compiler.cs create mode 100644 Nerfed.Compiler/Nerfed.Compiler.csproj create mode 100644 Nerfed.Compiler/Program.cs create mode 100644 Nerfed.Compiler/Project.cs diff --git a/Nerfed.Compiler/CSProject.cs b/Nerfed.Compiler/CSProject.cs new file mode 100644 index 0000000..9e58acd --- /dev/null +++ b/Nerfed.Compiler/CSProject.cs @@ -0,0 +1,62 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Nerfed.Compiler; + +public class CSProject +{ + public string Name { get; set; } + public string Guid { get; set; } + //public bool IsEditor { get; set; } + // Add platform stuff here..? + // Add dll's here..? + // Add dependencies here..? + + public static bool Create(string projectFilePath, string name, out CSProject project) + { + project = null; + + if (File.Exists(projectFilePath)) + { + Console.WriteLine($"ERROR: File already exists!"); + return false; + } + + // Create project file. + project = new CSProject + { + Name = name, + Guid = System.Guid.NewGuid().ToString("B").ToUpper(), + }; + + return true; + } + + public static bool Save(CSProject project, string projectFilePath) + { + string jsonString = JsonSerializer.Serialize(project, CSProjectContext.Default.CSProject); + + File.WriteAllText(projectFilePath, jsonString); + + return true; + } + + public static bool Open(string projectFilePath, out CSProject project) + { + string jsonString = File.ReadAllText(projectFilePath); + project = JsonSerializer.Deserialize(jsonString, CSProjectContext.Default.CSProject); + + if (project == null) + { + Console.WriteLine($"ERROR: Could not open {typeof(CSProject)}."); + return false; + } + + return true; + } +} + +[JsonSerializable(typeof(CSProject))] +public partial class CSProjectContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Nerfed.Compiler/Compiler.cs b/Nerfed.Compiler/Compiler.cs new file mode 100644 index 0000000..9433833 --- /dev/null +++ b/Nerfed.Compiler/Compiler.cs @@ -0,0 +1,229 @@ +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.Json; + +namespace Nerfed.Compiler; + +public static class Compiler +{ + public const string CSProjectFileName = ".csproject"; + public const string CSProjFileName = ".csproj"; + + public static bool Compile(string projectFilePath, string configuration = "Debug") + { + string projectPath = Path.GetDirectoryName(projectFilePath); + + if (!File.Exists(projectFilePath)) + { + Console.WriteLine($"ERROR: Project file not found at {projectPath}."); + return false; + } + + if (!Project.Open(projectFilePath, out Project project)) + { + return false; + } + + // TODO: Check project version, to make sure we can compile it or something... + + // Generate solution. + GenerateSolution(projectPath, project, out string solutionFilePath); + + // Compile solution. + ProcessStartInfo processInfo = new() + { + WorkingDirectory = Path.GetDirectoryName(solutionFilePath), + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardOutput = true, + }; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + processInfo.FileName = "/bin/bash"; + processInfo.Arguments = $"-c \"dotnet build '{Path.GetFileName(solutionFilePath)}'\"" + (string.IsNullOrWhiteSpace(configuration) ? $" --configuration {configuration}" : ""); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + processInfo.FileName = "cmd.exe"; + processInfo.Arguments = $"/c dotnet build \"{Path.GetFileName(solutionFilePath)}\"" + (string.IsNullOrWhiteSpace(configuration) ? $" --configuration {configuration}" : ""); + } + else + { + return false; + } + + Process process = Process.Start(processInfo) ?? throw new Exception(); + process.OutputDataReceived += (sender, dataArgs) => { + string data = dataArgs.Data; + + if (data is null) + { + return; + } + + Console.WriteLine(data); + }; + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.ErrorDataReceived += (sender, dataArgs) => { + if (dataArgs.Data is not null) + { + Console.WriteLine(dataArgs.Data); + } + }; + + process.WaitForExit(); + + int exitCode = process.ExitCode; + process.Close(); + + return true; + } + + public static void GenerateSolution(string projectPath, Project project, out string solutionPath) + { + // Clear files. + string[] csProjectFiles = Directory.GetFiles(projectPath, CSProjFileName, SearchOption.TopDirectoryOnly); + foreach (string csProjFile in csProjectFiles) + { + File.Delete(csProjFile); + } + + // Generate projects. + string[] csProjectFilePaths = Directory.GetFiles(projectPath, CSProjectFileName, SearchOption.AllDirectories); + foreach (string csProjectFilePath in csProjectFilePaths) + { + GenerateCSProject(csProjectFilePath, projectPath); + } + + // Generate solution. + string[] csProjPaths = Directory.GetFiles(projectPath, CSProjFileName, SearchOption.TopDirectoryOnly); + string[] csProjGuids = new string[csProjPaths.Length]; + for (int i = 0; i < csProjPaths.Length; i++) + { + csProjGuids[i] = Guid.NewGuid().ToString("B").ToUpper(); + } + + StringBuilder solutionContent = new StringBuilder(); + + // Write the solution file header + solutionContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); + solutionContent.AppendLine("# Visual Studio Version 17"); + solutionContent.AppendLine("VisualStudioVersion = 17.10.35013.160"); + solutionContent.AppendLine("MinimumVisualStudioVersion = 10.0.40219.1"); + + // Add each project to the solution file + for (int i = 0; i < csProjPaths.Length; i++) + { + string csProjPath = csProjPaths[i]; + string projectGuid = csProjGuids[i]; + string projectName = Path.GetFileNameWithoutExtension(csProjPath); + // FAE04EC0-301F-11D3-BF4B-00C04F79EFBC for C# projects. + solutionContent.AppendLine($"Project(\"{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}\") = \"{projectName}\", \"{csProjPath}\", \"{projectGuid}\""); + solutionContent.AppendLine("EndProject"); + } + + // Add global sections (these can be extended as needed) + solutionContent.AppendLine("Global"); + solutionContent.AppendLine(" GlobalSection(SolutionConfigurationPlatforms) = preSolution"); + solutionContent.AppendLine(" Test|x64 = Test|x64"); + solutionContent.AppendLine(" Release|x64 = Release|x64"); + solutionContent.AppendLine(" Debug|x64 = Debug|x64"); + solutionContent.AppendLine(" EndGlobalSection"); + solutionContent.AppendLine(" GlobalSection(ProjectConfigurationPlatforms) = postSolution"); + + for (int i = 0; i < csProjPaths.Length; i++) + { + string projectGuid = csProjGuids[i]; + solutionContent.AppendLine($" {projectGuid}.Test|x64.ActiveCfg = Test|x64"); + solutionContent.AppendLine($" {projectGuid}.Test|x64.Build.0 = Test|x64"); + solutionContent.AppendLine($" {projectGuid}.Release|x64.ActiveCfg = Release|x64"); + solutionContent.AppendLine($" {projectGuid}.Release|x64.Build.0 = Release|x64"); + solutionContent.AppendLine($" {projectGuid}.Debug|x64.ActiveCfg = Debug|x64"); + solutionContent.AppendLine($" {projectGuid}.Debug|x64.Build.0 = Debug|x64"); + } + + solutionContent.AppendLine(" EndGlobalSection"); + solutionContent.AppendLine(" GlobalSection(SolutionProperties) = preSolution"); + solutionContent.AppendLine(" HideSolutionNode = FALSE"); + solutionContent.AppendLine(" EndGlobalSection"); + solutionContent.AppendLine("EndGlobal"); + + // Write the solution file content to disk + string solutionName = project.Name + ".sln"; + string filePath = Path.Combine(projectPath, solutionName); + File.WriteAllText(filePath, solutionContent.ToString()); + + solutionPath = filePath; + } + + private static void GenerateCSProject(string csProjectFilePath, string projectPath) + { + if (!File.Exists(csProjectFilePath)) + { + return; + } + + string jsonString = File.ReadAllText(csProjectFilePath); + CSProject csProject = JsonSerializer.Deserialize(jsonString, CSProjectContext.Default.CSProject); + + Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + Assembly runtimeAssembly = loadedAssemblies.FirstOrDefault(assembly => assembly.GetName().Name == "Nerfed.Runtime") ?? throw new Exception("Failed to find Runtime Assembly!"); + + // TODO: get all dependencies. + // TODO: properly get assemblies. + + StringBuilder projectContent = new StringBuilder(); + + projectContent.AppendLine(""); + + projectContent.AppendLine(" "); + projectContent.AppendLine(" net8.0"); + projectContent.AppendLine(" enable"); + projectContent.AppendLine(" disable"); + projectContent.AppendLine(" true"); + projectContent.AppendLine(" true"); + projectContent.AppendLine(" true"); + projectContent.AppendLine(" false"); + projectContent.AppendLine(" false"); + projectContent.AppendLine(" Debug;Test;Release"); + projectContent.AppendLine(" x64"); + projectContent.AppendLine(" "); + + projectContent.AppendLine(" "); + projectContent.AppendLine(" TRACE;LOG_INFO;PROFILING"); + projectContent.AppendLine(" "); + + projectContent.AppendLine(" "); + projectContent.AppendLine(" TRACE;LOG_ERROR;PROFILING"); + projectContent.AppendLine(" true"); + projectContent.AppendLine(" "); + + projectContent.AppendLine(" "); + projectContent.AppendLine(" TRACE;LOG_ERROR"); + projectContent.AppendLine(" true"); + projectContent.AppendLine(" "); + + projectContent.AppendLine(" "); + projectContent.AppendLine($" "); + projectContent.AppendLine(" "); + + projectContent.AppendLine(" "); + projectContent.AppendLine(" "); + projectContent.AppendLine($" {runtimeAssembly.Location}"); + projectContent.AppendLine(" false"); + projectContent.AppendLine(" "); + projectContent.AppendLine(" "); + + projectContent.AppendLine(""); + + string projectName = csProject.Name + ".Runtime.csproj"; + string filePath = Path.Combine(projectPath, projectName); + File.WriteAllText(filePath, projectContent.ToString()); + } +} diff --git a/Nerfed.Compiler/Nerfed.Compiler.csproj b/Nerfed.Compiler/Nerfed.Compiler.csproj new file mode 100644 index 0000000..9e543bb --- /dev/null +++ b/Nerfed.Compiler/Nerfed.Compiler.csproj @@ -0,0 +1,26 @@ + + + + Exe + net8.0 + enable + disable + false + false + Debug;Test;Release + x64 + + + + false + + + + true + + + + true + + + diff --git a/Nerfed.Compiler/Program.cs b/Nerfed.Compiler/Program.cs new file mode 100644 index 0000000..1c34f84 --- /dev/null +++ b/Nerfed.Compiler/Program.cs @@ -0,0 +1,10 @@ +namespace Nerfed.Compiler +{ + public class Program + { + internal static void Main(string[] args) + { + Compiler.Compile(args[0], args[1]); + } + } +} \ No newline at end of file diff --git a/Nerfed.Compiler/Project.cs b/Nerfed.Compiler/Project.cs new file mode 100644 index 0000000..e709622 --- /dev/null +++ b/Nerfed.Compiler/Project.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Nerfed.Compiler; + +public class Project +{ + public string Name { get; set; } + + public static bool Create(string path, string name, out Project project) + { + project = null; + + // Create project file. + project = new Project + { + Name = name, + }; + + Save(project, path); + + return true; + } + + public static bool Save(Project project, string projectFilePath) + { + string jsonString = JsonSerializer.Serialize(project, ProjectContext.Default.Project); + + File.WriteAllText(projectFilePath, jsonString); + + return true; + } + + public static bool Open(string path, out Project project) + { + string jsonString = File.ReadAllText(path); + project = JsonSerializer.Deserialize(jsonString, ProjectContext.Default.Project); + + if (project == null) + { + Console.WriteLine($"ERROR: Could not open {typeof(Project)}."); + return false; + } + + return true; + } +} + +[JsonSerializable(typeof(Project))] +public partial class ProjectContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Nerfed.sln b/Nerfed.sln index c36f1bf..a698767 100644 --- a/Nerfed.sln +++ b/Nerfed.sln @@ -5,38 +5,46 @@ VisualStudioVersion = 17.10.35013.160 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nerfed.Runtime", "Nerfed.Runtime\Nerfed.Runtime.csproj", "{98E09BAF-587F-4238-89BD-7693C036C233}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nerfed.Builder", "Nerfed.Builder\Nerfed.Builder.csproj", "{1B88DE56-2AD8-441E-9B10-073AA43840BF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nerfed.Builder", "Nerfed.Builder\Nerfed.Builder.csproj", "{1B88DE56-2AD8-441E-9B10-073AA43840BF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nerfed.Editor", "Nerfed.Editor\Nerfed.Editor.csproj", "{FF7D032D-7F0B-4700-A818-0606D66AECF8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nerfed.Editor", "Nerfed.Editor\Nerfed.Editor.csproj", "{FF7D032D-7F0B-4700-A818-0606D66AECF8}" ProjectSection(ProjectDependencies) = postProject {1B88DE56-2AD8-441E-9B10-073AA43840BF} = {1B88DE56-2AD8-441E-9B10-073AA43840BF} EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nerfed.Compiler", "Nerfed.Compiler\Nerfed.Compiler.csproj", "{3DFEB8A4-5354-41EA-A249-27ADC7F666CF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Test|x64 = Test|x64 - Release|x64 = Release|x64 Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + Test|x64 = Test|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Test|x64.ActiveCfg = Test|x64 - {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Test|x64.Build.0 = Test|x64 - {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Release|x64.ActiveCfg = Release|x64 - {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Release|x64.Build.0 = Release|x64 - {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Debug|x64.ActiveCfg = Debug|x64 - {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Debug|x64.Build.0 = Debug|x64 - {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Test|x64.ActiveCfg = Test|x64 - {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Test|x64.Build.0 = Test|x64 - {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Release|x64.ActiveCfg = Release|x64 - {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Release|x64.Build.0 = Release|x64 - {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Debug|x64.ActiveCfg = Debug|x64 - {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Debug|x64.Build.0 = Debug|x64 - {98E09BAF-587F-4238-89BD-7693C036C233}.Test|x64.ActiveCfg = Test|x64 - {98E09BAF-587F-4238-89BD-7693C036C233}.Test|x64.Build.0 = Test|x64 - {98E09BAF-587F-4238-89BD-7693C036C233}.Release|x64.ActiveCfg = Release|x64 - {98E09BAF-587F-4238-89BD-7693C036C233}.Release|x64.Build.0 = Release|x64 {98E09BAF-587F-4238-89BD-7693C036C233}.Debug|x64.ActiveCfg = Debug|x64 {98E09BAF-587F-4238-89BD-7693C036C233}.Debug|x64.Build.0 = Debug|x64 + {98E09BAF-587F-4238-89BD-7693C036C233}.Release|x64.ActiveCfg = Release|x64 + {98E09BAF-587F-4238-89BD-7693C036C233}.Release|x64.Build.0 = Release|x64 + {98E09BAF-587F-4238-89BD-7693C036C233}.Test|x64.ActiveCfg = Test|x64 + {98E09BAF-587F-4238-89BD-7693C036C233}.Test|x64.Build.0 = Test|x64 + {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Debug|x64.ActiveCfg = Debug|x64 + {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Debug|x64.Build.0 = Debug|x64 + {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Release|x64.ActiveCfg = Release|x64 + {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Release|x64.Build.0 = Release|x64 + {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Test|x64.ActiveCfg = Test|x64 + {1B88DE56-2AD8-441E-9B10-073AA43840BF}.Test|x64.Build.0 = Test|x64 + {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Debug|x64.ActiveCfg = Debug|x64 + {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Debug|x64.Build.0 = Debug|x64 + {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Release|x64.ActiveCfg = Release|x64 + {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Release|x64.Build.0 = Release|x64 + {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Test|x64.ActiveCfg = Test|x64 + {FF7D032D-7F0B-4700-A818-0606D66AECF8}.Test|x64.Build.0 = Test|x64 + {3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Debug|x64.ActiveCfg = Debug|x64 + {3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Debug|x64.Build.0 = Debug|x64 + {3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Release|x64.ActiveCfg = Release|x64 + {3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Release|x64.Build.0 = Release|x64 + {3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Test|x64.ActiveCfg = Test|x64 + {3DFEB8A4-5354-41EA-A249-27ADC7F666CF}.Test|x64.Build.0 = Test|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE