public static <R extends BuildRootDescriptor, T extends BuildTarget<R>> DirtyFilesHolderBuilder<R, T> createDirtyFilesHolderBuilder(CompileContext context, final CompilationRound round) {
    return new DirtyFilesHolderBuilder<R, T>() {
      private final Map<T, Map<R, Set<File>>> dirtyFiles = new HashMap<>();
      @Override
      public DirtyFilesHolderBuilder<R, T> markDirtyFile(T target, File file) throws IOException {
        final ProjectDescriptor pd = context.getProjectDescriptor();
        final R rd = pd.getBuildRootIndex().findParentDescriptor(file, Collections.singleton(target.getTargetType()), context);
        if (rd != null) {
          if (pd.fsState.markDirtyIfNotDeleted(context, round, file, rd, pd.getProjectStamps().getStampStorage()) || pd.fsState.isMarkedForRecompilation(context, round, rd, file)) {
            Map<R, Set<File>> targetFiles = dirtyFiles.get(target);
            if (targetFiles == null) {
              targetFiles = new HashMap<>();
              dirtyFiles.put(target, targetFiles);
            }
            Set<File> rootFiles = targetFiles.get(rd);
            if (rootFiles == null) {
              rootFiles = FileCollectionFactory.createCanonicalFileSet();
              targetFiles.put(rd, rootFiles);
            }
            rootFiles.add(file);
          }
        }
        return this;
      }

      @Override
      public DirtyFilesHolder<R, T> create() {
        return new DirtyFilesHolder<>() {
          @Override
          public void processDirtyFiles(@NotNull FileProcessor<R, T> processor) throws IOException {
            for (Map.Entry<T, Map<R, Set<File>>> entry : dirtyFiles.entrySet()) {
              final T target = entry.getKey();
              for (Map.Entry<R, Set<File>>  targetEntry: entry.getValue().entrySet()) {
                final R rd = targetEntry.getKey();
                for (File file : targetEntry.getValue()) {
                  processor.apply(target, file, rd);
                }
              }
            }
          }

          @Override
          public boolean hasDirtyFiles() {
            return !dirtyFiles.isEmpty();
          }

          @Override
          public boolean hasRemovedFiles() {
            return false;
          }

          @Override
          public @NotNull Collection<String> getRemovedFiles(@NotNull T target) {
            return Collections.emptyList();
          }
        };
      }
    };
  }
public static <R extends BuildRootDescriptor, T extends BuildTarget<R>> DirtyFilesHolderBuilder<R, T> createDirtyFilesHolderBuilder(CompileContext context, final CompilationRound round)