// Returns true when compilation is finished due to one of these reasons
        // * Was stopped (CompilationTask.Stopped will be true)
        // * Compilation had errors (CompilationTask.CompileErrors will be true)
        // * Compilation succesfully completed without errors.
        public bool Poll()
        {
            HandleOnCompilationTaskStarted();

            if (Stopped)
            {
                HandleOnCompilationTaskFinished();
                return(true);
            }

            Dictionary <ScriptAssembly, ScriptCompilerBase> finishedCompilerTasks = null;

            // Check if any compiler processes are finished.
            foreach (var task in compilerTasks)
            {
                var compiler = task.Value;

                // Did compiler task finish?
                if (compiler.Poll())
                {
                    if (finishedCompilerTasks == null)
                    {
                        finishedCompilerTasks = new Dictionary <ScriptAssembly, ScriptCompilerBase>();
                    }

                    var assembly = task.Key;
                    finishedCompilerTasks.Add(assembly, compiler);
                }
            }

            // Save compiler messages from finished compiler processes and check for compile errors.
            if (finishedCompilerTasks != null)
            {
                foreach (var task in finishedCompilerTasks)
                {
                    var assembly = task.Key;
                    var compiler = task.Value;

                    var messages     = compiler.GetCompilerMessages();
                    var messagesList = messages.ToList();

                    compiledAssemblies.Add(assembly, messagesList.ToArray());

                    if (RunPostProcessors && !messagesList.Any(m => m.type == CompilerMessageType.Error))
                    {
                        var sourcePath = AssetPath.Combine(buildOutputDirectory, assembly.Filename);

                        try
                        {
                            File.Copy(sourcePath, assembly.FullPath, true);
                            var postProcessorTask = new PostProcessorTask(assembly, messagesList, buildOutputDirectory, postProcessFunc);

                            postProcessorTask.Poll();
                            OnPostProcessingStarted?.Invoke(assembly);

                            postProcessorTasks.Add(postProcessorTask);
                        }
                        catch (IOException e)
                        {
                            UnityEngine.Debug.LogError($"Fail to copy {sourcePath} to {assembly.FullPath} before post processing the assembly. Skipping post processing.\n{e}");
                            // OnCompilationFinished callbacks might add more compiler messages
                            OnCompilationFinished?.Invoke(assembly, messagesList);
                            processedAssemblies.Add(assembly, messagesList.ToArray());
                        }
                    }
                    else
                    {
                        // OnCompilationFinished callbacks might add more compiler messages
                        OnCompilationFinished?.Invoke(assembly, messagesList);
                        processedAssemblies.Add(assembly, messagesList.ToArray());
                    }

                    if (!CompileErrors)
                    {
                        CompileErrors = messagesList.Any(m => m.type == CompilerMessageType.Error);
                    }

                    compilerTasks.Remove(assembly);
                    compiler.Dispose();
                }
            }

            HashSet <PostProcessorTask> finishedPostProcessorTasks = null;

            foreach (var postProcessorTask in postProcessorTasks)
            {
                // We break out of this loop instead to continuing to ensure that
                // OnCompilationFinished events are emitted in the same order as
                // they finished compiling.

                if (!postProcessorTask.Poll())
                {
                    break;
                }

                // Do not copy the post processed assembly in OnCompilationFinished
                // if any of the running compilers have a reference to the assembly.
                // As we might copy it while the compiler has the assembly open.
                if (AnyRunningCompilerHasReference(postProcessorTask.Assembly))
                {
                    break;
                }

                var messagesList = postProcessorTask.CompilerMessages;

                // OnCompilationFinished callbacks might add more compiler messages
                OnCompilationFinished?.Invoke(postProcessorTask.Assembly, messagesList);
                processedAssemblies.Add(postProcessorTask.Assembly, messagesList.ToArray());

                if (!CompileErrors)
                {
                    CompileErrors = messagesList.Any(m => m.type == CompilerMessageType.Error);
                }

                if (finishedPostProcessorTasks == null)
                {
                    finishedPostProcessorTasks = new HashSet <PostProcessorTask>();
                }

                finishedPostProcessorTasks.Add(postProcessorTask);
            }

            if (finishedPostProcessorTasks != null)
            {
                foreach (var finishedPostProcessorTask in finishedPostProcessorTasks)
                {
                    postProcessorTasks.Remove(finishedPostProcessorTask);
                }
            }

            // If StopOnFirstError is set, do not queue assemblies for compilation in case of compile errors.
            bool stopOnFirstError = (compilationTaskOptions & CompilationTaskOptions.StopOnFirstError) == CompilationTaskOptions.StopOnFirstError;

            if (stopOnFirstError && CompileErrors)
            {
                pendingAssemblies.Clear();

                if (FinishedCompilation)
                {
                    HandleOnCompilationTaskFinished();
                }

                return(FinishedCompilation);
            }

            // Queue pending assemblies for compilation if we have no running compilers or if compilers have finished.
            if (compilerTasks.Count == 0 || (finishedCompilerTasks != null && finishedCompilerTasks.Count > 0))
            {
                QueuePendingAssemblies();
            }

            if (FinishedCompilation)
            {
                HandleOnCompilationTaskFinished();
            }

            return(FinishedCompilation);
        }
        // Returns true when compilation is finished due to one of these reasons
        // * Was stopped (CompilationTask.Stopped will be true)
        // * Compilation had errors (CompilationTask.CompileErrors will be true)
        // * Compilation succesfully completed without errors.
        public bool Poll()
        {
            HandleOnCompilationTaskStarted();

            if (Stopped)
            {
                HandleOnCompilationTaskFinished();
                return(true);
            }

            Dictionary <ScriptAssembly, ScriptCompilerBase> finishedCompilerTasks = null;

            // Check if any compiler processes are finished.
            foreach (var task in compilerTasks)
            {
                var compiler = task.Value;

                // Did compiler task finish?
                if (compiler.Poll())
                {
                    if (finishedCompilerTasks == null)
                    {
                        finishedCompilerTasks = new Dictionary <ScriptAssembly, ScriptCompilerBase>();
                    }

                    var assembly = task.Key;
                    finishedCompilerTasks.Add(assembly, compiler);
                }
            }

            // Save compiler messages from finished compiler processes and check for compile errors.
            if (finishedCompilerTasks != null)
            {
                foreach (var task in finishedCompilerTasks)
                {
                    var assembly = task.Key;
                    var compiler = task.Value;

                    var messages     = compiler.GetCompilerMessages();
                    var messagesList = messages.ToList();

                    compiledAssemblies.Add(assembly, messagesList.ToArray());

                    bool havePostProcessors = ilPostProcessing != null && ilPostProcessing.HasPostProcessors;
                    bool isCodeGenAssembly  = codeGenAssemblies.Contains(assembly);
                    bool hasCompileErrors   = messagesList.Any(m => m.type == CompilerMessageType.Error);

                    if (isCodeGenAssembly)
                    {
                        if (hasCompileErrors)
                        {
                            notCompiledCodeGenAssemblies.Add(assembly);
                        }
                        else
                        {
                            compiledCodeGenAssemblies.Add(assembly);
                        }
                    }

                    if (havePostProcessors &&
                        notCompiledCodeGenAssemblies.Count == 0 &&
                        !hasCompileErrors &&
                        !isCodeGenAssembly)
                    {
                        var assemblySourcePath = AssetPath.Combine(buildOutputDirectory, assembly.Filename);
                        var pdbSourcePath      = AssetPath.Combine(buildOutputDirectory, assembly.PdbFilename);

                        try
                        {
                            if (assemblySourcePath != assembly.FullPath)
                            {
                                File.Copy(assemblySourcePath, assembly.FullPath, true);
                            }

                            if (pdbSourcePath != assembly.PdbFullPath)
                            {
                                File.Copy(pdbSourcePath, assembly.PdbFullPath, true);
                            }

                            var postProcessorTask = new PostProcessorTask(assembly, messagesList, buildOutputDirectory, ilPostProcessing);
                            pendingPostProcessorTasks.Add(postProcessorTask);
                        }
                        catch (IOException e)
                        {
                            UnityEngine.Debug.LogError($"Fail to copy {assemblySourcePath} or {pdbSourcePath} to {AssetPath.GetDirectoryName(assembly.FullPath)} before post processing the assembly. Skipping post processing.\n{e}");
                            // OnCompilationFinished callbacks might add more compiler messages
                            OnCompilationFinished?.Invoke(assembly, messagesList);
                            processedAssemblies.Add(assembly, messagesList.ToArray());
                        }
                    }
                    else
                    {
                        // OnCompilationFinished callbacks might add more compiler messages
                        OnCompilationFinished?.Invoke(assembly, messagesList);
                        processedAssemblies.Add(assembly, messagesList.ToArray());
                    }

                    if (!CompileErrors)
                    {
                        CompileErrors = messagesList.Any(m => m.type == CompilerMessageType.Error);
                    }

                    // If a codgen / IL Post processor has compile errors, clear
                    // pending assemblies waiting for compilation and assemblies
                    // waiting to get post processed.
                    if (isCodeGenAssembly && hasCompileErrors)
                    {
                        pendingPostProcessorTasks.Clear();
                        pendingAssemblies.Clear();
                    }

                    compilerTasks.Remove(assembly);
                    compiler.Dispose();
                }
            }


            if (ilPostProcessing != null && ilPostProcessing.HasPostProcessors)
            {
                PollPostProcessors();
            }

            // If StopOnFirstError is set, do not queue assemblies for compilation in case of compile errors.
            bool stopOnFirstError = (compilationTaskOptions & CompilationTaskOptions.StopOnFirstError) == CompilationTaskOptions.StopOnFirstError;

            if (stopOnFirstError && CompileErrors)
            {
                foreach (var pendingAssembly in pendingAssemblies)
                {
                    if (UnityCodeGenHelpers.IsCodeGen(pendingAssembly.Filename))
                    {
                        notCompiledCodeGenAssemblies.Add(pendingAssembly);
                    }
                }

                pendingAssemblies.Clear();

                if (FinishedCompilation)
                {
                    HandleOnCompilationTaskFinished();
                }

                return(FinishedCompilation);
            }

            // Queue pending assemblies for compilation if we have no running compilers or if compilers have finished.
            if (compilerTasks.Count == 0 || (finishedCompilerTasks != null && finishedCompilerTasks.Count > 0))
            {
                QueuePendingAssemblies();
            }

            if (FinishedCompilation)
            {
                HandleOnCompilationTaskFinished();
            }

            return(FinishedCompilation);
        }