From cd0781b4282aeb0be6177580f4ee8626a66ef15f Mon Sep 17 00:00:00 2001 From: BiDuang Date: Sat, 2 Dec 2023 00:04:14 +0800 Subject: [PATCH] refactor: extract `Dependencies` from `WoolangInstaller` --- src/Chief.App/Chief.App.csproj | 3 +- src/Chief.Core/BaoziInstaller.cs | 5 + src/Chief.Core/Chief.Core.csproj | 9 +- src/Chief.Core/Dependencies.cs | 73 +++++++++++++ src/Chief.Core/WoolangInstaller.cs | 163 +++++++++++++++++++++++++++++ 5 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 src/Chief.Core/BaoziInstaller.cs create mode 100644 src/Chief.Core/Dependencies.cs create mode 100644 src/Chief.Core/WoolangInstaller.cs diff --git a/src/Chief.App/Chief.App.csproj b/src/Chief.App/Chief.App.csproj index 92b5d0f..98c0c3c 100644 --- a/src/Chief.App/Chief.App.csproj +++ b/src/Chief.App/Chief.App.csproj @@ -1,12 +1,13 @@  WinExe - net7.0 + net8.0 enable true app.manifest false + 12 diff --git a/src/Chief.Core/BaoziInstaller.cs b/src/Chief.Core/BaoziInstaller.cs new file mode 100644 index 0000000..a610aac --- /dev/null +++ b/src/Chief.Core/BaoziInstaller.cs @@ -0,0 +1,5 @@ +namespace Chief.Core; + +public class BaoziInstaller +{ +} \ No newline at end of file diff --git a/src/Chief.Core/Chief.Core.csproj b/src/Chief.Core/Chief.Core.csproj index 6836c68..d72edff 100644 --- a/src/Chief.Core/Chief.Core.csproj +++ b/src/Chief.Core/Chief.Core.csproj @@ -1,9 +1,16 @@ - net7.0 + net8.0 enable enable + 12 + + + + + + diff --git a/src/Chief.Core/Dependencies.cs b/src/Chief.Core/Dependencies.cs new file mode 100644 index 0000000..19ebad5 --- /dev/null +++ b/src/Chief.Core/Dependencies.cs @@ -0,0 +1,73 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Principal; +using Medallion.Shell; +using Microsoft.VisualStudio.Setup.Configuration; + +namespace Chief.Core; + +public record VisualStudioInfo +{ + public required string Path; + public InstanceState State = InstanceState.None; + public required string Version; +} + +public static class Dependencies +{ + public static async Task IsAdminPermission() + { + if (OperatingSystem.IsWindows()) + { + var user = WindowsIdentity.GetCurrent(); + var principal = new WindowsPrincipal(user); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + if (!OperatingSystem.IsLinux() && !OperatingSystem.IsMacOS()) + throw new PlatformNotSupportedException("Chief does not support this platform."); + + var result = new List(); + await Command.Run("whoami").RedirectTo(result).Task; + return result[0] == "root"; + } + + public static List? GetAllVisualStudioInfos() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return null; + + var result = new List(); + try + { + var query = new SetupConfiguration(); + var query2 = (ISetupConfiguration2)query; + var e = query2.EnumAllInstances(); + int fetched; + var instances = new ISetupInstance[1]; + do + { + e.Next(1, instances, out fetched); + if (fetched <= 0) continue; + var instance = (ISetupInstance2)instances[0]; + var state = instance.GetState(); + result.Add(new VisualStudioInfo + { + State = state, + Version = instance.GetInstallationVersion(), + Path = instance.GetInstallationPath() + }); + } while (fetched > 0); + } + catch (COMException ex) when (ex.HResult == unchecked((int)0x80040154)) + { + Debug.WriteLine("The query API is not registered. Assuming no instances are installed."); + } + catch (Exception ex) + { + Debug.WriteLine($"Error 0x{ex.HResult:x8}: {ex.Message}"); + } + + return result; + } +} \ No newline at end of file diff --git a/src/Chief.Core/WoolangInstaller.cs b/src/Chief.Core/WoolangInstaller.cs new file mode 100644 index 0000000..4cd8239 --- /dev/null +++ b/src/Chief.Core/WoolangInstaller.cs @@ -0,0 +1,163 @@ +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Text; +using LibGit2Sharp; +using LibGit2Sharp.Handlers; +using Medallion.Shell; + +namespace Chief.Core; + +public record WoolangCompilerInfo +{ + public DateTime? BuildTime; + public string? Commit; + public required string Path; + public required string Version; +} + +public static class WoolangInstallerExtensions +{ + public static async Task?> GetAllWoolangCompilerInfos() + { + var result = new List(); + string finder, woolang; + + if (OperatingSystem.IsWindows()) + { + finder = "where.exe"; + woolang = "woodriver.exe"; + } + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + { + finder = "which"; + woolang = "woodriver"; + } + else return null; + + var cmdResult = new List(); + + await Command.Run(finder, woolang).RedirectTo(cmdResult).Task; + + var woolangDirs = cmdResult.Where(x => x.Contains(woolang)).ToList(); + foreach (var copDir in woolangDirs) + { + cmdResult.Clear(); + try + { + await Command.Run(copDir).RedirectTo(cmdResult).Task; + result.Add(new WoolangCompilerInfo + { + BuildTime = Convert.ToDateTime(cmdResult[3].Replace("Date: ", ""), CultureInfo.InvariantCulture), + Commit = cmdResult[2].Replace("Commit: ", "") != "untracked" + ? cmdResult[2].Replace("Commit: ", "") + : null, + Path = copDir, + Version = cmdResult[1].Replace("Version: ", "") + }); + } + catch (Exception e) + { + Debug.WriteLine(e); + } + } + + return result; + } +} + +public class WoolangInstaller( + string cachePath, + ICollection outputPipe, + VisualStudioInfo? visualStudioInfo = null) +{ + public ProgressHandler? ProgressEvent { get; set; } + + public async Task BuildWoolangCompiler() + { + if (Directory.Exists(cachePath) && Directory.EnumerateFileSystemEntries(cachePath).Any()) + return false; + + var repoGitPath = Repository.Clone("https://git.cinogama.net/cinogamaproject/woolang.git", cachePath, + new CloneOptions + { + BranchName = "release", + Checkout = true, + RecurseSubmodules = true, + OnProgress = ProgressEvent + }); + var repo = new Repository(repoGitPath); + await File.WriteAllTextAsync(Path.Combine(cachePath, "src", "wo_info.hpp"), $"\"{repo.Commits.First().Sha}\""); + + Directory.CreateDirectory(Path.Combine(cachePath, "build")); + + var cmd = new Shell(opts => opts + .WorkingDirectory(Path.Combine(cachePath, "build"))); + if (OperatingSystem.IsWindows()) + { + if (visualStudioInfo is null) return false; + if (RuntimeInformation.OSArchitecture is Architecture.X64 or Architecture.X86 or Architecture.Arm64) + await cmd.Run(Path.Combine(visualStudioInfo.Path, + "Common7", "IDE", "CommonExtensions", "Microsoft", "CMake", "CMake", "bin", "cmake.exe"), + cachePath, "-DWO_MAKE_OUTPUT_IN_SAME_PATH=ON", "-DCMAKE_BUILD_TYPE=RELWITHDEBINFO") + .RedirectTo(outputPipe) + .RedirectStandardErrorTo(outputPipe).Task; + else + await cmd.Run(Path.Combine(visualStudioInfo.Path, + "Common7", "IDE", "CommonExtensions", "Microsoft", "CMake", "CMake", "bin", "cmake.exe"), + cachePath, "-DWO_MAKE_OUTPUT_IN_SAME_PATH=ON", "-DCMAKE_BUILD_TYPE=RELWITHDEBINFO", + "-DWO_SUPPORT_ASMJIT=OFF") // disable asmjit + .RedirectTo(outputPipe) + .RedirectStandardErrorTo(outputPipe).Task; + + await cmd.Run(Path.Combine(visualStudioInfo.Path, "MSBuild", "Current", "Bin", "MSBuild.exe"), + Path.Combine(cachePath, "build", "driver", "woodriver.vcxproj"), "/p:Configuration=Release", + "-maxCpuCount", "-m") + .RedirectTo(outputPipe) + .RedirectStandardErrorTo(outputPipe).Task; + } + else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + { + if (RuntimeInformation.OSArchitecture is Architecture.X64 or Architecture.X86 or Architecture.Arm64) + await cmd.Run("cmake", cachePath, "-DWO_MAKE_OUTPUT_IN_SAME_PATH=ON", + "-DCMAKE_BUILD_TYPE=RELWITHDEBINFO") + .RedirectTo(outputPipe) + .RedirectStandardErrorTo(outputPipe).Task; + else + await cmd.Run("cmake", cachePath, "-DWO_MAKE_OUTPUT_IN_SAME_PATH=ON", + "-DCMAKE_BUILD_TYPE=RELWITHDEBINFO", + "-DWO_SUPPORT_ASMJIT=OFF") // disable asmjit + .RedirectTo(outputPipe) + .RedirectStandardErrorTo(outputPipe).Task; + + await cmd.Run("make", "-C", Path.Combine(cachePath, "build"), "-j") + .RedirectTo(outputPipe) + .RedirectStandardErrorTo(outputPipe).Task; + } + + return true; + } + + public int InstallWoolangCompiler(string installPath, bool writeEnv = true) + { + var woodriver = Path.Combine(cachePath, "build", "Release", "woodriver.exe"); + var libwooExt = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dll" : "so"; + var libwoo = Path.Combine(cachePath, "build", "Release", $"libwoo.{libwooExt}"); + if (!File.Exists(woodriver) || !File.Exists(libwoo)) return 4; + Directory.CreateDirectory(installPath); + File.Copy(woodriver, Path.Combine(installPath, "woodriver.exe"), true); + File.Copy(libwoo, Path.Combine(installPath, $"libwoo.{libwooExt}"), true); + if (writeEnv) + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return 1; + var envPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.User); + var sb = new StringBuilder(envPath); + sb.Append(';'); + sb.Append(installPath); + Environment.SetEnvironmentVariable("PATH", sb.ToString(), EnvironmentVariableTarget.User); + } + + return 0; + } +} \ No newline at end of file