Skip to content
This repository has been archived by the owner on Apr 21, 2023. It is now read-only.

Commit

Permalink
[#281] Improved interaction between Xtext builder and JDT
Browse files Browse the repository at this point in the history
  • Loading branch information
szarnekow committed Aug 14, 2019
1 parent 89297b8 commit 08f7b55
Show file tree
Hide file tree
Showing 12 changed files with 380 additions and 22 deletions.
Expand Up @@ -30,6 +30,7 @@
/**
* @author Knut Wannheden - Initial contribution and API
*/
@SuppressWarnings("restriction")
public class BuildCancellationTest extends AbstractParticipatingBuilderTest {

private OperationCanceledException cancelException;
Expand Down
Expand Up @@ -14,6 +14,7 @@
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;

import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
Expand All @@ -22,6 +23,7 @@
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourceAttributes;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
Expand All @@ -36,6 +38,7 @@
import org.eclipse.xtext.resource.IReferenceDescription;
import org.eclipse.xtext.ui.XtextProjectHelper;
import org.eclipse.xtext.ui.resource.IResourceUIServiceProvider;
import org.eclipse.xtext.ui.testing.util.JavaProjectSetupUtil;
import org.eclipse.xtext.ui.testing.util.JavaProjectSetupUtil.TextFile;
import org.eclipse.xtext.util.StringInputStream;
import org.junit.After;
Expand Down Expand Up @@ -209,8 +212,6 @@ protected String printMarkers(IMarker[] findMarkers) throws CoreException {
assertEquals(1, countMarkers(bar_file));
}

// TODO fix https://github.com/eclipse/xtext-eclipse/issues/400
@Ignore("TODO fix https://github.com/eclipse/xtext-eclipse/issues/400")
@Test public void testTwoFilesInTwoReferencedProjectsAddNature() throws Exception {
foo_project = createJavaProjectWithRootSrc("foo");
removeNature(foo_project.getProject(), XtextProjectHelper.NATURE_ID);
Expand Down Expand Up @@ -285,8 +286,6 @@ protected void createTwoFilesInTwoReferencedProjects() throws Exception {
assertEquals(printMarkers(bar_file), 0, countMarkers(bar_file));
}

// TODO fix https://github.com/eclipse/xtext-eclipse/issues/400
@Ignore("TODO fix https://github.com/eclipse/xtext-eclipse/issues/400")
@Test public void testChangeReferencedFile() throws Exception {
createTwoFilesInTwoReferencedProjects();

Expand All @@ -301,8 +300,6 @@ protected void createTwoFilesInTwoReferencedProjects() throws Exception {
assertEquals(0, countMarkers(bar_file));
}

// TODO fix https://github.com/eclipse/xtext-eclipse/issues/400
@Ignore("TODO fix https://github.com/eclipse/xtext-eclipse/issues/400")
@Test public void testDeleteReferencedFile() throws Exception {
createTwoFilesInTwoReferencedProjects();

Expand All @@ -317,6 +314,84 @@ protected void createTwoFilesInTwoReferencedProjects() throws Exception {
assertEquals(0, countMarkers(foo_file));
assertEquals(0, countMarkers(bar_file));
}

@SuppressWarnings("restriction")
@Test public void testBuildOrderIsCorrect() throws Exception {
foo_project = createJavaProjectWithRootSrc("foo");
bar_project = createJavaProjectWithRootSrc("bar");

org.eclipse.core.internal.resources.Workspace workspace =
(org.eclipse.core.internal.resources.Workspace) ResourcesPlugin.getWorkspace();
IBuildConfiguration[] buildOrder = workspace.getBuildOrder();
assertEquals(bar_project.getProject(), buildOrder[0].getProject());
assertEquals(foo_project.getProject(), buildOrder[1].getProject());
addProjectReference(bar_project, foo_project);

buildOrder = workspace.getBuildOrder();
assertEquals(foo_project.getProject(), buildOrder[0].getProject());
assertEquals(bar_project.getProject(), buildOrder[1].getProject());
}

@SuppressWarnings("restriction")
@Test public void testBuildOrderIsWrong() throws Exception {
foo_project = createJavaProjectWithRootSrc("foo");
bar_project = createJavaProjectWithRootSrc("bar");

org.eclipse.core.internal.resources.Workspace workspace =
(org.eclipse.core.internal.resources.Workspace) ResourcesPlugin.getWorkspace();
IBuildConfiguration[] buildOrder = workspace.getBuildOrder();
assertEquals(bar_project.getProject(), buildOrder[0].getProject());
assertEquals(foo_project.getProject(), buildOrder[1].getProject());
JavaProjectSetupUtil.addProjectReference(bar_project, foo_project);

buildOrder = workspace.getBuildOrder();
assertEquals(bar_project.getProject(), buildOrder[0].getProject());
assertEquals(foo_project.getProject(), buildOrder[1].getProject());
}

@Test
public void testJavaChangeTriggersBuild() throws Exception {
foo_project = createJavaProject("foo");
IJavaProject zonkProject = createJavaProject("zonk");
bar_project = createJavaProjectWithRootSrc("bar");

addProjectReference(bar_project, zonkProject);
IClasspathEntry classpathEntry = JavaCore.newProjectEntry(foo_project.getPath(), true);
workspace.addToClasspath(zonkProject, classpathEntry);

IFolder javaPackage = foo_project.getProject().getFolder("src").getFolder("pack");
javaPackage.create(true, true, null);
IFile javaFile = javaPackage.getFile("Type.java");
javaFile.create(new StringInputStream("package pack; public class Type {}"), true, monitor());

IFolder dslFolder = bar_project.getProject().getFolder("src");
IFile dslFile = dslFolder.getFile("Dsl.typesAssistTest");
dslFile.create(new StringInputStream("default pack.Type"), true, monitor());

build();
assertEquals(printMarkers(dslFile), 0, countMarkers(dslFile));

javaFile.setContents(new StringInputStream("package pack; class X {}"), true, true, null);

build();
// Xtext proxy validation and EObjectValidator proxy validation
assertEquals(2, countMarkers(dslFile));

javaFile.setContents(new StringInputStream("package pack; public class Type {}"), true, true, null);

build();
assertEquals(0, countMarkers(dslFile));

workspace.removeClasspathEntry(zonkProject, classpathEntry);

build();
// Xtext proxy validation and EObjectValidator proxy validation
assertEquals(2, countMarkers(dslFile));

workspace.addToClasspath(zonkProject, classpathEntry);
build();
assertEquals(0, countMarkers(dslFile));
}

@Test public void testUpdateOfReferencedFile() throws Exception {
IJavaProject project = createJavaProject("foo");
Expand Down
Expand Up @@ -32,7 +32,6 @@
import org.eclipse.xtext.ui.testing.util.JavaProjectSetupUtil.TextFile;
import org.eclipse.xtext.util.StringInputStream;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;

/**
Expand Down Expand Up @@ -350,8 +349,6 @@ public void testReexportedSource() throws Exception {
assertEquals(BuilderTestLanguagePackage.Literals.ELEMENT__REFERENCES,next.getEReference());
}

// TODO fix https://github.com/eclipse/xtext-eclipse/issues/400
@Ignore("TODO fix https://github.com/eclipse/xtext-eclipse/issues/400")
@Test
public void testNewlyAddedReexportedSource() throws Exception {
IJavaProject foo = createJavaProject("foo");
Expand All @@ -374,8 +371,6 @@ public void testNewlyAddedReexportedSource() throws Exception {
assertEquals(0,countMarkers(bazFile));
}

// TODO fix https://github.com/eclipse/xtext-eclipse/issues/400
@Ignore("TODO fix https://github.com/eclipse/xtext-eclipse/issues/400")
@Test
public void testReexportedJarRemoved() throws Exception {
IJavaProject foo = createJavaProject("foo");
Expand Down
Expand Up @@ -7,9 +7,12 @@
*******************************************************************************/
package org.eclipse.xtext.builder.impl;

import java.util.Set;

import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;

Expand Down Expand Up @@ -59,4 +62,17 @@ public interface IToBeBuiltComputerContribution {
* should not be processed by the Xtext builder.
*/
boolean isRejected(IFolder folder);

/**
* Add all the projects to the result, that may affect the resources in this projects.
* The XtextBuilder will return the collected set of projects as the interesting projects after
* each build round.
*
* @see IncrementalProjectBuilder#build
*
* @since 2.19
*/
default void addInterestingProjects(IProject thisProject, Set<IProject> result) {
// per default nothing to do
}
}
Expand Up @@ -7,7 +7,10 @@
*******************************************************************************/
package org.eclipse.xtext.builder.impl;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
Expand All @@ -30,6 +33,7 @@
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;

Expand Down Expand Up @@ -154,6 +158,15 @@ public boolean isRejected(IFolder folder) {
return false;
}

/**
* @since 2.19
*/
@Override
public void addInterestingProjects(IProject thisProject, Set<IProject> result) {
for (int i = 0; i < contributions.size(); i++) {
contributions.get(i).addInterestingProjects(thisProject, result);
}
}
}

@Inject
Expand Down Expand Up @@ -358,4 +371,10 @@ protected boolean isValid(URI uri, IStorage storage) {
return uriValidator.canBuild(uri, storage);
}

protected Set<IProject> getInterestingProjects(IProject project) throws CoreException {
Set<IProject> result = new LinkedHashSet<>(Arrays.asList(project.getReferencedProjects()));
contribution.addInterestingProjects(project, result);
return result;
}

}
Expand Up @@ -9,6 +9,7 @@

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -97,6 +98,11 @@ public class XtextBuilder extends IncrementalProjectBuilder {

private ClosedProjectsQueue closedProjectsQueue;

/**
* @since 2.19
*/
private Set<IProject> interestingProjects = Collections.emptySet();

@Inject
private void injectClosedProjectsQueue(ISharedStateContributionRegistry sharedState) {
this.closedProjectsQueue = sharedState.getSingleContributedInstance(ClosedProjectsQueue.class);
Expand Down Expand Up @@ -201,7 +207,7 @@ public void scheduled(IJobChangeEvent event) {
protected IProject[] build(final int kind, Map args, IProgressMonitor monitor) throws CoreException {
if (IBuildFlag.FORGET_BUILD_STATE_ONLY.isSet(args)) {
forgetLastBuiltState();
return getProject().getReferencedProjects();
return getReferencedProjects();
}
Job.getJobManager().addJobChangeListener(MAKE_EGIT_JOB_SYSTEM);
long startTime = System.currentTimeMillis();
Expand Down Expand Up @@ -261,7 +267,22 @@ public boolean isCanceled() {
task.stop();
Job.getJobManager().removeJobChangeListener(MAKE_EGIT_JOB_SYSTEM);
}
return getProject().getReferencedProjects();
return getReferencedProjects();
}

/**
* @since 2.19
*/
protected IProject[] getReferencedProjects() throws CoreException {
interestingProjects = toBeBuiltComputer.getInterestingProjects(getProject());
return interestingProjects.toArray(new IProject[0]);
}

/**
* @since 2.19
*/
protected final Set<IProject> getInternalInterestingProjects() {
return interestingProjects;
}

private boolean shouldCancelBuild(int buildKind) {
Expand Down Expand Up @@ -306,9 +327,7 @@ protected void incrementalBuild(IResourceDelta delta, final IProgressMonitor mon
final SubMonitor progress = SubMonitor.convert(monitor, Messages.XtextBuilder_CollectingResources, 10);
progress.subTask(Messages.XtextBuilder_CollectingResources);

if (queuedBuildData.needRebuild(getProject())) {
needRebuild();
}
pollQueuedBuildData();

final ToBeBuilt toBeBuilt = new ToBeBuilt();
IResourceDeltaVisitor visitor = createDeltaVisitor(toBeBuiltComputer, toBeBuilt, progress);
Expand Down Expand Up @@ -435,6 +454,8 @@ protected void doBuild(ToBeBuilt toBeBuilt, Set<String> removedProjects, IProgre
protected void fullBuild(final IProgressMonitor monitor, boolean isRecoveryBuild) throws CoreException {
SubMonitor progress = SubMonitor.convert(monitor, 10);

pollQueuedBuildData();

IProject project = getProject();
ToBeBuilt toBeBuilt =
isRecoveryBuild
Expand Down Expand Up @@ -495,6 +516,8 @@ protected boolean isOpened(IResourceDelta delta) {
protected void clean(IProgressMonitor monitor) throws CoreException {
SubMonitor progress = SubMonitor.convert(monitor, 10);
try {
pollQueuedBuildData();

ToBeBuilt toBeBuilt = toBeBuiltComputer.removeProject(getProject(), progress.split(2));
if (monitor.isCanceled()) {
throw new OperationCanceledException();
Expand Down Expand Up @@ -537,6 +560,33 @@ protected void addInfosFromTaskAndClean(ToBeBuilt toBeBuilt, Task task, IProgres
}
}

/**
* @since 2.19
*/
protected void pollQueuedBuildData() {
boolean needRebuild = false;
if (pollQueuedBuildData(getProject())) {
needRebuild = true;
}
for(IProject project: interestingProjects) {
if (!XtextProjectHelper.hasNature(project)) {
if (pollQueuedBuildData(project)) {
needRebuild = true;
}
}
}
if(needRebuild) {
needRebuild();
}
}

/**
* @since 2.19
*/
protected boolean pollQueuedBuildData(IProject project) {
return queuedBuildData.needRebuild(project);
}

/**
* @since 2.18
* @deprecated This method is present for backwards compatibility reasons and will be removed in a future release.
Expand All @@ -561,13 +611,25 @@ private boolean isMethodSpecialized(Class<?> subclassOfBuilder, String name, Cla
}

/**
* @param monitor the progress monitor to use for reporting progress to the user. It is the caller's responsibility
* to call done() on the given monitor. Accepts null, indicating that no progress should be
* reported and that the operation cannot be cancelled.
* @deprecated call {@link #doClean(ToBeBuilt, Set, IProgressMonitor)} instead.
*/
@Deprecated
protected void doClean(ToBeBuilt toBeBuilt, IProgressMonitor monitor) throws CoreException {
doClean(toBeBuilt, ImmutableSet.of(), monitor);
}

/**
*
* @param toBeBuilt the collected URIs that should be handled by this clean operation.
* @param removedProjects additional projects that were available but do no longer have the Xtext nature or have been deleted
*
* @param monitor the progress monitor to use for reporting progress to the user. It is the caller's responsibility
* to call done() on the given monitor. Accepts null, indicating that no progress should be
* reported and that the operation cannot be cancelled.
*/
protected void doClean(ToBeBuilt toBeBuilt, Set<String> removedProjects, IProgressMonitor monitor) throws CoreException {
SubMonitor progress = SubMonitor.convert(monitor, 2);
SetWithProjectNames toBeDeletedAndProjects = new SetWithProjectNames(toBeBuilt.getToBeDeleted(), getProject().getName(), removedProjects);
Expand Down

0 comments on commit 08f7b55

Please sign in to comment.