#if UNITY_EDITOR using System; using System.Diagnostics; using System.Text; using System.Threading.Tasks; using UnityEditor; using UnityEngine; namespace MessagePack.Unity.Editor { internal class MessagePackWindow : EditorWindow { static MessagePackWindow window; bool processInitialized; bool isDotnetInstalled; string dotnetVersion; bool isInstalledMpc; bool installingMpc; bool invokingMpc; MpcArgument mpcArgument; [MenuItem("Window/MessagePack/CodeGenerator")] public static void OpenWindow() { if (window != null) { window.Close(); } // will called OnEnable(singleton instance will be set). GetWindow("MessagePack CodeGen").Show(); } async void OnEnable() { window = this; // set singleton. try { var dotnet = await ProcessHelper.FindDotnetAsync(); isDotnetInstalled = dotnet.found; dotnetVersion = dotnet.version; if (isDotnetInstalled) { isInstalledMpc = await ProcessHelper.IsInstalledMpc(); } } finally { mpcArgument = MpcArgument.Restore(); processInitialized = true; } } async void OnGUI() { if (!processInitialized) { GUILayout.Label("Check .NET Core SDK/CodeGen install status."); return; } if (mpcArgument == null) { return; } if (!isDotnetInstalled) { GUILayout.Label(".NET Core SDK not found."); GUILayout.Label("MessagePack CodeGen requires .NET Core Runtime."); if (GUILayout.Button("Open .NET Core install page.")) { Application.OpenURL("https://dotnet.microsoft.com/download"); } return; } if (!isInstalledMpc) { GUILayout.Label("MessagePack CodeGen does not instaled."); EditorGUI.BeginDisabledGroup(installingMpc); if (GUILayout.Button("Install MessagePack CodeGen.")) { installingMpc = true; try { var log = await ProcessHelper.InstallMpc(); if (!string.IsNullOrWhiteSpace(log)) { UnityEngine.Debug.Log(log); } if (log != null && log.Contains("error")) { isInstalledMpc = false; } else { isInstalledMpc = true; } } finally { installingMpc = false; } return; } EditorGUI.EndDisabledGroup(); return; } EditorGUILayout.LabelField("-i input path(csproj or directory):"); TextField(mpcArgument, x => x.Input, (x, y) => x.Input = y); EditorGUILayout.LabelField("-o output filepath(.cs) or directory(multiple):"); TextField(mpcArgument, x => x.Output, (x, y) => x.Output = y); EditorGUILayout.LabelField("-m(optional) use map mode:"); var newToggle = EditorGUILayout.Toggle(mpcArgument.UseMapMode); if (mpcArgument.UseMapMode != newToggle) { mpcArgument.UseMapMode = newToggle; mpcArgument.Save(); } EditorGUILayout.LabelField("-c(optional) conditional compiler symbols(split with ','):"); TextField(mpcArgument, x => x.ConditionalSymbol, (x, y) => x.ConditionalSymbol = y); EditorGUILayout.LabelField("-r(optional) generated resolver name:"); TextField(mpcArgument, x => x.ResolverName, (x, y) => x.ResolverName = y); EditorGUILayout.LabelField("-n(optional) namespace root name:"); TextField(mpcArgument, x => x.Namespace, (x, y) => x.Namespace = y); EditorGUILayout.LabelField("-ms(optional) Generate #if-- files by symbols, split with ','"); TextField(mpcArgument, x => x.MultipleIfDirectiveOutputSymbols, (x, y) => x.MultipleIfDirectiveOutputSymbols = y); EditorGUI.BeginDisabledGroup(invokingMpc); if (GUILayout.Button("Generate")) { var commnadLineArguments = mpcArgument.ToString(); UnityEngine.Debug.Log("Generate MessagePack Files, command:" + commnadLineArguments); invokingMpc = true; try { var log = await ProcessHelper.InvokeProcessStartAsync("mpc", commnadLineArguments); UnityEngine.Debug.Log(log); } finally { invokingMpc = false; } } EditorGUI.EndDisabledGroup(); } void TextField(MpcArgument args, Func getter, Action setter) { var current = getter(args); var newValue = EditorGUILayout.TextField(current); if (newValue != current) { setter(args, newValue); args.Save(); } } } internal class MpcArgument { public string Input; public string Output; public string ConditionalSymbol; public string ResolverName; public string Namespace; public bool UseMapMode; public string MultipleIfDirectiveOutputSymbols; static string Key => "MessagePackCodeGen." + Application.productName; public static MpcArgument Restore() { if (EditorPrefs.HasKey(Key)) { var json = EditorPrefs.GetString(Key); return JsonUtility.FromJson(json); } else { return new MpcArgument(); } } public void Save() { var json = JsonUtility.ToJson(this); EditorPrefs.SetString(Key, json); } public override string ToString() { var sb = new StringBuilder(); sb.Append("-i "); sb.Append(Input); sb.Append(" -o "); sb.Append(Output); if (!string.IsNullOrWhiteSpace(ConditionalSymbol)) { sb.Append(" -c "); sb.Append(ConditionalSymbol); } if (!string.IsNullOrWhiteSpace(ResolverName)) { sb.Append(" -r "); sb.Append(ResolverName); } if (UseMapMode) { sb.Append(" -m"); } if (!string.IsNullOrWhiteSpace(Namespace)) { sb.Append(" -n "); sb.Append(Namespace); } if (!string.IsNullOrWhiteSpace(MultipleIfDirectiveOutputSymbols)) { sb.Append(" -ms "); sb.Append(MultipleIfDirectiveOutputSymbols); } return sb.ToString(); } } internal static class ProcessHelper { const string InstallName = "messagepack.generator"; public static async Task IsInstalledMpc() { var list = await InvokeProcessStartAsync("dotnet", "tool list -g"); if (list.Contains(InstallName)) { return true; } else { return false; } } public static async Task InstallMpc() { return await InvokeProcessStartAsync("dotnet", "tool install --global " + InstallName); } public static async Task<(bool found, string version)> FindDotnetAsync() { try { var version = await InvokeProcessStartAsync("dotnet", "--version"); return (true, version); } catch { return (false, null); } } public static Task InvokeProcessStartAsync(string fileName, string arguments) { var psi = new ProcessStartInfo() { CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, FileName = fileName, Arguments = arguments, WorkingDirectory = Application.dataPath }; Process p; try { p = Process.Start(psi); } catch (Exception ex) { return Task.FromException(ex); } var tcs = new TaskCompletionSource(); p.EnableRaisingEvents = true; p.Exited += (object sender, System.EventArgs e) => { var data = p.StandardOutput.ReadToEnd(); p.Dispose(); p = null; tcs.TrySetResult(data); }; return tcs.Task; } } } #endif