Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
cesarsotovalero committed Apr 6, 2022
2 parents 5c13f9d + cde7189 commit 1c96de9
Show file tree
Hide file tree
Showing 35 changed files with 977 additions and 242 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public void write() throws IOException {
log.info("Starting debloating file");
logChanges();
setDependencies(analysis.getDebloatedDependencies().stream()
.map(this::toMavenDependency)
.map(this::toProviderDependency)
.collect(Collectors.toList()));

if (log.isDebugEnabled()) {
Expand All @@ -34,7 +34,7 @@ public void write() throws IOException {
writeFile();
}

protected abstract T toMavenDependency(DebloatedDependency debloatedDependency);
protected abstract T toProviderDependency(DebloatedDependency debloatedDependency);

protected abstract void setDependencies(List<T> dependencies);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,16 @@
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.logging.Log;
import org.jetbrains.annotations.Nullable;
import se.kth.depclean.core.analysis.AnalysisFailureException;
import se.kth.depclean.core.analysis.DefaultProjectDependencyAnalyzer;
import se.kth.depclean.core.analysis.ProjectDependencyAnalyzerException;
import se.kth.depclean.core.analysis.graph.DependencyGraph;
import se.kth.depclean.core.analysis.model.ProjectDependencyAnalysis;
import se.kth.depclean.core.model.ClassName;
import se.kth.depclean.core.model.Dependency;
import se.kth.depclean.core.model.ProjectContext;
import se.kth.depclean.core.model.Scope;
import se.kth.depclean.core.util.JarUtils;
import se.kth.depclean.core.wrapper.DependencyManagerWrapper;
import se.kth.depclean.core.wrapper.LogWrapper;

/**
* Runs the depclean process, regardless of a specific dependency manager.
Expand All @@ -31,6 +29,7 @@
public class DepCleanManager {

private static final String SEPARATOR = "-------------------------------------------------------";
private static final String DIRECTORY_TO_EXTRACT_DEPENDENCIES = "dependency";

private final DependencyManagerWrapper dependencyManager;
private final boolean skipDepClean;
Expand All @@ -48,27 +47,25 @@ public class DepCleanManager {
* Execute the depClean manager.
*/
@SneakyThrows
public void execute() throws AnalysisFailureException {
public ProjectDependencyAnalysis execute() throws AnalysisFailureException {
final long startTime = System.currentTimeMillis();

if (skipDepClean) {
getLog().info("Skipping DepClean plugin execution");
return;
return null;
}
printString(SEPARATOR);
getLog().info("Starting DepClean dependency analysis");

if (dependencyManager.isMaven() && dependencyManager.isPackagingPom()) {
getLog().info("Skipping because packaging type is pom.");
return;
getLog().info("Skipping because packaging type is pom");
return null;
}

dependencyManager.copyAndExtractDependencies();
extractLibClasses();

final ProjectDependencyAnalysis analysis = getAnalysis();
if (analysis == null) {
return;
}
final DefaultProjectDependencyAnalyzer projectDependencyAnalyzer = new DefaultProjectDependencyAnalyzer();
final ProjectDependencyAnalysis analysis = projectDependencyAnalyzer.analyze(buildProjectContext());
analysis.print();

/* Fail the build if there are unused direct dependencies */
Expand Down Expand Up @@ -101,6 +98,44 @@ public void execute() throws AnalysisFailureException {

final long stopTime = System.currentTimeMillis();
getLog().info("Analysis done in " + getTime(stopTime - startTime));

return analysis;
}

@SneakyThrows
private void extractLibClasses() {
final File dependencyDirectory =
dependencyManager.getBuildDirectory().resolve(DIRECTORY_TO_EXTRACT_DEPENDENCIES).toFile();
FileUtils.deleteDirectory(dependencyDirectory);
dependencyManager.dependencyGraph().allDependencies()
.forEach(jarFile -> copyDependencies(jarFile, dependencyDirectory));

// TODO remove this workaround later
if (dependencyManager.getBuildDirectory().resolve("libs").toFile().exists()) {
try {
FileUtils.copyDirectory(
dependencyManager.getBuildDirectory().resolve("libs").toFile(),
dependencyDirectory
);
} catch (IOException | NullPointerException e) {
getLog().error("Error copying directory libs to dependency");
throw new RuntimeException(e);
}
}

/* Decompress dependencies */
if (dependencyDirectory.exists()) {
JarUtils.decompress(dependencyDirectory.getAbsolutePath());
}
}

private void copyDependencies(Dependency dependency, File destFolder) {
copyDependencies(dependency.getFile(), destFolder);
}

@SneakyThrows
private void copyDependencies(File jarFile, File destFolder) {
FileUtils.copyFileToDirectory(jarFile, destFolder);
}

private void createResultJson(ProjectDependencyAnalysis analysis) {
Expand Down Expand Up @@ -144,21 +179,6 @@ private void createResultJson(ProjectDependencyAnalysis analysis) {
}
}

@Nullable
private ProjectDependencyAnalysis getAnalysis() {
/* Analyze dependencies usage status */
final ProjectContext projectContext = buildProjectContext();
final ProjectDependencyAnalysis analysis;
final DefaultProjectDependencyAnalyzer dependencyAnalyzer = new DefaultProjectDependencyAnalyzer(projectContext);
try {
analysis = dependencyAnalyzer.analyze();
} catch (ProjectDependencyAnalyzerException e) {
getLog().error("Unable to analyze dependencies.");
return null;
}
return analysis;
}

private ProjectContext buildProjectContext() {
if (ignoreTests) {
ignoreScopes.add("test");
Expand All @@ -182,16 +202,15 @@ private ProjectContext buildProjectContext() {
allUsedClasses.addAll(usedClassesFromProcessors);
allUsedClasses.addAll(usedClassesFromSource);

final DependencyGraph dependencyGraph = dependencyManager.dependencyGraph();
return new ProjectContext(
dependencyGraph,
dependencyManager.getOutputDirectory(),
dependencyManager.getTestOutputDirectory(),
dependencyManager.dependencyGraph(),
dependencyManager.getOutputDirectories(),
dependencyManager.getTestOutputDirectories(),
dependencyManager.getSourceDirectory(),
dependencyManager.getTestDirectory(),
dependencyManager.getDependenciesDirectory(),
ignoreScopes.stream().map(Scope::new).collect(Collectors.toSet()),
toDependency(dependencyGraph.allDependencies(), ignoreDependencies),
toDependency(dependencyManager.dependencyGraph().allDependencies(), ignoreDependencies),
allUsedClasses
);
}
Expand Down Expand Up @@ -227,7 +246,7 @@ private void printString(final String string) {
System.out.println(string); //NOSONAR avoid a warning of non-used logger
}

private Log getLog() {
private LogWrapper getLog() {
return dependencyManager.getLog();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import se.kth.depclean.core.analysis.asm.ASMDependencyAnalyzer;
import se.kth.depclean.core.analysis.graph.DefaultCallGraph;
Expand All @@ -36,61 +37,52 @@
public class DefaultProjectDependencyAnalyzer {

private final DependencyAnalyzer dependencyAnalyzer = new ASMDependencyAnalyzer();
private final ProjectContext projectContext;

/**
* Ctor.
*/
public DefaultProjectDependencyAnalyzer(ProjectContext projectContext) {
this.projectContext = projectContext;
}

/**
* Analyze the dependencies in a project.
*
* @param projectContext The project's context
*
* @return An object representing the analysis result.
* @throws ProjectDependencyAnalyzerException if the analysis fails.
*/
public ProjectDependencyAnalysis analyze() throws ProjectDependencyAnalyzerException {
try {
// a map of [dependency] -> [classes]
final ActualUsedClasses actualUsedClasses = new ActualUsedClasses(projectContext);

/* ******************** bytecode analysis ********************* */

// analyze project's class files
actualUsedClasses.registerClasses(getProjectDependencyClasses(projectContext.getOutputFolder()));
// analyze project's tests class files
if (!projectContext.ignoreTests()) {
log.trace("Parsing test folder");
actualUsedClasses.registerClasses(getProjectTestDependencyClasses(projectContext.getTestOutputFolder()));
}
// the set of compiled classes and tests in the project
Set<String> projectClasses = new HashSet<>(DefaultCallGraph.getProjectVertices());
// analyze dependencies' class files
actualUsedClasses.registerClasses(getProjectDependencyClasses(projectContext.getDependenciesFolder()));
// analyze extra classes (collected through static analysis of source code)
actualUsedClasses.registerClasses(projectContext.getExtraClasses());

/* ******************** usage analysis ********************* */
actualUsedClasses.registerClasses(getReferencedClassMembers(projectClasses));
public ProjectDependencyAnalysis analyze(final ProjectContext projectContext) {

/* ******************** results as statically used at the bytecode *********************** */
return new ProjectDependencyAnalysisBuilder(projectContext, actualUsedClasses).analyse();
// a map of [dependency] -> [classes]
final ActualUsedClasses actualUsedClasses = new ActualUsedClasses(projectContext);

/* ******************** bytecode analysis ********************* */

} catch (IOException exception) {
throw new ProjectDependencyAnalyzerException("Cannot analyze dependencies", exception);
// analyze project's class files
projectContext.getOutputFolders()
.forEach(folder -> actualUsedClasses.registerClasses(getProjectDependencyClasses(folder)));
// analyze project's tests class files
if (!projectContext.ignoreTests()) {
log.trace("Parsing test folder");
projectContext.getTestOutputFolders()
.forEach(folder -> actualUsedClasses.registerClasses(getProjectTestDependencyClasses(folder)));
}
// the set of compiled classes and tests in the project
Set<String> projectClasses = new HashSet<>(DefaultCallGraph.getProjectVertices());
// analyze dependencies' class files
actualUsedClasses.registerClasses(getProjectDependencyClasses(projectContext.getDependenciesFolder()));
// analyze extra classes (collected through static analysis of source code)
actualUsedClasses.registerClasses(projectContext.getExtraClasses());

/* ******************** usage analysis ********************* */
actualUsedClasses.registerClasses(getReferencedClassMembers(projectClasses));

/* ******************** results as statically used at the bytecode *********************** */
return new ProjectDependencyAnalysisBuilder(projectContext, actualUsedClasses).analyse();
}

private Iterable<ClassName> getProjectDependencyClasses(Path outputFolder) throws IOException {
@SneakyThrows
private Iterable<ClassName> getProjectDependencyClasses(Path outputFolder) {
// Analyze src classes in the project
log.trace("# getProjectDependencyClasses()");
return collectDependencyClasses(outputFolder);
}

private Iterable<ClassName> getProjectTestDependencyClasses(Path testOutputFolder) throws IOException {
@SneakyThrows
private Iterable<ClassName> getProjectTestDependencyClasses(Path testOutputFolder) {
// Analyze test classes in the project
log.trace("# getProjectTestDependencyClasses()");
return collectDependencyClasses(testOutputFolder);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,9 @@ public static Set<String> getProjectVertices() {
return projectVertices;
}

public static Set<String> getVertices() {
return directedGraph.vertexSet();
}

public static void cleanDirectedGraph() {
directedGraph.vertexSet().clear();
directedGraph.edgeSet().clear();
public static void clear() {
projectVertices.clear();
usagesPerClass.clear();
}

public static Map<String, Set<String>> getUsagesPerClass() {
Expand Down
Loading

0 comments on commit 1c96de9

Please sign in to comment.