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 { Console.WriteLine($"ERROR: Platform not supported!"); 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); string projectRelativePath = Path.GetRelativePath(projectPath, csProjPath); // FAE04EC0-301F-11D3-BF4B-00C04F79EFBC for C# projects. solutionContent.AppendLine($"Project(\"{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}\") = \"{projectName}\", \"{projectRelativePath}\", \"{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 + ".csproj"; string filePath = Path.Combine(projectPath, projectName); File.WriteAllText(filePath, projectContent.ToString()); } }