From 254a40b321d2bf24e10a088fe211ff1a1adba74f Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Mon, 14 Oct 2024 23:17:30 +0200 Subject: [PATCH] project rebuild cancels preloaded process and cleans reliably whole caches directory (IDEA-359660) (cherry picked from commit 3fb66ec10b750995624b6ef4d88cc5087d544e47) IJ-CR-147297 GitOrigin-RevId: 875e3114abc64a783ee554a384f0d99281340217 --- .../compiler/CompilerManagerImpl.java | 12 +++- .../intellij/compiler/impl/CompileDriver.java | 37 ++++++++++-- .../compiler/server/BuildManager.java | 57 +++++++++++-------- .../task/impl/JpsProjectTaskRunner.java | 4 +- .../openapi/compiler/CompilerManager.java | 10 ++++ 5 files changed, 87 insertions(+), 33 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java index f0a4a46131b4..7b8504e40284 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java @@ -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 diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index 6d730782d68b..4946d3182530 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -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 COMPILATION_STARTED_AUTOMATICALLY = Key.create("compilation_started_automatically"); private static final Key COMPILE_SERVER_BUILD_STATUS = Key.create("COMPILE_SERVER_BUILD_STATUS"); + private static final Key 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> 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)() -> 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)() -> { + TaskFuture 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 cleanupTask = buildSystemDataCleanupTask.get(); + if (cleanupTask != null) { + cleanupTask.waitFor(); + } } }; diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index c1a1bd2137a1..e8419ff1a1d4 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -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 cancelPreloadedBuilds(@NotNull Project project) { + return cancelPreloadedBuilds(getProjectPath(project)); } - private void cancelPreloadedBuilds(@NotNull String projectPath) { + @NotNull + private TaskFuture cancelPreloadedBuilds(@NotNull String projectPath) { if (LOG.isDebugEnabled()) { LOG.debug("Cancel preloaded build for " + projectPath + "\n" + getThreadTrace(Thread.currentThread(), 50)); } + CompletableFuture future = new CompletableFuture<>(); runCommand(() -> { Pair, 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 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 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); - } - }); + } }); } diff --git a/java/compiler/impl/src/com/intellij/task/impl/JpsProjectTaskRunner.java b/java/compiler/impl/src/com/intellij/task/impl/JpsProjectTaskRunner.java index 85c0847ad586..e6dcd1bc4516 100644 --- a/java/compiler/impl/src/com/intellij/task/impl/JpsProjectTaskRunner.java +++ b/java/compiler/impl/src/com/intellij/task/impl/JpsProjectTaskRunner.java @@ -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( diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java index ea5fcb90f930..6a25cfc4c5f5 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java @@ -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. *