diff --git a/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/impl/BuilderStateDiscarder.java b/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/impl/BuilderStateDiscarder.java index 5437f96446..37b60a6bc5 100644 --- a/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/impl/BuilderStateDiscarder.java +++ b/org.eclipse.xtext.builder/src/org/eclipse/xtext/builder/impl/BuilderStateDiscarder.java @@ -8,6 +8,9 @@ *******************************************************************************/ package org.eclipse.xtext.builder.impl; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.Map; import org.apache.log4j.Logger; @@ -34,12 +37,34 @@ public class BuilderStateDiscarder { private static final Logger logger = Logger.getLogger(BuilderStateDiscarder.class); + private static final MethodHandle InternalBuilder_getLastBuiltTree; + static { + try { + @SuppressWarnings("restriction") + MethodHandles.Lookup lookup = MethodHandles.privateLookupIn( + org.eclipse.core.internal.events.InternalBuilder.class, MethodHandles.lookup()); + @SuppressWarnings("restriction") + MethodHandle handle = lookup.findVirtual( + org.eclipse.core.internal.events.InternalBuilder.class, + "getLastBuiltTree", + MethodType.methodType(org.eclipse.core.internal.watson.ElementTree.class)); + InternalBuilder_getLastBuiltTree = handle; + } catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + /** * Returns true, if the given builderArguments indicate that we should discard the built state * for the given projects. */ public boolean forgetLastBuildState(Iterable toUpdate, Map builderArguments) { + /* + * Implementation note: + * This is executed with the Workspace.lock being acquired, but the tree might be locked for modifications, + * so we may not directly touch a project. + */ if (canHandleBuildFlag(builderArguments)) { for (IProject project : toUpdate) { XtextBuilder builder = BuildManagerAccess.findBuilder(project); @@ -61,7 +86,8 @@ public boolean forgetLastBuildState(Iterable toUpdate, Map { try { - if (project.isAccessible()) { + // Check if there is already a known built tree before touching the project + if (project.isAccessible() && !hasBeenBuilt(project)) { project.touch(progressMonitor); } return Status.OK_STATUS; @@ -77,6 +103,17 @@ protected void touchProject(IProject project) { } + protected boolean hasBeenBuilt(IProject project) { + @SuppressWarnings("restriction") + org.eclipse.core.internal.events.InternalBuilder builder = BuildManagerAccess.findBuilder(project); + try { + return InternalBuilder_getLastBuiltTree.invoke(builder) != null; + } catch (Throwable e) { + logger.error("Failed to dermine if the project was already built. Assuming it wasn't built yet", e); + return false; + } + } + protected boolean canHandleBuildFlag(Map builderArguments) { return IBuildFlag.FORGET_BUILD_STATE_ONLY.isSet(builderArguments) || IBuildFlag.RECOVERY_BUILD.isSet(builderArguments);