From 2a5c1708b9c85f02aaaecb54b7fdbe2683ceae63 Mon Sep 17 00:00:00 2001 From: ABHAY SINGH <75938293+ABHAY0O7@users.noreply.github.com> Date: Thu, 12 Aug 2021 18:15:16 +0530 Subject: [PATCH] Added support for multi-module-analysis Now the depclean-maven-plugin will be able to analyze the dependenct modules of a maven based multi-module java project coorectly --- .../se/kth/depclean/DepCleanGradleAction.java | 2 +- .../java/se/kth/depclean/DepCleanMojo.java | 84 ++++++++++++++++++- .../util/ChangeDependencyResultUtils.java | 60 +++++++++++++ .../se/kth/depclean/util/ResultsUtils.java | 58 +++++++++++++ 4 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 depclean-maven-plugin/src/main/java/se/kth/depclean/util/ChangeDependencyResultUtils.java create mode 100644 depclean-maven-plugin/src/main/java/se/kth/depclean/util/ResultsUtils.java diff --git a/depclean-gradle-plugin/src/main/java/se/kth/depclean/DepCleanGradleAction.java b/depclean-gradle-plugin/src/main/java/se/kth/depclean/DepCleanGradleAction.java index 76ba4c94..8dfa11ae 100644 --- a/depclean-gradle-plugin/src/main/java/se/kth/depclean/DepCleanGradleAction.java +++ b/depclean-gradle-plugin/src/main/java/se/kth/depclean/DepCleanGradleAction.java @@ -509,7 +509,7 @@ public void addDependencySize(final Path dependencyDirPath, final Logger logger) SizeOfDependencies.put(file.getName(), FileUtils.sizeOf(file)); } } else { - logger.warn("Dependencies where not copied locally"); + logger.warn("Dependencies were not copied locally"); } } diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/DepCleanMojo.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/DepCleanMojo.java index c9e09f02..1dd8ace9 100644 --- a/depclean-maven-plugin/src/main/java/se/kth/depclean/DepCleanMojo.java +++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/DepCleanMojo.java @@ -60,8 +60,10 @@ import se.kth.depclean.core.analysis.DefaultProjectDependencyAnalyzer; import se.kth.depclean.core.analysis.ProjectDependencyAnalysis; import se.kth.depclean.core.analysis.ProjectDependencyAnalyzerException; +import se.kth.depclean.util.ChangeDependencyResultUtils; import se.kth.depclean.util.JarUtils; import se.kth.depclean.util.MavenInvoker; +import se.kth.depclean.util.ResultsUtils; import se.kth.depclean.util.json.ParsedDependencies; /** @@ -80,6 +82,16 @@ public class DepCleanMojo extends AbstractMojo { private static final String SEPARATOR = "-------------------------------------------------------"; public static final String DIRECTORY_TO_COPY_DEPENDENCIES = "dependency"; + /** + * A map [Module coordinates] -> [Depclean result]. + */ + private static final Map ModuleResult = new HashMap<>(); + + /** + * A set to store module id. + */ + private static final Set ModuleDependency = new HashSet<>(); + /** * The Maven project to analyze. */ @@ -403,7 +415,7 @@ public final void execute() throws MojoExecutionException { sizeOfDependencies.put(file.getName(), FileUtils.sizeOf(file)); } } else { - log.warn("Dependencies where not copied locally"); + log.warn("Dependencies were not copied locally"); } /* Decompress dependencies */ @@ -424,10 +436,20 @@ public final void execute() throws MojoExecutionException { return; } + // Getting coordinates of all artifacts without version. + Set allDependenciesCoordinates = new HashSet<>(); + Set allArtifacts = project.getArtifacts(); + for (Artifact artifact : allArtifacts) { + String coordinate = artifact.getGroupId() + ":" + + artifact.getArtifactId() + ":" + + artifact.getVersion(); + allDependenciesCoordinates.add(coordinate); + } + Set usedTransitiveArtifacts = projectDependencyAnalysis.getUsedUndeclaredArtifacts(); Set usedDirectArtifacts = projectDependencyAnalysis.getUsedDeclaredArtifacts(); Set unusedDirectArtifacts = projectDependencyAnalysis.getUnusedDeclaredArtifacts(); - Set unusedTransitiveArtifacts = project.getArtifacts(); + Set unusedTransitiveArtifacts = new HashSet<>(allArtifacts); unusedTransitiveArtifacts.removeAll(usedDirectArtifacts); unusedTransitiveArtifacts.removeAll(usedTransitiveArtifacts); @@ -526,6 +548,17 @@ public final void execute() throws MojoExecutionException { } } + // Adding module coordinates as a dependency. + String moduleId = project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion(); + + // Collecting the result. + ResultsUtils resultInfo = new ResultsUtils( + unusedDirectArtifactsCoordinates, + unusedInheritedArtifactsCoordinates, + unusedTransitiveArtifactsCoordinates); + // Mapping the result with module for further usage. + ModuleResult.put(moduleId, resultInfo); + /* Printing the results to the terminal */ printString(SEPARATOR); printString(" D E P C L E A N A N A L Y S I S R E S U L T S"); @@ -548,6 +581,53 @@ public final void execute() throws MojoExecutionException { ignoreDependencies.stream().forEach(s -> printString("\t" + s)); } + // Getting those dependencies from previous modules whose status might have been changed now. + Set dependenciesResultChange = new HashSet<>(); + for (String module : ModuleDependency) { + /* If the module is used as a dependency in the project, + then it will be present in allDependenciesCoordinates. */ + if (allDependenciesCoordinates.contains(module)) { + // Getting the result of specified module. + ResultsUtils result = ModuleResult.get(module); + /* Build will only fail when status of any dependencies has been changed + from unused to used, so getting all the unused dependencies from the + previous modules and comparing that with all the used transitive + dependencies of the current module. */ + Set allUnusedDependency = result.getAllUnusedDependenciesCoordinates(); + for (String usedDependency : usedTransitiveArtifactsCoordinates) { + if (allUnusedDependency.contains(usedDependency)) { + // This dependency status need to be changed. + dependenciesResultChange.add( + new ChangeDependencyResultUtils(usedDependency, + module, + result.getType(usedDependency))); + } + } + } + } + + // Adding the module whose result has been collected. (Alert: This position is specific for adding it) + ModuleDependency.add(moduleId); + + // Printing those dependencies to the terminal whose status needs to be changed. + if (!dependenciesResultChange.isEmpty()) { + printString("\n" + SEPARATOR); + getLog().info("DEPENDENT MODULES FOUND"); + printString("Due to dependent modules, the debloated result of some dependencies" + + " from previous modules has been changed now."); + printString("The dependency-module details of such dependencies with the" + + " new results are as follows :\n"); + int serialNumber = 0; + for (ChangeDependencyResultUtils result : dependenciesResultChange) { + printString("\t" + ++serialNumber + ") ModuleCoordinates : " + result.getModule()); + printString("\t DependencyCoordinates : " + result.getDependencyCoordinate()); + printString("\t OldType : " + result.getType()); + printString("\t NewType : " + result.getNewType()); + printString(""); + } + printString(SEPARATOR); + } + /* Fail the build if there are unused direct dependencies */ if (failIfUnusedDirect && !unusedDirectArtifactsCoordinates.isEmpty()) { throw new MojoExecutionException( diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/ChangeDependencyResultUtils.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/ChangeDependencyResultUtils.java new file mode 100644 index 00000000..e669e6df --- /dev/null +++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/ChangeDependencyResultUtils.java @@ -0,0 +1,60 @@ +package se.kth.depclean.util; + +import java.util.Objects; + +/** + * This will help in changing the status of the dependencies involved in + * dependent modules of a multi-module java project. + */ +public class ChangeDependencyResultUtils { + + private final String dependencyCoordinate; + private final String module; + private final String type; + + /** + * Ctor. + * + * @param dependencyCoordinate Target dependency. + * @param module Target module. + * @param type Debloat status. + */ + public ChangeDependencyResultUtils(final String dependencyCoordinate, + final String module, + final String type) { + this.dependencyCoordinate = dependencyCoordinate; + this.module = module; + this.type = type; + } + + // Getters ------------------------------------------------------------- + public String getDependencyCoordinate() { + return dependencyCoordinate; + } + + public String getModule() { + return module; + } + + public String getType() { + return type; + } + + /** + * Return the new type (status) of the dependency. + * + * @return New type + */ + public String getNewType() { + // Changing the status of debloat. + String newType; + if (Objects.equals(type, "unusedDirect")) { + newType = "usedDirect"; + } else if (Objects.equals(type, "unusedTransitive")) { + newType = "usedTransitive"; + } else { + newType = "usedInherited"; + } + return newType; + } +} diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/ResultsUtils.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/ResultsUtils.java new file mode 100644 index 00000000..27880c7f --- /dev/null +++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/ResultsUtils.java @@ -0,0 +1,58 @@ +package se.kth.depclean.util; + +import java.util.HashSet; +import java.util.Set; + +/** + * Collects the data from the report generated by depclean on any module of the project. + */ +public class ResultsUtils { + + private final Set unusedDirectArtifactsCoordinates; + private final Set unusedInheritedArtifactsCoordinates; + private final Set unusedTransitiveArtifactsCoordinates; + private final Set allUnusedDependenciesCoordinates = new HashSet<>(); + + /** + * Ctor. + */ + public ResultsUtils(final Set unusedDirectArtifactsCoordinates, + final Set unusedInheritedArtifactsCoordinates, + final Set unusedTransitiveArtifactsCoordinates) { + this.unusedDirectArtifactsCoordinates = unusedDirectArtifactsCoordinates; + this.unusedInheritedArtifactsCoordinates = unusedInheritedArtifactsCoordinates; + this.unusedTransitiveArtifactsCoordinates = unusedTransitiveArtifactsCoordinates; + } + + // Getters ------------------------------------------------------------------------------ + + /** + * Collect all the unused dependencies from the provided result. + * + * @return A set of all unused dependencies + */ + public Set getAllUnusedDependenciesCoordinates() { + /* Collecting only the unused dependencies, cause in multi-module analysis, build + will only fail when any unused dependencies iof one module is used by another. */ + allUnusedDependenciesCoordinates.addAll(unusedDirectArtifactsCoordinates); + allUnusedDependenciesCoordinates.addAll(unusedInheritedArtifactsCoordinates); + allUnusedDependenciesCoordinates.addAll(unusedTransitiveArtifactsCoordinates); + return allUnusedDependenciesCoordinates; + } + + /** + * To get the type (status) of a dependency. + * + * @param coordinates The dependency. + * @return Type (status) + */ + public String getType(final String coordinates) { + if (unusedDirectArtifactsCoordinates.contains(coordinates)) { + return "unusedDirect"; + } else if (unusedTransitiveArtifactsCoordinates.contains(coordinates)) { + return "unusedTransitive"; + } else { + return "unusedInherited"; + } + } +}