Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephan Herrmann2019-09-22 18:31:24 +0000
committerStephan Herrmann2019-09-24 13:39:24 +0000
commitbd56845ca277b92292b685d59ebbddf51541f702 (patch)
tree1e4cbe4c334f3dec20b8383386338b5fd571b98c
parent212e6b23af942ca28f9a8eed4ca33c2053dc2108 (diff)
downloadeclipse.jdt.core-bd56845ca277b92292b685d59ebbddf51541f702.tar.gz
eclipse.jdt.core-bd56845ca277b92292b685d59ebbddf51541f702.tar.xz
eclipse.jdt.core-bd56845ca277b92292b685d59ebbddf51541f702.zip
Bug 551284 - build path cycle message lists too many projects to be
-rw-r--r--org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiProjectTests.java237
-rw-r--r--org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java76
-rw-r--r--org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java131
-rw-r--r--org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java2
-rw-r--r--org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/messages.properties2
5 files changed, 368 insertions, 80 deletions
diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiProjectTests.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiProjectTests.java
index 73d5448c58..5f5ab47847 100644
--- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiProjectTests.java
+++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiProjectTests.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -315,20 +315,22 @@ public class MultiProjectTests extends BuilderTests {
printProblems();
expectingOnlySpecificProblemsFor(p1, new Problem[] {
new Problem("p1",
- "A cycle was detected in the build path of project 'P1'. The cycle consists of projects {P1, P2}",
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}",
p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_ERROR),
new Problem("p1",
"The project cannot be built until build path errors are resolved",
p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_ERROR)
- });//$NON-NLS-1$ //$NON-NLS-2$
+ });
expectingOnlySpecificProblemsFor(p2, new Problem[] {
new Problem("p2",
- "A cycle was detected in the build path of project 'P2'. The cycle consists of projects {P1, P2}",
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}",
p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_ERROR),
new Problem("p2",
"The project cannot be built until build path errors are resolved",
p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_ERROR)
- });//$NON-NLS-1$ //$NON-NLS-2$
+ });
env.removeRequiredProject(p1, p2);
@@ -429,9 +431,22 @@ public class MultiProjectTests extends BuilderTests {
expectingCompilingOrder(new String[] { "/P1/src/p1/X.java", "/P2/src/p2/Y.java", "/P3/src/p3/Z.java",
"/P1/src/p1/X.java", "/P2/src/p2/Y.java", "/P3/src/p3/Z.java", "/P1/src/p1/X.java" });
- expectingOnlySpecificProblemFor(p1, new Problem("p1", "A cycle was detected in the build path of project 'P1'. The cycle consists of projects {P1, P2, P3}", p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
- expectingOnlySpecificProblemFor(p2,new Problem("p2", "A cycle was detected in the build path of project 'P2'. The cycle consists of projects {P1, P2, P3}", p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
- expectingOnlySpecificProblemFor(p3,new Problem("p3", "A cycle was detected in the build path of project 'P3'. The cycle consists of projects {P1, P2, P3}", p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
+ expectingOnlySpecificProblemFor(p1, new Problem("p1",
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}",
+ p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
+ expectingOnlySpecificProblemFor(p2,new Problem("p2",
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}",
+ p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
+ expectingOnlySpecificProblemFor(p3,new Problem("p3",
+ "One or more cycles were detected in the build path of project 'P3'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}",
+ p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
JavaCore.setOptions(options);
} finally {
@@ -528,12 +543,24 @@ public class MultiProjectTests extends BuilderTests {
env.waitForAutoBuild();
expectingCompilingOrder(new String[] { "/P1/src/p1/X.java", "/P2/src/p2/Y.java", "/P3/src/p3/Z.java",
"/P1/src/p1/X.java", "/P2/src/p2/Y.java", "/P3/src/p3/Z.java", "/P1/src/p1/X.java" });
- expectingOnlySpecificProblemFor(p1,new Problem("p1", "A cycle was detected in the build path of project 'P1'. The cycle consists of projects {P1, P2, P3}", p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
+ expectingOnlySpecificProblemFor(p1,new Problem("p1",
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}",
+ p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
expectingOnlySpecificProblemsFor(p2,new Problem[]{
new Problem("p2", "The method bar(Y, int) in the type X is not applicable for the arguments (Y)", c2, 106, 109, CategorizedProblem.CAT_MEMBER, IMarker.SEVERITY_ERROR),//$NON-NLS-1$ //$NON-NLS-2$
- new Problem("p2", "A cycle was detected in the build path of project 'P2'. The cycle consists of projects {P1, P2, P3}", p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)//$NON-NLS-1$ //$NON-NLS-2$
+ new Problem("p2",
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}",
+ p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
});
- expectingOnlySpecificProblemFor(p3,new Problem("p3", "A cycle was detected in the build path of project 'P3'. The cycle consists of projects {P1, P2, P3}", p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
+ expectingOnlySpecificProblemFor(p3,new Problem("p3",
+ "One or more cycles were detected in the build path of project 'P3'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}", p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
JavaCore.setOptions(options);
} finally {
@@ -631,9 +658,22 @@ public class MultiProjectTests extends BuilderTests {
expectingCompilingOrder(new String[] { "/P1/src/p1/X.java", "/P2/src/p2/Y.java", "/P3/src/p3/Z.java",
"/P1/src/p1/X.java", "/P2/src/p2/Y.java", "/P3/src/p3/Z.java", "/P1/src/p1/X.java" });
- expectingOnlySpecificProblemFor(p1,new Problem("p1", "A cycle was detected in the build path of project 'P1'. The cycle consists of projects {P1, P2, P3}", p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
- expectingOnlySpecificProblemFor(p2,new Problem("p2", "A cycle was detected in the build path of project 'P2'. The cycle consists of projects {P1, P2, P3}", p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
- expectingOnlySpecificProblemFor(p3,new Problem("p3", "A cycle was detected in the build path of project 'P3'. The cycle consists of projects {P1, P2, P3}", p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
+ expectingOnlySpecificProblemFor(p1,new Problem("p1",
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}",
+ p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
+ expectingOnlySpecificProblemFor(p2,new Problem("p2",
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}",
+ p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
+ expectingOnlySpecificProblemFor(p3,new Problem("p3",
+ "One or more cycles were detected in the build path of project 'P3'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}",
+ p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
env.addClass(root1, "p1", "X", //$NON-NLS-1$ //$NON-NLS-2$
"package p1;\n"+ //$NON-NLS-1$
@@ -647,12 +687,25 @@ public class MultiProjectTests extends BuilderTests {
incrementalBuild();
env.waitForAutoBuild();
expectingCompilingOrder(new String[] { "/P1/src/p1/X.java", "/P2/src/p2/Y.java", "/P3/src/p3/Z.java" });
- expectingOnlySpecificProblemFor(p1,new Problem("p1", "A cycle was detected in the build path of project 'P1'. The cycle consists of projects {P1, P2, P3}", p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
+ expectingOnlySpecificProblemFor(p1,new Problem("p1",
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}",
+ p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
expectingOnlySpecificProblemsFor(p2,new Problem[]{
new Problem("p2", "The method bar(Y, int) in the type X is not applicable for the arguments (Y)", c2, 106, 109, CategorizedProblem.CAT_MEMBER, IMarker.SEVERITY_ERROR),//$NON-NLS-1$ //$NON-NLS-2$
- new Problem("p2", "A cycle was detected in the build path of project 'P2'. The cycle consists of projects {P1, P2, P3}", p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)//$NON-NLS-1$ //$NON-NLS-2$
+ new Problem("p2",
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}",
+ p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
});
- expectingOnlySpecificProblemFor(p3,new Problem("p3", "A cycle was detected in the build path of project 'P3'. The cycle consists of projects {P1, P2, P3}", p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
+ expectingOnlySpecificProblemFor(p3,new Problem("p3",
+ "One or more cycles were detected in the build path of project 'P3'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}",
+ p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
JavaCore.setOptions(options);
} finally {
@@ -738,18 +791,31 @@ public class MultiProjectTests extends BuilderTests {
env.waitForAutoBuild();
expectingCompilingOrder(new String[] { "/P2/src/p2/Y.java", "/P3/src/p3/Z.java", "/P2/src/p2/Y.java" });
- expectingOnlySpecificProblemFor(p1,new Problem("p1", "A cycle was detected in the build path of project 'P1'. The cycle consists of projects {P1, P2, P3}", p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
+ expectingOnlySpecificProblemFor(p1,new Problem("p1",
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}",
+ p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
expectingOnlySpecificProblemsFor(p2,new Problem[]{
new Problem("p2", "X cannot be resolved to a type", c2, 87, 88, CategorizedProblem.CAT_TYPE, IMarker.SEVERITY_ERROR),//$NON-NLS-1$ //$NON-NLS-2$
new Problem("p2", "The method foo() from the type Z refers to the missing type X", c2, 93, 96, CategorizedProblem.CAT_MEMBER, IMarker.SEVERITY_ERROR),//$NON-NLS-1$ //$NON-NLS-2$
new Problem("p2", "The import p1 cannot be resolved", c2, 19, 21, CategorizedProblem.CAT_IMPORT, IMarker.SEVERITY_ERROR),//$NON-NLS-1$ //$NON-NLS-2$
new Problem("p2", "X cannot be resolved to a type", c2, 73, 74, CategorizedProblem.CAT_TYPE, IMarker.SEVERITY_ERROR),//$NON-NLS-1$ //$NON-NLS-2$
- new Problem("p2", "A cycle was detected in the build path of project 'P2'. The cycle consists of projects {P1, P2, P3}", p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)//$NON-NLS-1$ //$NON-NLS-2$
+ new Problem("p2",
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}",
+ p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
});
expectingOnlySpecificProblemsFor(p3,new Problem[]{
new Problem("p3", "X cannot be resolved to a type", c3, 51, 52, CategorizedProblem.CAT_TYPE, IMarker.SEVERITY_ERROR),//$NON-NLS-1$ //$NON-NLS-2$
new Problem("p3", "The import p1 cannot be resolved", c3, 19, 21, CategorizedProblem.CAT_IMPORT, IMarker.SEVERITY_ERROR),//$NON-NLS-1$ //$NON-NLS-2$
- new Problem("p3", "A cycle was detected in the build path of project 'P3'. The cycle consists of projects {P1, P2, P3}", p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)//$NON-NLS-1$ //$NON-NLS-2$
+ new Problem("p3",
+ "One or more cycles were detected in the build path of project 'P3'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}",
+ p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
});
env.addClass(root1, "p1", "X", //$NON-NLS-1$ //$NON-NLS-2$
@@ -765,9 +831,22 @@ public class MultiProjectTests extends BuilderTests {
env.waitForAutoBuild();
expectingCompilingOrder(new String[] { "/P1/src/p1/X.java", "/P2/src/p2/Y.java", "/P3/src/p3/Z.java",
"/P1/src/p1/X.java", "/P2/src/p2/Y.java" });
- expectingOnlySpecificProblemFor(p1,new Problem("p1", "A cycle was detected in the build path of project 'P1'. The cycle consists of projects {P1, P2, P3}", p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
- expectingOnlySpecificProblemFor(p2,new Problem("p2", "A cycle was detected in the build path of project 'P2'. The cycle consists of projects {P1, P2, P3}", p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
- expectingOnlySpecificProblemFor(p3,new Problem("p3", "A cycle was detected in the build path of project 'P3'. The cycle consists of projects {P1, P2, P3}", p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));//$NON-NLS-1$ //$NON-NLS-2$
+ expectingOnlySpecificProblemFor(p1,new Problem("p1",
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}",
+ p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
+ expectingOnlySpecificProblemFor(p2,new Problem("p2",
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}",
+ p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
+ expectingOnlySpecificProblemFor(p3,new Problem("p3",
+ "One or more cycles were detected in the build path of project 'P3'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P1, P3}",
+ p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING));
JavaCore.setOptions(options);
} finally {
@@ -838,13 +917,15 @@ public class MultiProjectTests extends BuilderTests {
"/P2/src/p2/Y.java" });
expectingOnlySpecificProblemsFor(p1, new Problem[] {
new Problem("p1", "The import p22 cannot be resolved", c1, 32, 35, CategorizedProblem.CAT_IMPORT, //$NON-NLS-1$ //$NON-NLS-2$
- IMarker.SEVERITY_ERROR), new Problem("p1", //$NON-NLS-1$
- "A cycle was detected in the build path of project 'P1'. The cycle consists of projects {P1, P2}", //$NON-NLS-1$
+ IMarker.SEVERITY_ERROR), new Problem("p1",
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}",
p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING) });
expectingOnlySpecificProblemsFor(p2, new Problem[] {
new Problem("p2", "The import p11 cannot be resolved", c2, 32, 35, CategorizedProblem.CAT_IMPORT, //$NON-NLS-1$ //$NON-NLS-2$
- IMarker.SEVERITY_ERROR), new Problem("p2", //$NON-NLS-1$
- "A cycle was detected in the build path of project 'P2'. The cycle consists of projects {P1, P2}", //$NON-NLS-1$
+ IMarker.SEVERITY_ERROR), new Problem("p2",
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}",
p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING) });
env.addClass(root1, "p11", "XX", //$NON-NLS-1$ //$NON-NLS-2$
@@ -865,12 +946,14 @@ public class MultiProjectTests extends BuilderTests {
expectingOnlySpecificProblemsFor(p1, new Problem[] {
new Problem("p1", "The import p22 is never used", c1, 32, 35, //$NON-NLS-1$ //$NON-NLS-2$
CategorizedProblem.CAT_UNNECESSARY_CODE, IMarker.SEVERITY_WARNING), new Problem("p1", //$NON-NLS-1$
- "A cycle was detected in the build path of project 'P1'. The cycle consists of projects {P1, P2}", //$NON-NLS-1$
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}",
p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING) });
expectingOnlySpecificProblemsFor(p2, new Problem[] {
new Problem("p2", "The import p11 is never used", c2, 32, 35, //$NON-NLS-1$ //$NON-NLS-2$
CategorizedProblem.CAT_UNNECESSARY_CODE, IMarker.SEVERITY_WARNING), new Problem("p2", //$NON-NLS-1$
- "A cycle was detected in the build path of project 'P2'. The cycle consists of projects {P1, P2}", //$NON-NLS-1$
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}",
p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING) });
JavaCore.setOptions(options);
@@ -954,13 +1037,29 @@ public void testCycle6() throws JavaModelException {
fullBuild();
env.waitForAutoBuild();
expectingOnlySpecificProblemsFor(p1,new Problem[]{
- new Problem("p1", "A cycle was detected in the build path of project 'P1'. The cycle consists of projects {P1, P2, P3}", p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)//$NON-NLS-1$ //$NON-NLS-2$
+ new Problem("p1",
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}\n" +
+ "P1->{P2, P3}\n" +
+ "->{P1, P3}",
+ p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
});
expectingOnlySpecificProblemsFor(p2,new Problem[]{
- new Problem("p2", "A cycle was detected in the build path of project 'P2'. The cycle consists of projects {P1, P2, P3}", p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)//$NON-NLS-1$ //$NON-NLS-2$
+ new Problem("p2",
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P2, P3}",
+ p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
});
expectingOnlySpecificProblemsFor(p3,new Problem[]{
- new Problem("p3", "A cycle was detected in the build path of project 'P3'. The cycle consists of projects {P1, P2, P3}", p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)//$NON-NLS-1$ //$NON-NLS-2$
+ new Problem("p3",
+ "One or more cycles were detected in the build path of project 'P3'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P2, P3}\n" +
+ "->{P1, P3}",
+ p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
});
} finally {
@@ -1044,13 +1143,29 @@ public void testCycle7() throws JavaModelException {
fullBuild();
env.waitForAutoBuild();
expectingOnlySpecificProblemsFor(p1,new Problem[]{
- new Problem("p1", "A cycle was detected in the build path of project 'P1'. The cycle consists of projects {P1, P2, P3}", p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)//$NON-NLS-1$ //$NON-NLS-2$
+ new Problem("p1",
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}\n" +
+ "P1->{P2, P3}\n" +
+ "->{P1, P3}",
+ p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
});
expectingOnlySpecificProblemsFor(p2,new Problem[]{
- new Problem("p2", "A cycle was detected in the build path of project 'P2'. The cycle consists of projects {P1, P2, P3}", p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)//$NON-NLS-1$ //$NON-NLS-2$
+ new Problem("p2",
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2}\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P2, P3}",
+ p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
});
expectingOnlySpecificProblemsFor(p3,new Problem[]{
- new Problem("p3", "A cycle was detected in the build path of project 'P3'. The cycle consists of projects {P1, P2, P3}", p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)//$NON-NLS-1$ //$NON-NLS-2$
+ new Problem("p3",
+ "One or more cycles were detected in the build path of project 'P3'. The paths towards the cycle and cycle are:\n" +
+ "->{P1, P2, P3}\n" +
+ "->{P2, P3}\n" +
+ "->{P1, P3}",
+ p3, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
});
} finally {
@@ -1060,6 +1175,58 @@ public void testCycle7() throws JavaModelException {
env.removeProject(p3);
}
}
+ public void testCycle8() throws JavaModelException {
+ // specifically tests projects with (transitive) dependencies on a cycle, i.e., error messages with a non-empty prefix
+ Hashtable options = JavaCore.getOptions();
+ Hashtable newOptions = JavaCore.getOptions();
+ newOptions.put(JavaCore.CORE_CIRCULAR_CLASSPATH, JavaCore.WARNING);
+
+ JavaCore.setOptions(newOptions);
+
+ IPath p1 = env.addProject("P1");
+ IPath p2 = env.addProject("P2");
+ IPath p3 = env.addProject("P3");
+ IPath p4 = env.addProject("P4");
+
+ // Dependencies
+ IPath[] accessiblePaths = new IPath[] {new Path("java/lang/*")};
+ IPath[] forbiddenPaths = new IPath[] {new Path("**/*")};
+ env.addRequiredProject(p1, p2, accessiblePaths, forbiddenPaths, false);
+ env.addRequiredProject(p2, p3, accessiblePaths, forbiddenPaths, false);
+ env.addRequiredProject(p3, p4, accessiblePaths, forbiddenPaths, false);
+ env.addRequiredProject(p4, p3, accessiblePaths, forbiddenPaths, false);
+
+ try {
+ env.waitForManualRefresh();
+ fullBuild();
+ env.waitForAutoBuild();
+ expectingOnlySpecificProblemsFor(p1,new Problem[]{
+ new Problem("p1",
+ "One or more cycles were detected in the build path of project 'P1'. The paths towards the cycle and cycle are:\n" +
+ "P1, P2->{P3, P4}",
+ p1, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
+ });
+ expectingOnlySpecificProblemsFor(p2,new Problem[]{
+ new Problem("p2",
+ "One or more cycles were detected in the build path of project 'P2'. The paths towards the cycle and cycle are:\n" +
+ "P2->{P3, P4}",
+ p2, -1, -1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING),
+ });
+ expectingOnlySpecificProblemsFor(p3,new Problem[]{
+ new Problem("p3",
+ "One or more cycles were detected in the build path of project 'P3'. The paths towards the cycle and cycle are:\n" +
+ "->{P3, P4}",
+ p3, -1,-1, CategorizedProblem.CAT_BUILDPATH, IMarker.SEVERITY_WARNING)
+ });
+
+ } finally {
+ JavaCore.setOptions(options);
+ env.removeProject(p1);
+ env.removeProject(p2);
+ env.removeProject(p3);
+ env.removeProject(p4);
+ }
+ }
/*
* Full buid case
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java
index 1ebb8cb05d..5bcb24cc3d 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java
@@ -118,21 +118,22 @@ public void setUpSuite() throws Exception {
setupExternalJCL("jclMin");
setupExternalJCL("jclMin1.5");
}
-protected void assertCycleMarkers(IJavaProject project, IJavaProject[] p, int[] expectedCycleParticipants) throws CoreException {
+protected void assertCycleMarkers(IJavaProject project, IJavaProject[] p, int[] expectedCycleParticipants, boolean includeAffected) throws CoreException {
waitForAutoBuild();
StringBuffer expected = new StringBuffer("{");
int expectedCount = 0;
StringBuffer computed = new StringBuffer("{");
int computedCount = 0;
+ int mask = includeAffected ? 3 : 1;
for (int j = 0; j < p.length; j++){
- int markerCount = numberOfCycleMarkers(p[j]);
- if (markerCount > 0){
+ int markerFlags = cycleMarkerFlags(p[j]);
+ if ((markerFlags & mask) > 0){
if (computedCount++ > 0) computed.append(", ");
computed.append(p[j].getElementName());
//computed.append(" (" + markerCount + ")");
}
- markerCount = expectedCycleParticipants[j];
- if (markerCount > 0){
+ markerFlags = expectedCycleParticipants[j];
+ if ((markerFlags & mask) > 0){
if (expectedCount++ > 0) expected.append(", ");
expected.append(p[j].getElementName());
//expected.append(" (" + markerCount + ")");
@@ -169,14 +170,20 @@ protected File createFile(File parent, String name, String content) throws IOExc
file.setLastModified(System.currentTimeMillis() + 2000);
return file;
}
-protected int numberOfCycleMarkers(IJavaProject javaProject) throws CoreException {
+/** @return 1: participates in cycle, 2: affected by cycle (depends on) */
+protected int cycleMarkerFlags(IJavaProject javaProject) throws CoreException {
IMarker[] markers = javaProject.getProject().findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
int result = 0;
for (int i = 0, length = markers.length; i < length; i++) {
IMarker marker = markers[i];
String cycleAttr = (String)marker.getAttribute(IJavaModelMarker.CYCLE_DETECTED);
if (cycleAttr != null && cycleAttr.equals("true")){ //$NON-NLS-1$
- result++;
+ String message = marker.getAttribute(IMarker.MESSAGE, "");
+ boolean isCycleMember = message.indexOf("\n->{") != -1; // cycle with no prefix
+ if (isCycleMember)
+ result |= 1;
+ else
+ result |= 2;
}
}
return result;
@@ -2572,7 +2579,7 @@ public void testCycleReport() throws CoreException {
IJavaProject[] projects = { p1, p2, p3 };
int cycleMarkerCount = 0;
for (int i = 0; i < projects.length; i++){
- cycleMarkerCount += numberOfCycleMarkers(projects[i]);
+ cycleMarkerCount += (cycleMarkerFlags(projects[i]) & 1);
}
assertTrue("Should have no cycle markers", cycleMarkerCount == 0);
@@ -2597,7 +2604,7 @@ public void testCycleReport() throws CoreException {
waitForAutoBuild(); // wait for cycle markers to be created
cycleMarkerCount = 0;
for (int i = 0; i < projects.length; i++){
- cycleMarkerCount += numberOfCycleMarkers(projects[i]);
+ cycleMarkerCount += (cycleMarkerFlags(projects[i]) & 1);
}
assertEquals("Unexpected number of projects involved in a classpath cycle", 3, cycleMarkerCount);
@@ -4183,11 +4190,13 @@ public void testMissingPrereq4() throws CoreException {
"");
this.assertMarkers(
"Unexpected markers for project A",
- "A cycle was detected in the build path of project 'A'. The cycle consists of projects {A, B}",
+ "One or more cycles were detected in the build path of project 'A'. The paths towards the cycle and cycle are:\n" +
+ "->{A, B}",
projectA);
this.assertMarkers(
"Unexpected markers for project B",
- "A cycle was detected in the build path of project 'B'. The cycle consists of projects {A, B}",
+ "One or more cycles were detected in the build path of project 'B'. The paths towards the cycle and cycle are:\n" +
+ "->{A, B}",
projectB);
// delete project B
@@ -4207,11 +4216,13 @@ public void testMissingPrereq4() throws CoreException {
"");
this.assertMarkers(
"Unexpected markers for project A after adding project B back",
- "A cycle was detected in the build path of project 'A'. The cycle consists of projects {A, B}",
+ "One or more cycles were detected in the build path of project 'A'. The paths towards the cycle and cycle are:\n" +
+ "->{A, B}",
projectA);
this.assertMarkers(
"Unexpected markers for project B after adding project B back",
- "A cycle was detected in the build path of project 'B'. The cycle consists of projects {A, B}",
+ "One or more cycles were detected in the build path of project 'B'. The paths towards the cycle and cycle are:\n" +
+ "->{A, B}",
projectB);
} finally {
@@ -4458,6 +4469,14 @@ public void testCycleDetection() throws CoreException {
{ 1, 1, 1, 1, 1 }, // after setting CP p[4]
};
+ int[][] expectedAffectedProjects = new int[][] {
+ { 0, 0, 0, 0, 0 }, // after setting CP p[0]
+ { 0, 0, 0, 0, 0 }, // after setting CP p[1]
+ { 1, 1, 1, 0, 0 }, // after setting CP p[2]
+ { 1, 1, 1, 0, 0 }, // after setting CP p[3]
+ { 1, 1, 1, 1, 1 }, // after setting CP p[4]
+ };
+
for (int i = 0; i < p.length; i++){
// append project references
@@ -4471,7 +4490,8 @@ public void testCycleDetection() throws CoreException {
p[i].setRawClasspath(newClasspath, null);
// check cycle markers
- assertCycleMarkers(p[i], p, expectedCycleParticipants[i]);
+ assertCycleMarkers(p[i], p, expectedCycleParticipants[i], false);
+ assertCycleMarkers(p[i], p, expectedAffectedProjects[i], true);
}
//this.startDeltas();
@@ -4533,7 +4553,7 @@ public void testCycleDetectionThroughVariables() throws CoreException {
JavaCore.setClasspathVariables(var, variableValues[i], null);
// check cycle markers
- assertCycleMarkers(p[i], p, expectedCycleParticipants[i]);
+ assertCycleMarkers(p[i], p, expectedCycleParticipants[i], false);
}
//this.startDeltas();
@@ -4616,7 +4636,7 @@ public void testCycleDetectionThroughContainers() throws CoreException {
}
// check cycle markers
- assertCycleMarkers(p[i], p, expectedCycleParticipants[i]);
+ assertCycleMarkers(p[i], p, expectedCycleParticipants[i], false);
}
//this.startDeltas();
@@ -4699,7 +4719,7 @@ public void testCycleDetectionThroughContainerVariants() throws CoreException {
}
// check cycle markers
- assertCycleMarkers(p[i], p, expectedCycleParticipants[i]);
+ assertCycleMarkers(p[i], p, expectedCycleParticipants[i], false);
}
//this.startDeltas();
@@ -4748,7 +4768,8 @@ public void testCycleDetection2() throws CoreException {
p[i].setRawClasspath(newClasspath, null);
// check cycle markers
- assertCycleMarkers(p[i], p, expectedCycleParticipants[i]);
+ assertCycleMarkers(p[i], p, expectedCycleParticipants[i], false);
+ assertCycleMarkers(p[i], p, expectedCycleParticipants[i], true);
}
//this.startDeltas();
@@ -4784,7 +4805,7 @@ public void testCycleDetection3() throws CoreException {
{ 0, 0, 0, 0, 0, 0 }, // after setting CP p[2]
{ 1, 1, 1, 1, 0, 0 }, // after setting CP p[3]
{ 1, 1, 1, 1, 0, 0 }, // after setting CP p[4]
- { 1, 1, 1, 1, 1 , 1}, // after setting CP p[5]
+ { 1, 1, 1, 1, 1 ,1 }, // after setting CP p[5]
};
for (int i = 0; i < p.length; i++){
@@ -4800,8 +4821,17 @@ public void testCycleDetection3() throws CoreException {
p[i].setRawClasspath(newClasspath, null);
// check cycle markers
- assertCycleMarkers(p[i], p, expectedCycleParticipants[i]);
+ assertCycleMarkers(p[i], p, expectedCycleParticipants[i], false);
+ assertCycleMarkers(p[i], p, expectedCycleParticipants[i], true);
}
+
+ IMarker[] markers = p[0].getProject().findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
+ // additionally see that we actually have 2 cycles for P0!
+ assertMarkers("Markers of P0",
+ "One or more cycles were detected in the build path of project 'P0'. The paths towards the cycle and cycle are:\n" +
+ "->{P0, P2, P3, P1}\n" +
+ "->{P0, P4, P5, P1}",
+ markers);
//this.startDeltas();
} finally {
@@ -4840,7 +4870,7 @@ public void testCycleDetection4() throws CoreException {
waitForAutoBuild();
getWorkspace().addResourceChangeListener(listener, IResourceChangeEvent.POST_CHANGE);
createFile("/P1/test.txt", "");
- assertCycleMarkers(p1, new IJavaProject[] {p1, p2}, new int[] {1, 1});
+ assertCycleMarkers(p1, new IJavaProject[] {p1, p2}, new int[] {1, 1}, false);
} finally {
getWorkspace().removeResourceChangeListener(listener);
deleteProjects(new String[] {"P1", "P2"});
@@ -5134,7 +5164,7 @@ private void denseCycleDetection(final int numberOfParticipants) throws CoreExce
for (int i = 0; i < numberOfParticipants; i++){
// check cycle markers
- assertCycleMarkers(projects[i], projects, allProjectsInCycle);
+ assertCycleMarkers(projects[i], projects, allProjectsInCycle, false);
}
} finally {
@@ -5207,7 +5237,7 @@ private void noCycleDetection(final int numberOfParticipants, final boolean useF
for (int i = 0; i < numberOfParticipants; i++){
// check cycle markers
- assertCycleMarkers(projects[i], projects, allProjectsInCycle);
+ assertCycleMarkers(projects[i], projects, allProjectsInCycle, false);
}
} finally {
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
index 223f78da3e..9f573c98a1 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
@@ -22,6 +22,7 @@ import java.nio.file.FileVisitResult;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -30,6 +31,7 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.jar.Manifest;
@@ -399,30 +401,35 @@ public class JavaProject
HashSet traversed = new HashSet();
// compute cycle participants
- ArrayList prereqChain = new ArrayList();
+ List<IPath> prereqChain = new ArrayList<>();
+ Map<IPath,List<CycleInfo>> cyclesPerProject = new HashMap<>();
for (int i = 0; i < length; i++){
if (hasJavaNature(rscProjects[i])) {
JavaProject project = (projects[i] = (JavaProject)JavaCore.create(rscProjects[i]));
if (!traversed.contains(project.getPath())){
prereqChain.clear();
- project.updateCycleParticipants(prereqChain, cycleParticipants, workspaceRoot, traversed, preferredClasspaths);
+ project.updateCycleParticipants(prereqChain, cycleParticipants, cyclesPerProject, workspaceRoot, traversed, preferredClasspaths);
}
}
}
//System.out.println("updateAllCycleMarkers: " + (System.currentTimeMillis() - start) + " ms");
- String cycleString = cycleParticipants.stream()
- .map(path -> workspaceRoot.findMember(path))
- .filter(r -> r != null)
- .map(r -> JavaCore.create((IProject)r))
- .filter(p -> p != null)
- .map(p -> p.getElementName())
- .collect(Collectors.joining(", ")); //$NON-NLS-1$
-
for (int i = 0; i < length; i++){
JavaProject project = projects[i];
if (project != null) {
- if (cycleParticipants.contains(project.getPath())) {
+ List<CycleInfo> cycles = cyclesPerProject.get(project.getPath());
+ if (cycles != null) {
+ StringBuilder cycleString = new StringBuilder();
+ boolean first = true;
+ for (CycleInfo cycleInfo : cycles) {
+ if (!first) cycleString.append('\n');
+ cycleString.append(cycleInfo.pathToCycleAsString());
+ cycleString.append("->{"); //$NON-NLS-1$
+ cycleString.append(cycleInfo.cycleAsString());
+ cycleString.append('}');
+ first = false;
+ }
+
IMarker cycleMarker = project.getCycleMarker();
String circularCPOption = project.getOption(JavaCore.CORE_CIRCULAR_CLASSPATH, true);
int circularCPSeverity = JavaCore.ERROR.equals(circularCPOption) ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING;
@@ -435,7 +442,7 @@ public class JavaProject
}
String existingMessage = cycleMarker.getAttribute(IMarker.MESSAGE, ""); //$NON-NLS-1$
String newMessage = new JavaModelStatus(IJavaModelStatusConstants.CLASSPATH_CYCLE,
- project, cycleString).getMessage();
+ project, cycleString.toString()).getMessage();
if (!newMessage.equals(existingMessage)) {
cycleMarker.setAttribute(IMarker.MESSAGE, newMessage);
}
@@ -445,7 +452,7 @@ public class JavaProject
} else {
// create new marker
project.createClasspathProblemMarker(
- new JavaModelStatus(IJavaModelStatusConstants.CLASSPATH_CYCLE, project, cycleString));
+ new JavaModelStatus(IJavaModelStatusConstants.CLASSPATH_CYCLE, project, cycleString.toString()));
}
} else {
project.flushClasspathProblemMarkers(true, false, false);
@@ -2554,7 +2561,7 @@ public class JavaProject
LinkedHashSet cycleParticipants = new LinkedHashSet();
HashMap preferredClasspaths = new HashMap(1);
preferredClasspaths.put(this, preferredClasspath);
- updateCycleParticipants(new ArrayList(2), cycleParticipants, ResourcesPlugin.getWorkspace().getRoot(), new HashSet(2), preferredClasspaths);
+ updateCycleParticipants(new ArrayList(2), cycleParticipants, new HashMap<>(), ResourcesPlugin.getWorkspace().getRoot(), new HashSet(2), preferredClasspaths);
return !cycleParticipants.isEmpty();
}
@@ -3625,6 +3632,48 @@ public class JavaProject
}
}
+ /** internal structure for detected build path cycles. */
+ static class CycleInfo {
+
+ private List<IPath> pathToCycle;
+ public final List<IPath> cycle;
+
+ public CycleInfo(List<IPath> pathToCycle, List<IPath> cycle) {
+ this.pathToCycle = new ArrayList<>(pathToCycle);
+ this.cycle = new ArrayList<>(cycle);
+ }
+
+ public static Optional<CycleInfo> findCycleContaining(Collection<List<CycleInfo>> infos, IPath path) {
+ return infos.stream().flatMap(l -> l.stream()).filter(c -> c.cycle.contains(path)).findAny();
+ }
+
+ public static void add(IPath project, List<IPath> prefix, List<IPath> cycle, Map<IPath, List<CycleInfo>> cyclesPerProject) {
+ List<CycleInfo> list = cyclesPerProject.get(project);
+ if (list == null) {
+ cyclesPerProject.put(project, list = new ArrayList<>());
+ } else {
+ for (CycleInfo cycleInfo: list) {
+ if (cycleInfo.cycle.equals(cycle)) {
+ // same cycle: use the shorter prefix:
+ if (cycleInfo.pathToCycle.size() > prefix.size()) {
+ cycleInfo.pathToCycle.clear();
+ cycleInfo.pathToCycle.addAll(prefix);
+ }
+ return;
+ }
+ }
+ }
+ list.add(new CycleInfo(prefix, cycle));
+ }
+
+ public String pathToCycleAsString() {
+ return this.pathToCycle.stream().map(IPath::lastSegment).collect(Collectors.joining(", ")); //$NON-NLS-1$
+ }
+
+ public String cycleAsString() {
+ return this.cycle.stream().map(IPath::lastSegment).collect(Collectors.joining(", ")); //$NON-NLS-1$
+ }
+ }
/**
* If a cycle is detected, then cycleParticipants contains all the paths of projects involved in this cycle (directly and indirectly),
* no cycle if the set is empty (and started empty)
@@ -3635,8 +3684,9 @@ public class JavaProject
* @param preferredClasspaths Map
*/
public void updateCycleParticipants(
- ArrayList prereqChain,
+ List<IPath> prereqChain,
LinkedHashSet cycleParticipants,
+ Map<IPath,List<CycleInfo>> cyclesPerProject,
IWorkspaceRoot workspaceRoot,
HashSet traversed,
Map preferredClasspaths){
@@ -3653,19 +3703,60 @@ public class JavaProject
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT){
IPath prereqProjectPath = entry.getPath();
- int index = cycleParticipants.contains(prereqProjectPath) ? 0 : prereqChain.indexOf(prereqProjectPath);
- if (index >= 0) { // refer to cycle, or in cycle itself
- for (int size = prereqChain.size(); index < size; index++) {
- cycleParticipants.add(prereqChain.get(index));
+ int prereqIndex = prereqChain.indexOf(prereqProjectPath);
+ if (prereqIndex > -1) {
+ // record a new cycle:
+ List<IPath> cycle = prereqChain.subList(prereqIndex, prereqChain.size());
+ // empty-prefix CycleInfo for all members of the cycle:
+ List<IPath> prefix = Collections.emptyList();
+ for (IPath prjInCycle : cycle) {
+ CycleInfo.add(prjInCycle, prefix, cycle, cyclesPerProject);
+ }
+ // also record with all members of the prereqChain with transitive dependency on the cycle:
+ for (int j = 0; j < prereqIndex; j++) {
+ CycleInfo.add(prereqChain.get(j), prereqChain.subList(j, prereqIndex), cycle, cyclesPerProject);
}
+ } else if (cycleParticipants.contains(prereqProjectPath)) {
+ // record existing cycle as dependency of each project in prereqChain:
+ Optional<CycleInfo> cycle = CycleInfo.findCycleContaining(cyclesPerProject.values(), prereqProjectPath);
+ if (cycle.isPresent()) {
+ List<IPath> theCycle = cycle.get().cycle;
+ for (int j = 0; j < prereqChain.size(); j++) {
+ IPath prereq = prereqChain.get(j);
+ List<IPath> prereqSubList = prereqChain.subList(j, prereqChain.size());
+ int joinIndex1 = theCycle.indexOf(prereq);
+ if (joinIndex1 != -1) {
+ // prereqSubList -> prereqProjectPath + theCycle create a new cycle
+ List<IPath> newCycle = new ArrayList<>(prereqSubList);
+ int joinIndex2 = theCycle.indexOf(prereqProjectPath); // always != -1 since that's how we found 'cycle'
+ while (joinIndex2 != joinIndex1) {
+ newCycle.add(theCycle.get(joinIndex2++));
+ if (joinIndex2 == theCycle.size())
+ joinIndex2 = 0; // it's a cycle :)
+ }
+ for (IPath newMember : newCycle) {
+ CycleInfo.add(newMember, Collections.emptyList(), newCycle, cyclesPerProject);
+ }
+ break; // the rest of prereqChain is already included via newCycle
+ } else {
+ CycleInfo.add(prereq, prereqSubList, theCycle, cyclesPerProject);
+ }
+ }
+ }
+ prereqIndex = 0;
} else {
if (!traversed.contains(prereqProjectPath)) {
IResource member = workspaceRoot.findMember(prereqProjectPath);
if (member != null && member.getType() == IResource.PROJECT){
JavaProject javaProject = (JavaProject)JavaCore.create((IProject)member);
- javaProject.updateCycleParticipants(prereqChain, cycleParticipants, workspaceRoot, traversed, preferredClasspaths);
+ javaProject.updateCycleParticipants(prereqChain, cycleParticipants, cyclesPerProject, workspaceRoot, traversed, preferredClasspaths);
}
}
+ continue;
+ }
+ // fall through from both positive branches above
+ for (int index = prereqIndex, size = prereqChain.size(); index < size; index++) {
+ cycleParticipants.add(prereqChain.get(index));
}
}
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java
index 8ff8ad0464..a59c563916 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java
@@ -778,7 +778,7 @@ private boolean isWorthBuilding() throws CoreException {
*/
void mustPropagateStructuralChanges() {
LinkedHashSet cycleParticipants = new LinkedHashSet(3);
- this.javaProject.updateCycleParticipants(new ArrayList(), cycleParticipants, this.workspaceRoot, new HashSet(3), null);
+ this.javaProject.updateCycleParticipants(new ArrayList(), cycleParticipants, new HashMap<>(), this.workspaceRoot, new HashSet(3), null);
IPath currentPath = this.javaProject.getPath();
Iterator i= cycleParticipants.iterator();
while (i.hasNext()) {
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/messages.properties b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/messages.properties
index ec02740544..725a1ccb5f 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/messages.properties
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/messages.properties
@@ -150,7 +150,7 @@ classpath_cannotUseDistinctSourceFolderAsOutput = Source folder ''{0}'' in proje
classpath_cannotUseLibraryAsOutput = Source folder ''{0}'' in project ''{2}'' cannot output to library ''{1}''
classpath_closedProject = Required project ''{0}'' needs to be open
classpath_couldNotWriteClasspathFile = Could not write ''.classpath'' file of project ''{0}'': {1}
-classpath_cycle = A cycle was detected in the build path of project ''{0}''. The cycle consists of projects '{'{1}'}'
+classpath_cycle = One or more cycles were detected in the build path of project ''{0}''. The paths towards the cycle and cycle are:\n{1}
classpath_duplicateEntryPath = Build path contains duplicate entry: ''{0}'' for project ''{1}''
classpath_illegalContainerPath = Illegal classpath container path: ''{0}'' in project ''{1}'', must have at least one segment (containerID+hints)
classpath_illegalEntryInClasspathFile = Illegal entry in ''.classpath'' of project ''{0}'' file: {1}

Back to the top