rewrite file traversal, output files deletion and resource copying with NIO

This commit is contained in:
Eugene Zhuravlev
2018-07-25 13:36:35 +02:00
parent 8733c7580a
commit 461f9cb3af
5 changed files with 119 additions and 60 deletions
@@ -86,6 +86,9 @@ public final class ProjectDescriptor {
myTargetsState = targetsState;
}
/**
* @Deprecated not used after file traversal rewrite to NIO
*/
@NotNull
public FSCache getFSCache() {
return myFSCache;
@@ -36,6 +36,11 @@ import org.jetbrains.jps.incremental.storage.Timestamps;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
/**
@@ -212,17 +217,26 @@ public class BuildOperations {
return deleted;
}
private static boolean deleteRecursively(File file, Collection<String> deletedPaths) {
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
deleteRecursively(child, deletedPaths);
}
private static boolean deleteRecursively(final File file, final Collection<String> deletedPaths) {
try {
Files.walkFileTree(file.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path f, BasicFileAttributes attrs) throws IOException {
Files.delete(f);
deletedPaths.add(FileUtil.toSystemIndependentName(f.toString()));
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
return true;
}
boolean deleted = file.delete();
if (deleted && children == null) {
deletedPaths.add(FileUtil.toSystemIndependentName(file.getPath()));
catch (IOException e) {
return false;
}
return deleted;
}
}
@@ -15,7 +15,7 @@
*/
package org.jetbrains.jps.incremental;
import com.intellij.openapi.util.io.FileSystemUtil;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
@@ -39,6 +39,11 @@ import org.jetbrains.jps.model.module.JpsModule;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashSet;
import java.util.Set;
@@ -222,8 +227,8 @@ public class FSOperations {
if (filter == null) {
context.getProjectDescriptor().fsState.clearRecompile(rd);
}
final FSCache fsCache = rd.canUseFileCache() ? context.getProjectDescriptor().getFSCache() : FSCache.NO_CACHE;
completelyMarkedDirty &= traverseRecursively(context, rd, round, rd.getRootFile(), timestamps, forceMarkDirty, currentFiles, filter, fsCache);
//final FSCache fsCache = rd.canUseFileCache() ? context.getProjectDescriptor().getFSCache() : FSCache.NO_CACHE;
completelyMarkedDirty &= traverseRecursively(context, rd, round, rd.getRootFile(), timestamps, forceMarkDirty, currentFiles, filter);
}
if (completelyMarkedDirty) {
@@ -242,41 +247,50 @@ public class FSOperations {
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
boolean allMarkedDirty = true;
if (children.length > 0 && rootIndex.isDirectoryAccepted(file, rd)) {
for (File child : children) {
allMarkedDirty &= traverseRecursively(context, rd, round, child, tsStorage, forceDirty, currentFiles, filter, fsCache);
}
@Nullable Set<File> currentFiles, @Nullable FileFilter filter) throws IOException {
final BuildRootIndex rootIndex = context.getProjectDescriptor().getBuildRootIndex();
final Ref<Boolean> allFilesMarked = Ref.create(Boolean.TRUE);
Files.walkFileTree(file.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
return rootIndex.isDirectoryAccepted(dir.toFile(), rd)? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE;
}
return allMarkedDirty;
}
// is file
if (!rootIndex.isFileAccepted(file, rd)) {
return true;
}
if (filter != null && !filter.accept(file)) {
return false;
}
@Override
public FileVisitResult visitFile(Path f, BasicFileAttributes attrs) throws IOException {
final File _file = f.toFile();
if (!rootIndex.isFileAccepted(_file, rd)) { // ignored file
return FileVisitResult.CONTINUE;
}
if (filter != null && !filter.accept(_file)) {
allFilesMarked.set(Boolean.FALSE);
}
else {
boolean markDirty = forceDirty;
if (!markDirty) {
markDirty = tsStorage.getStamp(_file, rd.getTarget()) != attrs.lastModifiedTime().toMillis();
}
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, false);
}
if (currentFiles != null) {
currentFiles.add(_file);
}
if (!markDirty) {
allFilesMarked.set(Boolean.FALSE);
}
}
return FileVisitResult.CONTINUE;
}
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, false);
}
if (currentFiles != null) {
currentFiles.add(file);
}
return markDirty;
});
return allFilesMarked.get();
}
public static void pruneEmptyDirs(CompileContext context, @Nullable final Set<File> dirsToDelete) {
@@ -34,6 +34,10 @@ import org.jetbrains.jps.model.module.JpsModule;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
/**
@@ -65,25 +69,26 @@ public class ResourcesBuilder extends TargetBuilder<ResourceRootDescriptor, Reso
}
try {
Map<ResourceRootDescriptor, Boolean> skippedRoots = new HashMap<>();
holder.processDirtyFiles((target_, file, sourceRoot) -> {
Boolean isSkipped = skippedRoots.get(sourceRoot);
final Map<ResourceRootDescriptor, Boolean> skippedRoots = new HashMap<>();
holder.processDirtyFiles((t, f, srcRoot) -> {
Boolean isSkipped = skippedRoots.get(srcRoot);
if (isSkipped == null) {
File outputDir = target_.getOutputDir();
isSkipped = Boolean.valueOf(outputDir == null || FileUtil.filesEqual(outputDir, sourceRoot.getRootFile()));
skippedRoots.put(sourceRoot, isSkipped);
File outputDir = t.getOutputDir();
isSkipped = Boolean.valueOf(outputDir == null || FileUtil.filesEqual(outputDir, srcRoot.getRootFile()));
skippedRoots.put(srcRoot, isSkipped);
}
if (isSkipped.booleanValue()) {
return true;
}
try {
copyResource(context, sourceRoot, file, outputConsumer);
copyResource(context, srcRoot, f, outputConsumer);
return !context.getCancelStatus().isCanceled();
}
catch (IOException e) {
LOG.info(e);
String sourcePath = FileUtil.toSystemIndependentName(file.getPath());
context.processMessage(new CompilerMessage("resources", BuildMessage.Kind.ERROR, e.getMessage(), sourcePath));
context.processMessage(
new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, e.getMessage(), FileUtil.toSystemIndependentName(f.getPath()))
);
return false;
}
});
@@ -129,19 +134,28 @@ public class ResourcesBuilder extends TargetBuilder<ResourceRootDescriptor, Reso
context.processMessage(new ProgressMessage("Copying resources... [" + rd.getTarget().getModule().getName() + "]"));
final String outputPath = targetPath.toString();
final File targetFile = new File(outputPath);
FileUtil.copyContent(file, targetFile);
final File targetFile = new File(targetPath.toString());
try {
final Path from = file.toPath();
final Path to = targetFile.toPath();
try {
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
}
catch (NoSuchFileException e) {
final File parent = targetFile.getParentFile();
if (parent != null && parent.mkdirs()) {
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING); // repeat on successful target dir creation
}
}
outputConsumer.registerOutputFile(targetFile, Collections.singletonList(file.getPath()));
}
catch (Exception e) {
context.processMessage(new CompilerMessage(BUILDER_NAME, e));
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, CompilerMessage.getTextFromThrowable(e)));
}
}
@NotNull
public String getPresentableName() {
return "Resource Compiler";
return BUILDER_NAME;
}
}
@@ -31,6 +31,10 @@ import org.jetbrains.jps.model.JpsEncodingProjectConfiguration;
import org.jetbrains.jps.model.JpsProject;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
@@ -77,7 +81,17 @@ public class MavenResourceFileProcessor {
copyWithFiltering(file, targetFile);
}
else {
FileUtil.copyContent(file, targetFile);
final Path from = file.toPath();
final Path to = targetFile.toPath();
try {
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
}
catch (NoSuchFileException e) {
final File parent = targetFile.getParentFile();
if (parent != null && parent.mkdirs()) {
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING); // repeat on successful target dir creation
}
}
}
}