@Override
  public Promise<Result> run(@NotNull Project project,
                             @NotNull ProjectTaskContext context,
                             ProjectTask @NotNull ... tasks) {
    AsyncPromise<Result> resultPromise = new AsyncPromise<>();
    TasksExecutionSettingsBuilder executionSettingsBuilder = new TasksExecutionSettingsBuilder(project, tasks);
    Set<String> rootPaths = executionSettingsBuilder.getRootPaths();
    if (rootPaths.isEmpty()) {
      LOG.warn("Nothing will be run for: " + Arrays.toString(tasks));
      resultPromise.setResult(TaskRunnerResults.SUCCESS);
      return resultPromise;
    }

    AtomicInteger successCounter = new AtomicInteger();
    AtomicInteger errorCounter = new AtomicInteger();

    File outputPathsFile = createTempOutputPathsFileIfNeeded(context);
    TaskCallback taskCallback = new TaskCallback() {
      @Override
      public void onSuccess() {
        handle(true);
      }

      @Override
      public void onFailure() {
        handle(false);
      }

      private void handle(boolean success) {
        int successes = success ? successCounter.incrementAndGet() : successCounter.get();
        int errors = success ? errorCounter.get() : errorCounter.incrementAndGet();
        if (successes + errors == rootPaths.size()) {
          if (!project.isDisposed()) {
            try {
              if (GradleImprovedHotswapDetection.isEnabled()) {
                GradleImprovedHotswapDetection.processInitScriptOutput(context, outputPathsFile);
              }
              else {
                Set<String> affectedRoots = getAffectedOutputRoots(outputPathsFile, context, executionSettingsBuilder);
                if (!affectedRoots.isEmpty()) {
                  if (context.isCollectionOfGeneratedFilesEnabled()) {
                    context.addDirtyOutputPathsProvider(() -> affectedRoots);
                  }
                  // refresh on output roots is required in order for the order enumerator to see all roots via VFS
                  // have to refresh in case of errors too, because run configuration may be set to ignore errors
                  CompilerUtil.refreshOutputRoots(affectedRoots);
                }
              }
            }
            finally {
              if (outputPathsFile != null) {
                FileUtil.delete(outputPathsFile);
              }
            }
          }
          resultPromise.setResult(errors > 0 ? TaskRunnerResults.FAILURE : TaskRunnerResults.SUCCESS);
        }
        else {
          if (successes + errors > rootPaths.size()) {
            LOG.error("Unexpected callback!");
          }
        }
      }
    };

    for (String rootProjectPath : rootPaths) {
      if (!executionSettingsBuilder.containsTasksToExecuteFor(rootProjectPath)) {
        taskCallback.onSuccess();
        LOG.warn("Nothing will be run for: " + Arrays.toString(tasks) + " at '" + rootProjectPath + "'");
        continue;
      }

      if (outputPathsFile != null && context.isCollectionOfGeneratedFilesEnabled()) {
        String outputFilePath = FileUtil.toCanonicalPath(outputPathsFile.getAbsolutePath());
        GradleVersion v68 = GradleVersion.version("6.8");

        Supplier<String> initScriptSupplier;
        Supplier<String> initScriptUsingServiceSupplier;

        if (GradleImprovedHotswapDetection.isEnabled()) {
          initScriptSupplier = () -> GradleImprovedHotswapDetection.getInitScript(outputPathsFile);
          initScriptUsingServiceSupplier = () -> GradleImprovedHotswapDetection.getInitScriptUsingService(outputPathsFile);
        }
        else {
          initScriptSupplier = () -> String.format(COLLECT_OUTPUT_PATHS_INIT_SCRIPT_TEMPLATE, outputFilePath);
          initScriptUsingServiceSupplier = () -> String.format(COLLECT_OUTPUT_PATHS_USING_SERVICES_INIT_SCRIPT_TEMPLATE, outputFilePath);
        }
        var simple = new LazyVersionSpecificInitScript(initScriptSupplier, "ijpathcollect", v -> v.compareTo(v68) < 0);
        var services =
          new LazyVersionSpecificInitScript(initScriptUsingServiceSupplier, "ijpathcollect", v -> v.compareTo(v68) >= 0);
        executionSettingsBuilder.addInitScripts(rootProjectPath, simple, services);
      }

      ExternalSystemTaskExecutionSettings settings = executionSettingsBuilder.build(rootProjectPath);
      UserDataHolderBase userData = new UserDataHolderBase();
      userData.putUserData(PROGRESS_LISTENER_KEY, BuildViewManager.class);
      userData.putUserData(GradleTaskManager.VERSION_SPECIFIC_SCRIPTS_KEY,
                           executionSettingsBuilder.getVersionedInitScripts(rootProjectPath));
      userData.putUserData(GradleTaskManager.INIT_SCRIPT_KEY, join(executionSettingsBuilder.getInitScripts(rootProjectPath), System.lineSeparator()));
      userData.putUserData(GradleTaskManager.INIT_SCRIPT_PREFIX_KEY, settings.getExecutionName());

      ExternalSystemUtil.runTask(settings, DefaultRunExecutor.EXECUTOR_ID, project, GradleConstants.SYSTEM_ID,
                                 taskCallback, ProgressExecutionMode.IN_BACKGROUND_ASYNC, false, userData);
    }
    return resultPromise;
  }
@Override
  public Promise<Result> run(@NotNull Project project,
                             @NotNull ProjectTaskContext context,
                             ProjectTask @NotNull ... tasks)