@Override
  public Promise<Result> run(@NotNull ProjectTaskContext context, @NotNull ProjectTask projectTask) {
    Tracer.Span buildSpan = Tracer.start("build");
    AsyncPromise<Result> promiseResult = new AsyncPromise<>();
    List<Pair<ProjectTaskRunner, Collection<? extends ProjectTask>>> toRun = groupByRunner(projectTask, context);

    buildSpan.complete();
    context.putUserData(ProjectTaskScope.KEY, new ProjectTaskScope() {
      @Override
      public @NotNull <T extends ProjectTask> List<T> getRequestedTasks(@NotNull Class<T> instanceOf) {
        List<T> tasks = new ArrayList<>();
        //noinspection unchecked
        toRun.forEach(pair -> pair.second.stream().filter(instanceOf::isInstance).map(task -> (T)task).forEach(tasks::add));
        return tasks;
      }
    });


    Pair<StructuredIdeActivity, List<EventPair<?>>> activity = reportBuildStart(projectTask, toRun);
    myEventPublisher.started(context);

    Runnable runnable = () -> {
      for (ProjectTaskManagerListener listener : myListeners) {
        try {
          listener.beforeRun(context);
        }
        catch (ExecutionException e) {
          sendAbortedEmptyResult(context, new ResultConsumer(promiseResult));
          activity.first.finished(() -> activity.second);
          return;
        }
      }

      if (toRun.isEmpty()) {
        sendSuccessEmptyResult(context, new ResultConsumer(promiseResult));
        activity.first.finished(() -> activity.second);
        return;
      }

      ProjectTaskResultsAggregator resultsCollector =
        new ProjectTaskResultsAggregator(context, new ResultConsumer(promiseResult), toRun.size(), activity);
      for (Pair<ProjectTaskRunner, Collection<? extends ProjectTask>> pair : toRun) {
        Consumer<ProjectTaskRunner.Result> runnerResultConsumer = result -> resultsCollector.add(result, pair.second);
        if (pair.second.isEmpty()) {
          runnerResultConsumer.accept(TaskRunnerResults.SUCCESS);
        }
        else {
          ProjectTaskRunner runner = pair.first;
          if (context.isCollectionOfGeneratedFilesEnabled() && !runner.isFileGeneratedEventsSupported()) {
            pair.second.stream()
              .filter(ModuleBuildTask.class::isInstance)
              .map(task -> ((ModuleBuildTask)task).getModule())
              .forEach(module -> context.addDirtyOutputPathsProvider(moduleOutputPathsProvider(module)));
          }
          runner
            .run(myProject, context, pair.second.toArray(EMPTY_TASKS_ARRAY))
            .onSuccess(runnerResultConsumer)
            .onError(throwable -> runnerResultConsumer.accept(TaskRunnerResults.ABORTED));
        }
      }
    };
    // do not run before tasks on EDT
    if (ApplicationManager.getApplication().isDispatchThread()) {
      ApplicationManager.getApplication().executeOnPooledThread(runnable);
    }
    else {
      runnable.run();
    }
    promiseResult.onProcessed(result -> {
      buildSpan.complete();
    });
    return promiseResult;
  }
@Override
  public Promise<Result> run(@NotNull ProjectTaskContext context, @NotNull ProjectTask projectTask)