Build: do not use JPS compilation directly when building modules/artifacts of running JEE applications IDEA-210338

GitOrigin-RevId: 7e065541303104d3e8a8e5fd17a29554e8068d8d
This commit is contained in:
Vladislav.Soroka
2019-07-02 06:52:16 +03:00
committed by intellij-monorepo-bot
parent 22a62b553a
commit 20e27b7d8e
14 changed files with 300 additions and 252 deletions
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.compiler.actions;
import com.intellij.history.LocalHistory;
@@ -21,6 +7,7 @@ import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.compiler.CompilerBundle;
import com.intellij.openapi.project.Project;
import com.intellij.task.ProjectTaskContext;
import com.intellij.task.ProjectTaskManager;
import com.intellij.task.ProjectTaskNotification;
import com.intellij.task.ProjectTaskResult;
@@ -31,7 +18,7 @@ public class CompileProjectAction extends CompileActionBase {
protected void doAction(DataContext dataContext, final Project project) {
ProjectTaskManager.getInstance(project).rebuildAllModules(new ProjectTaskNotification() {
@Override
public void finished(@NotNull ProjectTaskResult executionResult) {
public void finished(@NotNull ProjectTaskContext context, @NotNull ProjectTaskResult executionResult) {
if (executionResult.isAborted() || project.isDisposed()) {
return;
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
/*
* @author: Eugene Zhuravlev
@@ -25,7 +11,10 @@ import com.intellij.compiler.ProblemsView;
import com.intellij.compiler.progress.CompilerTask;
import com.intellij.execution.impl.ExecutionManagerImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.*;
import com.intellij.openapi.compiler.CompileScope;
import com.intellij.openapi.compiler.CompilerMessage;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.compiler.CompilerPaths;
import com.intellij.openapi.compiler.ex.CompileContextEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
@@ -33,7 +22,6 @@ import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
@@ -43,7 +31,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class CompileContextImpl extends UserDataHolderBase implements CompileContextEx {
public static final Key<CompileContext> CONTEXT_KEY = Key.create("jps_compile_context");
private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.CompileContextImpl");
private final Project myProject;
private final CompilerTask myBuildSession;
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.packaging.impl.compiler;
import com.intellij.openapi.compiler.CompileContext;
@@ -21,6 +7,9 @@ import com.intellij.openapi.compiler.Compiler;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.task.ProjectTaskContext;
import com.intellij.task.impl.JpsBuildData;
import com.intellij.task.impl.JpsProjectTaskRunner;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -66,4 +55,10 @@ public class ArtifactsCompiler implements Compiler {
public static Set<String> getWrittenPaths(@NotNull CompileContext context) {
return context.getUserData(WRITTEN_PATHS_KEY);
}
@Nullable
public static Set<String> getWrittenPaths(@NotNull ProjectTaskContext context) {
JpsBuildData jpsBuildData = context.getUserData(JpsProjectTaskRunner.JPS_BUILD_DATA_KEY);
return jpsBuildData == null ? null : jpsBuildData.getArtifactsWrittenPaths();
}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.packaging.impl.run;
import com.intellij.execution.BeforeRunTaskProvider;
@@ -125,7 +125,7 @@ public abstract class BuildArtifactsBeforeRunTaskProviderBase<T extends BuildArt
final ProjectTaskNotification callback = new ProjectTaskNotification() {
@Override
public void finished(@NotNull ProjectTaskResult executionResult) {
public void finished(@NotNull ProjectTaskContext context, @NotNull ProjectTaskResult executionResult) {
result.set(!executionResult.isAborted() && executionResult.getErrors() == 0);
finished.up();
}
@@ -138,7 +138,9 @@ public abstract class BuildArtifactsBeforeRunTaskProviderBase<T extends BuildArt
ProjectTask artifactsBuildProjectTask = createProjectTask(myProject, artifacts);
finished.down();
Object sessionId = ExecutionManagerImpl.EXECUTION_SESSION_ID_KEY.get(env);
ProjectTaskManager.getInstance(myProject).run(new ProjectTaskContext(sessionId), artifactsBuildProjectTask, callback);
ProjectTaskContext projectTaskContext = new ProjectTaskContext(sessionId);
env.copyUserDataTo(projectTaskContext);
ProjectTaskManager.getInstance(myProject).run(projectTaskContext, artifactsBuildProjectTask, callback);
}, ModalityState.NON_MODAL);
finished.waitFor();
@@ -0,0 +1,26 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.task.impl;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.task.ProjectTaskContext;
import com.intellij.task.ProjectTaskManager;
import org.jetbrains.annotations.ApiStatus;
import java.util.List;
import java.util.Set;
/**
* {@link JpsBuildData} is available via the {@link ProjectTaskContext}.
* It provides aggregated details for the multiple invocations of the "JPS" builds during the single run session of a {@link ProjectTaskManager}.
* This data was introduced for compatibility reasons with the code which still depends on the internal context of "JPS" builder.
* And shouldn't be used by plugins or new code.
*
* @see ProjectTaskContext
* @see JpsProjectTaskRunner#JPS_BUILD_DATA_KEY
*/
@ApiStatus.Internal
public interface JpsBuildData {
Set<String> getArtifactsWrittenPaths();
List<CompileContext> getFinishedBuildsContexts();
}
@@ -1,21 +1,6 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.task.impl;
import com.intellij.compiler.impl.CompileContextImpl;
import com.intellij.compiler.impl.CompileDriver;
import com.intellij.compiler.impl.CompileScopeUtil;
import com.intellij.compiler.impl.CompositeScope;
@@ -29,23 +14,23 @@ import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectModelBuildableElement;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.KeyWithDefaultValue;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
import com.intellij.packaging.impl.compiler.ArtifactsCompiler;
import com.intellij.packaging.impl.compiler.ArtifactsWorkspaceSettings;
import com.intellij.task.*;
import com.intellij.ui.GuiUtils;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -54,6 +39,9 @@ import java.util.stream.Stream;
*/
public class JpsProjectTaskRunner extends ProjectTaskRunner {
private static final Logger LOG = Logger.getInstance(JpsProjectTaskRunner.class);
@ApiStatus.Internal
public static final Key<JpsBuildData> JPS_BUILD_DATA_KEY = KeyWithDefaultValue.create("jps_build_data", MyJpsBuildData::new);
@ApiStatus.Internal
public static final Key<Object> EXECUTION_SESSION_ID_KEY = ExecutionManagerImpl.EXECUTION_SESSION_ID_KEY;
@Override
@@ -61,6 +49,7 @@ public class JpsProjectTaskRunner extends ProjectTaskRunner {
@NotNull ProjectTaskContext context,
@Nullable ProjectTaskNotification callback,
@NotNull Collection<? extends ProjectTask> tasks) {
context.putUserData(JPS_BUILD_DATA_KEY, new MyJpsBuildData());
MessageBusConnection connection = project.getMessageBus().connect(project);
connection.subscribe(CompilerTopics.COMPILATION_STATUS, new CompilationStatusListener() {
@Override
@@ -68,27 +57,22 @@ public class JpsProjectTaskRunner extends ProjectTaskRunner {
context.fileGenerated(outputRoot, relativePath);
}
});
CompileStatusNotification compileNotification = (aborted, errors, warnings, compileContext) -> {
context.putUserData(CompileContextImpl.CONTEXT_KEY, compileContext);
if (callback != null) {
callback.finished(new ProjectTaskResult(aborted, errors, warnings));
}
connection.disconnect();
};
Map<Class<? extends ProjectTask>, List<ProjectTask>> taskMap = groupBy(tasks);
GuiUtils.invokeLaterIfNeeded(() -> {
runModulesResourcesBuildTasks(project, context, compileNotification, taskMap);
runModulesBuildTasks(project, context, compileNotification, taskMap);
runFilesBuildTasks(project, compileNotification, taskMap);
runEmptyBuildTask(project, context, compileNotification, taskMap);
runArtifactsBuildTasks(project, context, compileNotification, taskMap);
try (MyNotificationCollector notificationCollector = new MyNotificationCollector(context, callback, () -> connection.disconnect())) {
runModulesResourcesBuildTasks(project, context, notificationCollector, taskMap);
runModulesBuildTasks(project, context, notificationCollector, taskMap);
runFilesBuildTasks(project, notificationCollector, taskMap);
runEmptyBuildTask(project, context, notificationCollector, taskMap);
runArtifactsBuildTasks(project, context, notificationCollector, taskMap);
}
}, ModalityState.defaultModalityState(), project.getDisposed());
}
@Override
public boolean canRun(@NotNull ProjectTask projectTask) {
return projectTask instanceof ModuleBuildTask || projectTask instanceof EmptyCompileScopeBuildTask ||
return projectTask instanceof ModuleBuildTask || projectTask instanceof EmptyCompileScopeBuildTask ||
(projectTask instanceof ProjectModelBuildTask && ((ProjectModelBuildTask)projectTask).getBuildableElement() instanceof Artifact);
}
@@ -105,33 +89,33 @@ public class JpsProjectTaskRunner extends ProjectTaskRunner {
private static void runModulesBuildTasks(@NotNull Project project,
@NotNull ProjectTaskContext context,
@NotNull CompileStatusNotification compileNotification,
@NotNull MyNotificationCollector notificationCollector,
@NotNull Map<Class<? extends ProjectTask>, List<ProjectTask>> tasksMap) {
Collection<? extends ProjectTask> buildTasks = tasksMap.get(ModuleBuildTask.class);
if (ContainerUtil.isEmpty(buildTasks)) return;
ModulesBuildSettings modulesBuildSettings = assembleModulesBuildSettings(buildTasks);
CompilerManager compilerManager = CompilerManager.getInstance(project);
if (modulesBuildSettings.isRebuild()){
compilerManager.rebuild(compileNotification);
compilerManager.rebuild(new MyCompileStatusNotification(notificationCollector));
}
else {
CompileScope scope = createScope(
compilerManager, context, modulesBuildSettings.modules, modulesBuildSettings.includeDependentModules, modulesBuildSettings.includeRuntimeDependencies
);
if (modulesBuildSettings.isIncrementalBuild) {
compilerManager.make(scope, compileNotification);
compilerManager.make(scope, new MyCompileStatusNotification(notificationCollector));
}
else {
compilerManager.compile(scope, compileNotification);
compilerManager.compile(scope, new MyCompileStatusNotification(notificationCollector));
}
}
}
private static void runEmptyBuildTask(@NotNull Project project,
@NotNull ProjectTaskContext context,
@NotNull CompileStatusNotification compileNotification,
@NotNull Map<Class<? extends ProjectTask>, List<ProjectTask>> tasksMap) {
@NotNull ProjectTaskContext context,
@NotNull MyNotificationCollector notificationCollector,
@NotNull Map<Class<? extends ProjectTask>, List<ProjectTask>> tasksMap) {
Collection<? extends ProjectTask> buildTasks = tasksMap.get(EmptyCompileScopeBuildTask.class);
if (ContainerUtil.isEmpty(buildTasks)) return;
@@ -140,16 +124,16 @@ public class JpsProjectTaskRunner extends ProjectTaskRunner {
// this will effectively run all before- and after- compilation tasks registered within CompilerManager
EmptyCompileScopeBuildTask task = (EmptyCompileScopeBuildTask)buildTasks.iterator().next();
if (task.isIncrementalBuild()) {
compilerManager.make(scope, compileNotification);
compilerManager.make(scope, new MyCompileStatusNotification(notificationCollector));
}
else {
compilerManager.compile(scope, compileNotification);
compilerManager.compile(scope, new MyCompileStatusNotification(notificationCollector));
}
}
private static void runModulesResourcesBuildTasks(@NotNull Project project,
@NotNull ProjectTaskContext context,
@NotNull CompileStatusNotification compileNotification,
@NotNull MyNotificationCollector notificationCollector,
@NotNull Map<Class<? extends ProjectTask>, List<ProjectTask>> tasksMap) {
Collection<? extends ProjectTask> buildTasks = tasksMap.get(ModuleResourcesBuildTask.class);
if (ContainerUtil.isEmpty(buildTasks)) return;
@@ -165,10 +149,10 @@ public class JpsProjectTaskRunner extends ProjectTaskRunner {
CompileScopeUtil.setResourcesScopeForExternalBuild(scope, moduleNames);
if (modulesBuildSettings.isIncrementalBuild) {
compilerManager.make(scope, compileNotification);
compilerManager.make(scope, new MyCompileStatusNotification(notificationCollector));
}
else {
compilerManager.compile(scope, compileNotification);
compilerManager.compile(scope, new MyCompileStatusNotification(notificationCollector));
}
}
@@ -263,20 +247,20 @@ public class JpsProjectTaskRunner extends ProjectTaskRunner {
}
private static void runFilesBuildTasks(@NotNull Project project,
@NotNull CompileStatusNotification compileNotification,
@NotNull MyNotificationCollector notificationCollector,
@NotNull Map<Class<? extends ProjectTask>, List<ProjectTask>> tasksMap) {
Collection<? extends ProjectTask> filesTargets = tasksMap.get(ModuleFilesBuildTask.class);
if (!ContainerUtil.isEmpty(filesTargets)) {
VirtualFile[] files = filesTargets.stream()
.flatMap(target -> Stream.of(((ModuleFilesBuildTask)target).getFiles()))
.toArray(VirtualFile[]::new);
CompilerManager.getInstance(project).compile(files, compileNotification);
CompilerManager.getInstance(project).compile(files, new MyCompileStatusNotification(notificationCollector));
}
}
private static void runArtifactsBuildTasks(@NotNull Project project,
@NotNull ProjectTaskContext context,
@NotNull CompileStatusNotification compileNotification,
@NotNull MyNotificationCollector notificationCollector,
@NotNull Map<Class<? extends ProjectTask>, List<ProjectTask>> tasksMap) {
Collection<? extends ProjectTask> buildTasks = tasksMap.get(ProjectModelBuildTask.class);
@@ -296,22 +280,116 @@ public class JpsProjectTaskRunner extends ProjectTaskRunner {
}
}
buildArtifacts(project, toMake, context.getSessionId(), compileNotification, false);
buildArtifacts(project, toCompile, context.getSessionId(), compileNotification, true);
buildArtifacts(project, toMake, context.getSessionId(), notificationCollector, false);
buildArtifacts(project, toCompile, context.getSessionId(), notificationCollector, true);
}
}
private static void buildArtifacts(@NotNull Project project,
@NotNull List<? extends Artifact> artifacts,
@Nullable Object sessionId,
@Nullable CompileStatusNotification compileNotification,
@NotNull MyNotificationCollector notificationCollector,
boolean forceArtifactBuild) {
if (!artifacts.isEmpty()) {
final CompileScope scope = ArtifactCompileScope.createArtifactsScope(project, artifacts, forceArtifactBuild);
ArtifactsWorkspaceSettings.getInstance(project).setArtifactsToBuild(artifacts);
ExecutionManagerImpl.EXECUTION_SESSION_ID_KEY.set(scope, sessionId);
//in external build we can set 'rebuild' flag per target type
CompilerManager.getInstance(project).make(scope, compileNotification);
CompilerManager.getInstance(project).make(scope, new MyCompileStatusNotification(notificationCollector));
}
}
private static class MyNotificationCollector implements AutoCloseable {
@NotNull private final ProjectTaskContext myContext;
@Nullable private final ProjectTaskNotification myTaskNotification;
@NotNull private final Runnable myOnFinished;
private boolean myCollectingStopped;
private final Set<MyCompileStatusNotification> myNotifications = ContainerUtil.newIdentityTroveSet();
private int myErrors;
private int myWarnings;
private boolean myAborted;
private MyNotificationCollector(@NotNull ProjectTaskContext context,
@Nullable ProjectTaskNotification taskNotification,
@NotNull Runnable onFinished) {
myContext = context;
myTaskNotification = taskNotification;
myOnFinished = onFinished;
}
@Override
synchronized public void close() {
myCollectingStopped = true;
}
synchronized private void notifyFinished() {
if (myTaskNotification != null) {
myTaskNotification.finished(myContext, new ProjectTaskResult(myAborted, myErrors, myWarnings));
}
myOnFinished.run();
}
synchronized private void appendJpsBuildResult(boolean aborted, int errors, int warnings,
@NotNull CompileContext compileContext,
@NotNull MyCompileStatusNotification notification) {
if (!myNotifications.remove(notification)) {
LOG.error("Multiple invocation of the same callback");
}
myErrors += errors;
myWarnings += warnings;
if (aborted) myAborted = true;
MyJpsBuildData jpsBuildData = (MyJpsBuildData)JPS_BUILD_DATA_KEY.get(myContext);
jpsBuildData.add(compileContext);
if (myCollectingStopped && myNotifications.isEmpty()) {
notifyFinished();
}
}
synchronized private void add(@NotNull MyCompileStatusNotification notification) {
assert !myCollectingStopped;
if (!myNotifications.add(notification)) {
LOG.error("Do not use the same callback for different JPS invocations");
}
}
}
private static class MyCompileStatusNotification implements CompileStatusNotification {
private final MyNotificationCollector myCollector;
private MyCompileStatusNotification(@NotNull MyNotificationCollector collector) {
myCollector = collector;
myCollector.add(this);
}
@Override
public void finished(boolean aborted, int errors, int warnings, @NotNull CompileContext compileContext) {
myCollector.appendJpsBuildResult(aborted, errors, warnings, compileContext, this);
}
}
private static class MyJpsBuildData implements JpsBuildData {
private final List<CompileContext> myContexts = new ArrayList<>();
@Override
public Set<String> getArtifactsWrittenPaths() {
return myContexts.stream()
.map(ctx -> ArtifactsCompiler.getWrittenPaths(ctx))
.filter(Objects::nonNull)
.flatMap(set -> set.stream())
.collect(Collectors.toSet());
}
@Override
public List<CompileContext> getFinishedBuildsContexts() {
return Collections.unmodifiableList(myContexts);
}
private void add(@NotNull CompileContext context) {
myContexts.add(context);
}
}
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.ui;
@@ -34,8 +20,6 @@ public abstract class HotSwapUI {
public abstract void reloadChangedClasses(@NotNull DebuggerSession session, boolean compileBeforeHotswap,
@Nullable HotSwapStatusListener callback);
public abstract void dontPerformHotswapAfterThisCompilation();
public abstract void addListener(HotSwapVetoableListener listener);
public abstract void removeListener(HotSwapVetoableListener listener);
@@ -21,6 +21,7 @@ import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.KeyWithDefaultValue;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.task.*;
@@ -38,12 +39,12 @@ import java.util.*;
import java.util.stream.Collectors;
public class HotSwapUIImpl extends HotSwapUI {
public static final Key<Boolean> SKIP_HOT_SWAP_KEY = KeyWithDefaultValue.create("skip_hotswap_after_this_compilation", false);
private static final Key<HotSwapStatusListener> HOT_SWAP_CALLBACK_KEY = Key.create("hot_swap_callback");
private final List<HotSwapVetoableListener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private boolean myAskBeforeHotswap = true;
private final Project myProject;
private boolean myPerformHotswapAfterThisCompilation = true;
public HotSwapUIImpl(@NotNull Project project) {
myProject = project;
@@ -294,11 +295,6 @@ public class HotSwapUIImpl extends HotSwapUI {
}
}
@Override
public void dontPerformHotswapAfterThisCompilation() {
myPerformHotswapAfterThisCompilation = false;
}
public void dontAskHotswapAfterThisCompilation() {
myAskBeforeHotswap = false;
}
@@ -324,49 +320,45 @@ public class HotSwapUIImpl extends HotSwapUI {
if (myProject.isDisposed()) {
return;
}
try {
if (!hasCompilationResults(executionResult)) return;
int errors = executionResult.getErrors();
boolean aborted = executionResult.isAborted();
if (errors == 0 && !aborted && myPerformHotswapAfterThisCompilation) {
for (HotSwapVetoableListener listener : myListeners) {
if (!listener.shouldHotSwap(context, executionResult)) {
return;
}
}
if (!hasCompilationResults(executionResult)) return;
List<DebuggerSession> sessions = getHotSwappableDebugSessions();
if (!sessions.isEmpty()) {
Map<String, Collection<String>> generatedPaths;
Collection<String> generatedFilesRoots = context.getGeneratedFilesRoots();
if (!generatedFilesRoots.isEmpty()) {
generatedPaths = new HashMap<>();
for (String outputRoot : generatedFilesRoots) {
// collect only classes under IDE output roots
if (!JpsPathUtil.isUnder(myOutputRoots, new File(outputRoot))) continue;
Collection<String> relativePaths = context.getGeneratedFilesRelativePaths(outputRoot).stream()
.filter(relativePath -> StringUtil.endsWith(relativePath, ".class"))
.collect(Collectors.toCollection(SmartList::new));
if (!relativePaths.isEmpty()) {
generatedPaths.put(outputRoot, relativePaths);
}
}
if (generatedPaths.isEmpty()) {
generatedPaths = null;
}
}
else {
generatedPaths = null;
}
HotSwapStatusListener callback = context.getUserData(HOT_SWAP_CALLBACK_KEY);
hotSwapSessions(sessions, generatedPaths, callback);
int errors = executionResult.getErrors();
boolean aborted = executionResult.isAborted();
if (errors == 0 && !aborted && !SKIP_HOT_SWAP_KEY.getRequired(context)) {
for (HotSwapVetoableListener listener : myListeners) {
if (!listener.shouldHotSwap(context, executionResult)) {
return;
}
}
}
finally {
myPerformHotswapAfterThisCompilation = true;
List<DebuggerSession> sessions = getHotSwappableDebugSessions();
if (!sessions.isEmpty()) {
Map<String, Collection<String>> generatedPaths;
Collection<String> generatedFilesRoots = context.getGeneratedFilesRoots();
if (!generatedFilesRoots.isEmpty()) {
generatedPaths = new HashMap<>();
for (String outputRoot : generatedFilesRoots) {
// collect only classes under IDE output roots
if (!JpsPathUtil.isUnder(myOutputRoots, new File(outputRoot))) continue;
Collection<String> relativePaths = context.getGeneratedFilesRelativePaths(outputRoot).stream()
.filter(relativePath -> StringUtil.endsWith(relativePath, ".class"))
.collect(Collectors.toCollection(SmartList::new));
if (!relativePaths.isEmpty()) {
generatedPaths.put(outputRoot, relativePaths);
}
}
if (generatedPaths.isEmpty()) {
generatedPaths = null;
}
}
else {
generatedPaths = null;
}
HotSwapStatusListener callback = context.getUserData(HOT_SWAP_CALLBACK_KEY);
hotSwapSessions(sessions, generatedPaths, callback);
}
}
}
@@ -1,26 +1,13 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.ui;
import com.intellij.compiler.impl.CompileContextImpl;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.DummyCompileContext;
import com.intellij.task.ProjectTaskContext;
import com.intellij.task.ProjectTaskResult;
import com.intellij.task.impl.JpsBuildData;
import com.intellij.task.impl.JpsProjectTaskRunner;
import org.jetbrains.annotations.NotNull;
/**
@@ -40,7 +27,11 @@ public interface HotSwapVetoableListener {
* Returns {@code false} if Hot Swap shouldn't be invoked after the given compilation session.
*/
default boolean shouldHotSwap(@NotNull ProjectTaskContext context, @NotNull ProjectTaskResult finishedTasksResult) {
CompileContext compileContext = context.getUserData(CompileContextImpl.CONTEXT_KEY);
return shouldHotSwap(compileContext != null ? compileContext : DummyCompileContext.getInstance());
CompileContext compileContext = DummyCompileContext.getInstance();
JpsBuildData jpsBuildData = context.getUserData(JpsProjectTaskRunner.JPS_BUILD_DATA_KEY);
if(jpsBuildData != null && jpsBuildData.getFinishedBuildsContexts().size() == 1) {
compileContext = jpsBuildData.getFinishedBuildsContexts().get(0);
}
return shouldHotSwap(compileContext);
}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.compiler.options;
import com.intellij.execution.BeforeRunTask;
@@ -21,9 +21,7 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.task.ProjectTask;
import com.intellij.task.ProjectTaskContext;
import com.intellij.task.ProjectTaskManager;
import com.intellij.task.*;
import com.intellij.task.impl.EmptyCompileScopeBuildTaskImpl;
import com.intellij.util.concurrency.Semaphore;
import org.jetbrains.annotations.ApiStatus;
@@ -160,12 +158,19 @@ public class CompileStepBeforeRun extends BeforeRunTaskProvider<CompileStepBefor
}
if (!myProject.isDisposed()) {
projectTaskManager.run(new ProjectTaskContext(sessionId, configuration), projectTask, executionResult -> {
if ((executionResult.getErrors() == 0 || ignoreErrors) && !executionResult.isAborted()) {
result.set(Boolean.TRUE);
}
done.up();
});
ProjectTaskContext context = new ProjectTaskContext(sessionId, configuration);
env.copyUserDataTo(context);
projectTaskManager.run(context, projectTask,
new ProjectTaskNotification() {
@Override
public void finished(@NotNull ProjectTaskContext context, @NotNull ProjectTaskResult executionResult) {
if ((executionResult.getErrors() == 0 || ignoreErrors) && !executionResult.isAborted()) {
result.set(Boolean.TRUE);
}
done.up();
}
}
);
}
else {
done.up();
@@ -30,7 +30,10 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess;
import com.intellij.packaging.artifacts.Artifact;
import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
import com.intellij.task.ProjectTaskContext;
import com.intellij.task.ProjectTaskManager;
import com.intellij.task.ProjectTaskNotification;
import com.intellij.task.ProjectTaskResult;
import com.intellij.testFramework.*;
import com.intellij.testFramework.fixtures.IdeaProjectTestFixture;
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
@@ -412,11 +415,17 @@ public abstract class ExternalSystemTestCase extends UsefulTestCase {
private void build(@NotNull Object[] buildableElements) {
final Semaphore semaphore = new Semaphore();
semaphore.down();
ProjectTaskNotification callback = new ProjectTaskNotification() {
@Override
public void finished(@NotNull ProjectTaskContext context, @NotNull ProjectTaskResult executionResult) {
semaphore.up();
}
};
if (buildableElements instanceof Module[]) {
ProjectTaskManager.getInstance(myProject).build((Module[])buildableElements, executionResult -> semaphore.up());
ProjectTaskManager.getInstance(myProject).build((Module[])buildableElements, callback);
}
else if (buildableElements instanceof Artifact[]) {
ProjectTaskManager.getInstance(myProject).build((Artifact[])buildableElements, executionResult -> semaphore.up());
ProjectTaskManager.getInstance(myProject).build((Artifact[])buildableElements, callback);
}
else {
assert false : "Unsupported buildableElements: " + Arrays.toString(buildableElements);
@@ -1,20 +1,7 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.task;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
@@ -23,6 +10,15 @@ import org.jetbrains.annotations.NotNull;
public interface ProjectTaskNotification {
/**
* @param executionResult provides aggregated information about the {@link ProjectTask} execution
* @deprecated use {@link #finished(ProjectTaskContext, ProjectTaskResult)} instead
*/
void finished(@NotNull ProjectTaskResult executionResult);
@Deprecated
@ApiStatus.ScheduledForRemoval(inVersion = "2020.1")
default void finished(@NotNull ProjectTaskResult executionResult) {}
/**
* @param context tasks execution context
* @param executionResult provides aggregated information about the {@link ProjectTask} execution
*/
default void finished(@NotNull ProjectTaskContext context, @NotNull ProjectTaskResult executionResult) { finished(executionResult); }
}
@@ -1,30 +1,20 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.task.impl;
import com.intellij.task.ProjectTask;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/**
* @author Vladislav.Soroka
*/
public class ProjectTaskList extends ArrayList<ProjectTask> implements ProjectTask {
public class ProjectTaskList extends SmartList<ProjectTask> implements ProjectTask {
public ProjectTaskList() {
}
public ProjectTaskList(@NotNull Collection<? extends ProjectTask> c) {
super(c);
}
@@ -34,4 +24,9 @@ public class ProjectTaskList extends ArrayList<ProjectTask> implements ProjectTa
public String getPresentableName() {
return toString();
}
@NotNull
public static ProjectTaskList asList(ProjectTask... tasks) {
return new ProjectTaskList(Arrays.asList(tasks));
}
}
@@ -168,22 +168,22 @@ public class ProjectTaskManagerImpl extends ProjectTaskManager {
listener.beforeRun(context);
}
catch (ExecutionException e) {
sendAbortedNotify(new ListenerNotificator(context, callback));
sendAbortedNotify(context, new ListenerNotificator(callback));
return;
}
}
if (toRun.isEmpty()) {
sendSuccessNotify(new ListenerNotificator(context, callback));
sendSuccessNotify(context, new ListenerNotificator(callback));
return;
}
ProjectTaskResultsAggregator callbacksCollector =
new ProjectTaskResultsAggregator(new ListenerNotificator(context, callback), toRun.size());
new ProjectTaskResultsAggregator(context, new ListenerNotificator(callback), toRun.size());
for (Pair<ProjectTaskRunner, Collection<? extends ProjectTask>> pair : toRun) {
ProjectTaskRunnerNotification notification = new ProjectTaskRunnerNotification(pair.second, callbacksCollector);
if (pair.second.isEmpty()) {
sendSuccessNotify(notification);
sendSuccessNotify(context, notification);
}
else {
pair.first.run(myProject, context, notification, pair.second);
@@ -203,15 +203,15 @@ public class ProjectTaskManagerImpl extends ProjectTaskManager {
myListeners.add(listener);
}
private static void sendSuccessNotify(@Nullable ProjectTaskNotification notification) {
private static void sendSuccessNotify(@NotNull ProjectTaskContext context, @Nullable ProjectTaskNotification notification) {
if (notification != null) {
notification.finished(new ProjectTaskResult(false, 0, 0));
notification.finished(context, new ProjectTaskResult(false, 0, 0));
}
}
private static void sendAbortedNotify(@Nullable ProjectTaskNotification notification) {
private static void sendAbortedNotify(@NotNull ProjectTaskContext context, @Nullable ProjectTaskNotification notification) {
if (notification != null) {
notification.finished(new ProjectTaskResult(true, 0, 0));
notification.finished(context, new ProjectTaskResult(true, 0, 0));
}
}
@@ -251,7 +251,7 @@ public class ProjectTaskManagerImpl extends ProjectTaskManager {
@NotNull ProjectTaskContext context,
@Nullable ProjectTaskNotification callback,
@NotNull Collection<? extends ProjectTask> tasks) {
sendSuccessNotify(callback);
sendSuccessNotify(context, callback);
}
@Override
@@ -262,34 +262,31 @@ public class ProjectTaskManagerImpl extends ProjectTaskManager {
private class ListenerNotificator implements ProjectTaskNotification {
@Nullable private final ProjectTaskNotification myDelegate;
@NotNull private final ProjectTaskContext myContext;
private ListenerNotificator(@NotNull ProjectTaskContext context,
@Nullable ProjectTaskNotification delegate) {
myContext = context;
private ListenerNotificator(@Nullable ProjectTaskNotification delegate) {
myDelegate = delegate;
}
@Override
public void finished(@NotNull ProjectTaskResult executionResult) {
public void finished(@NotNull ProjectTaskContext context, @NotNull ProjectTaskResult executionResult) {
if (!executionResult.isAborted() && executionResult.getErrors() == 0) {
// do not run after tasks on EDT
ApplicationManager.getApplication().executeOnPooledThread(() -> {
try {
for (ProjectTaskManagerListener listener : myListeners) {
listener.afterRun(myContext, executionResult);
listener.afterRun(context, executionResult);
}
notify(myContext, executionResult);
notify(context, executionResult);
}
catch (ExecutionException e) {
LOG.debug(e);
notify(myContext, new ProjectTaskResult(
notify(context, new ProjectTaskResult(
false, executionResult.getErrors() + 1, executionResult.getWarnings(), executionResult.getTasksState()));
}
});
}
else {
notify(myContext, executionResult);
notify(context, executionResult);
}
}
@@ -299,7 +296,7 @@ public class ProjectTaskManagerImpl extends ProjectTaskManager {
myEventPublisher.finished(context, executionResult);
}
if (myDelegate != null) {
myDelegate.finished(executionResult);
myDelegate.finished(context, executionResult);
}
}, ModalityState.defaultModalityState());
}
@@ -316,7 +313,7 @@ public class ProjectTaskManagerImpl extends ProjectTaskManager {
}
@Override
public void finished(@NotNull ProjectTaskResult result) {
public void finished(@NotNull ProjectTaskContext context, @NotNull ProjectTaskResult result) {
if (result.getTasksState().isEmpty()) {
final boolean aborted = result.isAborted();
final int errors = result.getErrors();
@@ -339,6 +336,7 @@ public class ProjectTaskManagerImpl extends ProjectTaskManager {
}
private static class ProjectTaskResultsAggregator {
private final ProjectTaskContext myContext;
private final ProjectTaskNotification myDelegate;
private final AtomicInteger myProgressCounter;
private final AtomicInteger myErrorsCounter;
@@ -346,7 +344,10 @@ public class ProjectTaskManagerImpl extends ProjectTaskManager {
private final AtomicBoolean myAbortedFlag;
private final Map<ProjectTask, ProjectTaskState> myTasksState = ContainerUtil.newConcurrentMap();
private ProjectTaskResultsAggregator(@NotNull ProjectTaskNotification delegate, int expectedResults) {
private ProjectTaskResultsAggregator(@NotNull ProjectTaskContext context,
@NotNull ProjectTaskNotification delegate,
int expectedResults) {
myContext = context;
myDelegate = delegate;
myProgressCounter = new AtomicInteger(expectedResults);
myErrorsCounter = new AtomicInteger();
@@ -364,7 +365,7 @@ public class ProjectTaskManagerImpl extends ProjectTaskManager {
}
if (inProgress <= 0) {
ProjectTaskResult result = new ProjectTaskResult(myAbortedFlag.get(), allErrors, allWarnings, myTasksState);
myDelegate.finished(result);
myDelegate.finished(myContext, result);
}
}
}