Skip to content

Instantly share code, notes, and snippets.

@ryanqy
Created January 29, 2018 13:13
Show Gist options
  • Save ryanqy/d8fc845be327eefcc3be1ebd3a8dbcea to your computer and use it in GitHub Desktop.
Save ryanqy/d8fc845be327eefcc3be1ebd3a8dbcea to your computer and use it in GitHub Desktop.
analyze gradle project dependencies
import org.apache.maven.shared.dependency.analyzer.ClassAnalyzer
import org.apache.maven.shared.dependency.analyzer.DefaultClassAnalyzer
import org.apache.maven.shared.dependency.analyzer.DependencyAnalyzer
import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalysis
import org.apache.maven.shared.dependency.analyzer.asm.ASMDependencyAnalyzer
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.apache.maven.shared:maven-dependency-analyzer:1.7'
}
}
task analyze {
doLast {
//单工程项目
if (childProjects.size() == 0) {
printAnalyze(project)
} else {
for (Project childrenProject : childProjects.values()) {
printAnalyze(childrenProject)
}
}
}
}
void printAnalyze(Project project) {
ProjectDependencyAnalysis analysis = new ProjectDependencyResolver().analyzeDependencies(project)
StringBuffer buffer = new StringBuffer()
buffer.append("project:" + project.name + "\n")
['usedDeclaredArtifacts', 'usedUndeclaredArtifacts', 'unusedDeclaredArtifacts'].each { section ->
def violations = analysis."$section"
buffer.append("$section: \n")
violations*.moduleVersion*.id.each {
buffer.append(" - $it.group:$it.name:$it.version\n")
}
}
logger.warn(buffer.toString())
}
class ProjectDependencyResolver {
private ClassAnalyzer classAnalyzer = new DefaultClassAnalyzer();
private DependencyAnalyzer dependencyAnalyzer = new ASMDependencyAnalyzer();
/**
*
* @param project
* @return
*/
ProjectDependencyAnalysis analyzeDependencies(Project project) {
if (!project.plugins.hasPlugin('java')) {
throw new IllegalStateException("Project does not have the java plugin applied.")
}
Set<ResolvedDependency> firstLevelDeps = getFirstLevelDependencies(project, 'compile')
Set<File> dependencyArtifacts = findModuleArtifactFiles(firstLevelDeps)
Map<File, Set<String>> fileClassMap = buildArtifactClassMap(dependencyArtifacts)
project.logger.info "fileClassMap = $fileClassMap"
Set<String> dependencyClasses = analyzeClassDependencies(project)
project.logger.info "dependencyClasses = $dependencyClasses"
Set<File> usedArtifacts = buildUsedArtifacts(fileClassMap, dependencyClasses)
project.logger.info "usedArtifacts = $usedArtifacts"
Set<File> usedDeclaredArtifacts = new LinkedHashSet<File>(dependencyArtifacts)
usedDeclaredArtifacts.retainAll(usedArtifacts)
project.logger.info "usedDeclaredArtifacts = $usedDeclaredArtifacts"
Set<File> usedUndeclaredArtifacts = new LinkedHashSet<File>(usedArtifacts)
usedUndeclaredArtifacts.removeAll(dependencyArtifacts)
project.logger.info "usedUndeclaredArtifacts = $usedUndeclaredArtifacts"
Set<File> unusedDeclaredArtifacts = new LinkedHashSet<File>(dependencyArtifacts)
unusedDeclaredArtifacts.removeAll(usedArtifacts)
project.logger.info "unusedDeclaredArtifacts = $unusedDeclaredArtifacts"
//Now work back from the files to the artifact information
ConfigurationContainer configurations = project.configurations
def nonTestConfigurations = configurations.findAll { !it.name.contains('test') }
List<ResolvedArtifact> artifacts = nonTestConfigurations*.resolvedConfiguration*.resolvedArtifacts.unique {
it
}.flatten()
return new ProjectDependencyAnalysis(
artifacts.findAll { ResolvedArtifact artifact -> artifact.file in usedDeclaredArtifacts }.unique {
it.file
} as Set<org.apache.maven.artifact.Artifact>,
artifacts.findAll { ResolvedArtifact artifact -> artifact.file in usedUndeclaredArtifacts }.unique {
it.file
} as Set<org.apache.maven.artifact.Artifact>,
artifacts.findAll { ResolvedArtifact artifact -> artifact.file in unusedDeclaredArtifacts }.unique {
it.file
} as Set<org.apache.maven.artifact.Artifact>)
}
private Set<ResolvedDependency> getFirstLevelDependencies(Project project, String configurationName) {
project.configurations."$configurationName".resolvedConfiguration.firstLevelModuleDependencies
}
private Map<File, Set<String>> buildArtifactClassMap(Set<File> dependencyArtifacts) throws IOException {
Map<File, Set<String>> artifactClassMap = [:]
dependencyArtifacts.each { File file ->
if (file.name.endsWith('jar')) {
artifactClassMap.put(file, classAnalyzer.analyze(file.toURL()))
} else {
project.logger.info "Skipping analysis of file for classes: $file"
}
}
return artifactClassMap
}
private Set<File> findModuleArtifactFiles(Set<ResolvedDependency> dependencies) {
dependencies*.moduleArtifacts*.collect { it.file }.unique().flatten()
}
private Collection analyzeClassDependencies(Project project) {
return project.sourceSets*.output.classesDir?.collect { File file ->
dependencyAnalyzer.analyze(file.toURI().toURL())
}?.flatten()?.unique()
}
private Set<File> buildUsedArtifacts(Map<File, Set<String>> artifactClassMap, Set<String> dependencyClasses) {
Set<File> usedArtifacts = new HashSet()
dependencyClasses.each { String className ->
File artifact = artifactClassMap.find { it.value.contains(className) }?.key
if (artifact) {
usedArtifacts << artifact
}
}
return usedArtifacts
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment