@RequiresEdt
  private void startup(final CompileScope scope,
                       final boolean isRebuild,
                       final boolean forceCompile,
                       final boolean withModalProgress,
                       final CompileStatusNotification callback,
                       final CompilerMessage message) {
    ModalityState modalityState = scope.getUserData(SKIP_SAVE) == Boolean.TRUE ? null : ModalityState.current();

    boolean isUnitTestMode = ApplicationManager.getApplication().isUnitTestMode();
    String name = JavaCompilerBundle.message(
      isRebuild ? "compiler.content.name.rebuild" : forceCompile ? "compiler.content.name.recompile" : "compiler.content.name.make"
    );
    Tracer.Span span = Tracer.start(name + " preparation");
    final CompilerTask compileTask = new CompilerTask(
      myProject, name, isUnitTestMode, !withModalProgress, true, isCompilationStartedAutomatically(scope), withModalProgress
    );

    StatusBar.Info.set("", myProject, "Compiler");

    if (modalityState != null) {
      PsiDocumentManager.getInstance(myProject).commitAllDocuments();
      FileDocumentManager.getInstance().saveAllDocuments();
    }

    final CompileContextImpl compileContext = new CompileContextImpl(myProject, compileTask, scope, !isRebuild && !forceCompile, isRebuild);
    span.complete();
    final Runnable compileWork = () -> {
      final ProgressIndicator indicator = compileContext.getProgressIndicator();
      if (indicator.isCanceled() || myProject.isDisposed() || !validateCompilerConfiguration(scope, indicator)) {
        if (callback != null) {
          callback.finished(true, 0, 0, compileContext);
        }
        return;
      }

      // ensure the project model seen by build process is up-to-date
      if (modalityState != null) {
        CompilerDriverHelperKt.saveSettings(myProject, modalityState, isUnitTestMode);
      }
      Tracer.Span compileWorkSpan = Tracer.start("compileWork");
      CompilerCacheManager compilerCacheManager = CompilerCacheManager.getInstance(myProject);
      final BuildManager buildManager = BuildManager.getInstance();
      try {
        buildManager.postponeBackgroundTasks();
        buildManager.cancelAutoMakeTasks(myProject);
        LOG.info("COMPILATION STARTED (BUILD PROCESS)");
        if (message != null) {
          compileContext.addMessage(message);
        }
        if (isRebuild) {
          CompilerUtil.runInContext(compileContext, JavaCompilerBundle.message("progress.text.clearing.build.system.data"),
                                    (ThrowableRunnable<Throwable>)() -> compilerCacheManager
                                      .clearCaches(compileContext));
        }
        final boolean beforeTasksOk = executeCompileTasks(compileContext, true);

        final int errorCount = compileContext.getMessageCount(CompilerMessageCategory.ERROR);
        if (!beforeTasksOk || errorCount > 0) {
          COMPILE_SERVER_BUILD_STATUS.set(compileContext, errorCount > 0 ? ExitStatus.ERRORS : ExitStatus.CANCELLED);
          return;
        }

        TaskFuture<?> future = compileInExternalProcess(compileContext, false);
        Tracer.Span compileInExternalProcessSpan = Tracer.start("compile in external process");
        while (!future.waitFor(200L, TimeUnit.MILLISECONDS)) {
          if (indicator.isCanceled()) {
            future.cancel(false);
          }
        }
        compileInExternalProcessSpan.complete();
        if (!executeCompileTasks(compileContext, false)) {
          COMPILE_SERVER_BUILD_STATUS.set(compileContext, ExitStatus.CANCELLED);
        }
        if (compileContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
          COMPILE_SERVER_BUILD_STATUS.set(compileContext, ExitStatus.ERRORS);
        }
      }
      catch (ProcessCanceledException ignored) {
        compileContext.putUserDataIfAbsent(COMPILE_SERVER_BUILD_STATUS, ExitStatus.CANCELLED);
      }
      catch (Throwable e) {
        LOG.error(e); // todo
      }
      finally {
        compileWorkSpan.complete();
        buildManager.allowBackgroundTasks(
          true // reset state on explicit build to compensate possibly unbalanced postpone/allow calls (e.g. via BatchFileChangeListener.start/stop)
        );
        Tracer.Span flushCompilerCaches = Tracer.start("flush compiler caches");
        compilerCacheManager.flushCaches();
        flushCompilerCaches.complete();

        final ExitStatus status = COMPILE_SERVER_BUILD_STATUS.get(compileContext);
        final long duration = notifyCompilationCompleted(compileContext, callback, status);
        CompilerUtil.logDuration(
          "\tCOMPILATION FINISHED (BUILD PROCESS); Errors: " + compileContext.getMessageCount(CompilerMessageCategory.ERROR) +
          "; warnings: " + compileContext.getMessageCount(CompilerMessageCategory.WARNING),
          duration
        );
        if (status == ExitStatus.SUCCESS) {
          BuildUsageCollector.logBuildCompleted(duration, isRebuild, false);
        }
      }
    };

    compileTask.start(compileWork, () -> {
      if (isRebuild) {
        final int rv = Messages.showOkCancelDialog(
          myProject, JavaCompilerBundle.message("you.are.about.to.rebuild.the.whole.project"),
          JavaCompilerBundle.message("confirm.project.rebuild"),
          CommonBundle.message("button.build"), JavaCompilerBundle.message("button.rebuild"), Messages.getQuestionIcon()
        );
        if (rv == Messages.OK /*yes, please, do run make*/) {
          startup(scope, false, false, false, callback, null);
          return;
        }
      }
      startup(scope, isRebuild, forceCompile, false, callback, message);
    });
  }
@RequiresEdt
  private void startup(final CompileScope scope,
                       final boolean isRebuild,
                       final boolean forceCompile,
                       final boolean withModalProgress,
                       final CompileStatusNotification callback,
                       final CompilerMessage message)