private void runBuild(final CompileContextImpl context, boolean forceCleanCaches) throws ProjectBuildException {
    context.setDone(0.0f);

    LOG.info("Building project; isRebuild:" +
             context.isProjectRebuild() +
             "; isMake:" +
             context.isMake() +
             " parallel compilation:" +
             isParallelBuild() +
             "; dependency graph enabled:" +
             isDepGraphEnabled());

    context.addBuildListener(new ChainedTargetsBuildListener(context));

    // deletes class loader classpath index files for changed output roots
    context.addBuildListener(new BuildListener() {
      @Override
      public void filesGenerated(@NotNull FileGeneratedEvent event) {
        Collection<Pair<String, String>> paths = event.getPaths();
        FileSystem fs = FileSystems.getDefault();
        if (paths.size() == 1) {
          deleteFiles(paths.iterator().next().first, fs);
          return;
        }

        Set<String> outputs = new HashSet<>();
        for (Pair<String, String> pair : paths) {
          String root = pair.getFirst();
          if (outputs.add(root)) {
            deleteFiles(root, fs);
          }
        }
      }

      private void deleteFiles(String rootPath, FileSystem fs) {
        Path root = fs.getPath(rootPath);
        try {
          Files.deleteIfExists(root.resolve(CLASSPATH_INDEX_FILE_NAME));
          Files.deleteIfExists(root.resolve(UNMODIFIED_MARK_FILE_NAME));
        }
        catch (IOException ignore) {
        }
      }
    });
    Tracer.Span allTargetBuilderBuildStartedSpan = Tracer.start("All TargetBuilder.buildStarted");
    for (TargetBuilder<?, ?> builder : myBuilderRegistry.getTargetBuilders()) {
      builder.buildStarted(context);
    }
    allTargetBuilderBuildStartedSpan.complete();
    Tracer.Span allModuleLevelBuildersBuildStartedSpan = Tracer.start("All ModuleLevelBuilder.buildStarted");
    for (ModuleLevelBuilder builder : myBuilderRegistry.getModuleLevelBuilders()) {
      builder.buildStarted(context);
    }
    allModuleLevelBuildersBuildStartedSpan.complete();

    BuildProgress buildProgress = null;
    try {
      buildProgress = new BuildProgress(myProjectDescriptor.dataManager, myProjectDescriptor.getBuildTargetIndex(),
                                        myProjectDescriptor.getBuildTargetIndex().getSortedTargetChunks(context),
                                        chunk -> isAffected(context.getScope(), chunk));

      // clean roots for targets for which rebuild is forced
      Tracer.Span cleanOutputSourcesSpan = Tracer.start("Clean output sources");
      cleanOutputRoots(context, context.isProjectRebuild() || forceCleanCaches);
      cleanOutputSourcesSpan.complete();

      Tracer.Span beforeTasksSpan = Tracer.start("'before' tasks");
      context.processMessage(new ProgressMessage(JpsBuildBundle.message("progress.message.running.before.tasks")));
      runTasks(context, myBuilderRegistry.getBeforeTasks());
      TimingLog.LOG.debug("'before' tasks finished");
      beforeTasksSpan.complete();

      Tracer.Span checkingSourcesSpan = Tracer.start("Building targets");
      context.processMessage(new ProgressMessage(JpsBuildBundle.message("progress.message.checking.sources")));
      buildChunks(context, buildProgress);
      TimingLog.LOG.debug("Building targets finished");
      checkingSourcesSpan.complete();

      Tracer.Span afterTasksSpan = Tracer.start("'after' span");
      context.processMessage(new ProgressMessage(JpsBuildBundle.message("progress.message.running.after.tasks")));
      runTasks(context, myBuilderRegistry.getAfterTasks());
      TimingLog.LOG.debug("'after' tasks finished");
      sendElapsedTimeMessages(context);
      afterTasksSpan.complete();
    }
    finally {
      if (buildProgress != null) {
        buildProgress.updateExpectedAverageTime();
        if (context.isProjectRebuild() && !Utils.errorsDetected(context) && !context.getCancelStatus().isCanceled()) {
          myProjectDescriptor.getTargetsState().setLastSuccessfulRebuildDuration(buildProgress.getAbsoluteBuildTime());
        }
      }
      for (TargetBuilder<?, ?> builder : myBuilderRegistry.getTargetBuilders()) {
        builder.buildFinished(context);
      }
      for (ModuleLevelBuilder builder : myBuilderRegistry.getModuleLevelBuilders()) {
        builder.buildFinished(context);
      }
      context.processMessage(new ProgressMessage(JpsBuildBundle.message("progress.message.finished.saving.caches")));
    }

  }
private void runBuild(final CompileContextImpl context, boolean forceCleanCaches)