AutoBuildDeactivator is capable of inhibiting follow up builds
Yet it does not lock the workspace in any way....
Change-Id: I2c88a8273faec937baae9e8df9dfc4de7fe03535
Signed-off-by: Markus Duft <markus.duft@ssi-schaefer.com>
diff --git a/org.eclipse.tea.core.ui/META-INF/MANIFEST.MF b/org.eclipse.tea.core.ui/META-INF/MANIFEST.MF
index bace8db..f8b06f1 100644
--- a/org.eclipse.tea.core.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.tea.core.ui/META-INF/MANIFEST.MF
@@ -9,7 +9,8 @@
Export-Package: org.eclipse.tea.core.ui,
org.eclipse.tea.core.ui.annotations,
org.eclipse.tea.core.ui.config,
- org.eclipse.tea.core.ui.internal.menu
+ org.eclipse.tea.core.ui.internal.listeners;x-internal:=true,
+ org.eclipse.tea.core.ui.internal.menu;x-internal:=true
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.tea.core,
org.eclipse.osgi.services,
diff --git a/org.eclipse.tea.core.ui/src/org/eclipse/tea/core/ui/internal/listeners/AutoBuildDeactivator.java b/org.eclipse.tea.core.ui/src/org/eclipse/tea/core/ui/internal/listeners/AutoBuildDeactivator.java
index baf6369..f57e251 100644
--- a/org.eclipse.tea.core.ui/src/org/eclipse/tea/core/ui/internal/listeners/AutoBuildDeactivator.java
+++ b/org.eclipse.tea.core.ui/src/org/eclipse/tea/core/ui/internal/listeners/AutoBuildDeactivator.java
@@ -10,13 +10,30 @@
*******************************************************************************/
package org.eclipse.tea.core.ui.internal.listeners;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
+import org.eclipse.core.internal.events.BuildCommand;
+import org.eclipse.core.internal.events.BuildManager;
+import org.eclipse.core.internal.events.InternalBuilder;
+import org.eclipse.core.internal.resources.Project;
+import org.eclipse.core.internal.resources.Workspace;
+import org.eclipse.core.internal.watson.ElementTree;
+import org.eclipse.core.resources.IBuildConfiguration;
+import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceDescription;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.tea.core.TaskExecutionContext;
@@ -33,21 +50,33 @@
* task chains are running.
*/
@Component
+@SuppressWarnings("restriction")
public class AutoBuildDeactivator implements TaskingLifeCycleListener {
private boolean autoBuildOriginalState = false;
+ private int unprotectedDepth;
private static AtomicInteger nestCount = new AtomicInteger(0);
+ private static final Map<IProject, ElementTree> suppressedProjects = new HashMap<>();
+
+ public Workspace getWorkspace() {
+ return ((Workspace) ResourcesPlugin.getWorkspace());
+ }
@BeginTaskChain
- public synchronized void begin(TaskingLog log) {
+ public synchronized void begin(TaskingLog log) throws CoreException {
if (nestCount.getAndIncrement() == 0) {
log.debug("Disabling automatic build...");
autoBuildOriginalState = setAutoBuild(log, false);
+
+ getWorkspace().prepareOperation(null, null);
+ getWorkspace().beginOperation(true);
+ unprotectedDepth = getWorkspace().getWorkManager().beginUnprotected();
}
}
@FinishTaskChain
- public synchronized void finish(TaskExecutionContext context, TaskingLog log, MultiStatus status) {
+ public synchronized void finish(TaskExecutionContext context, TaskingLog log, MultiStatus status)
+ throws CoreException {
if (nestCount.decrementAndGet() != 0) {
return;
}
@@ -68,20 +97,141 @@
} else {
setAutoBuild(log, autoBuildOriginalState);
}
+
+ getWorkspace().getWorkManager().endUnprotected(unprotectedDepth);
+ getWorkspace().endOperation(null, false, null);
+ }
+
+ /**
+ * Allows to suppress a build of the specified projects. This has two
+ * effects. After finishing the currently running task chain, auto build
+ * will be enabled and forced by Eclipse - at this point, auto build is
+ * immediately cancelled and the force flag is reset. After that, the "last
+ * built" state of each of the given projects is updated with the current
+ * state.
+ *
+ * @param project
+ * the project to assume to be cleanly built.
+ */
+ public static void avoidBuild(IProject project) {
+ synchronized (suppressedProjects) {
+ ElementTree currentTree = null;
+ try {
+ IBuildConfiguration bc = project.getActiveBuildConfig();
+ int highestStamp = 0;
+ for (ICommand c : ((Project) project).internalGetDescription().getBuildSpec(false)) {
+ IncrementalProjectBuilder builder = ((BuildCommand) c).getBuilder(bc);
+
+ Method getTree = InternalBuilder.class.getDeclaredMethod("getLastBuiltTree");
+ getTree.setAccessible(true);
+ ElementTree t = (ElementTree) getTree.invoke(builder);
+ getTree.setAccessible(false);
+
+ Field stampField = ElementTree.class.getDeclaredField("treeStamp");
+ stampField.setAccessible(true);
+ int stamp = (int) stampField.get(t);
+ stampField.setAccessible(false);
+
+ if (stamp > highestStamp) {
+ highestStamp = stamp;
+ currentTree = t;
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ // oups;
+ }
+
+ if (currentTree != null) {
+ suppressedProjects.put(project, currentTree);
+ } else {
+ System.err.println("no tree for " + project);
+ }
+ }
}
private boolean setAutoBuild(TaskingLog log, boolean autoBuild) {
boolean originalState = false;
- try {
- final IWorkspace workspace = ResourcesPlugin.getWorkspace();
- final IWorkspaceDescription workspaceDesc = workspace.getDescription();
- originalState = workspaceDesc.isAutoBuilding();
- workspaceDesc.setAutoBuilding(autoBuild);
- workspace.setDescription(workspaceDesc);
- } catch (Exception e) {
- log.error("Failed to restore AutoBuild, target=" + autoBuild, e);
+ synchronized (suppressedProjects) {
+ try {
+ final IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ final IWorkspaceDescription workspaceDesc = workspace.getDescription();
+ originalState = workspaceDesc.isAutoBuilding();
+ workspaceDesc.setAutoBuilding(autoBuild);
+ workspace.setDescription(workspaceDesc);
+
+ // we have 100ms worst case to react (scheduling delay of auto
+ // build).
+ if (!suppressedProjects.isEmpty()) {
+ suppressBuild(suppressedProjects);
+ }
+ } catch (Exception e) {
+ log.error("Failed to restore AutoBuild, target=" + autoBuild, e);
+ } finally {
+ suppressedProjects.clear();
+ }
}
return originalState;
}
+ /**
+ * Immediately cancels auto build, and suppresses any forced build and all
+ * deltas for the given projects.
+ *
+ * @param suppressedProjects
+ */
+ private static void suppressBuild(Map<IProject, ElementTree> suppressedProjects) {
+ // This is a little hacky but avoids that the autobuild will immediately
+ // re-build what we built just now.
+ try {
+ BuildManager mgr = ((Workspace) ResourcesPlugin.getWorkspace()).getBuildManager();
+
+ // get the job
+ Field jobField = mgr.getClass().getDeclaredField("autoBuildJob");
+ jobField.setAccessible(true);
+ Object o = jobField.get(mgr);
+ jobField.setAccessible(false);
+
+ // cancel the job
+ ((Job) o).cancel();
+
+ // reset force flag
+ Field force = o.getClass().getDeclaredField("forceBuild");
+ force.setAccessible(true);
+ force.set(o, false);
+ force.setAccessible(false);
+
+ // now we need re-set the current delta trees on all projects
+ if (!suppressedProjects.isEmpty()) {
+ for (Entry<IProject, ElementTree> entry : suppressedProjects.entrySet()) {
+ IProject project = entry.getKey();
+ IBuildConfiguration bc = project.getActiveBuildConfig();
+ ElementTree elementTree = suppressedProjects.get(project);
+ if (elementTree != null) {
+ ICommand[] commands;
+ // yay - now make sure the tree of all builders is the
+ // current tree of the project
+ if (project.isAccessible()) {
+ commands = ((Project) project).internalGetDescription().getBuildSpec(false);
+ } else {
+ continue;
+ }
+
+ for (ICommand c : commands) {
+ IncrementalProjectBuilder b = ((BuildCommand) c).getBuilder(bc);
+ Method setTree = InternalBuilder.class.getDeclaredMethod("setLastBuiltTree",
+ ElementTree.class);
+ setTree.setAccessible(true);
+ setTree.invoke(b, elementTree);
+ setTree.setAccessible(false);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ System.err.println("cannot avoid autobuild");
+ e.printStackTrace();
+ }
+ }
+
}