diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java index fdcf95c68e90..3c9650fa4742 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerManagerImpl.java @@ -263,6 +263,11 @@ public class CompilerManagerImpl extends CompilerManager { new CompileDriver(myProject).make(scope, new ListenerNotificator(callback)); } + @Override + public void makeWithModalProgress(@NotNull CompileScope scope, @Nullable CompileStatusNotification callback) { + new CompileDriver(myProject).make(scope, true, new ListenerNotificator(callback)); + } + @Override public void make(@NotNull CompileScope scope, CompilerFilter filter, @Nullable CompileStatusNotification callback) { final CompileDriver compileDriver = new CompileDriver(myProject); diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index 6e1e76260c67..adebcbe898c9 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -66,10 +66,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; -import org.jetbrains.jps.api.CmdlineProtoUtil; -import org.jetbrains.jps.api.CmdlineRemoteProto; -import org.jetbrains.jps.api.GlobalOptions; -import org.jetbrains.jps.api.TaskFuture; +import org.jetbrains.jps.api.*; import org.jetbrains.jps.model.java.JavaSourceRootType; import javax.swing.*; @@ -107,8 +104,12 @@ public class CompileDriver { } public void make(CompileScope scope, CompileStatusNotification callback) { + make(scope, false, callback); + } + + public void make(CompileScope scope, boolean withModalProgress, CompileStatusNotification callback) { if (validateCompilerConfiguration(scope)) { - startup(scope, false, false, callback, null); + startup(scope, false, false, withModalProgress, callback, null); } else { callback.finished(true, 0, 0, DummyCompileContext.getInstance()); @@ -195,7 +196,7 @@ public class CompileDriver { scopes.addAll(explicitScopes); } else if (!compileContext.isRebuild() && !CompileScopeUtil.allProjectModulesAffected(compileContext)) { - CompileScopeUtil.addScopesForModules(Arrays.asList(scope.getAffectedModules()), scopes, forceBuild); + CompileScopeUtil.addScopesForModules(Arrays.asList(scope.getAffectedModules()), scope.getAffectedUnloadedModules(), scopes, forceBuild); } else { scopes.addAll(CmdlineProtoUtil.createAllModulesScopes(forceBuild)); @@ -228,7 +229,7 @@ public class CompileDriver { // need to pass scope's user data to server final Map builderParams; if (onlyCheckUpToDate) { - builderParams = Collections.emptyMap(); + builderParams = new HashMap<>(); } else { final Map exported = scope.exportUserData(); @@ -241,9 +242,12 @@ public class CompileDriver { } } else { - builderParams = Collections.emptyMap(); + builderParams = new HashMap<>(); } } + if (!scope.getAffectedUnloadedModules().isEmpty()) { + builderParams.put(BuildParametersKeys.LOAD_UNLOADED_MODULES, Boolean.TRUE.toString()); + } final MessageBus messageBus = myProject.getMessageBus(); final MultiMap outputToArtifact = ArtifactCompilerUtil.containsArtifacts(scopes) ? ArtifactCompilerUtil.createOutputToArtifactMap(myProject) : null; @@ -372,16 +376,22 @@ public class CompileDriver { }); } + private void startup(final CompileScope scope, final boolean isRebuild, final boolean forceCompile, + final CompileStatusNotification callback, final CompilerMessage message) { + startup(scope, isRebuild, forceCompile, false, callback, message); + } + private void startup(final CompileScope scope, final boolean isRebuild, final boolean forceCompile, - final CompileStatusNotification callback, + boolean withModalProgress, final CompileStatusNotification callback, final CompilerMessage message) { ApplicationManager.getApplication().assertIsDispatchThread(); final String contentName = CompilerBundle.message(forceCompile ? "compiler.content.name.compile" : "compiler.content.name.make"); final boolean isUnitTestMode = ApplicationManager.getApplication().isUnitTestMode(); - final CompilerTask compileTask = new CompilerTask(myProject, contentName, isUnitTestMode, true, true, isCompilationStartedAutomatically(scope)); + final CompilerTask compileTask = new CompilerTask(myProject, contentName, isUnitTestMode, !withModalProgress, true, + isCompilationStartedAutomatically(scope), withModalProgress); StatusBar.Info.set("", myProject, "Compiler"); // ensure the project model seen by build process is up-to-date diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileScopeUtil.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileScopeUtil.java index ce83b1755bb7..d3db11d32957 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileScopeUtil.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileScopeUtil.java @@ -36,13 +36,19 @@ public class CompileScopeUtil { scope.putUserData(BASE_SCOPE_FOR_EXTERNAL_BUILD, scopes); } - public static void addScopesForModules(Collection modules, List scopes, boolean forceBuild) { - if (!modules.isEmpty()) { + public static void addScopesForModules(Collection modules, + Collection unloadedModules, + List scopes, + boolean forceBuild) { + if (!modules.isEmpty() || !unloadedModules.isEmpty()) { for (JavaModuleBuildTargetType type : JavaModuleBuildTargetType.ALL_TYPES) { TargetTypeBuildScope.Builder builder = TargetTypeBuildScope.newBuilder().setTypeId(type.getTypeId()).setForceBuild(forceBuild); for (Module module : modules) { builder.addTargetId(module.getName()); } + for (String unloadedModule : unloadedModules) { + builder.addTargetId(unloadedModule); + } scopes.add(builder.build()); } } diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompositeScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompositeScope.java index df6de5d10bd4..fbbf769a558a 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompositeScope.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompositeScope.java @@ -90,6 +90,16 @@ public class CompositeScope extends ExportableUserDataHolderBase implements Comp return modules.toArray(new Module[modules.size()]); } + @NotNull + @Override + public Collection getAffectedUnloadedModules() { + Set unloadedModules = new LinkedHashSet<>(); + for (final CompileScope compileScope : myScopes) { + ContainerUtil.addAll(unloadedModules, compileScope.getAffectedUnloadedModules()); + } + return unloadedModules; + } + public T getUserData(@NotNull Key key) { for (CompileScope compileScope : myScopes) { T userData = compileScope.getUserData(key); diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java index ff79b0721ac9..80098ea5106d 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java @@ -24,11 +24,13 @@ package com.intellij.compiler.impl; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.module.UnloadedModuleDescription; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import com.intellij.util.CommonProcessors; import org.jetbrains.annotations.NotNull; @@ -38,9 +40,10 @@ public class ModuleCompileScope extends FileIndexCompileScope { private final Project myProject; private final Set myScopeModules; private final Module[] myModules; + private final Collection myIncludedUnloadedModules; public ModuleCompileScope(final Module module, boolean includeDependentModules) { - this(module.getProject(), Collections.singleton(module), includeDependentModules, false); + this(module.getProject(), Collections.singleton(module), Collections.emptyList(), includeDependentModules, false); } public ModuleCompileScope(Project project, final Module[] modules, boolean includeDependentModules) { @@ -48,11 +51,12 @@ public class ModuleCompileScope extends FileIndexCompileScope { } public ModuleCompileScope(Project project, final Module[] modules, boolean includeDependentModules, boolean includeRuntimeDependencies) { - this(project, Arrays.asList(modules), includeDependentModules, includeRuntimeDependencies); + this(project, Arrays.asList(modules), Collections.emptyList(), includeDependentModules, includeRuntimeDependencies); } - private ModuleCompileScope(Project project, final Collection modules, boolean includeDependentModules, boolean includeRuntimeDeps) { + public ModuleCompileScope(Project project, final Collection modules, Collection includedUnloadedModules, boolean includeDependentModules, boolean includeRuntimeDeps) { myProject = project; + myIncludedUnloadedModules = includedUnloadedModules; myScopeModules = new HashSet<>(); for (Module module : modules) { if (module == null) { @@ -77,6 +81,12 @@ public class ModuleCompileScope extends FileIndexCompileScope { return myScopeModules.toArray(new Module[myScopeModules.size()]); } + @NotNull + @Override + public Collection getAffectedUnloadedModules() { + return Collections.unmodifiableCollection(myIncludedUnloadedModules); + } + protected FileIndex[] getFileIndices() { final FileIndex[] indices = new FileIndex[myScopeModules.size()]; int idx = 0; @@ -87,7 +97,7 @@ public class ModuleCompileScope extends FileIndexCompileScope { } public boolean belongs(final String url) { - if (myScopeModules.isEmpty()) { + if (myScopeModules.isEmpty() && myIncludedUnloadedModules.isEmpty()) { return false; // optimization } Module candidateModule = null; @@ -142,6 +152,18 @@ public class ModuleCompileScope extends FileIndexCompileScope { } } + ModuleManager moduleManager = ModuleManager.getInstance(myProject); + for (String unloadedModule : myIncludedUnloadedModules) { + UnloadedModuleDescription moduleDescription = moduleManager.getUnloadedModuleDescription(unloadedModule); + if (moduleDescription != null) { + for (VirtualFilePointer pointer : moduleDescription.getContentRoots()) { + if (isUrlUnderRoot(url, pointer.getUrl())) { + return true; + } + } + } + } + return false; } diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/vcs/UnloadedModulesCompilationCheckinHandler.java b/java/compiler/impl/src/com/intellij/compiler/impl/vcs/UnloadedModulesCompilationCheckinHandler.java new file mode 100644 index 000000000000..40619d3d9d29 --- /dev/null +++ b/java/compiler/impl/src/com/intellij/compiler/impl/vcs/UnloadedModulesCompilationCheckinHandler.java @@ -0,0 +1,154 @@ +// Copyright 2000-2017 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.impl.vcs; + +import com.intellij.CommonBundle; +import com.intellij.compiler.CompilerWorkspaceConfiguration; +import com.intellij.compiler.impl.ModuleCompileScope; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.CompileStatusNotification; +import com.intellij.openapi.compiler.CompilerBundle; +import com.intellij.openapi.compiler.CompilerManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.impl.DirectoryIndex; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.vcs.CheckinProjectPanel; +import com.intellij.openapi.vcs.changes.CommitContext; +import com.intellij.openapi.vcs.changes.CommitExecutor; +import com.intellij.openapi.vcs.checkin.CheckinHandler; +import com.intellij.openapi.vcs.checkin.CheckinHandlerFactory; +import com.intellij.openapi.vcs.ui.RefreshableOnComponent; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowId; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.ui.NonFocusableCheckBox; +import com.intellij.util.PairConsumer; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.JBUI; +import com.intellij.xml.util.XmlStringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author nik + */ +public class UnloadedModulesCompilationCheckinHandler extends CheckinHandler { + private final Project myProject; + private final CheckinProjectPanel myCheckinPanel; + + public UnloadedModulesCompilationCheckinHandler(Project project, CheckinProjectPanel checkinPanel) { + myProject = project; + myCheckinPanel = checkinPanel; + } + + @Nullable + @Override + public RefreshableOnComponent getBeforeCheckinConfigurationPanel() { + if (ModuleManager.getInstance(myProject).getUnloadedModuleDescriptions().isEmpty()) { + return null; + } + + JCheckBox checkBox = new NonFocusableCheckBox(CompilerBundle.message("checkbox.text.compile.affected.unloaded.modules")); + return new RefreshableOnComponent() { + @Override + public JComponent getComponent() { + return JBUI.Panels.simplePanel().addToLeft(checkBox); + } + + @Override + public void refresh() { + } + + @Override + public void saveState() { + CompilerWorkspaceConfiguration.getInstance(myProject).COMPILE_AFFECTED_UNLOADED_MODULES_BEFORE_COMMIT = checkBox.isSelected(); + } + + @Override + public void restoreState() { + checkBox.setSelected(CompilerWorkspaceConfiguration.getInstance(myProject).COMPILE_AFFECTED_UNLOADED_MODULES_BEFORE_COMMIT); + } + }; + } + + @Override + public ReturnResult beforeCheckin(@Nullable CommitExecutor executor, PairConsumer additionalDataConsumer) { + if (!CompilerWorkspaceConfiguration.getInstance(myProject).COMPILE_AFFECTED_UNLOADED_MODULES_BEFORE_COMMIT + || ModuleManager.getInstance(myProject).getUnloadedModuleDescriptions().isEmpty()) { + return ReturnResult.COMMIT; + } + + ProjectFileIndex fileIndex = ProjectFileIndex.getInstance(myProject); + CompilerManager compilerManager = CompilerManager.getInstance(myProject); + Set affectedModules = new LinkedHashSet<>(); + for (VirtualFile file : myCheckinPanel.getVirtualFiles()) { + if (compilerManager.isCompilableFileType(file.getFileType())) { + ContainerUtil.addIfNotNull(affectedModules, fileIndex.getModuleForFile(file)); + } + } + + Set affectedUnloadedModules = new LinkedHashSet<>(); + for (Module module : affectedModules) { + affectedUnloadedModules.addAll(DirectoryIndex.getInstance(myProject).getDependentUnloadedModules(module)); + } + + if (affectedUnloadedModules.isEmpty()) { + return ReturnResult.COMMIT; + } + + AtomicReference result = new AtomicReference<>(); + compilerManager.makeWithModalProgress(new ModuleCompileScope(myProject, affectedModules, affectedUnloadedModules, true, false), + new CompileStatusNotification() { + @Override + public void finished(boolean aborted, int errors, int warnings, CompileContext compileContext) { + result.set( + aborted ? BuildResult.CANCELED : errors > 0 ? BuildResult.FAILED : BuildResult.SUCCESSFUL); + } + }); + + if (result.get() == BuildResult.SUCCESSFUL) { + return ReturnResult.COMMIT; + } + String message = CompilerBundle.message("dialog.message.compilation.of.unloaded.modules.failed"); + int answer = Messages.showYesNoCancelDialog(myProject, XmlStringUtil.wrapInHtml(message), CompilerBundle.message("dialog.title.compilation.failed"), + CompilerBundle.message("button.text.checkin.handler.commit"), + CompilerBundle.message("button.text.checkin.handler.show.errors"), + CommonBundle.getCancelButtonText(), null); + + if (answer == Messages.CANCEL) { + return ReturnResult.CANCEL; + } + else if (answer == Messages.YES) { + return ReturnResult.COMMIT; + } + else { + ApplicationManager.getApplication().invokeLater(() -> { + final ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.MESSAGES_WINDOW); + if (toolWindow != null) { + toolWindow.activate(null, false); + } + }, ModalityState.NON_MODAL); + return ReturnResult.CLOSE_WINDOW; + } + } + + private enum BuildResult { SUCCESSFUL, FAILED, CANCELED } + + public static class Factory extends CheckinHandlerFactory { + @NotNull + @Override + public CheckinHandler createHandler(@NotNull CheckinProjectPanel panel, @NotNull CommitContext commitContext) { + return new UnloadedModulesCompilationCheckinHandler(panel.getProject(), panel); + } + } +} diff --git a/java/compiler/impl/src/com/intellij/compiler/progress/CompilerTask.java b/java/compiler/impl/src/com/intellij/compiler/progress/CompilerTask.java index 4a5afe186608..c274fe5ceb12 100644 --- a/java/compiler/impl/src/com/intellij/compiler/progress/CompilerTask.java +++ b/java/compiler/impl/src/com/intellij/compiler/progress/CompilerTask.java @@ -78,6 +78,7 @@ public class CompilerTask extends Task.Backgroundable { private static final String APP_ICON_ID = "compiler"; @NotNull private final Object myContentId = new IDObject("content_id"); + private final boolean myModal; @NotNull private Object mySessionId = myContentId; // by default sessionID should be unique, just as content ID @@ -105,12 +106,18 @@ public class CompilerTask extends Task.Backgroundable { public CompilerTask(@NotNull Project project, String contentName, final boolean headlessMode, boolean forceAsync, boolean waitForPreviousSession, boolean compilationStartedAutomatically) { + this(project, contentName, headlessMode, forceAsync, waitForPreviousSession, compilationStartedAutomatically, false); + } + + public CompilerTask(@NotNull Project project, String contentName, final boolean headlessMode, boolean forceAsync, + boolean waitForPreviousSession, boolean compilationStartedAutomatically, boolean modal) { super(project, contentName); myContentName = contentName; myHeadlessMode = headlessMode; myForceAsyncExecution = forceAsync; myWaitForPreviousSession = waitForPreviousSession; myCompilationStartedAutomatically = compilationStartedAutomatically; + myModal = modal; } @NotNull @@ -149,7 +156,12 @@ public class CompilerTask extends Task.Backgroundable { @Override public boolean shouldStartInBackground() { - return true; + return !myModal; + } + + @Override + public boolean isConditionalModal() { + return myModal; } public ProgressIndicator getIndicator() { diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index 71925988312b..49726270fc2c 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -522,7 +522,8 @@ public class BuildManager implements Disposable { final List scopes = CmdlineProtoUtil.createAllModulesScopes(false); final AutoMakeMessageHandler handler = new AutoMakeMessageHandler(project); final TaskFuture future = scheduleBuild( - project, false, true, false, scopes, Collections.emptyList(), Collections.emptyMap(), handler + project, false, true, false, scopes, Collections.emptyList(), Collections.emptyMap(), + handler ); if (future != null) { myAutomakeFutures.put(future, project); diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTargetScopeProvider.java b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTargetScopeProvider.java index 22783212fc2b..22a4325311fe 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTargetScopeProvider.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/compiler/ArtifactBuildTargetScopeProvider.java @@ -52,7 +52,7 @@ public class ArtifactBuildTargetScopeProvider extends BuildTargetScopeProvider { final Set artifacts = ArtifactCompileScope.getArtifactsToBuild(project, baseScope, false); if (ArtifactCompileScope.getArtifacts(baseScope) == null) { Set modules = ArtifactUtil.getModulesIncludedInArtifacts(artifacts, project); - CompileScopeUtil.addScopesForModules(modules, scopes, forceBuild); + CompileScopeUtil.addScopesForModules(modules, Collections.emptyList(), scopes, forceBuild); } if (!artifacts.isEmpty()) { TargetTypeBuildScope.Builder builder = TargetTypeBuildScope.newBuilder() diff --git a/java/compiler/openapi/src/com/intellij/compiler/CompilerWorkspaceConfiguration.java b/java/compiler/openapi/src/com/intellij/compiler/CompilerWorkspaceConfiguration.java index ac62b1a2dbc0..09ed72257c6c 100644 --- a/java/compiler/openapi/src/com/intellij/compiler/CompilerWorkspaceConfiguration.java +++ b/java/compiler/openapi/src/com/intellij/compiler/CompilerWorkspaceConfiguration.java @@ -46,6 +46,7 @@ public class CompilerWorkspaceConfiguration implements PersistentStateComponent< public int COMPILER_PROCESS_HEAP_SIZE = 700; public String COMPILER_PROCESS_ADDITIONAL_VM_OPTIONS = ""; public boolean REBUILD_ON_DEPENDENCY_CHANGE = true; + public boolean COMPILE_AFFECTED_UNLOADED_MODULES_BEFORE_COMMIT = true; public static CompilerWorkspaceConfiguration getInstance(Project project) { return ServiceManager.getService(project, CompilerWorkspaceConfiguration.class); diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileScope.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileScope.java index 5e1c30445f89..1517120bce6c 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileScope.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompileScope.java @@ -21,6 +21,9 @@ import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; +import java.util.Collections; + /** * Interface describing the current compilation scope. * Only sources that belong to the scope are compiled. @@ -56,4 +59,12 @@ public interface CompileScope extends ExportableUserDataHolder { */ @NotNull Module[] getAffectedModules(); + + /** + * @return list of names of unloaded modules this scope affects. + */ + @NotNull + default Collection getAffectedUnloadedModules() { + return Collections.emptyList(); + } } diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java index 947d3c3bf29e..f09e88fbb9f2 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerManager.java @@ -221,6 +221,11 @@ public abstract class CompilerManager { */ public abstract void make(@NotNull CompileScope scope, @Nullable CompileStatusNotification callback); + /** + * Same as {@link #make(CompileScope, CompileStatusNotification)} but with modal progress window instead of background progress + */ + public abstract void makeWithModalProgress(@NotNull CompileScope scope, @Nullable CompileStatusNotification callback); + /** * Compile all modified files and all files that depend on them from the scope given. * Files are compiled according to dependencies between the modules they belong to. Compiler excludes are honored. All modules must belong to the same project diff --git a/java/compiler/tests/com/intellij/compiler/UnloadedModulesCompilationTest.java b/java/compiler/tests/com/intellij/compiler/UnloadedModulesCompilationTest.java new file mode 100644 index 000000000000..5b9092bdd28d --- /dev/null +++ b/java/compiler/tests/com/intellij/compiler/UnloadedModulesCompilationTest.java @@ -0,0 +1,39 @@ +// Copyright 2000-2017 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; + +import com.intellij.compiler.impl.ModuleCompileScope; +import com.intellij.openapi.compiler.CompilerFilter; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.vfs.VirtualFile; + +import java.io.File; +import java.util.Collections; +import java.util.List; + +import static com.intellij.util.io.TestFileSystemBuilder.fs; + +/** + * @author nik + */ +public class UnloadedModulesCompilationTest extends BaseCompilerTestCase { + public void testDoNotCompileUnloadedModulesByDefault() { + VirtualFile a = createFile("unloaded/src/A.java", "class A{ error }"); + Module unloaded = addModule("unloaded", a.getParent()); + List unloadedList = Collections.singletonList(unloaded.getName()); + ModuleManager.getInstance(myProject).setUnloadedModules(unloadedList); + buildAllModules().assertUpToDate(); + } + + public void testCompileUnloadedModulesIfExplicitlySpecified() { + VirtualFile a = createFile("unloaded/src/A.java", "class A{}"); + Module unloaded = addModule("unloaded", a.getParent()); + File outputDir = getOutputDir(unloaded, false); + + List unloadedList = Collections.singletonList(unloaded.getName()); + ModuleManager.getInstance(myProject).setUnloadedModules(unloadedList); + + make(new ModuleCompileScope(myProject, Collections.emptyList(), unloadedList, true, false), CompilerFilter.ALL); + fs().file("A.class").build().assertDirectoryEqual(outputDir); + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/api/BuildParametersKeys.java b/jps/jps-builders/src/org/jetbrains/jps/api/BuildParametersKeys.java index 3621bd00e726..de6f1496bd3b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/api/BuildParametersKeys.java +++ b/jps/jps-builders/src/org/jetbrains/jps/api/BuildParametersKeys.java @@ -5,4 +5,5 @@ package org.jetbrains.jps.api; */ public interface BuildParametersKeys { String FORCE_MODEL_LOADING = "_force_model_loading"; + String LOAD_UNLOADED_MODULES = "load_unloaded_modules"; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java index cbfdf0dab651..46fe593bffc9 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java @@ -129,7 +129,7 @@ public class BuildMain { try { FileSystemUtil.getAttributes(projectPathToPreload); // this will pre-load all FS optimizations - final BuildRunner runner = new BuildRunner(new JpsModelLoaderImpl(projectPathToPreload, globalsPathToPreload, null)); + final BuildRunner runner = new BuildRunner(new JpsModelLoaderImpl(projectPathToPreload, globalsPathToPreload, false, null)); data.setRunner(runner); final File dataStorageRoot = Utils.getDataStorageRoot(projectPathToPreload); diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java index 510055b0f276..e5b12b4ec4c9 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java @@ -86,8 +86,7 @@ final class BuildSession implements Runnable, CanceledStatus { @Nullable CmdlineRemoteProto.Message.ControllerMessage.FSEvent delta, @Nullable PreloadedData preloaded) { mySessionId = sessionId; myChannel = channel; - myPreloadedData = preloaded; - + final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals = params.getGlobalSettings(); myProjectPath = FileUtil.toCanonicalPath(params.getProjectId()); String globalOptionsPath = FileUtil.toCanonicalPath(globals.getGlobalOptionsPath()); @@ -99,11 +98,24 @@ final class BuildSession implements Runnable, CanceledStatus { builderParams.put(pair.getKey(), pair.getValue()); } myInitialFSDelta = delta; - if (preloaded == null || preloaded.getRunner() == null) { - myBuildRunner = new BuildRunner(new JpsModelLoaderImpl(myProjectPath, globalOptionsPath, null)); + boolean loadUnloadedModules = Boolean.parseBoolean(builderParams.get(BuildParametersKeys.LOAD_UNLOADED_MODULES)); + if (loadUnloadedModules && preloaded != null) { + myPreloadedData = null; + ProjectDescriptor projectDescriptor = preloaded.getProjectDescriptor(); + if (projectDescriptor != null) { + projectDescriptor.release(); + preloaded.setProjectDescriptor(null); + } } else { - myBuildRunner = preloaded.getRunner(); + myPreloadedData = preloaded; + } + + if (myPreloadedData == null || myPreloadedData.getRunner() == null) { + myBuildRunner = new BuildRunner(new JpsModelLoaderImpl(myProjectPath, globalOptionsPath, loadUnloadedModules, null)); + } + else { + myBuildRunner = myPreloadedData.getRunner(); } myBuildRunner.setFilePaths(filePaths); myBuildRunner.setBuilderParams(builderParams); diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/JpsModelLoaderImpl.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/JpsModelLoaderImpl.java index 5ad11fdaf8b6..e6712c4f1e6e 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/JpsModelLoaderImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/JpsModelLoaderImpl.java @@ -30,11 +30,16 @@ public class JpsModelLoaderImpl implements JpsModelLoader { private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.cmdline.JpsModelLoaderImpl"); private final String myProjectPath; private final String myGlobalOptionsPath; + private final boolean myLoadUnloadedModules; private final ParameterizedRunnable myModelInitializer; - public JpsModelLoaderImpl(String projectPath, String globalOptionsPath, @Nullable ParameterizedRunnable initializer) { + public JpsModelLoaderImpl(String projectPath, + String globalOptionsPath, + boolean loadUnloadedModules, + @Nullable ParameterizedRunnable initializer) { myProjectPath = projectPath; myGlobalOptionsPath = globalOptionsPath; + myLoadUnloadedModules = loadUnloadedModules; myModelInitializer = initializer; } @@ -42,7 +47,7 @@ public class JpsModelLoaderImpl implements JpsModelLoader { public JpsModel loadModel() throws IOException { final long start = System.currentTimeMillis(); LOG.info("Loading model: project path = " + myProjectPath + ", global options path = " + myGlobalOptionsPath); - final JpsModel model = JpsSerializationManager.getInstance().loadModel(myProjectPath, myGlobalOptionsPath); + final JpsModel model = JpsSerializationManager.getInstance().loadModel(myProjectPath, myGlobalOptionsPath, myLoadUnloadedModules); if (myModelInitializer != null) { myModelInitializer.run(model); } diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java index 1eace6fd9610..d277587f761e 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsProjectLoader.java @@ -67,12 +67,14 @@ public class JpsProjectLoader extends JpsLoaderBase { public static final String CLASSPATH_DIR_ATTRIBUTE = "classpath-dir"; private final JpsProject myProject; private final Map myPathVariables; + private final boolean myLoadUnloadedModules; - private JpsProjectLoader(JpsProject project, Map pathVariables, Path baseDir) { + private JpsProjectLoader(JpsProject project, Map pathVariables, Path baseDir, boolean loadUnloadedModules) { super(createProjectMacroExpander(pathVariables, baseDir)); myProject = project; myPathVariables = pathVariables; myProject.getContainer().setChild(JpsProjectSerializationDataExtensionImpl.ROLE, new JpsProjectSerializationDataExtensionImpl(baseDir)); + myLoadUnloadedModules = loadUnloadedModules; } static JpsMacroExpander createProjectMacroExpander(Map pathVariables, @NotNull Path baseDir) { @@ -81,10 +83,17 @@ public class JpsProjectLoader extends JpsLoaderBase { return expander; } - public static void loadProject(final JpsProject project, Map pathVariables, String projectPath) throws IOException { + public static void loadProject(final JpsProject project, + Map pathVariables, + String projectPath) throws IOException { + loadProject(project, pathVariables, projectPath, false); + } + + public static void loadProject(final JpsProject project, Map pathVariables, String projectPath, + boolean loadUnloadedModules) throws IOException { Path file = Paths.get(FileUtil.toCanonicalPath(projectPath)); if (Files.isRegularFile(file) && projectPath.endsWith(".ipr")) { - new JpsProjectLoader(project, pathVariables, file.getParent()).loadFromIpr(file); + new JpsProjectLoader(project, pathVariables, file.getParent(), loadUnloadedModules).loadFromIpr(file); } else { Path dotIdea = file.resolve(PathMacroUtil.DIRECTORY_STORE_NAME); @@ -98,7 +107,7 @@ public class JpsProjectLoader extends JpsLoaderBase { else { throw new IOException("Cannot find IntelliJ IDEA project files at " + projectPath); } - new JpsProjectLoader(project, pathVariables, directory.getParent()).loadFromDirectory(directory); + new JpsProjectLoader(project, pathVariables, directory.getParent(), loadUnloadedModules).loadFromDirectory(directory); } } @@ -236,7 +245,7 @@ public class JpsProjectLoader extends JpsLoaderBase { if (componentRoot == null) return; Set unloadedModules = new HashSet<>(); - if (Files.exists(workspaceFile)) { + if (!myLoadUnloadedModules && Files.exists(workspaceFile)) { Element unloadedModulesList = JDomSerializationUtil.findComponent(loadRootElement(workspaceFile), "UnloadedModulesList"); for (Element element : JDOMUtil.getChildren(unloadedModulesList, "module")) { unloadedModules.add(element.getAttributeValue("name")); diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsSerializationManager.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsSerializationManager.java index 2261d3ccea77..aa89e4cde73c 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsSerializationManager.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsSerializationManager.java @@ -34,7 +34,12 @@ public abstract class JpsSerializationManager { } @NotNull - public abstract JpsModel loadModel(@NotNull String projectPath, @Nullable String optionsPath) throws IOException; + public JpsModel loadModel(@NotNull String projectPath, @Nullable String optionsPath) throws IOException { + return loadModel(projectPath, optionsPath, false); + } + + @NotNull + public abstract JpsModel loadModel(@NotNull String projectPath, @Nullable String optionsPath, boolean loadUnloadedModules) throws IOException; @NotNull public abstract JpsProject loadProject(@NotNull String projectPath, @NotNull Map pathVariables) throws IOException; diff --git a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationManagerImpl.java b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationManagerImpl.java index c77b1647e16c..06f08f121ab5 100644 --- a/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationManagerImpl.java +++ b/jps/model-serialization/src/org/jetbrains/jps/model/serialization/impl/JpsSerializationManagerImpl.java @@ -32,14 +32,14 @@ import java.util.Map; public class JpsSerializationManagerImpl extends JpsSerializationManager { @NotNull @Override - public JpsModel loadModel(@NotNull String projectPath, @Nullable String optionsPath) + public JpsModel loadModel(@NotNull String projectPath, @Nullable String optionsPath, boolean loadUnloadedModules) throws IOException { JpsModel model = JpsElementFactory.getInstance().createModel(); if (optionsPath != null) { JpsGlobalLoader.loadGlobalSettings(model.getGlobal(), optionsPath); } Map pathVariables = JpsModelSerializationDataService.computeAllPathVariables(model.getGlobal()); - JpsProjectLoader.loadProject(model.getProject(), pathVariables, projectPath); + JpsProjectLoader.loadProject(model.getProject(), pathVariables, projectPath, loadUnloadedModules); return model; } diff --git a/jps/standalone-builder/src/org/jetbrains/jps/build/Standalone.java b/jps/standalone-builder/src/org/jetbrains/jps/build/Standalone.java index f6abfaac104d..261f4bf3ad19 100644 --- a/jps/standalone-builder/src/org/jetbrains/jps/build/Standalone.java +++ b/jps/standalone-builder/src/org/jetbrains/jps/build/Standalone.java @@ -127,7 +127,7 @@ public class Standalone { return 1; } - JpsModelLoaderImpl loader = new JpsModelLoaderImpl(projectPath, globalOptionsPath, initializer); + JpsModelLoaderImpl loader = new JpsModelLoaderImpl(projectPath, globalOptionsPath, false, initializer); Set modulesSet = new HashSet<>(Arrays.asList(modules)); List artifactsList = Arrays.asList(artifacts); File dataStorageRoot; diff --git a/resources-en/src/messages/CompilerBundle.properties b/resources-en/src/messages/CompilerBundle.properties index cfcbaf7092c9..80c7abf9ca8e 100644 --- a/resources-en/src/messages/CompilerBundle.properties +++ b/resources-en/src/messages/CompilerBundle.properties @@ -140,6 +140,13 @@ mesage.text.deployment.descriptor.file.not.exist=Deployment descriptor file ''{0 message.text.deployment.description.invalid.file=Invalid file warning.text.file.has.been.changed=File has been changed during compilation, inspection validation skipped +dialog.message.compilation.of.unloaded.modules.failed=There are unloaded modules in the project which depend on changed files.
\ + Compilation of these modules finished with errors. +dialog.title.compilation.failed=Compilation Failed +button.text.checkin.handler.commit=&Commit +button.text.checkin.handler.show.errors=&Show Errors +checkbox.text.compile.affected.unloaded.modules=Compile affected &unloaded modules + #artifacts dialog.title.output.directory.for.artifact=Output Directory for Artifact chooser.description.select.output.directory.for.0.artifact=Select output directory for ''{0}'' artifact diff --git a/resources/src/idea/RichPlatformPlugin.xml b/resources/src/idea/RichPlatformPlugin.xml index a016af51cc54..7b5ccfdb6061 100644 --- a/resources/src/idea/RichPlatformPlugin.xml +++ b/resources/src/idea/RichPlatformPlugin.xml @@ -356,6 +356,8 @@ + +