Skip to content

Commit 504752c

Browse files
committed
Perform a full build if output folder was deleted externally
If the output folder is deleted externally, Java builder doesn't rebuild anything on next auto / incremental build request. The change checks the missing output folder on the next build request and converts build kind to a FULL_BUILD if at least one output folder in the project is missing. Fixes #4745
1 parent a75a1f6 commit 504752c

File tree

2 files changed

+87
-3
lines changed

2 files changed

+87
-3
lines changed

org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/BuildpathTests.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,24 +1018,52 @@ public void test0100() throws JavaModelException {
10181018
}
10191019

10201020
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=143025
1021+
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4745
10211022
public void testMissingOutputFolder() throws JavaModelException {
10221023
IPath projectPath = env.addProject("P"); //$NON-NLS-1$
1024+
IJavaProject p = env.getJavaProject(projectPath);
10231025
env.removePackageFragmentRoot(projectPath, ""); //$NON-NLS-1$
1024-
env.addPackageFragmentRoot(projectPath, "src"); //$NON-NLS-1$
1026+
IPath root = env.addPackageFragmentRoot(projectPath, "src"); //$NON-NLS-1$
10251027
IPath bin = env.setOutputFolder(projectPath, "bin"); //$NON-NLS-1$
1026-
1028+
env.addExternalJars(projectPath, Util.getJavaClassLibs());
1029+
env.addClass(root, "q", "Y", """
1030+
package q;
1031+
public class Y {}
1032+
""");
10271033
fullBuild();
10281034
expectingNoProblems();
1035+
IPath expectedClassFilePath = bin.append("q").append("Y.class");
1036+
expectingPresenceOf(new IPath[]{ expectedClassFilePath });
1037+
1038+
env.removeFolder(bin);
1039+
expectingNoPresenceOf(new IPath[]{ expectedClassFilePath });
1040+
1041+
incrementalBuild();
1042+
expectingNoProblems();
1043+
expectingPresenceOf(bin); // check that bin folder was recreated and is marked as derived
1044+
if (!env.getProject(projectPath).getFolder("bin").isDerived()) {
1045+
fail("output folder is not derived");
1046+
}
1047+
expectingNoPresenceOf(new IPath[]{ expectedClassFilePath });
1048+
1049+
// now enable the (disabled by default) preference and try again
1050+
p.setOption(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER, JavaCore.ENABLED);
10291051

10301052
env.removeFolder(bin);
1053+
expectingNoPresenceOf(new IPath[]{ expectedClassFilePath });
10311054

10321055
incrementalBuild();
10331056
expectingNoProblems();
10341057
expectingPresenceOf(bin); // check that bin folder was recreated and is marked as derived
1035-
if (!env.getProject(projectPath).getFolder("bin").isDerived())
1058+
if (!env.getProject(projectPath).getFolder("bin").isDerived()) {
10361059
fail("output folder is not derived");
1060+
}
1061+
1062+
expectingPresenceOf(new IPath[]{ expectedClassFilePath });
10371063
env.removeProject(projectPath);
10381064
}
1065+
1066+
10391067
@Override
10401068
protected void tearDown() throws Exception {
10411069
super.tearDown();

org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Map.Entry;
2929
import java.util.Set;
3030
import org.eclipse.core.resources.ICommand;
31+
import org.eclipse.core.resources.IContainer;
3132
import org.eclipse.core.resources.IMarker;
3233
import org.eclipse.core.resources.IProject;
3334
import org.eclipse.core.resources.IResource;
@@ -42,6 +43,7 @@
4243
import org.eclipse.jdt.core.IClasspathEntry;
4344
import org.eclipse.jdt.core.IJavaModelMarker;
4445
import org.eclipse.jdt.core.IJavaModelStatusConstants;
46+
import org.eclipse.jdt.core.IJavaProject;
4547
import org.eclipse.jdt.core.JavaCore;
4648
import org.eclipse.jdt.core.JavaModelException;
4749
import org.eclipse.jdt.core.compiler.CategorizedProblem;
@@ -686,6 +688,11 @@ private int initializeBuilder(int kind, boolean forBuild) throws CoreException {
686688
builtProjects = new LinkedHashSet<>();
687689
}
688690
builtProjects.add(projectName);
691+
692+
if (kind != CLEAN_BUILD && kind != FULL_BUILD) {
693+
// check if we need to switch to full build due to missing output folder(s)
694+
kind = checkOutputFolders(this.javaProject, kind);
695+
}
689696
}
690697

691698
this.binaryLocationsPerProject = new HashMap<>(3);
@@ -878,6 +885,55 @@ private void recordNewState(State state) {
878885
JavaModelManager.getJavaModelManager().setLastBuiltState(this.currentProject, state);
879886
}
880887

888+
/**
889+
* Checks whether all output folders for the given project exist on disk.
890+
*
891+
* @param project
892+
* The Java project to check
893+
* @return If any output folder is missing, {@link IncrementalProjectBuilder#FULL_BUILD} is returned and original
894+
* <code>buildKind</code> argument otherwise.
895+
*/
896+
protected int checkOutputFolders(IJavaProject project, int buildKind) {
897+
if (!JavaCore.ENABLED
898+
.equals(project.getOption(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER, true))) {
899+
return buildKind;
900+
}
901+
try {
902+
IWorkspaceRoot root = project.getProject().getWorkspace().getRoot();
903+
for (IClasspathEntry entry : project.getRawClasspath()) {
904+
if (entry.getEntryKind() != IClasspathEntry.CPE_SOURCE) {
905+
continue;
906+
}
907+
IPath outputLocation = entry.getOutputLocation();
908+
if (outputLocation != null) {
909+
IContainer outputContainer;
910+
if(outputLocation.segmentCount() == 1) {
911+
outputContainer = project.getProject();
912+
} else {
913+
outputContainer = root.getFolder(outputLocation);
914+
}
915+
if (!existsOnDisk(outputContainer)) {
916+
return FULL_BUILD;
917+
}
918+
}
919+
}
920+
} catch (JavaModelException e) {
921+
// If we can't read the classpath, just return the original build kind
922+
}
923+
return buildKind;
924+
}
925+
926+
private static boolean existsOnDisk(IContainer defaultOutputLocation) {
927+
boolean exists = defaultOutputLocation.exists();
928+
// Resource is not present in workspace model
929+
if (!exists) {
930+
return false;
931+
}
932+
// check that the folder *really* exists on disk and is a folder
933+
IPath location = defaultOutputLocation.getLocation();
934+
return location != null && location.toFile().isDirectory();
935+
}
936+
881937
/**
882938
* String representation for debugging purposes
883939
*/

0 commit comments

Comments
 (0)