Hello,
I have a custom editor script that creates an android build which takes around 5-10 minutes to compile. Does anybody know if there is a way to detect C# compilation errors in an Editor script so I can prevent the script from running at all if the code has errors?
I already use EditorApplication.isCompiling to detect if compilation is currently in progress.
Cheers!
You can use CompilationPipeline.assemblyCompilationFinished:
[InitializeOnLoad]
class NoErrorsValidator
{
static NoErrorsValidator()
{
if (Application.isBatchMode)
CompilationPipeline.assemblyCompilationFinished += ProcessBatchModeCompileFinish;
}
private void ProcessBatchModeCompileFinish(string s, CompilerMessage[] compilerMessages)
{
if (compilerMessages.Count(m => m.type == CompilerMessageType.Error) > 0)
EditorApplication.Exit(-1);
}
}
Also, it may be helpful to move it to separate assembly. To put to another assembly, you can use asmdef Unity - Manual: Assembly definitions
To receive compile error in batchmode, I subscribe to it as early as possible. The only way, I found, is use InitializeOnLoad Unity - Scripting API: InitializeOnLoadAttribute
I found a solution! The following function will clear the unity console and throw an exception if you have compile errors. This works because clearing the log view doesn’t remove compilation error logs.
static void ClearLog()
{
Assembly assembly = Assembly.GetAssembly(typeof(SceneView));
Type logEntries = assembly.GetType("UnityEditorInternal.LogEntries");
logEntries.GetMethod("Clear").Invoke (new object (), null);
int count = (int)logEntries.GetMethod("GetCount").Invoke(new object (), null);
if (count > 0)
throw new Exception("Cannot build because you have compile errors!");
}
You can simply do this…
The Update to listen if compiling is just non sense. The most upvoted answer is also crazy…
using UnityEngine;
using UnityEditor;
[InitializeOnLoad]
class CompileListener
{
static CompileListener()
{
Application.logMessageReceived += Application_logMessageReceived;
}
private static void Application_logMessageReceived(string condition, string stackTrace, LogType type)
{
Debug.Log("Condition" + condition + " StackTrace " + stackTrace + " LogType " + type);
if (type == LogType.Error)
{
//Do stuff
}
}
}
Rather than clearing the console log, the more elegant solution is to hook into Application.LogMessageReceived during the compilation. Inside the UnityLog callback method check if the LogType is “Error”. If so, you can be certain that compilation failed.
A complete but untested (I stripped down my version to a minimum) script that records and logs script compile time, but only if the script compilation did not fail by adding and removing itself from the LogMessageReceived event:
using UnityEditor;
using UnityEngine;
[InitializeOnLoad]
class CompileTimeMonitor : ScriptableObject
{
static bool s_isTrackingTime;
static double s_startTime;
// since class has InitializeOnLoad, these values are re-set for every compilation
static bool s_compilationSucceeded = true;
static string s_messages = string.Empty;
static CompileTimeMonitor()
{
EditorApplication.update += Update;
s_startTime = PlayerPrefs.GetFloat("CompileStartTime", 0);
if (s_startTime > 0)
s_isTrackingTime = true;
}
static void Update()
{
if (EditorApplication.isCompiling && s_isTrackingTime == false)
{
s_isTrackingTime = true;
StartRecordingCompileTime();
}
else if (EditorApplication.isCompiling == false && s_isTrackingTime)
{
s_isTrackingTime = false;
StopRecordingCompileTime();
LogTimes();
}
}
static void UnityDebugLog(string message, string stackTrace, LogType logType)
{
// if we receive a Debug.LogError we can assume that compilation failed
if (logType == LogType.Error)
s_compilationSucceeded = false;
}
static void StartRecordingCompileTime()
{
Application.logMessageReceived += UnityDebugLog;
s_startTime = EditorApplication.timeSinceStartup;
PlayerPrefs.SetFloat("CompileStartTime", (float)s_startTime);
}
static void StopRecordingCompileTime()
{
Application.logMessageReceived -= UnityDebugLog;
var finishTime = EditorApplication.timeSinceStartup;
s_lastCompileTime = (float)(finishTime - s_startTime);
PlayerPrefs.DeleteKey("CompileStartTime");
}
static void LogTimes()
{
if (s_lastCompileTime <= 0f)
return;
if (s_compilationSucceeded)
Debug.Log("Compilation Success. Congratulations, you are awesome!");
else
Debug.LogError("Compilation FAILED. #$%*&§!!!!");
if (s_compilationSucceeded)
Debug.Log("It took " + s_lastCompileTime + "s to compile scripts.");
}
}