経緯
現在のワークスペース以下のブランチ名が欲しいぞウオァァァァァという衝動で作りました。
コマンドたたいてるだけですが思いのほか汎用性が高かったので公開
内容
using System;
using System.Collections.Generic;
using System.Diagnostics;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Hoge
{
/// <summary>
/// 外部プロセスに関する汎用クラス
/// </summary>
public static partial class ProcessUtility
{
#if UNITY_EDITOR
/// <summary>
/// テスト用処理
/// </summary>
[MenuItem("Tools/ExecProcess/git_blanches_name")]
static void ExecBlanchesNameTest()
{
ExecBlanchesName( ( ret ) => UnityEngine.Debug.Log( ret ) );
}
#endif
/// <summary>
/// プロセスの実行
/// </summary>
public static void ExecBlanchesName( Action<string> onCompelted )
{
#if !UNITY_EDITOR
onCompelted( string.Empty );
return;
#endif
// usingステートメントを使用するとスコープから抜けたタイミングでDisposeされて
// 完了通知が来る前にイベントを破棄してしまうのでこの書き方はダメ
//using ( var process = new OneProcess() )
//{
// process.Exec( "@echo off /k git branch --contains", ( str ) =>
// {
// onCompelted( str );
// });
//}
var process = new OneProcess();
process.Exec( "@echo off /k git branch --contains", ( str ) =>
{
process.Dispose();
onCompelted( str );
});
}
/// <summary>
/// 1プロセス単位
/// </summary>
private class OneProcess : IDisposable
{
//=============================
//! メンバー変数
//=============================
private Process m_process = null;
private List<string> m_list = new List<string>();
private Action<string> m_onCompleted = null;
/// <summary>
/// 破棄処理
/// </summary>
public void Dispose()
{
m_onCompleted = null;
m_list.Clear();
m_list = null;
if ( m_process == null )
{
return;
}
m_process.OutputDataReceived -= Process_OutputDataReceived;
m_process.Exited -= Process_Exited;
m_process.Close();
m_process = null;
}
/// <summary>
/// 実行
/// </summary>
public void Exec( string cmd, Action<string> onCompleted = null )
{
m_process = new Process();
m_process.StartInfo.FileName = Environment.GetEnvironmentVariable( "ComSpec" ); // 実行するファイル名(ComSpecがコマンドプロンプト
m_process.StartInfo.Arguments = cmd; // 引数
m_process.StartInfo.UseShellExecute = false; // プロセス起動時にシェルを実行するか
m_process.StartInfo.RedirectStandardOutput = true; // 標準出力読み取り
m_process.OutputDataReceived += Process_OutputDataReceived; // 出力時のコールバック
m_process.StartInfo.CreateNoWindow = true; // 新しいwindowを作らない
m_process.EnableRaisingEvents = true; // プロセス終了時にExitedイベント発生するかどうか
m_process.Exited += Process_Exited; // プロセス終了時イベント
m_onCompleted = onCompleted;
// プロセスの起動
m_process.Start();
// プロセス標準出力
m_process.BeginOutputReadLine();
}
/// <summary>
/// 標準出力を拾う
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Process_OutputDataReceived( object sender, DataReceivedEventArgs e )
{
m_list.Add( e.Data );
}
/// <summary>
/// 完了処理を拾う
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Process_Exited( object sender, EventArgs e )
{
if ( m_onCompleted == null )
{
return;
}
m_onCompleted( m_list[0] );
}
}
}
}
OneProcessで保持する値はList<string>
じゃなくてStringBuilder
の方がいいかなと思いつつ、一旦はこの形に。
Macの開発環境は未対応
たぶん実行するファイル名とコマンドの内容を変えたらいけると思います。(未検証
あと コールバックから一連の処理でUnityのAPIに触れると例外出ます (別プロセスなので)
UniRx使用した場合の対応方法は下記
var diposable = Observable.Create<string>( observer =>
{
ProcessUtility.ExecBlanchesName( ( str ) => observer.OnNext( str ) );
return null;
} )
.SubscribeOn( Scheduler.ThreadPool ) // Subscribeをスレッドプール上で実行する
.ObserveOnMainThread() // UnityAPIを使用するためにメインスレッドに切り替える
.Subscribe( str => Debug.Log( str ) ); // 戻り値がブランチ名
// 破棄処理忘れずに
// AddTo( Component );が出来るならそれでも可
// diposable.Dispose();
// diposable = null;
まとめ
ユーザ名は Environment.UserName
で取得
エラーログは Application.logMessageReceived
にイベント登録して取得
エラー時に↑をまとめてをSlackに投げるようにしたらとてもGood
毎回してる事は積極的にプログラムに任せていきましょう