private void checkRebuildRequired(final CompileScope scope) throws RebuildRequestedException {
    if (myIsTestMode || isAutoBuild()) {
      // do not use the heuristic in tests in order to properly test all cases
      // automatic builds should not cause start full project rebuilds to avoid situations when rebuild is not expected by user
      if (LOG.isDebugEnabled()) {
        LOG.debug("Rebuild heuristic: skipping the check; isTestMode = " + myIsTestMode + "; isAutoBuild = " + isAutoBuild());
      }
      return;
    }
    final BuildTargetsState targetsState = myProjectDescriptor.getTargetsState();
    final long timeThreshold = targetsState.getLastSuccessfulRebuildDuration() * 95 / 100; // 95% of last registered clean rebuild time
    if (timeThreshold <= 0) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Rebuild heuristic: no stats available");
      }
      return;
    }
    // check that this is a whole-project incremental build
    // checking only JavaModuleBuildTargetType because these target types directly correspond to project modules
    for (BuildTargetType<?> type : JavaModuleBuildTargetType.ALL_TYPES) {
      if (!scope.isBuildIncrementally(type)) {
        if (LOG.isDebugEnabled()) {
          LOG.debug("Rebuild heuristic: skipping the check because rebuild is forced for targets of type " + type.getTypeId());
        }
        return;
      }
      if (!scope.isAllTargetsOfTypeAffected(type)) {
        if (LOG.isDebugEnabled()) {
          LOG.debug("Rebuild heuristic: skipping the check because some targets are excluded from compilation scope, e.g. targets of type " + type.getTypeId());
        }
        return;
      }
    }
    // compute estimated times for dirty targets
    final long estimatedWorkTime = calculateEstimatedBuildTime(myProjectDescriptor, new Predicate<>() {
      private final Set<BuildTargetType<?>> allTargetsAffected = new HashSet<>(JavaModuleBuildTargetType.ALL_TYPES);
      @Override
      public boolean test(BuildTarget<?> target) {
        // optimization, since we know here that all targets of types JavaModuleBuildTargetType are affected
        return allTargetsAffected.contains(target.getTargetType()) || scope.isAffected(target);
      }
    });
    if (LOG.isDebugEnabled()) {
      LOG.debug("Rebuild heuristic: estimated build time / timeThreshold : " + estimatedWorkTime + " / " + timeThreshold);
    }

    if (estimatedWorkTime >= timeThreshold) {
      final String message = JpsBuildBundle.message("build.message.too.many.modules.require.recompilation.forcing.full.project.rebuild");
      LOG.info(message);
      LOG.info("Estimated build duration (linear): " + StringUtil.formatDuration(estimatedWorkTime));
      LOG.info("Last successful rebuild duration (linear): " + StringUtil.formatDuration(targetsState.getLastSuccessfulRebuildDuration()));
      LOG.info("Rebuild heuristic time threshold: " + StringUtil.formatDuration(timeThreshold));
      myMessageDispatcher.processMessage(new CompilerMessage("", BuildMessage.Kind.INFO, message));
      throw new RebuildRequestedException(null);
    }
  }
private void checkRebuildRequired(final CompileScope scope)