@NotNull
  private TaskFuture<?> compileInExternalProcess(@NotNull final CompileContextImpl compileContext, final boolean onlyCheckUpToDate) {
    final CompileScope scope = compileContext.getCompileScope();
    final Collection<String> paths = ReadAction.compute(() -> CompileScopeUtil.fetchFiles(compileContext));
    List<TargetTypeBuildScope> scopes = ReadAction.compute(() -> getBuildScopes(compileContext, scope, paths));

    // need to pass scope's user data to server
    final Map<String, String> builderParams;
    if (onlyCheckUpToDate) {
      builderParams = new HashMap<>();
    }
    else {
      Map<Key<?>, Object> exported = scope.exportUserData();
      if (!exported.isEmpty()) {
        builderParams = new HashMap<>();
        for (Map.Entry<Key<?>, Object> entry : exported.entrySet()) {
          final String _key = entry.getKey().toString();
          final String _value = entry.getValue().toString();
          builderParams.put(_key, _value);
        }
      }
      else {
        builderParams = new HashMap<>();
      }
    }
    if (!scope.getAffectedUnloadedModules().isEmpty()) {
      builderParams.put(BuildParametersKeys.LOAD_UNLOADED_MODULES, Boolean.TRUE.toString());
    }

    final Map<String, List<Artifact>> outputToArtifact = ArtifactCompilerUtil.containsArtifacts(scopes) ? ArtifactCompilerUtil.createOutputToArtifactMap(myProject) : null;
    return BuildManager.getInstance().scheduleBuild(myProject, compileContext.isRebuild(), compileContext.isMake(), onlyCheckUpToDate, scopes, paths, builderParams, new DefaultMessageHandler(myProject) {
      @Override
      public void buildStarted(@NotNull UUID sessionId) {
        if (!onlyCheckUpToDate && compileContext.shouldUpdateProblemsView()) {
          ProblemsView view = ProblemsView.getInstanceIfCreated(myProject);
          if (view != null) {
            view.buildStarted(sessionId);
          }
        }
      }

      @Override
        public void sessionTerminated(@NotNull UUID sessionId) {
          if (!onlyCheckUpToDate && compileContext.shouldUpdateProblemsView()) {
            ProblemsView view = ProblemsView.getInstanceIfCreated(myProject);
            if (view != null) {
              view.clearProgress();
              view.clearOldMessages(compileContext.getCompileScope(), compileContext.getSessionId());
            }
          }
        }

        @Override
        public void handleFailure(@NotNull UUID sessionId, CmdlineRemoteProto.Message.Failure failure) {
          //noinspection HardCodedStringLiteral
          compileContext.addMessage(CompilerMessageCategory.ERROR, failure.hasDescription() ? failure.getDescription() : "", null, -1, -1);
          final String trace = failure.hasStacktrace() ? failure.getStacktrace() : null;
          if (trace != null) {
            LOG.info(trace);
          }
          compileContext.putUserData(COMPILE_SERVER_BUILD_STATUS, ExitStatus.ERRORS);
        }

        @Override
        protected void handleCompileMessage(UUID sessionId,
                                            CmdlineRemoteProto.Message.BuilderMessage.CompileMessage message) {
          final CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind kind = message.getKind();
          //System.out.println(compilerMessage.getText());
          //noinspection HardCodedStringLiteral
          final String messageText = message.getText();
          if (kind == CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind.PROGRESS) {
            final ProgressIndicator indicator = compileContext.getProgressIndicator();
            indicator.setText(messageText);
            if (message.hasDone()) {
              indicator.setFraction(message.getDone());
            }
          }
          else {
            final CompilerMessageCategory category = convertToCategory(kind, CompilerMessageCategory.INFORMATION);

            String sourceFilePath = message.hasSourceFilePath() ? message.getSourceFilePath() : null;
            if (sourceFilePath != null) {
              sourceFilePath = FileUtil.toSystemIndependentName(sourceFilePath);
            }
            final long line = message.hasLine() ? message.getLine() : -1;
            final long column = message.hasColumn() ? message.getColumn() : -1;
            final String srcUrl =
              sourceFilePath != null ? VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, sourceFilePath) : null;
            compileContext
              .addMessage(category, messageText, srcUrl, (int)line, (int)column, null, message.getModuleNamesList());
            if (compileContext.shouldUpdateProblemsView() &&
              kind == CmdlineRemoteProto.Message.BuilderMessage.CompileMessage.Kind.JPS_INFO) {
              // treat JPS_INFO messages in a special way: add them as info messages to the problems view
              final Project project = compileContext.getProject();
              ProblemsView.getInstance(project).addMessage(
                new CompilerMessageImpl(project, category, messageText),
                compileContext.getSessionId()
              );
            }
          }
        }

        @Override
        protected void handleBuildEvent(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event) {
          final CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Type eventType = event.getEventType();
          switch (eventType) {
            case FILES_GENERATED -> {
              final List<CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.GeneratedFile> generated =
                event.getGeneratedFilesList();
              CompilationStatusListener publisher =
                myProject.isDisposed() ? null : myProject.getMessageBus().syncPublisher(CompilerTopics.COMPILATION_STATUS);
              Set<String> writtenArtifactOutputPaths =
                outputToArtifact != null ? CollectionFactory.createFilePathSet() : null;
              for (CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.GeneratedFile generatedFile : generated) {
                final String root = FileUtil.toSystemIndependentName(generatedFile.getOutputRoot());
                final String relativePath = FileUtil.toSystemIndependentName(generatedFile.getRelativePath());
                if (publisher != null) {
                  publisher.fileGenerated(root, relativePath);
                }
                if (outputToArtifact != null) {
                  Collection<Artifact> artifacts = outputToArtifact.get(root);
                  if (artifacts != null && !artifacts.isEmpty()) {
                    writtenArtifactOutputPaths
                      .add(FileUtil.toSystemDependentName(DeploymentUtil.appendToPath(root, relativePath)));
                  }
                }
              }
              if (writtenArtifactOutputPaths != null && !writtenArtifactOutputPaths.isEmpty()) {
                ArtifactsCompiler.addWrittenPaths(compileContext, writtenArtifactOutputPaths);
              }
            }
            case BUILD_COMPLETED -> {
              ExitStatus status = !event.hasCompletionStatus() ? ExitStatus.SUCCESS : 
                                  switch (event.getCompletionStatus()) {
                case CANCELED -> ExitStatus.CANCELLED;
                case ERRORS -> ExitStatus.ERRORS;
                case SUCCESS -> ExitStatus.SUCCESS;
                case UP_TO_DATE -> ExitStatus.UP_TO_DATE;
              };
              compileContext.putUserDataIfAbsent(COMPILE_SERVER_BUILD_STATUS, status);
            }
            case CUSTOM_BUILDER_MESSAGE -> {
              if (event.hasCustomBuilderMessage()) {
                final CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.CustomBuilderMessage message =
                  event.getCustomBuilderMessage();
                if (GlobalOptions.JPS_SYSTEM_BUILDER_ID.equals(message.getBuilderId()) &&
                    GlobalOptions.JPS_UNPROCESSED_FS_CHANGES_MESSAGE_ID.equals(message.getMessageType())) {
                  //noinspection HardCodedStringLiteral
                  final String text = message.getMessageText();
                  if (!StringUtil.isEmpty(text)) {
                    compileContext.addMessage(CompilerMessageCategory.INFORMATION, text, null, -1, -1);
                  }
                }
              }
            }
          }
        }

        @Override
        public @NotNull ProgressIndicator getProgressIndicator() {
          return compileContext.getProgressIndicator();
        }
      });
  }
@NotNull
  private TaskFuture<?> compileInExternalProcess(@NotNull final CompileContextImpl compileContext, final boolean onlyCheckUpToDate)