mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
Delegated build: hotswap support (IDEA-163187, IDEA-194994, IDEA-178036, IDEA-200543)
This commit is contained in:
@@ -40,6 +40,7 @@ import com.intellij.packaging.impl.compiler.ArtifactCompilerUtil;
|
||||
import com.intellij.packaging.impl.compiler.ArtifactsCompiler;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.task.ProjectTaskListener;
|
||||
import com.intellij.util.Chunk;
|
||||
import com.intellij.util.ThrowableRunnable;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -299,6 +300,7 @@ public class CompileDriver {
|
||||
case FILES_GENERATED:
|
||||
final List<CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.GeneratedFile> generated = event.getGeneratedFilesList();
|
||||
final CompilationStatusListener publisher = !myProject.isDisposed()? messageBus.syncPublisher(CompilerTopics.COMPILATION_STATUS) : null;
|
||||
final ProjectTaskListener buildTaskEventPublisher = !myProject.isDisposed()? messageBus.syncPublisher(ProjectTaskListener.TOPIC) : null;
|
||||
Set<String> writtenArtifactOutputPaths = outputToArtifact != null ? new THashSet<>(FileUtil.PATH_HASHING_STRATEGY) : null;
|
||||
for (CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.GeneratedFile generatedFile : generated) {
|
||||
final String root = FileUtil.toSystemIndependentName(generatedFile.getOutputRoot());
|
||||
@@ -306,6 +308,9 @@ public class CompileDriver {
|
||||
if (publisher != null) {
|
||||
publisher.fileGenerated(root, relativePath);
|
||||
}
|
||||
if (buildTaskEventPublisher != null) {
|
||||
buildTaskEventPublisher.fileGenerated(root, relativePath);
|
||||
}
|
||||
if (outputToArtifact != null) {
|
||||
Collection<Artifact> artifacts = outputToArtifact.get(root);
|
||||
if (!artifacts.isEmpty()) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.intellij.task.impl;
|
||||
|
||||
import com.intellij.compiler.impl.CompileDriver;
|
||||
import com.intellij.compiler.impl.CompileScopeUtil;
|
||||
import com.intellij.execution.configurations.RunConfiguration;
|
||||
import com.intellij.execution.impl.ExecutionManagerImpl;
|
||||
import com.intellij.openapi.compiler.CompileScope;
|
||||
@@ -60,6 +62,7 @@ public class JpsProjectTaskRunner extends ProjectTaskRunner {
|
||||
callback.finished(new ProjectTaskResult(aborted, errors, warnings));
|
||||
|
||||
Map<Class<? extends ProjectTask>, List<ProjectTask>> taskMap = groupBy(tasks);
|
||||
runModulesResourcesBuildTasks(project, context, compileNotification, taskMap);
|
||||
runModulesBuildTasks(project, context, compileNotification, taskMap);
|
||||
runFilesBuildTasks(project, compileNotification, taskMap);
|
||||
runArtifactsBuildTasks(project, context, compileNotification, taskMap);
|
||||
@@ -67,12 +70,14 @@ public class JpsProjectTaskRunner extends ProjectTaskRunner {
|
||||
|
||||
@Override
|
||||
public boolean canRun(@NotNull ProjectTask projectTask) {
|
||||
return true;
|
||||
return projectTask instanceof ModuleBuildTask ||
|
||||
(projectTask instanceof ProjectModelBuildTask && ((ProjectModelBuildTask)projectTask).getBuildableElement() instanceof Artifact);
|
||||
}
|
||||
|
||||
public static Map<Class<? extends ProjectTask>, List<ProjectTask>> groupBy(@NotNull Collection<? extends ProjectTask> tasks) {
|
||||
return tasks.stream().collect(Collectors.groupingBy(o -> {
|
||||
if (o instanceof ModuleFilesBuildTask) return ModuleFilesBuildTask.class;
|
||||
if (o instanceof ModuleResourcesBuildTask) return ModuleResourcesBuildTask.class;
|
||||
if (o instanceof ModuleBuildTask) return ModuleBuildTask.class;
|
||||
if (o instanceof ProjectModelBuildTask) return ProjectModelBuildTask.class;
|
||||
return o.getClass();
|
||||
@@ -105,6 +110,33 @@ public class JpsProjectTaskRunner extends ProjectTaskRunner {
|
||||
}
|
||||
}
|
||||
|
||||
private static void runModulesResourcesBuildTasks(@NotNull Project project,
|
||||
@NotNull ProjectTaskContext context,
|
||||
@Nullable CompileStatusNotification compileNotification,
|
||||
@NotNull Map<Class<? extends ProjectTask>, List<ProjectTask>> tasksMap) {
|
||||
Collection<? extends ProjectTask> buildTasks = tasksMap.get(ModuleResourcesBuildTask.class);
|
||||
if (ContainerUtil.isEmpty(buildTasks)) return;
|
||||
|
||||
CompilerManager compilerManager = CompilerManager.getInstance(project);
|
||||
|
||||
ModulesBuildSettings modulesBuildSettings = assembleModulesBuildSettings(buildTasks);
|
||||
CompileScope scope = createScope(compilerManager, context,
|
||||
modulesBuildSettings.modules,
|
||||
modulesBuildSettings.includeDependentModules,
|
||||
modulesBuildSettings.includeRuntimeDependencies);
|
||||
List<String> moduleNames = modulesBuildSettings.modules.stream()
|
||||
.map(Module::getName)
|
||||
.collect(Collectors.toList());
|
||||
CompileScopeUtil.setResourcesScopeForExternalBuild(scope, moduleNames);
|
||||
|
||||
if (modulesBuildSettings.isIncrementalBuild) {
|
||||
compilerManager.make(scope, compileNotification);
|
||||
}
|
||||
else {
|
||||
compilerManager.compile(scope, compileNotification);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ModulesBuildSettings {
|
||||
final boolean isIncrementalBuild;
|
||||
final boolean includeDependentModules;
|
||||
@@ -180,6 +212,11 @@ public class JpsProjectTaskRunner extends ProjectTaskRunner {
|
||||
boolean includeRuntimeDependencies) {
|
||||
CompileScope scope = compilerManager.createModulesCompileScope(
|
||||
modules.toArray(Module.EMPTY_ARRAY), includeDependentModules, includeRuntimeDependencies);
|
||||
|
||||
if (context.isAutoRun()) {
|
||||
CompileDriver.setCompilationStartedAutomatically(scope);
|
||||
}
|
||||
|
||||
RunConfiguration configuration = context.getRunConfiguration();
|
||||
if (configuration != null) {
|
||||
scope.putUserData(CompilerManager.RUN_CONFIGURATION_KEY, configuration);
|
||||
|
||||
@@ -15,14 +15,17 @@
|
||||
*/
|
||||
package com.intellij.openapi.compiler;
|
||||
|
||||
import com.intellij.task.ProjectTaskListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
/**
|
||||
* A listener for compiler events.
|
||||
* Consider to use {@link ProjectTaskListener} to be able to support delegated build.
|
||||
*
|
||||
* @see CompilerTopics#COMPILATION_STATUS
|
||||
* @see ProjectTaskListener#TOPIC
|
||||
*/
|
||||
public interface CompilationStatusListener extends EventListener {
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,7 @@ import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.compiler.*;
|
||||
import com.intellij.openapi.compiler.DummyCompileContext;
|
||||
import com.intellij.openapi.compiler.ex.CompilerPathsEx;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
@@ -28,6 +28,7 @@ import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.task.*;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
@@ -61,7 +62,7 @@ public class HotSwapUIImpl extends HotSwapUI {
|
||||
public void sessionAttached(DebuggerSession session) {
|
||||
if (myConn == null) {
|
||||
myConn = bus.connect();
|
||||
myConn.subscribe(CompilerTopics.COMPILATION_STATUS, new MyCompilationStatusListener());
|
||||
myConn.subscribe(ProjectTaskListener.TOPIC, new MyCompilationStatusListener());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,14 +284,14 @@ public class HotSwapUIImpl extends HotSwapUI {
|
||||
@Nullable HotSwapStatusListener callback) {
|
||||
dontAskHotswapAfterThisCompilation();
|
||||
if (compileBeforeHotswap) {
|
||||
CompilerManager compilerManager = CompilerManager.getInstance(session.getProject());
|
||||
ProjectTaskManager projectTaskManager = ProjectTaskManager.getInstance(session.getProject());
|
||||
if (callback == null) {
|
||||
compilerManager.make(null);
|
||||
projectTaskManager.buildAllModules();
|
||||
}
|
||||
else {
|
||||
CompileScope compileScope = compilerManager.createProjectCompileScope(session.getProject());
|
||||
compileScope.putUserData(HOT_SWAP_CALLBACK_KEY, callback);
|
||||
compilerManager.make(compileScope, null);
|
||||
ProjectTask buildProjectTask = projectTaskManager.createAllModulesBuildTask(true, session.getProject());
|
||||
ProjectTaskContext context = new ProjectTaskContext(callback).withUserData(HOT_SWAP_CALLBACK_KEY, callback);
|
||||
projectTaskManager.run(context, buildProjectTask, null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -312,7 +313,7 @@ public class HotSwapUIImpl extends HotSwapUI {
|
||||
myAskBeforeHotswap = false;
|
||||
}
|
||||
|
||||
private class MyCompilationStatusListener implements CompilationStatusListener {
|
||||
private class MyCompilationStatusListener implements ProjectTaskListener {
|
||||
|
||||
private final AtomicReference<Map<String, List<String>>> myGeneratedPaths = new AtomicReference<>(new HashMap<>());
|
||||
private final THashSet<File> myOutputRoots;
|
||||
@@ -333,29 +334,38 @@ public class HotSwapUIImpl extends HotSwapUI {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compilationFinished(boolean aborted, int errors, int warnings, @NotNull CompileContext compileContext) {
|
||||
final Map<String, List<String>> generated = myGeneratedPaths.getAndSet(new HashMap<>());
|
||||
public void finished(@NotNull ProjectTaskContext context, @NotNull ProjectTaskResult executionResult) {
|
||||
if (!hasCompilationResults(executionResult)) return;
|
||||
|
||||
Map<String, List<String>> generated = myGeneratedPaths.getAndSet(new HashMap<>());
|
||||
generated = generated.isEmpty() ? null : generated;
|
||||
if (myProject.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int errors = executionResult.getErrors();
|
||||
boolean aborted = executionResult.isAborted();
|
||||
if (errors == 0 && !aborted && myPerformHotswapAfterThisCompilation) {
|
||||
for (HotSwapVetoableListener listener : myListeners) {
|
||||
if (!listener.shouldHotSwap(compileContext)) {
|
||||
if (!listener.shouldHotSwap(DummyCompileContext.getInstance()) ||
|
||||
!listener.shouldHotSwap(context, executionResult)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
List<DebuggerSession> sessions = getHotSwappableDebugSessions();
|
||||
if (!sessions.isEmpty()) {
|
||||
CompileScope compileScope = compileContext.getCompileScope();
|
||||
HotSwapStatusListener callback = compileScope != null ? compileScope.getUserData(HOT_SWAP_CALLBACK_KEY) : null;
|
||||
|
||||
HotSwapStatusListener callback = context.getUserData(HOT_SWAP_CALLBACK_KEY);
|
||||
hotSwapSessions(sessions, generated, callback);
|
||||
}
|
||||
}
|
||||
myPerformHotswapAfterThisCompilation = true;
|
||||
}
|
||||
|
||||
private boolean hasCompilationResults(@NotNull ProjectTaskResult executionResult) {
|
||||
return executionResult.anyMatch((task, state) -> task instanceof ModuleBuildTask &&
|
||||
!state.isFailed() && !state.isSkipped());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canHotSwap(@NotNull DebuggerSession debuggerSession) {
|
||||
|
||||
@@ -17,15 +17,24 @@
|
||||
package com.intellij.debugger.ui;
|
||||
|
||||
import com.intellij.openapi.compiler.CompileContext;
|
||||
import com.intellij.task.ProjectTaskContext;
|
||||
import com.intellij.task.ProjectTaskResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Allows plugins to cancel hotswap after a particular compilation session.
|
||||
* @see HotSwapUI#addListener(HotSwapVetoableListener)
|
||||
*/
|
||||
public interface HotSwapVetoableListener {
|
||||
|
||||
/**
|
||||
* Returns {@code false} if Hot Swap shouldn't be invoked after the given compilation session.
|
||||
* @deprecated use {@link #shouldHotSwap(ProjectTaskResult)}
|
||||
*/
|
||||
boolean shouldHotSwap(CompileContext finishedCompilationContext);
|
||||
|
||||
/**
|
||||
* Returns {@code false} if Hot Swap shouldn't be invoked after the given compilation session.
|
||||
*/
|
||||
default boolean shouldHotSwap(@NotNull ProjectTaskContext context, @NotNull ProjectTaskResult finishedTasksResult) { return true; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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.
|
||||
package com.intellij.task;
|
||||
|
||||
/**
|
||||
* Assemble module resources
|
||||
*
|
||||
* @author Vladislav.Soroka
|
||||
*/
|
||||
public interface ModuleResourcesBuildTask extends ModuleBuildTask {
|
||||
}
|
||||
@@ -16,27 +16,41 @@
|
||||
package com.intellij.task;
|
||||
|
||||
import com.intellij.execution.configurations.RunConfiguration;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Vladislav.Soroka
|
||||
*/
|
||||
public class ProjectTaskContext {
|
||||
public class ProjectTaskContext extends UserDataHolderBase {
|
||||
@Nullable
|
||||
private Object mySessionId;
|
||||
private final Object mySessionId;
|
||||
@Nullable
|
||||
private RunConfiguration myRunConfiguration;
|
||||
private final RunConfiguration myRunConfiguration;
|
||||
private final boolean myAutoRun;
|
||||
|
||||
public ProjectTaskContext() {
|
||||
this(null, null, false);
|
||||
}
|
||||
|
||||
public ProjectTaskContext(boolean autoRun) {
|
||||
this(null, null, autoRun);
|
||||
}
|
||||
|
||||
public ProjectTaskContext(@Nullable Object sessionId) {
|
||||
mySessionId = sessionId;
|
||||
this(sessionId, null, false);
|
||||
}
|
||||
|
||||
public ProjectTaskContext(@Nullable Object sessionId, @Nullable RunConfiguration runConfiguration) {
|
||||
this(sessionId, runConfiguration, false);
|
||||
}
|
||||
|
||||
public ProjectTaskContext(@Nullable Object sessionId, @Nullable RunConfiguration runConfiguration, boolean autoRun) {
|
||||
mySessionId = sessionId;
|
||||
myRunConfiguration = runConfiguration;
|
||||
myAutoRun = autoRun;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -48,4 +62,13 @@ public class ProjectTaskContext {
|
||||
public RunConfiguration getRunConfiguration() {
|
||||
return myRunConfiguration;
|
||||
}
|
||||
|
||||
public boolean isAutoRun() {
|
||||
return myAutoRun;
|
||||
}
|
||||
|
||||
public <T> ProjectTaskContext withUserData(@NotNull Key<T> key, @Nullable T value) {
|
||||
putUserData(key, value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
package com.intellij.task;
|
||||
|
||||
import com.intellij.util.messages.Topic;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface ProjectTaskListener {
|
||||
Topic<ProjectTaskListener> TOPIC = new Topic<>("project task events", ProjectTaskListener.class);
|
||||
|
||||
/**
|
||||
* @param context tasks execution context
|
||||
* @param executionResult provides aggregated information about the {@link ProjectTask} execution
|
||||
*/
|
||||
default void finished(@NotNull ProjectTaskContext context, @NotNull ProjectTaskResult executionResult) {}
|
||||
|
||||
default void fileGenerated(@NotNull String outputRoot, @NotNull String relativePath) {}
|
||||
}
|
||||
@@ -15,29 +15,90 @@
|
||||
*/
|
||||
package com.intellij.task;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.roots.ProjectModelBuildableElement;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Vladislav.Soroka
|
||||
*/
|
||||
public class ProjectTaskResult {
|
||||
private final boolean aborted;
|
||||
private final int errors;
|
||||
private final int warnings;
|
||||
private final boolean myAborted;
|
||||
private final int myErrors;
|
||||
private final int myWarnings;
|
||||
private final Map<ProjectTask, ProjectTaskState> myTasksState;
|
||||
|
||||
public ProjectTaskResult(boolean aborted, int errors, int warnings) {
|
||||
this.aborted = aborted;
|
||||
this.errors = errors;
|
||||
this.warnings = warnings;
|
||||
myAborted = aborted;
|
||||
myErrors = errors;
|
||||
myWarnings = warnings;
|
||||
myTasksState = Collections.emptyMap();
|
||||
}
|
||||
|
||||
public ProjectTaskResult(boolean aborted,
|
||||
int errors,
|
||||
int warnings,
|
||||
@NotNull Map<ProjectTask, ProjectTaskState> tasksState) {
|
||||
myAborted = aborted;
|
||||
myErrors = errors;
|
||||
myWarnings = warnings;
|
||||
myTasksState = ContainerUtil.unmodifiableOrEmptyMap(tasksState);
|
||||
}
|
||||
|
||||
public boolean isAborted() {
|
||||
return aborted;
|
||||
return myAborted;
|
||||
}
|
||||
|
||||
public int getErrors() {
|
||||
return errors;
|
||||
return myErrors;
|
||||
}
|
||||
|
||||
public int getWarnings() {
|
||||
return warnings;
|
||||
return myWarnings;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<ProjectTask, ProjectTaskState> getTasksState() {
|
||||
return myTasksState;
|
||||
}
|
||||
|
||||
public boolean anyMatch(@NotNull BiPredicate<ProjectTask, ProjectTaskState> predicate) {
|
||||
return myTasksState.entrySet().stream().anyMatch(entry -> predicate.test(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<ProjectTask> getTasks(@NotNull BiPredicate<ProjectTask, ProjectTaskState> predicate) {
|
||||
return myTasksState.entrySet().stream()
|
||||
.filter(entry -> predicate.test(entry.getKey(), entry.getValue()))
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Module> getAffectedModules(@NotNull Predicate<ProjectTaskState> predicate) {
|
||||
return myTasksState.entrySet().stream()
|
||||
.filter(entry -> entry.getKey() instanceof ModuleBuildTask)
|
||||
.filter(entry -> predicate.test(entry.getValue()))
|
||||
.map(entry -> ((ModuleBuildTask)entry.getKey()).getModule())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public <T extends ProjectModelBuildableElement> List<T> getBuildableElements(@NotNull Class<T> buildableClass,
|
||||
@NotNull Predicate<ProjectTaskState> predicate) {
|
||||
return myTasksState.entrySet().stream()
|
||||
.filter(entry -> entry.getKey() instanceof ProjectModelBuildTask<?>)
|
||||
.filter(entry -> buildableClass.isInstance(((ProjectModelBuildTask)entry.getKey()).getBuildableElement()))
|
||||
.filter(entry -> predicate.test(entry.getValue()))
|
||||
.map(entry -> buildableClass.cast(((ProjectModelBuildTask)entry.getKey()).getBuildableElement()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// 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.
|
||||
package com.intellij.task;
|
||||
|
||||
public interface ProjectTaskState {
|
||||
boolean isSkipped();
|
||||
|
||||
boolean isFailed();
|
||||
}
|
||||
@@ -28,8 +28,12 @@ public class ModuleBuildTaskImpl extends AbstractBuildTask implements ModuleBuil
|
||||
private final boolean myIncludeDependentModules;
|
||||
private final boolean myIncludeRuntimeDependencies;
|
||||
|
||||
public ModuleBuildTaskImpl(@NotNull Module module) {
|
||||
this(module, true);
|
||||
}
|
||||
|
||||
public ModuleBuildTaskImpl(@NotNull Module module, boolean isIncrementalBuild) {
|
||||
this(module, isIncrementalBuild, false, false);
|
||||
this(module, isIncrementalBuild, true, false);
|
||||
}
|
||||
|
||||
public ModuleBuildTaskImpl(@NotNull Module module,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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.
|
||||
package com.intellij.task.impl;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.task.ModuleResourcesBuildTask;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ModuleResourcesBuildTaskImpl extends ModuleBuildTaskImpl implements ModuleResourcesBuildTask {
|
||||
public ModuleResourcesBuildTaskImpl(@NotNull Module module) {
|
||||
super(module, true);
|
||||
}
|
||||
|
||||
public ModuleResourcesBuildTaskImpl(@NotNull Module module, boolean isIncrementalBuild) {
|
||||
super(module, isIncrementalBuild);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.task.impl;
|
||||
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -23,9 +24,11 @@ import com.intellij.openapi.roots.ProjectModelBuildableElement;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.task.*;
|
||||
import com.intellij.ui.GuiUtils;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -35,6 +38,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.intellij.util.containers.ContainerUtil.list;
|
||||
import static com.intellij.util.containers.ContainerUtil.map;
|
||||
@@ -47,9 +52,11 @@ import static java.util.stream.Collectors.groupingBy;
|
||||
public class ProjectTaskManagerImpl extends ProjectTaskManager {
|
||||
|
||||
private final ProjectTaskRunner myDummyTaskRunner = new DummyTaskRunner();
|
||||
private final ProjectTaskListener myEventPublisher;
|
||||
|
||||
public ProjectTaskManagerImpl(@NotNull Project project) {
|
||||
super(project);
|
||||
myEventPublisher = project.getMessageBus().syncPublisher(ProjectTaskListener.TOPIC);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,37 +156,21 @@ public class ProjectTaskManagerImpl extends ProjectTaskManager {
|
||||
visitTasks(projectTask instanceof ProjectTaskList ? (ProjectTaskList)projectTask : Collections.singleton(projectTask), taskClassifier);
|
||||
|
||||
if (toRun.isEmpty()) {
|
||||
sendSuccessNotify(callback);
|
||||
sendSuccessNotify(new ListenerNotificator(context, callback));
|
||||
return;
|
||||
}
|
||||
|
||||
AtomicInteger inProgressCounter = new AtomicInteger(toRun.size());
|
||||
AtomicInteger errorsCounter = new AtomicInteger();
|
||||
AtomicInteger warningsCounter = new AtomicInteger();
|
||||
AtomicBoolean abortedFlag = new AtomicBoolean(false);
|
||||
ProjectTaskNotification chunkStatusNotification = callback == null ? null : new ProjectTaskNotification() {
|
||||
@Override
|
||||
public void finished(@NotNull ProjectTaskResult executionResult) {
|
||||
int inProgress = inProgressCounter.decrementAndGet();
|
||||
int allErrors = errorsCounter.addAndGet(executionResult.getErrors());
|
||||
int allWarnings = warningsCounter.addAndGet(executionResult.getWarnings());
|
||||
if (executionResult.isAborted()) {
|
||||
abortedFlag.set(true);
|
||||
}
|
||||
if (inProgress == 0) {
|
||||
callback.finished(new ProjectTaskResult(abortedFlag.get(), allErrors, allWarnings));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
toRun.forEach(pair -> {
|
||||
ProjectTaskResultsAggregator callbacksCollector =
|
||||
new ProjectTaskResultsAggregator(new ListenerNotificator(context, callback), toRun.size());
|
||||
for (Pair<ProjectTaskRunner, Collection<? extends ProjectTask>> pair : toRun) {
|
||||
callback = new ProjectTaskRunnerNotification(pair.second, callbacksCollector);
|
||||
if (pair.second.isEmpty()) {
|
||||
sendSuccessNotify(chunkStatusNotification);
|
||||
sendSuccessNotify(callback);
|
||||
}
|
||||
else {
|
||||
pair.first.run(myProject, context, chunkStatusNotification, pair.second);
|
||||
pair.first.run(myProject, context, callback, pair.second);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendSuccessNotify(@Nullable ProjectTaskNotification notification) {
|
||||
@@ -229,7 +220,93 @@ public class ProjectTaskManagerImpl extends ProjectTaskManager {
|
||||
|
||||
@Override
|
||||
public boolean canRun(@NotNull ProjectTask projectTask) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
myDelegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finished(@NotNull ProjectTaskResult executionResult) {
|
||||
GuiUtils.invokeLaterIfNeeded(() -> {
|
||||
if (!myProject.isDisposed()) {
|
||||
myEventPublisher.finished(myContext, executionResult);
|
||||
}
|
||||
if (myDelegate != null) {
|
||||
myDelegate.finished(executionResult);
|
||||
}
|
||||
}, ModalityState.defaultModalityState());
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProjectTaskRunnerNotification implements ProjectTaskNotification {
|
||||
private final ProjectTaskResultsAggregator myAggregator;
|
||||
private final Collection<? extends ProjectTask> myTasks;
|
||||
|
||||
private ProjectTaskRunnerNotification(@NotNull Collection<? extends ProjectTask> tasks,
|
||||
@NotNull ProjectTaskResultsAggregator aggregator) {
|
||||
myTasks = tasks;
|
||||
myAggregator = aggregator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finished(@NotNull ProjectTaskResult result) {
|
||||
if (result.getTasksState().isEmpty()) {
|
||||
final boolean aborted = result.isAborted();
|
||||
final int errors = result.getErrors();
|
||||
ProjectTaskState state = new ProjectTaskState() {
|
||||
@Override
|
||||
public boolean isSkipped() {
|
||||
return aborted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFailed() {
|
||||
return errors > 0;
|
||||
}
|
||||
};
|
||||
Map<ProjectTask, ProjectTaskState> tasksState = StreamEx.of(myTasks).toMap(Function.identity(), task -> state);
|
||||
result = new ProjectTaskResult(aborted, errors, result.getWarnings(), tasksState);
|
||||
}
|
||||
myAggregator.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProjectTaskResultsAggregator {
|
||||
private final ProjectTaskNotification myDelegate;
|
||||
private final AtomicInteger myProgressCounter;
|
||||
private final AtomicInteger myErrorsCounter;
|
||||
private final AtomicInteger myWarningsCounter;
|
||||
private final AtomicBoolean myAbortedFlag;
|
||||
private final Map<ProjectTask, ProjectTaskState> myTasksState = ContainerUtil.newConcurrentMap();
|
||||
|
||||
private ProjectTaskResultsAggregator(@NotNull ProjectTaskNotification delegate, int expectedResults) {
|
||||
myDelegate = delegate;
|
||||
myProgressCounter = new AtomicInteger(expectedResults);
|
||||
myErrorsCounter = new AtomicInteger();
|
||||
myWarningsCounter = new AtomicInteger();
|
||||
myAbortedFlag = new AtomicBoolean(false);
|
||||
}
|
||||
|
||||
public void add(@NotNull ProjectTaskResult executionResult) {
|
||||
int inProgress = myProgressCounter.decrementAndGet();
|
||||
int allErrors = myErrorsCounter.addAndGet(executionResult.getErrors());
|
||||
int allWarnings = myWarningsCounter.addAndGet(executionResult.getWarnings());
|
||||
myTasksState.putAll(executionResult.getTasksState());
|
||||
if (executionResult.isAborted()) {
|
||||
myAbortedFlag.set(true);
|
||||
}
|
||||
if (inProgress <= 0) {
|
||||
ProjectTaskResult result = new ProjectTaskResult(myAbortedFlag.get(), allErrors, allWarnings, myTasksState);
|
||||
myDelegate.finished(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,8 @@ public class GradleProjectTaskRunner extends ProjectTaskRunner {
|
||||
|
||||
Map<Class<? extends ProjectTask>, List<ProjectTask>> taskMap = JpsProjectTaskRunner.groupBy(tasks);
|
||||
|
||||
List<Module> modules = addModulesBuildTasks(taskMap.get(ModuleBuildTask.class), buildTasksMap, initScripts);
|
||||
List<Module> modulesToBuild = addModulesBuildTasks(taskMap.get(ModuleBuildTask.class), buildTasksMap, initScripts);
|
||||
List<Module> modulesOfResourcesToBuild = addModulesBuildTasks(taskMap.get(ModuleResourcesBuildTask.class), buildTasksMap, initScripts);
|
||||
// TODO there should be 'gradle' way to build files instead of related modules entirely
|
||||
List<Module> modulesOfFiles = addModulesBuildTasks(taskMap.get(ModuleFilesBuildTask.class), buildTasksMap, initScripts);
|
||||
addArtifactsBuildTasks(taskMap.get(ProjectModelBuildTask.class), cleanTasksMap, buildTasksMap);
|
||||
@@ -117,7 +118,7 @@ public class GradleProjectTaskRunner extends ProjectTaskRunner {
|
||||
if (successes + errors == rootPaths.size()) {
|
||||
if (!project.isDisposed()) {
|
||||
// refresh on output roots is required in order for the order enumerator to see all roots via VFS
|
||||
final List<Module> affectedModules = ContainerUtil.concat(modules, modulesOfFiles);
|
||||
final List<Module> affectedModules = ContainerUtil.concat(modulesToBuild, modulesOfResourcesToBuild, modulesOfFiles);
|
||||
// have to refresh in case of errors too, because run configuration may be set to ignore errors
|
||||
Collection<String> affectedRoots = ContainerUtil.newHashSet(
|
||||
CompilerPathsEx.getOutputPaths(affectedModules.toArray(Module.EMPTY_ARRAY)));
|
||||
@@ -255,11 +256,11 @@ public class GradleProjectTaskRunner extends ProjectTaskRunner {
|
||||
|
||||
String gradlePath = GradleProjectResolverUtil.getGradlePath(module);
|
||||
if (gradlePath == null) continue;
|
||||
String taskPrefix = endsWithChar(gradlePath, ':') ? gradlePath : (gradlePath + ':');
|
||||
String taskPathPrefix = endsWithChar(gradlePath, ':') ? gradlePath : (gradlePath + ':');
|
||||
|
||||
List<String> gradleTasks = ContainerUtil.mapNotNull(
|
||||
List<String> gradleModuleTasks = ContainerUtil.mapNotNull(
|
||||
findAll(moduleDataNode, ProjectKeys.TASK), node ->
|
||||
node.getData().isInherited() ? null : trimStart(node.getData().getName(), taskPrefix));
|
||||
node.getData().isInherited() ? null : trimStart(node.getData().getName(), taskPathPrefix));
|
||||
|
||||
Collection<String> projectInitScripts = initScripts.getModifiable(rootProjectPath);
|
||||
Collection<String> buildRootTasks = buildTasksMap.getModifiable(rootProjectPath);
|
||||
@@ -269,29 +270,49 @@ public class GradleProjectTaskRunner extends ProjectTaskRunner {
|
||||
projectInitScripts.add(String.format(FORCE_COMPILE_TASKS_INIT_SCRIPT_TEMPLATE, gradlePath));
|
||||
}
|
||||
String assembleTask = "assemble";
|
||||
boolean buildOnlyResources = projectTask instanceof ModuleResourcesBuildTask;
|
||||
String buildTaskPrefix = buildOnlyResources ? "process" : "";
|
||||
String buildTaskSuffix = buildOnlyResources ? "resources" : "classes";
|
||||
if (GradleConstants.GRADLE_SOURCE_SET_MODULE_TYPE_KEY.equals(moduleType)) {
|
||||
String sourceSetName = GradleProjectResolverUtil.getSourceSetName(module);
|
||||
String gradleTask = isEmpty(sourceSetName) || "main".equals(sourceSetName) ? "classes" : sourceSetName + "Classes";
|
||||
if (gradleTasks.contains(gradleTask)) {
|
||||
buildRootTasks.add(taskPrefix + gradleTask);
|
||||
}
|
||||
else if ("main".equals(sourceSetName) || "test".equals(sourceSetName)) {
|
||||
buildRootTasks.add(taskPrefix + assembleTask);
|
||||
String gradleTask = getTaskName(buildTaskPrefix, buildTaskSuffix, sourceSetName);
|
||||
if (!addIfContains(taskPathPrefix, gradleTask, gradleModuleTasks, buildRootTasks) &&
|
||||
("main".equals(sourceSetName) || "test".equals(sourceSetName))) {
|
||||
buildRootTasks.add(taskPathPrefix + assembleTask);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (gradleTasks.contains("classes")) {
|
||||
buildRootTasks.add(taskPrefix + "classes");
|
||||
buildRootTasks.add(taskPrefix + "testClasses");
|
||||
String gradleTask = getTaskName(buildTaskPrefix, buildTaskSuffix, null);
|
||||
if (addIfContains(taskPathPrefix, gradleTask, gradleModuleTasks, buildRootTasks)) {
|
||||
String gradleTestTask = getTaskName(buildTaskPrefix, buildTaskSuffix, "test");
|
||||
addIfContains(taskPathPrefix, gradleTestTask, gradleModuleTasks, buildRootTasks);
|
||||
}
|
||||
else if (gradleTasks.contains(assembleTask)) {
|
||||
buildRootTasks.add(taskPrefix + assembleTask);
|
||||
else if (gradleModuleTasks.contains(assembleTask)) {
|
||||
buildRootTasks.add(taskPathPrefix + assembleTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
return affectedModules;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getTaskName(@NotNull String taskPrefix, @NotNull String taskSuffix, @Nullable String sourceSetName) {
|
||||
return isEmpty(sourceSetName) || "main".equals(sourceSetName) ?
|
||||
taskPrefix + (taskPrefix.isEmpty() ? taskSuffix : capitalize(taskSuffix)) :
|
||||
taskPrefix + (taskPrefix.isEmpty() ? sourceSetName : capitalize(sourceSetName)) + capitalize(taskSuffix);
|
||||
}
|
||||
|
||||
private static boolean addIfContains(@NotNull String taskPathPrefix,
|
||||
@NotNull String gradleTask,
|
||||
@NotNull List<String> moduleTasks,
|
||||
@NotNull Collection<String> buildRootTasks) {
|
||||
if (moduleTasks.contains(gradleTask)) {
|
||||
buildRootTasks.add(taskPathPrefix + gradleTask);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void addArtifactsBuildTasks(@Nullable Collection<? extends ProjectTask> tasks,
|
||||
@NotNull MultiMap<String, String> cleanTasksMap,
|
||||
@NotNull MultiMap<String, String> buildTasksMap) {
|
||||
|
||||
@@ -60,6 +60,7 @@ public class MavenProjectTaskRunner extends ProjectTaskRunner {
|
||||
Map<Class<? extends ProjectTask>, List<ProjectTask>> taskMap = JpsProjectTaskRunner.groupBy(tasks);
|
||||
|
||||
buildModuleFiles(project, callback, getFromGroupedMap(taskMap, ModuleFilesBuildTask.class, emptyList()));
|
||||
buildModules(project, callback, getFromGroupedMap(taskMap, ModuleResourcesBuildTask.class, emptyList()));
|
||||
buildModules(project, callback, getFromGroupedMap(taskMap, ModuleBuildTask.class, emptyList()));
|
||||
|
||||
buildArtifacts(project, callback, getFromGroupedMap(taskMap, ProjectModelBuildTask.class, emptyList()));
|
||||
@@ -163,23 +164,26 @@ public class MavenProjectTaskRunner extends ProjectTaskRunner {
|
||||
MavenExplicitProfiles explicitProfiles = mavenProjectsManager.getExplicitProfiles();
|
||||
Map<MavenProject, List<MavenProject>> rootProjectsToModules = new HashMap<>();
|
||||
|
||||
boolean buildOnlyResources = false;
|
||||
for (ModuleBuildTask moduleBuildTask : moduleBuildTasks) {
|
||||
MavenProject mavenProject = mavenProjectsManager.findProject(moduleBuildTask.getModule());
|
||||
if (mavenProject == null) continue;
|
||||
|
||||
buildOnlyResources = buildOnlyResources || moduleBuildTask instanceof ModuleResourcesBuildTask;
|
||||
MavenProject rootProject = mavenProjectsManager.findRootProject(mavenProject);
|
||||
rootProjectsToModules.computeIfAbsent(rootProject, p -> new ArrayList<>()).add(mavenProject);
|
||||
}
|
||||
|
||||
boolean clean = moduleBuildTasks.stream().anyMatch(task -> !(task instanceof ModuleFilesBuildTask) && !task.isIncrementalBuild());
|
||||
boolean includeDependentModules = moduleBuildTasks.stream().anyMatch(ModuleBuildTask::isIncludeDependentModules);
|
||||
String goal = buildOnlyResources ? "resources:resources" : "install";
|
||||
List<MavenRunnerParameters> commands = new ArrayList<>();
|
||||
for (Map.Entry<MavenProject, List<MavenProject>> entry : rootProjectsToModules.entrySet()) {
|
||||
ParametersList parameters = new ParametersList();
|
||||
if (clean) {
|
||||
parameters.add("clean");
|
||||
}
|
||||
parameters.add("install");
|
||||
parameters.add(goal);
|
||||
|
||||
List<MavenProject> mavenProjects = entry.getValue();
|
||||
if (!includeDependentModules) {
|
||||
|
||||
Reference in New Issue
Block a user