From e092b1f2e9ff0a587fca09dbdc25138034236213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Soto=20Valero?= Date: Thu, 7 May 2020 16:21:32 +0200 Subject: [PATCH] Add scope information to potentially unused dependencies, fix https://github.com/castor-software/depclean/issues/5 --- .../DefaultProjectDependencyAnalyzer.java | 16 + .../graph/ClassMembersVisitorCounter.java | 13 - .../core/analysis/graph/DefaultCallGraph.java | 14 - .../java/se/kth/depclean/DepCleanMojo.java | 685 +++++++++--------- .../java/se/kth/depclean/util/FileUtils.java | 13 - .../java/se/kth/depclean/util/JarUtils.java | 13 - .../se/kth/depclean/util/MavenInvoker.java | 13 - 7 files changed, 366 insertions(+), 401 deletions(-) diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/DefaultProjectDependencyAnalyzer.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/DefaultProjectDependencyAnalyzer.java index 63ffa14e..206b6446 100644 --- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/DefaultProjectDependencyAnalyzer.java +++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/DefaultProjectDependencyAnalyzer.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020, CASTOR Software Research Centre (www.castor.kth.se) + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ package se.kth.depclean.core.analysis; import java.io.File; diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/ClassMembersVisitorCounter.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/ClassMembersVisitorCounter.java index 1c90274a..f577078b 100644 --- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/ClassMembersVisitorCounter.java +++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/ClassMembersVisitorCounter.java @@ -1,18 +1,5 @@ /* * Copyright (c) 2020, CASTOR Software Research Centre (www.castor.kth.se) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: 1) Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2) Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. 3) Neither the name of the Qulice.com nor - * the names of its contributors may be used to endorse or promote - * products derived from this software without specific prior written - * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT diff --git a/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/DefaultCallGraph.java b/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/DefaultCallGraph.java index 805cd2c2..ed9ee91f 100644 --- a/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/DefaultCallGraph.java +++ b/depclean-core/src/main/java/se/kth/depclean/core/analysis/graph/DefaultCallGraph.java @@ -1,18 +1,5 @@ /* * Copyright (c) 2020, CASTOR Software Research Centre (www.castor.kth.se) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: 1) Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2) Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. 3) Neither the name of the Qulice.com nor - * the names of its contributors may be used to endorse or promote - * products derived from this software without specific prior written - * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT @@ -27,7 +14,6 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ - package se.kth.depclean.core.analysis.graph; import java.util.HashSet; 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 a2aeac77..4415526a 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 @@ -1,18 +1,5 @@ /* * Copyright (c) 2020, CASTOR Software Research Centre (www.castor.kth.se) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: 1) Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2) Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. 3) Neither the name of the Qulice.com nor - * the names of its contributors may be used to endorse or promote - * products derived from this software without specific prior written - * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT @@ -74,342 +61,370 @@ import se.kth.depclean.util.MavenInvoker; /** - * This Maven mojo produces a clean copy of the project's pom file without bloated dependencies. - * It is built on top of the maven-dependency-analyzer component. + * This Maven mojo is the main class of DepClean. + * DepClean is built on top of the maven-dependency-analyzer component. + * It produces a clean copy of the project's pom file, without bloated dependencies. * * @see * @see */ @Mojo(name = "depclean", defaultPhase = LifecyclePhase.PACKAGE, - requiresDependencyCollection = ResolutionScope.TEST, - requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true) + requiresDependencyCollection = ResolutionScope.TEST, + requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true) public class DepCleanMojo extends AbstractMojo { - /** - * The Maven project to analyze. - */ - @Parameter(defaultValue = "${project}", readonly = true) - private MavenProject project; - - /** - * The Maven session to analyze. - */ - @Parameter(defaultValue = "${session}", readonly = true) - private MavenSession session; - - /** - * If this is true, DepClean creates a debloated version of the pom without unused dependencies, - * called "debloated-pom.xml", in root of the project. - */ - @Parameter(property = "create.pom.debloated", defaultValue = "false") - private boolean createPomDebloated; - - /** - * Add a list of dependencies, identified by their coordinates, to be ignored by DepClean during the analysis and - * considered as used dependencies. Useful to override incomplete result caused by bytecode-level analysis - * Dependency format is groupId:artifactId:version. - */ - @Parameter(property = "ignore.dependencies") - private Set ignoreDependencies; - - /** - * If this is true, and DepClean reported any unused dependency in the dependency tree, - * the build fails immediately after running DepClean. - */ - @Parameter(defaultValue = "false") - private boolean failIfUnusedDependency; - - /** - * Skip plugin execution completely. - */ - @Parameter(defaultValue = "false") - private boolean skipDepClean; - - @Component - private ProjectBuilder mavenProjectBuilder; - - @Component - private RepositorySystem repositorySystem; - - @Component(hint = "default") - private DependencyGraphBuilder dependencyGraphBuilder; - - @Override - public void execute() throws MojoExecutionException, MojoFailureException - { - - if (skipDepClean) { - getLog().info("Skipping DepClean plugin execution"); - return; - } - - System.out.println("-------------------------------------------------------"); - getLog().info("Starting DepClean dependency analysis"); - - File pomFile = new File(project.getBasedir().getAbsolutePath() + "/" + "pom.xml"); - - String packaging = project.getPackaging(); - if (packaging.equals("pom")) { - getLog().info("Skipping because packaging type " + packaging + "."); - return; - } - - String pathToPutDebloatedPom = project.getBasedir().getAbsolutePath() + "/" + "pom-debloated.xml"; - - /* Build maven model to manipulate the pom */ - Model model; - FileReader reader; - MavenXpp3Reader mavenReader = new MavenXpp3Reader(); - try { - reader = new FileReader(pomFile); - model = mavenReader.read(reader); - model.setPomFile(pomFile); - } catch (Exception ex) { - getLog().error("Unable to build the maven project."); - return; - } - - /* Copy direct dependencies locally */ - try { - MavenInvoker.runCommand("mvn dependency:copy-dependencies"); - } catch (IOException e) { - getLog().error("Unable resolve all the dependencies."); - return; - } - - /* Decompress dependencies */ - JarUtils.decompressJars(project.getBuild().getDirectory() + "/" + "dependency"); - - /* Analyze dependencies usage status */ - ProjectDependencyAnalysis projectDependencyAnalysis; - try { - ProjectDependencyAnalyzer dependencyAnalyzer = new DefaultProjectDependencyAnalyzer(); - projectDependencyAnalysis = dependencyAnalyzer.analyze(project); - } catch (ProjectDependencyAnalyzerException e) { - getLog().error("Unable to analyze dependencies."); - return; - } - - Set usedUndeclaredArtifacts = projectDependencyAnalysis.getUsedUndeclaredArtifacts(); - Set usedDeclaredArtifacts = projectDependencyAnalysis.getUsedDeclaredArtifacts(); - Set unusedDeclaredArtifacts = projectDependencyAnalysis.getUnusedDeclaredArtifacts(); - - Set unusedUndeclaredArtifacts = project.getArtifacts(); - unusedUndeclaredArtifacts.removeAll(usedDeclaredArtifacts); - unusedUndeclaredArtifacts.removeAll(usedUndeclaredArtifacts); - unusedUndeclaredArtifacts.removeAll(unusedDeclaredArtifacts); - - /* Use artifacts coordinates for the report instead of the Artifact object */ - Set usedDeclaredArtifactsCoordinates = new HashSet<>(); - usedDeclaredArtifacts.forEach(s -> usedDeclaredArtifactsCoordinates.add(s.getGroupId() + ":" + s.getArtifactId() + ":" + s.getVersion())); - - Set usedUndeclaredArtifactsCoordinates = new HashSet<>(); - usedUndeclaredArtifacts.forEach(s -> usedUndeclaredArtifactsCoordinates.add(s.getGroupId() + ":" + s.getArtifactId() + ":" + s.getVersion())); - - Set unusedDeclaredArtifactsCoordinates = new HashSet<>(); - unusedDeclaredArtifacts.forEach(s -> unusedDeclaredArtifactsCoordinates.add(s.getGroupId() + ":" + s.getArtifactId() + ":" + s.getVersion())); - - Set unusedUndeclaredArtifactsCoordinates = new HashSet<>(); - unusedUndeclaredArtifacts.forEach(s -> unusedUndeclaredArtifactsCoordinates.add(s.getGroupId() + ":" + s.getArtifactId() + ":" + s.getVersion())); - - /* Ignoring dependencies from analysis */ - if (ignoreDependencies != null) { - for (String ignoredDependency : ignoreDependencies) { - // if the ignored dependency is an unused declared dependency then add it to the set of used declared - // and remove it from the set of unused declared - for (Iterator i = unusedDeclaredArtifactsCoordinates.iterator(); i.hasNext(); ) { - String unusedDeclaredArtifact = i.next(); - if (ignoredDependency.equals(unusedDeclaredArtifact)) { - usedDeclaredArtifactsCoordinates.add(unusedDeclaredArtifact); - i.remove(); - break; - } + /** + * The Maven project to analyze. + */ + @Parameter(defaultValue = "${project}", readonly = true) + private MavenProject project; + + /** + * The Maven session to analyze. + */ + @Parameter(defaultValue = "${session}", readonly = true) + private MavenSession session; + + /** + * If this is true, DepClean creates a debloated version of the pom without unused dependencies, + * called "debloated-pom.xml", in root of the project. + */ + @Parameter(property = "create.pom.debloated", defaultValue = "false") + private boolean createPomDebloated; + + /** + * Add a list of dependencies, identified by their coordinates, to be ignored by DepClean during the analysis and + * considered as used dependencies. Useful to override incomplete result caused by bytecode-level analysis + * Dependency format is groupId:artifactId:version. + */ + @Parameter(property = "ignore.dependencies") + private Set ignoreDependencies; + + /** + * Exclude dependencies with specific scopes from the DepClean analysis. + */ + @Parameter(property = "exclude.scopes") + private Set excludeScope; + + /** + * If this is true, and DepClean reported any unused dependency in the dependency tree, + * the build fails immediately after running DepClean. + */ + @Parameter(defaultValue = "false") + private boolean failIfUnusedDependency; + + /** + * Skip plugin execution completely. + */ + @Parameter(defaultValue = "false") + private boolean skipDepClean; + + @Component + private ProjectBuilder mavenProjectBuilder; + + @Component + private RepositorySystem repositorySystem; + + @Component(hint = "default") + private DependencyGraphBuilder dependencyGraphBuilder; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException + { + + if (skipDepClean) { + getLog().info("Skipping DepClean plugin execution"); + return; + } + + System.out.println("-------------------------------------------------------"); + getLog().info("Starting DepClean dependency analysis"); + + File pomFile = new File(project.getBasedir().getAbsolutePath() + "/" + "pom.xml"); + + String packaging = project.getPackaging(); + if (packaging.equals("pom")) { + getLog().info("Skipping because packaging type " + packaging + "."); + return; + } + + String pathToPutDebloatedPom = project.getBasedir().getAbsolutePath() + "/" + "pom-debloated.xml"; + + /* Build maven model to manipulate the pom */ + Model model; + FileReader reader; + MavenXpp3Reader mavenReader = new MavenXpp3Reader(); + try { + reader = new FileReader(pomFile); + model = mavenReader.read(reader); + model.setPomFile(pomFile); + } catch (Exception ex) { + getLog().error("Unable to build the maven project."); + return; + } + + /* Copy direct dependencies locally */ + try { + MavenInvoker.runCommand("mvn dependency:copy-dependencies"); + } catch (IOException e) { + getLog().error("Unable resolve all the dependencies."); + return; + } + + /* Decompress dependencies */ + JarUtils.decompressJars(project.getBuild().getDirectory() + "/" + "dependency"); + + /* Analyze dependencies usage status */ + ProjectDependencyAnalysis projectDependencyAnalysis; + try { + ProjectDependencyAnalyzer dependencyAnalyzer = new DefaultProjectDependencyAnalyzer(); + projectDependencyAnalysis = dependencyAnalyzer.analyze(project); + } catch (ProjectDependencyAnalyzerException e) { + getLog().error("Unable to analyze dependencies."); + return; + } + + Set usedUndeclaredArtifacts = projectDependencyAnalysis.getUsedUndeclaredArtifacts(); + Set usedDeclaredArtifacts = projectDependencyAnalysis.getUsedDeclaredArtifacts(); + Set unusedDeclaredArtifacts = projectDependencyAnalysis.getUnusedDeclaredArtifacts(); + Set unusedUndeclaredArtifacts = project.getArtifacts(); + + unusedUndeclaredArtifacts.removeAll(usedDeclaredArtifacts); + unusedUndeclaredArtifacts.removeAll(usedUndeclaredArtifacts); + unusedUndeclaredArtifacts.removeAll(unusedDeclaredArtifacts); + + /* Exclude dependencies with specific scopes from the DepClean analysis */ + if (excludeScope.size() >= 1) { + usedUndeclaredArtifacts = excludeScope(usedUndeclaredArtifacts); + usedDeclaredArtifacts = excludeScope(usedDeclaredArtifacts); + unusedDeclaredArtifacts = excludeScope(unusedDeclaredArtifacts); + unusedUndeclaredArtifacts = excludeScope(unusedUndeclaredArtifacts); + } + + + /* Use artifacts coordinates for the report instead of the Artifact object */ + Set usedDeclaredArtifactsCoordinates = new HashSet<>(); + usedDeclaredArtifacts.forEach(s -> usedDeclaredArtifactsCoordinates.add(s.getGroupId() + ":" + s.getArtifactId() + ":" + s.getVersion() + ":" + s.getScope())); + + Set usedUndeclaredArtifactsCoordinates = new HashSet<>(); + usedUndeclaredArtifacts.forEach(s -> usedUndeclaredArtifactsCoordinates.add(s.getGroupId() + ":" + s.getArtifactId() + ":" + s.getVersion() + ":" + s.getScope())); + + Set unusedDeclaredArtifactsCoordinates = new HashSet<>(); + unusedDeclaredArtifacts.forEach(s -> unusedDeclaredArtifactsCoordinates.add(s.getGroupId() + ":" + s.getArtifactId() + ":" + s.getVersion() + ":" + s.getScope())); + + Set unusedUndeclaredArtifactsCoordinates = new HashSet<>(); + unusedUndeclaredArtifacts.forEach(s -> unusedUndeclaredArtifactsCoordinates.add(s.getGroupId() + ":" + s.getArtifactId() + ":" + s.getVersion() + ":" + s.getScope())); + + /* Ignoring dependencies from analysis */ + if (ignoreDependencies != null) { + for (String ignoredDependency : ignoreDependencies) { + // if the ignored dependency is an unused declared dependency then add it to the set of used declared + // and remove it from the set of unused declared + for (Iterator i = unusedDeclaredArtifactsCoordinates.iterator(); i.hasNext(); ) { + String unusedDeclaredArtifact = i.next(); + if (ignoredDependency.equals(unusedDeclaredArtifact)) { + usedDeclaredArtifactsCoordinates.add(unusedDeclaredArtifact); + i.remove(); + break; + } + } + // if the ignored dependency is an unused undeclared dependency then add it to the set of used undeclared + // and remove it from the set of unused undeclared + for (Iterator j = unusedUndeclaredArtifactsCoordinates.iterator(); j.hasNext(); ) { + String unusedUndeclaredArtifact = j.next(); + if (ignoredDependency.equals(unusedUndeclaredArtifact)) { + usedUndeclaredArtifactsCoordinates.add(unusedUndeclaredArtifact); + j.remove(); + break; + } + } } - // if the ignored dependency is an unused undeclared dependency then add it to the set of used undeclared - // and remove it from the set of unused undeclared - for (Iterator j = unusedUndeclaredArtifactsCoordinates.iterator(); j.hasNext(); ) { - String unusedUndeclaredArtifact = j.next(); - if (ignoredDependency.equals(unusedUndeclaredArtifact)) { - usedUndeclaredArtifactsCoordinates.add(unusedUndeclaredArtifact); - j.remove(); - break; - } + } + + /* Printing the results to the console */ + System.out.println(" D E P C L E A N A N A L Y S I S R E S U L T S"); + System.out.println("-------------------------------------------------------"); + + System.out.println("Used direct dependencies" + " [" + usedDeclaredArtifactsCoordinates.size() + "]" + ": "); + usedDeclaredArtifactsCoordinates.stream().forEach(s -> System.out.println("\t" + s)); + + System.out.println("Used transitive dependencies" + " [" + usedUndeclaredArtifactsCoordinates.size() + "]" + ": "); + usedUndeclaredArtifactsCoordinates.stream().forEach(s -> System.out.println("\t" + s)); + + System.out.println("Potentially unused direct dependencies" + " [" + unusedDeclaredArtifactsCoordinates.size() + "]" + ": "); + unusedDeclaredArtifactsCoordinates.stream().forEach(s -> System.out.println("\t" + s)); + + System.out.println("Potentially unused transitive dependencies" + " [" + unusedUndeclaredArtifactsCoordinates.size() + "]" + ": "); + unusedUndeclaredArtifactsCoordinates.stream().forEach(s -> System.out.println("\t" + s)); + + if (!ignoreDependencies.isEmpty()) { + System.out.println("-------------------------------------------------------"); + System.out.println("Dependencies ignored in the analysis by the user" + " [" + ignoreDependencies.size() + "]" + ": "); + ignoreDependencies.stream().forEach(s -> System.out.println("\t" + s)); + } + + /* Fail the build if there are unused dependencies */ + if (failIfUnusedDependency && (unusedDeclaredArtifactsCoordinates.size() > 0 || unusedUndeclaredArtifactsCoordinates.size() > 0)) { + throw new MojoExecutionException("Build failed due to unused dependencies in the dependency tree."); + } + + /* Writing the debloated version of the pom */ + if (createPomDebloated) { + getLog().info("Starting debloating POM"); + + /* add used transitive as direct dependencies */ + try { + if (!usedUndeclaredArtifacts.isEmpty()) { + getLog().info("Adding " + usedUndeclaredArtifacts.size() + " used transitive dependencies as direct dependencies."); + for (Artifact usedUndeclaredArtifact : usedUndeclaredArtifacts) { + model.addDependency(createDependency(usedUndeclaredArtifact)); + } + } + } catch (Exception e) { + throw new MojoExecutionException(e.getMessage(), e); } - } - } - - /* Printing the results to the console */ - System.out.println(" D E P C L E A N A N A L Y S I S R E S U L T S"); - System.out.println("-------------------------------------------------------"); - - System.out.println("Used direct dependencies" + " [" + usedDeclaredArtifactsCoordinates.size() + "]" + ": "); - usedDeclaredArtifactsCoordinates.stream().forEach(s -> System.out.println("\t" + s)); - - System.out.println("Used transitive dependencies" + " [" + usedUndeclaredArtifactsCoordinates.size() + "]" + ": "); - usedUndeclaredArtifactsCoordinates.stream().forEach(s -> System.out.println("\t" + s)); - - System.out.println("Potentially unused direct dependencies" + " [" + unusedDeclaredArtifactsCoordinates.size() + "]" + ": "); - unusedDeclaredArtifactsCoordinates.stream().forEach(s -> System.out.println("\t" + s)); - - System.out.println("Potentially unused transitive dependencies" + " [" + unusedUndeclaredArtifactsCoordinates.size() + "]" + ": "); - unusedUndeclaredArtifactsCoordinates.stream().forEach(s -> System.out.println("\t" + s)); - if (!ignoreDependencies.isEmpty()) { - System.out.println("-------------------------------------------------------"); - System.out.println("Dependencies ignored in the analysis by the user" + " [" + ignoreDependencies.size() + "]" + ": "); - ignoreDependencies.stream().forEach(s -> System.out.println("\t" + s)); - } - - /* Fail the build if there are unused dependencies */ - if (failIfUnusedDependency && (unusedDeclaredArtifactsCoordinates.size() > 0 || unusedUndeclaredArtifactsCoordinates.size() > 0)) { - throw new MojoExecutionException("Build failed due to unused dependencies in the dependency tree."); - } + /* remove unused direct dependencies */ + try { + if (!unusedDeclaredArtifacts.isEmpty()) { + getLog().info("Removing " + unusedDeclaredArtifacts.size() + " unused direct dependencies."); + for (Artifact unusedDeclaredArtifact : unusedDeclaredArtifacts) { + for (Dependency dependency : model.getDependencies()) { + if (dependency.getGroupId().equals(unusedDeclaredArtifact.getGroupId()) && + dependency.getArtifactId().equals(unusedDeclaredArtifact.getArtifactId())) { + model.removeDependency(dependency); + break; + } + } + } + } + } catch (Exception e) { + throw new MojoExecutionException(e.getMessage(), e); + } + /* exclude unused transitive dependencies */ + try { + if (!unusedUndeclaredArtifacts.isEmpty()) { + getLog().info("Excluding " + unusedUndeclaredArtifacts.size() + " unused transitive dependencies one-by-one."); + for (Dependency dependency : model.getDependencies()) { + for (Artifact artifact : unusedUndeclaredArtifacts) { + if (isChildren(artifact, dependency)) { + System.out.println("Excluding " + artifact.toString() + " from dependency " + dependency.toString()); + Exclusion exclusion = new Exclusion(); + exclusion.setGroupId(artifact.getGroupId()); + exclusion.setArtifactId(artifact.getArtifactId()); + dependency.addExclusion(exclusion); + } + } + } + } + } catch (Exception e) { + throw new MojoExecutionException(e.getMessage(), e); + } - /* Writing the debloated version of the pom */ - if (createPomDebloated) { - getLog().info("Starting debloating POM"); - /* add used transitive as direct dependencies */ - try { - if (!usedUndeclaredArtifacts.isEmpty()) { - getLog().info("Adding " + usedUndeclaredArtifacts.size() + " used transitive dependencies as direct dependencies."); - for (Artifact usedUndeclaredArtifact : usedUndeclaredArtifacts) { - model.addDependency(createDependency(usedUndeclaredArtifact)); - } + /* write the debloated pom file */ + try { + Path path = Paths.get(pathToPutDebloatedPom); + writePom(path, model); + } catch (IOException e) { + throw new MojoExecutionException(e.getMessage(), e); } - } catch (Exception e) { - throw new MojoExecutionException(e.getMessage(), e); - } - - /* remove unused direct dependencies */ - try { - if (!unusedDeclaredArtifacts.isEmpty()) { - getLog().info("Removing " + unusedDeclaredArtifacts.size() + " unused direct dependencies."); - for (Artifact unusedDeclaredArtifact : unusedDeclaredArtifacts) { - for (Dependency dependency : model.getDependencies()) { - if (dependency.getGroupId().equals(unusedDeclaredArtifact.getGroupId()) && - dependency.getArtifactId().equals(unusedDeclaredArtifact.getArtifactId())) { - model.removeDependency(dependency); - break; - } - } - } - } - } catch (Exception e) { - throw new MojoExecutionException(e.getMessage(), e); - } - - /* exclude unused transitive dependencies */ - try { - if (!unusedUndeclaredArtifacts.isEmpty()) { - getLog().info("Excluding " + unusedUndeclaredArtifacts.size() + " unused transitive dependencies one-by-one."); - for (Dependency dependency : model.getDependencies()) { - for (Artifact artifact : unusedUndeclaredArtifacts) { - if (isChildren(artifact, dependency)) { - System.out.println("Excluding " + artifact.toString() + " from dependency " + dependency.toString()); - Exclusion exclusion = new Exclusion(); - exclusion.setGroupId(artifact.getGroupId()); - exclusion.setArtifactId(artifact.getArtifactId()); - dependency.addExclusion(exclusion); - } - } - } - } - } catch (Exception e) { - throw new MojoExecutionException(e.getMessage(), e); - } - - - /* write the debloated pom file */ - try { - Path path = Paths.get(pathToPutDebloatedPom); - writePom(path, model); - } catch (IOException e) { - throw new MojoExecutionException(e.getMessage(), e); - } - - getLog().info("POM debloated successfully"); - getLog().info("pom-debloated.xml file created in: " + pathToPutDebloatedPom); - } - } - - /** - * Determine if an artifact is a direct or transitive child of a dependency. - * - * @param artifact The artifact. - * @param dependency The dependency - * @return true if the artifact is a child of a dependency in the dependency tree. - * @throws DependencyGraphBuilderException If the graph cannot be constructed. - */ - private boolean isChildren(Artifact artifact, Dependency dependency) throws DependencyGraphBuilderException - { - List dependencyNodes = getDependencyNodes(); - for (DependencyNode node : dependencyNodes) { - Dependency dependencyNode = createDependency(node.getArtifact()); - if (dependency.getGroupId().equals(dependencyNode.getGroupId()) && - dependency.getArtifactId().equals(dependencyNode.getArtifactId())) { - // now we are in the target dependency - for (DependencyNode child : node.getChildren()) { - if (child.getArtifact().equals(artifact)) { - // the dependency contains the artifact as a child node - return true; - } + getLog().info("POM debloated successfully"); + getLog().info("pom-debloated.xml file created in: " + pathToPutDebloatedPom); + } + } + + private Set excludeScope(Set artifacts) + { + Set nonExcludedArtifacts = new HashSet<>(); + Iterator iterator = artifacts.iterator(); + while (iterator.hasNext()) { + Artifact artifact = iterator.next(); + if (!excludeScope.contains(artifact.getScope())) { + nonExcludedArtifacts.add(artifact); + } + } + return nonExcludedArtifacts; + } + + /** + * Determine if an artifact is a direct or transitive child of a dependency. + * + * @param artifact The artifact. + * @param dependency The dependency + * @return true if the artifact is a child of a dependency in the dependency tree. + * @throws DependencyGraphBuilderException If the graph cannot be constructed. + */ + private boolean isChildren(Artifact artifact, Dependency dependency) throws DependencyGraphBuilderException + { + List dependencyNodes = getDependencyNodes(); + for (DependencyNode node : dependencyNodes) { + Dependency dependencyNode = createDependency(node.getArtifact()); + if (dependency.getGroupId().equals(dependencyNode.getGroupId()) && + dependency.getArtifactId().equals(dependencyNode.getArtifactId())) { + // now we are in the target dependency + for (DependencyNode child : node.getChildren()) { + if (child.getArtifact().equals(artifact)) { + // the dependency contains the artifact as a child node + return true; + } + + } } - } - } - return false; - } - - /** - * This method returns a list of dependency nodes from a graph of dependency tree. - * - * @return The nodes in the dependency graph. - * @throws DependencyGraphBuilderException If the graph cannot be built. - */ - private List getDependencyNodes() throws DependencyGraphBuilderException - { - ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest()); - buildingRequest.setProject(project); - DependencyNode rootNode = dependencyGraphBuilder.buildDependencyGraph(buildingRequest, null); - CollectingDependencyNodeVisitor visitor = new CollectingDependencyNodeVisitor(); - rootNode.accept(visitor); - return visitor.getNodes(); - } - - /** - * This method creates a {@link org.apache.maven.model.Dependency} object from a - * Maven {@link org.apache.maven.artifact.Artifact}. - * - * @param artifact The artifact to create the dependency. - * @return The Dependency object. - */ - private Dependency createDependency(final Artifact artifact) - { - Dependency dependency = new Dependency(); - dependency.setGroupId(artifact.getGroupId()); - dependency.setArtifactId(artifact.getArtifactId()); - dependency.setVersion(artifact.getVersion()); - if (artifact.hasClassifier()) { - dependency.setClassifier(artifact.getClassifier()); - } - dependency.setOptional(artifact.isOptional()); - dependency.setScope(artifact.getScope()); - dependency.setType(artifact.getType()); - return dependency; - } - - /** - * Write pom file to the filesystem. - * - * @param pomFile The path to the pom. - * @param model The maven model to get the pom from. - * @throws IOException In case of any IO issue. - */ - private static void writePom(final Path pomFile, final Model model) throws IOException - { - MavenXpp3Writer writer = new MavenXpp3Writer(); - writer.write(Files.newBufferedWriter(pomFile), model); - } + } + return false; + } + + /** + * This method returns a list of dependency nodes from a graph of dependency tree. + * + * @return The nodes in the dependency graph. + * @throws DependencyGraphBuilderException If the graph cannot be built. + */ + private List getDependencyNodes() throws DependencyGraphBuilderException + { + ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest()); + buildingRequest.setProject(project); + DependencyNode rootNode = dependencyGraphBuilder.buildDependencyGraph(buildingRequest, null); + CollectingDependencyNodeVisitor visitor = new CollectingDependencyNodeVisitor(); + rootNode.accept(visitor); + return visitor.getNodes(); + } + + /** + * This method creates a {@link org.apache.maven.model.Dependency} object from a + * Maven {@link org.apache.maven.artifact.Artifact}. + * + * @param artifact The artifact to create the dependency. + * @return The Dependency object. + */ + private Dependency createDependency(final Artifact artifact) + { + Dependency dependency = new Dependency(); + dependency.setGroupId(artifact.getGroupId()); + dependency.setArtifactId(artifact.getArtifactId()); + dependency.setVersion(artifact.getVersion()); + if (artifact.hasClassifier()) { + dependency.setClassifier(artifact.getClassifier()); + } + dependency.setOptional(artifact.isOptional()); + dependency.setScope(artifact.getScope()); + dependency.setType(artifact.getType()); + return dependency; + } + + /** + * Write pom file to the filesystem. + * + * @param pomFile The path to the pom. + * @param model The maven model to get the pom from. + * @throws IOException In case of any IO issue. + */ + private static void writePom(final Path pomFile, final Model model) throws IOException + { + MavenXpp3Writer writer = new MavenXpp3Writer(); + writer.write(Files.newBufferedWriter(pomFile), model); + } } diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/FileUtils.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/FileUtils.java index e6d09877..54e4e7a1 100644 --- a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/FileUtils.java +++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/FileUtils.java @@ -1,18 +1,5 @@ /* * Copyright (c) 2020, CASTOR Software Research Centre (www.castor.kth.se) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: 1) Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2) Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. 3) Neither the name of the Qulice.com nor - * the names of its contributors may be used to endorse or promote - * products derived from this software without specific prior written - * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/JarUtils.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/JarUtils.java index 46c97a75..faae72cf 100644 --- a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/JarUtils.java +++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/JarUtils.java @@ -1,18 +1,5 @@ /* * Copyright (c) 2020, CASTOR Software Research Centre (www.castor.kth.se) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: 1) Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2) Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. 3) Neither the name of the Qulice.com nor - * the names of its contributors may be used to endorse or promote - * products derived from this software without specific prior written - * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT diff --git a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/MavenInvoker.java b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/MavenInvoker.java index a34814ea..4544bca8 100644 --- a/depclean-maven-plugin/src/main/java/se/kth/depclean/util/MavenInvoker.java +++ b/depclean-maven-plugin/src/main/java/se/kth/depclean/util/MavenInvoker.java @@ -1,18 +1,5 @@ /* * Copyright (c) 2020, CASTOR Software Research Centre (www.castor.kth.se) - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: 1) Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2) Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. 3) Neither the name of the Qulice.com nor - * the names of its contributors may be used to endorse or promote - * products derived from this software without specific prior written - * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT