project rebuild cancels preloaded process and cleans reliably whole caches directory (IDEA-359660)

(cherry picked from commit 3fb66ec10b750995624b6ef4d88cc5087d544e47)

IJ-CR-147297

GitOrigin-RevId: 875e3114abc64a783ee554a384f0d99281340217
This commit is contained in:
Eugene Zhuravlev
2024-10-14 23:17:30 +02:00
committed by intellij-monorepo-bot
parent 33ace13090
commit 254a40b321
5 changed files with 87 additions and 33 deletions

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.compiler;
import com.intellij.compiler.impl.*;
@@ -49,7 +49,8 @@ import org.jetbrains.jps.incremental.BinaryContent;
import org.jetbrains.jps.javac.*;
import org.jetbrains.jps.javac.ast.api.JavacFileData;
import javax.tools.*;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.awt.*;
import java.io.File;
import java.io.IOException;
@@ -343,7 +344,12 @@ public class CompilerManagerImpl extends CompilerManager {
@Override
public void rebuild(CompileStatusNotification callback) {
new CompileDriver(myProject).rebuild(new ListenerNotificator(callback));
new CompileDriver(myProject).rebuild(new ListenerNotificator(callback), false);
}
@Override
public void rebuildClean(@Nullable CompileStatusNotification callback) {
new CompileDriver(myProject).rebuild(new ListenerNotificator(callback), true);
}
@Override

View File

@@ -60,9 +60,11 @@ import org.jetbrains.jps.model.java.JavaSourceRootType;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope;
@@ -72,6 +74,7 @@ public final class CompileDriver {
private static final Key<Boolean> COMPILATION_STARTED_AUTOMATICALLY = Key.create("compilation_started_automatically");
private static final Key<ExitStatus> COMPILE_SERVER_BUILD_STATUS = Key.create("COMPILE_SERVER_BUILD_STATUS");
private static final Key<Boolean> REBUILD_CLEAN = Key.create("rebuild_clean_requested");
private static final long ONE_MINUTE_MS = 60L * 1000L;
@ApiStatus.Internal
@@ -90,8 +93,10 @@ public final class CompileDriver {
public void setCompilerFilter(@SuppressWarnings("unused") CompilerFilter compilerFilter) {
}
public void rebuild(CompileStatusNotification callback) {
startup(new ProjectCompileScope(myProject), true, false, false, callback, null);
public void rebuild(CompileStatusNotification callback, boolean cleanSystemData) {
ProjectCompileScope scope = new ProjectCompileScope(myProject);
REBUILD_CLEAN.set(scope, cleanSystemData);
startup(scope, true, false, false, callback, null);
}
public void make(CompileScope scope, CompileStatusNotification callback) {
@@ -427,6 +432,7 @@ public final class CompileDriver {
Tracer.Span compileWorkSpan = Tracer.start("compileWork");
CompilerCacheManager compilerCacheManager = CompilerCacheManager.getInstance(myProject);
final BuildManager buildManager = BuildManager.getInstance();
final Ref<TaskFutureAdapter<Void>> buildSystemDataCleanupTask = new Ref<>(null);
try {
buildManager.postponeBackgroundTasks();
buildManager.cancelAutoMakeTasks(myProject);
@@ -434,11 +440,27 @@ public final class CompileDriver {
if (message != null) {
compileContext.addMessage(message);
}
if (isRebuild) {
CompilerUtil.runInContext(compileContext, JavaCompilerBundle.message("progress.text.clearing.build.system.data"),
(ThrowableRunnable<Throwable>)() -> compilerCacheManager
.clearCaches(compileContext));
// if possible, ensure the rebuild starts from the clean state
// if CLEAR_OUTPUT_DIRECTORY is allowed, we can clear cached directory completely, otherwise the build won't be able to clean outputs correctly without build caches
boolean canCleanBuildSystemData = Boolean.TRUE.equals(REBUILD_CLEAN.get(scope)) && CompilerWorkspaceConfiguration.getInstance(myProject).CLEAR_OUTPUT_DIRECTORY;
CompilerUtil.runInContext(compileContext, JavaCompilerBundle.message("progress.text.clearing.build.system.data"), (ThrowableRunnable<Throwable>)() -> {
TaskFuture<Boolean> cancelPreload = canCleanBuildSystemData? buildManager.cancelPreloadedBuilds(myProject) : new TaskFutureAdapter<>(CompletableFuture.completedFuture(Boolean.TRUE));
compilerCacheManager.clearCaches(compileContext);
if (canCleanBuildSystemData) {
cancelPreload.waitFor();
File[] systemFiles = buildManager.getProjectSystemDirectory(myProject).listFiles();
if (systemFiles != null && systemFiles.length > 0) {
buildSystemDataCleanupTask.set(new TaskFutureAdapter<>(FileUtil.asyncDelete(Arrays.asList(systemFiles))));
}
}
});
}
final boolean beforeTasksOk = executeCompileTasks(compileContext, true);
final int errorCount = compileContext.getMessageCount(CompilerMessageCategory.ERROR);
@@ -487,6 +509,11 @@ public final class CompileDriver {
if (status == ExitStatus.SUCCESS) {
BuildUsageCollector.logBuildCompleted(duration, isRebuild, false);
}
TaskFutureAdapter<Void> cleanupTask = buildSystemDataCleanupTask.get();
if (cleanupTask != null) {
cleanupTask.waitFor();
}
}
};

View File

@@ -122,7 +122,8 @@ import org.jetbrains.jps.model.java.compiler.JavaCompilers;
import org.jvnet.winp.Priority;
import org.jvnet.winp.WinProcess;
import javax.tools.*;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.awt.*;
import java.io.File;
import java.io.IOException;
@@ -820,39 +821,49 @@ public final class BuildManager implements Disposable {
}
}
public void cancelPreloadedBuilds(@NotNull Project project) {
cancelPreloadedBuilds(getProjectPath(project));
@NotNull
public TaskFuture<Boolean> cancelPreloadedBuilds(@NotNull Project project) {
return cancelPreloadedBuilds(getProjectPath(project));
}
private void cancelPreloadedBuilds(@NotNull String projectPath) {
@NotNull
private TaskFuture<Boolean> cancelPreloadedBuilds(@NotNull String projectPath) {
if (LOG.isDebugEnabled()) {
LOG.debug("Cancel preloaded build for " + projectPath + "\n" + getThreadTrace(Thread.currentThread(), 50));
}
CompletableFuture<Boolean> future = new CompletableFuture<>();
runCommand(() -> {
Pair<RequestFuture<PreloadedProcessMessageHandler>, OSProcessHandler> pair = takePreloadedProcess(projectPath);
if (pair == null) {
return;
if (pair != null) {
stopProcess(projectPath, pair.first.getRequestID(), pair.second, future);
}
else {
future.complete(Boolean.TRUE);
}
});
return new TaskFutureAdapter<>(future);
}
final RequestFuture<PreloadedProcessMessageHandler> future = pair.first;
final OSProcessHandler processHandler = pair.second;
myMessageDispatcher.cancelSession(future.getRequestID());
// waiting for the preloaded process from project's task queue guarantees no build is started for this project
// until this one gracefully exits and closes all its storages
getProjectData(projectPath).taskQueue.execute(() -> {
Throwable error = null;
try {
while (!processHandler.waitFor()) {
LOG.info("processHandler.waitFor() returned false for session " + future.getRequestID() + ", continue waiting");
}
private void stopProcess(@NotNull String projectPath, @NotNull UUID sessionId, @NotNull OSProcessHandler processHandler, @Nullable CompletableFuture<Boolean> future) {
myMessageDispatcher.cancelSession(sessionId);
// waiting for the process from project's task queue guarantees no build is started for this project
// until this one gracefully exits and closes all its storages
getProjectData(projectPath).taskQueue.execute(() -> {
Throwable error = null;
try {
while (!processHandler.waitFor()) {
LOG.info("processHandler.waitFor() returned false for session " + sessionId + ", continue waiting");
}
catch (Throwable e) {
error = e;
}
catch (Throwable e) {
error = e;
}
finally {
notifySessionTerminationIfNeeded(sessionId, error);
if (future != null) {
future.complete(error == null? Boolean.TRUE : Boolean.FALSE);
}
finally {
notifySessionTerminationIfNeeded(future.getRequestID(), error);
}
});
}
});
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.task.impl;
import com.intellij.compiler.impl.CompileDriver;
@@ -115,7 +115,7 @@ public final class JpsProjectTaskRunner extends ProjectTaskRunner {
CompilerManager compilerManager = CompilerManager.getInstance(project);
if (buildSettings.isRebuild()) {
compilerManager.rebuild(new MyCompileStatusNotification(notificationCollector));
compilerManager.rebuildClean(new MyCompileStatusNotification(notificationCollector));
}
else {
CompileScope scope = createScope(

View File

@@ -211,6 +211,16 @@ public abstract class CompilerManager {
*/
public abstract void rebuild(@Nullable CompileStatusNotification callback);
/**
* Same as rebuild, but build system directory is forcibly removed from the IDE to ensure the build starts on the clean state
*
* @param callback a notification callback, or null if no notifications needed
*/
public void rebuildClean(@Nullable CompileStatusNotification callback) {
rebuild(callback); // default implementation
}
/**
* Execute a custom compile task.
*