3
\$\begingroup\$

I've been working on some custom build scripts, which will manipulate the references of certain MonoBehaviours.

I'm using IPreprocessBuildWithReport to do the manipulation, and I want to reset the references when the build completes. Even if the build fails, I want to do the operation, but I want to do some additional stuff if it fails. I'm also using IPostprocessBuildWithReport and IProcessSceneWithReport as well.

I've tried putting a Debug.Log in OnProcessScene and OnPostProcessBuild. I'm building without JAVA path defined, for Android, or without the Keystore password, which fails the build. The IPreprocessBuildWithReport correctly prints the Debug, but the others don't.

Is there any class or interface which has a callback for Build Failure which I can use? I've tried to get the summary from the BuildReport in OnPreprocessBuild, but the result is always returning Unknown.

Edit: I would also like to make the same changes if the Build was cancelled, rather than just if it was failing.

Platforms: Windows, Android

Unity 2019.3.7f1

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

We started to use CompilationPipeline to detect the errors and trigger some custom actions. Here is the script template:

using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEditor.Compilation;
using UnityEngine;

namespace Fiftytwo.BuildTools
{
    internal class BuildPreprocessor : IPreprocessBuildWithReport
    {
        private static object _compilationContext;
        private static int _compilationErrorCount;


        public int callbackOrder => 0;


        public void OnPreprocessBuild ( BuildReport report )
        {
            _compilationContext = null;
            _compilationErrorCount = 0;

            CompilationPipeline.compilationStarted += CompilationPipelineOnCompilationStarted;
            CompilationPipeline.assemblyCompilationFinished += CompilationPipelineOnAssemblyCompilationFinished;
            CompilationPipeline.compilationFinished += CompilationPipelineOnCompilationFinished;
        }

        private static void CompilationPipelineOnCompilationStarted ( object compilationContext )
        {
            _compilationContext = compilationContext;
            _compilationErrorCount = 0;
        }

        private static void CompilationPipelineOnAssemblyCompilationFinished ( string path, CompilerMessage[] messages )
        {
            for( int i = messages?.Length ?? 0; --i >= 0; )
            {
                if( messages[i].type == CompilerMessageType.Error )
                    ++_compilationErrorCount;
            }
        }

        private static void CompilationPipelineOnCompilationFinished ( object compilationContext )
        {
            if( compilationContext != _compilationContext )
                return;

            _compilationContext = null;

            CompilationPipeline.compilationStarted -= CompilationPipelineOnCompilationStarted;
            CompilationPipeline.assemblyCompilationFinished -= CompilationPipelineOnAssemblyCompilationFinished;
            CompilationPipeline.compilationFinished -= CompilationPipelineOnCompilationFinished;

            if( _compilationErrorCount > 0 )
            {
                Debug.LogError( $"Compilation finished with errors ({_compilationErrorCount})" );
                // Custom compilation failure processing
            }
        }
    }
}
\$\endgroup\$
0
\$\begingroup\$

You can use a custom build handler and invoke the unity default build process from there. For post-processing use BuildHandler.OnBuildCleanup, which is called when a build completes or fails.

[InitializeOnLoad]
public class BuildHandler : MonoBehaviour
{
    public static event Action OnBuildStarted;
    public static event Action OnBuildCleanup;

    static BuildHandler()
    {
        BuildPlayerWindow.RegisterBuildPlayerHandler(Build);
    }

    private static void Build(BuildPlayerOptions buildOptions)
    {
        try
        {
            BuildStarted();

            //now start Unity's default building procedure
            BuildPlayerWindow.DefaultBuildMethods.BuildPlayer(buildOptions);
        }
        catch (BuildPlayerWindow.BuildMethodException) { } //logged internally
        catch (Exception ex)
        {
            Exception log = ex.InnerException == null ? ex : ex.InnerException;
            Debug.LogException(log);
            Debug.LogErrorFormat("{0} in BuildHandler: '{1}'", log.GetType().Name, ex.Message);
        }
        finally
        {
            BuildCleanup();
        }
    }

    private static void BuildStarted()
    {
        try
        {
            if (OnBuildStarted != null)
                OnBuildStarted.Invoke();
        }
        catch (Exception ex)
        {
            throw new Exception("OnBuildStarted failed", ex);
        }
    }

    private static void BuildCleanup()
    {
        if (OnBuildCleanup != null)
        {
            foreach (Action cleanupBuild in OnBuildCleanup.GetInvocationList())
            {
                try
                {
                    cleanupBuild.Invoke();
                }
                catch (Exception ex)
                {
                    Object context = cleanupBuild.Target as Object;
                    Debug.LogException(ex, context);
                    Debug.LogWarning("Caught exception while BuildHandler.OnBuildCleanup... Continuing");
                }
            }
        }
    }
}
```
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .