diff --git a/javac-plugin/src/main/java/org/moditect/deptective/internal/PluginTask.java b/javac-plugin/src/main/java/org/moditect/deptective/internal/PluginTask.java index 5ab5ef5..932e8cd 100644 --- a/javac-plugin/src/main/java/org/moditect/deptective/internal/PluginTask.java +++ b/javac-plugin/src/main/java/org/moditect/deptective/internal/PluginTask.java @@ -29,6 +29,7 @@ import org.moditect.deptective.internal.model.Components; import org.moditect.deptective.internal.model.PackageDependencies; import org.moditect.deptective.internal.options.DeptectiveOptions; +import org.moditect.deptective.internal.options.ReportingPolicy; /** * Describes the {@link PackageReferenceHandler} to be invoked when traversing the ASTs of the project under @@ -48,7 +49,7 @@ public PackageReferenceHandler getPackageReferenceHandler(JavaFileManager jfm, D configSupplier.get(), options.getReportingPolicy(), options.getUnconfiguredPackageReportingPolicy(), - options.getCycleReportingPolicy(), + options.getCycleReportingPolicy(ReportingPolicy.ERROR), options.createDotFile(), log ); @@ -73,6 +74,7 @@ public PackageReferenceHandler getPackageReferenceHandler(JavaFileManager jfm, D jfm, log, options.getWhitelistedPackagePatterns(), + options.getCycleReportingPolicy(ReportingPolicy.WARN), new Components(components), options.createDotFile() ); diff --git a/javac-plugin/src/main/java/org/moditect/deptective/internal/handler/PackageReferenceCollector.java b/javac-plugin/src/main/java/org/moditect/deptective/internal/handler/PackageReferenceCollector.java index d1bea42..47af7d7 100644 --- a/javac-plugin/src/main/java/org/moditect/deptective/internal/handler/PackageReferenceCollector.java +++ b/javac-plugin/src/main/java/org/moditect/deptective/internal/handler/PackageReferenceCollector.java @@ -30,6 +30,8 @@ import org.moditect.deptective.internal.export.DotSerializer; import org.moditect.deptective.internal.export.JsonSerializer; import org.moditect.deptective.internal.export.ModelSerializer; +import org.moditect.deptective.internal.graph.Cycle; +import org.moditect.deptective.internal.graph.GraphUtils; import org.moditect.deptective.internal.log.DeptectiveMessages; import org.moditect.deptective.internal.log.Log; import org.moditect.deptective.internal.model.Component; @@ -58,6 +60,7 @@ public class PackageReferenceCollector implements PackageReferenceHandler { private final JavaFileManager jfm; private final List whitelistPatterns; + private final ReportingPolicy cycleReportingPolicy; /** * Any components that were declared externally. @@ -71,10 +74,11 @@ public class PackageReferenceCollector implements PackageReferenceHandler { private boolean createOutputFile = true; public PackageReferenceCollector(JavaFileManager jfm, Log log, List whitelistPatterns, - Components declaredComponents, boolean createDotFile) { + ReportingPolicy cycleReportingPolicy, Components declaredComponents, boolean createDotFile) { this.log = log; this.jfm = jfm; this.whitelistPatterns = Collections.unmodifiableList(whitelistPatterns); + this.cycleReportingPolicy = cycleReportingPolicy; this.declaredComponents = declaredComponents; this.createDotFile = createDotFile; @@ -185,7 +189,31 @@ public void onCompletingCompilation() { throw new RuntimeException("Failed to write deptective.json file", e); } + List> cycles = GraphUtils.detectCycles(packageDependencies.getComponents()); + + if (!cycles.isEmpty()) { + String cyclesAsString = "- " + cycles.stream() + .map(Cycle::toString) + .collect(Collectors.joining("," + System.lineSeparator() + "- ")); + + log.report(cycleReportingPolicy, DeptectiveMessages.CYCLE_IN_CODE_BASE, cyclesAsString); + } + if (createDotFile) { + if (!cycles.isEmpty()) { + for (Component.Builder component : builder.getComponents()) { + for (Cycle cycle : cycles) { + if (contains(cycle, component.getName())) { + for (Component nodeInCycle : cycle.getNodes()) { + if (component.getReads().containsKey(nodeInCycle.getName())) { + component.addRead(nodeInCycle.getName(), ReadKind.CYCLE); + } + } + } + } + } + } + serializer = new DotSerializer(); packageDependencies.serialize(serializer); @@ -202,6 +230,16 @@ public void onCompletingCompilation() { } } + private boolean contains(Cycle cycle, String name) { + for (Component component : cycle.getNodes()) { + if (component.getName().equals(name)) { + return true; + } + } + + return false; + } + private boolean isWhitelistAllExternal() { return whitelistPatterns.contains(PackagePattern.ALL_EXTERNAL); } diff --git a/javac-plugin/src/main/java/org/moditect/deptective/internal/log/DeptectiveMessages.java b/javac-plugin/src/main/java/org/moditect/deptective/internal/log/DeptectiveMessages.java index c053cbc..5a63213 100644 --- a/javac-plugin/src/main/java/org/moditect/deptective/internal/log/DeptectiveMessages.java +++ b/javac-plugin/src/main/java/org/moditect/deptective/internal/log/DeptectiveMessages.java @@ -30,6 +30,7 @@ public class DeptectiveMessages extends ListResourceBundle { public static final String GENERATED_DOT_REPRESENTATION = "deptective.dotrepresentation"; public static final String PACKAGE_CONTAINED_IN_MULTIPLE_COMPONENTS = "deptective.packageinmultiplecomponents"; public static final String CYCLE_IN_ARCHITECTURE = "deptective.cycleinarchitecture"; + public static final String CYCLE_IN_CODE_BASE = "deptective.cycleincodebase"; @Override protected final Object[][] getContents() { @@ -50,6 +51,12 @@ protected final Object[][] getContents() { { WARNING_PREFIX + CYCLE_IN_ARCHITECTURE, "Architecture model contains cycle(s) between these components: " + System.lineSeparator() + "{0}" }, + { ERROR_PREFIX + CYCLE_IN_CODE_BASE, + "Analysed code base contains cycle(s) between these components: " + System.lineSeparator() + + "{0}" }, + { WARNING_PREFIX + CYCLE_IN_CODE_BASE, + "Analysed code base contains cycle(s) between these components: " + System.lineSeparator() + + "{0}" }, }; } diff --git a/javac-plugin/src/main/java/org/moditect/deptective/internal/options/DeptectiveOptions.java b/javac-plugin/src/main/java/org/moditect/deptective/internal/options/DeptectiveOptions.java index d3f2df5..7fe787f 100644 --- a/javac-plugin/src/main/java/org/moditect/deptective/internal/options/DeptectiveOptions.java +++ b/javac-plugin/src/main/java/org/moditect/deptective/internal/options/DeptectiveOptions.java @@ -84,14 +84,14 @@ public ReportingPolicy getUnconfiguredPackageReportingPolicy() { /** * Returns the policy for reporting cycles between components. */ - public ReportingPolicy getCycleReportingPolicy() { + public ReportingPolicy getCycleReportingPolicy(ReportingPolicy defaultPolicy) { String policy = options.get("deptective.cycle_reporting_policy"); if (policy != null) { return ReportingPolicy.valueOf(policy.trim().toUpperCase()); } else { - return ReportingPolicy.ERROR; + return defaultPolicy; } } diff --git a/javac-plugin/src/test/java/org/moditect/deptective/plugintest/visualize/VisualizeTest.java b/javac-plugin/src/test/java/org/moditect/deptective/plugintest/visualize/VisualizeTest.java index cb9ab20..957f87a 100644 --- a/javac-plugin/src/test/java/org/moditect/deptective/plugintest/visualize/VisualizeTest.java +++ b/javac-plugin/src/test/java/org/moditect/deptective/plugintest/visualize/VisualizeTest.java @@ -58,6 +58,11 @@ public void shouldGenerateDotFileForAnalyse() throws Exception { ); assertThat(compilation).hadNoteCount(2); + assertThat(compilation).hadWarningContaining("Analysed code base contains cycle(s) between these components:"); + assertThat(compilation).hadWarningContaining( + " - org.moditect.deptective.plugintest.visualize.bar, org.moditect.deptective.plugintest.visualize.qux" + ); + String expectedConfig = Strings.lines( "digraph \"package dependencies\"", "{", @@ -65,9 +70,12 @@ public void shouldGenerateDotFileForAnalyse() throws Exception { " \"org.moditect.deptective.plugintest.visualize.foo\";", " \"org.moditect.deptective.plugintest.visualize.qux\";", " subgraph Allowed {", - " \"org.moditect.deptective.plugintest.visualize.bar\" -> \"org.moditect.deptective.plugintest.visualize.qux\";", " \"org.moditect.deptective.plugintest.visualize.foo\" -> \"org.moditect.deptective.plugintest.visualize.bar\";", " \"org.moditect.deptective.plugintest.visualize.foo\" -> \"org.moditect.deptective.plugintest.visualize.qux\";", + " }", + " subgraph Cycle {", + " edge [color=purple]", + " \"org.moditect.deptective.plugintest.visualize.bar\" -> \"org.moditect.deptective.plugintest.visualize.qux\";", " \"org.moditect.deptective.plugintest.visualize.qux\" -> \"org.moditect.deptective.plugintest.visualize.bar\";", " }", "}"