private static void traverseRecursively(
     CompileContext context,
     final JavaSourceRootDescriptor rd,
     final File file,
     Set<File> excludes,
     @NotNull final Timestamps tsStorage,
     final boolean forceDirty,
     @Nullable Set<File> currentFiles)
     throws IOException {
   final File[] children = file.listFiles();
   if (children != null) { // is directory
     if (children.length > 0 && !JpsPathUtil.isUnder(excludes, file)) {
       for (File child : children) {
         traverseRecursively(context, rd, child, excludes, tsStorage, forceDirty, currentFiles);
       }
     }
   } else { // is file
     boolean markDirty = forceDirty;
     if (!markDirty) {
       markDirty = tsStorage.getStamp(file, rd.target) != FileSystemUtil.lastModified(file);
     }
     if (markDirty) {
       // if it is full project rebuild, all storages are already completely cleared;
       // so passing null because there is no need to access the storage to clear non-existing data
       final Timestamps marker = context.isProjectRebuild() ? null : tsStorage;
       context.getProjectDescriptor().fsState.markDirty(context, file, rd, marker);
     }
     if (currentFiles != null) {
       currentFiles.add(file);
     }
   }
 }
 static void markDirtyFiles(
     CompileContext context,
     ModuleBuildTarget target,
     Timestamps timestamps,
     boolean forceMarkDirty,
     @Nullable THashSet<File> currentFiles)
     throws IOException {
   final ModuleExcludeIndex rootsIndex = context.getProjectDescriptor().getModuleExcludeIndex();
   final Set<File> excludes = new HashSet<File>(rootsIndex.getModuleExcludes(target.getModule()));
   for (JavaSourceRootDescriptor rd :
       context.getProjectDescriptor().getBuildRootIndex().getTargetRoots(target, context)) {
     if (!rd.root.exists()) {
       continue;
     }
     context.getProjectDescriptor().fsState.clearRecompile(rd);
     traverseRecursively(context, rd, rd.root, excludes, timestamps, forceMarkDirty, currentFiles);
   }
 }
 private static void traverseRecursively(
     CompileContext context,
     final BuildRootDescriptor rd,
     final CompilationRound round,
     final File file,
     @NotNull final Timestamps tsStorage,
     final boolean forceDirty,
     @Nullable Set<File> currentFiles,
     @Nullable FileFilter filter,
     @NotNull FSCache fsCache)
     throws IOException {
   BuildRootIndex rootIndex = context.getProjectDescriptor().getBuildRootIndex();
   final File[] children = fsCache.getChildren(file);
   if (children != null) { // is directory
     if (children.length > 0 && rootIndex.isDirectoryAccepted(file, rd)) {
       for (File child : children) {
         traverseRecursively(
             context, rd, round, child, tsStorage, forceDirty, currentFiles, filter, fsCache);
       }
     }
   } else { // is file
     if (rootIndex.isFileAccepted(file, rd) && (filter == null || filter.accept(file))) {
       boolean markDirty = forceDirty;
       if (!markDirty) {
         markDirty = tsStorage.getStamp(file, rd.getTarget()) != FileSystemUtil.lastModified(file);
       }
       if (markDirty) {
         // if it is full project rebuild, all storages are already completely cleared;
         // so passing null because there is no need to access the storage to clear non-existing
         // data
         final Timestamps marker = context.isProjectRebuild() ? null : tsStorage;
         context.getProjectDescriptor().fsState.markDirty(context, round, file, rd, marker);
       }
       if (currentFiles != null) {
         currentFiles.add(file);
       }
     }
   }
 }
  static void markDirtyFiles(
      CompileContext context,
      BuildTarget<?> target,
      final CompilationRound round,
      Timestamps timestamps,
      boolean forceMarkDirty,
      @Nullable THashSet<File> currentFiles,
      @Nullable FileFilter filter)
      throws IOException {
    if (filter == null && forceMarkDirty) {
      addCompletelyMarkedDirtyTarget(context, target);
    }

    for (BuildRootDescriptor rd :
        context.getProjectDescriptor().getBuildRootIndex().getTargetRoots(target, context)) {
      if (!rd.getRootFile().exists()
          ||
          // temp roots are managed by compilers themselves
          (rd instanceof JavaSourceRootDescriptor && ((JavaSourceRootDescriptor) rd).isTemp)) {
        continue;
      }
      if (filter == null) {
        context.getProjectDescriptor().fsState.clearRecompile(rd);
      }
      final FSCache fsCache =
          rd.canUseFileCache() ? context.getProjectDescriptor().getFSCache() : FSCache.NO_CACHE;
      traverseRecursively(
          context,
          rd,
          round,
          rd.getRootFile(),
          timestamps,
          forceMarkDirty,
          currentFiles,
          filter,
          fsCache);
    }
  }