Delegated build: hotswap support (IDEA-163187, IDEA-194994, IDEA-178036, IDEA-200543)

This commit is contained in:
Vladislav.Soroka
2018-10-15 14:52:58 +03:00
parent 2df107e903
commit 798e8327c3
15 changed files with 376 additions and 71 deletions

View File

@@ -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()) {

View File

@@ -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);

View File

@@ -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 {
/**

View File

@@ -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) {

View File

@@ -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; }
}

View File

@@ -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 {
}

View File

@@ -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;
}
}

View File

@@ -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) {}
}

View File

@@ -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());
}
}

View File

@@ -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();
}

View File

@@ -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,

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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) {

View File

@@ -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) {