Skip to content

Commit

Permalink
Improve checkUnusedDependencies message (#948)
Browse files Browse the repository at this point in the history
  • Loading branch information
pkoenig10 authored and bulldozer-bot[bot] committed Oct 24, 2019
1 parent 064a2cf commit 60f04d6
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.ResolvedDependency;
import org.gradle.api.artifacts.component.ComponentIdentifier;
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
Expand Down Expand Up @@ -99,11 +101,45 @@ public static Stream<String> referencedClasses(File classFile) {
}

public static String asString(ResolvedArtifact artifact) {
return asString(artifact.getModuleVersion().getId());
ModuleVersionIdentifier moduleVersionId = artifact.getModuleVersion().getId();
StringBuilder builder = new StringBuilder()
.append(moduleVersionId.getGroup())
.append(":")
.append(moduleVersionId.getName());
if (artifact.getClassifier() != null) {
builder
.append("::")
.append(artifact.getClassifier());
}
return builder.toString();
}

public static String asDependencyStringWithName(ResolvedArtifact artifact) {
return asDependencyString(artifact, true);
}

public static String asString(ModuleVersionIdentifier id) {
return id.getGroup() + ":" + id.getName();
public static String asDependencyStringWithoutName(ResolvedArtifact artifact) {
return asDependencyString(artifact, false);
}

private static String asDependencyString(ResolvedArtifact artifact, boolean withName) {
ComponentIdentifier componentId = artifact.getId().getComponentIdentifier();
if (componentId instanceof ProjectComponentIdentifier) {
ProjectComponentIdentifier projectComponentId = (ProjectComponentIdentifier) componentId;
StringBuilder builder = new StringBuilder()
.append("project('")
.append(projectComponentId.getProjectPath())
.append("')");
if (withName) {
builder
.append(" (")
.append(artifact.getId().getDisplayName())
.append(")");
}
return builder.toString();
}

return asString(artifact);
}

@ThreadSafe
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ModuleVersionIdentifier;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.ResolvedDependency;
import org.gradle.api.file.FileCollection;
Expand Down Expand Up @@ -95,11 +94,15 @@ public final void checkUnusedDependencies() {
.collect(Collectors.toList());
if (!declaredButUnused.isEmpty()) {
// TODO(dfox): don't print warnings for jars that define service loaded classes (e.g. meta-inf)
StringBuilder sb = new StringBuilder();
sb.append(String.format("Found %s dependencies unused during compilation, please delete them from '%s' or "
+ "choose one of the suggested fixes:\n", declaredButUnused.size(), buildFile()));
StringBuilder builder = new StringBuilder();
builder.append(String.format(
"Found %s dependencies unused during compilation, please delete them from '%s' or choose one of "
+ "the suggested fixes:\n", declaredButUnused.size(), buildFile()));
for (ResolvedArtifact resolvedArtifact : declaredButUnused) {
sb.append('\t').append(BaselineExactDependencies.asString(resolvedArtifact)).append('\n');
builder
.append('\t')
.append(BaselineExactDependencies.asDependencyStringWithName(resolvedArtifact))
.append('\n');

// Suggest fixes by looking at all transitive classes, filtering the ones we have declarations on,
// and mapping the remaining ones back to the jars they came from.
Expand All @@ -117,16 +120,17 @@ public final void checkUnusedDependencies() {
.collect(Collectors.toSet());

if (!didYouMean.isEmpty()) {
sb.append("\t\tDid you mean:\n");
builder.append("\t\tDid you mean:\n");
didYouMean.stream()
.map(BaselineExactDependencies::asString)
.map(BaselineExactDependencies::asDependencyStringWithoutName)
.sorted()
.forEach(transitive -> sb.append("\t\t\timplementation '")
.append(transitive)
.append("\' \n"));
.forEach(dependencyString -> builder
.append("\t\t\timplementation ")
.append(dependencyString)
.append("\n"));
}
}
throw new GradleException(sb.toString());
throw new GradleException(builder.toString());
}
}

Expand All @@ -138,16 +142,14 @@ public final void checkUnusedDependencies() {
*/
private void excludeSourceOnlyDependencies() {
sourceOnlyConfigurations.get().forEach(config ->
config.getResolvedConfiguration().getFirstLevelModuleDependencies().forEach(dependency -> {
ignoreDependency(config, dependency.getModule().getId());
dependency.getModuleArtifacts().forEach(artifact ->
ignoreDependency(config, artifact.getModuleVersion().getId()));
}));
config.getResolvedConfiguration().getFirstLevelModuleDependencies().stream()
.flatMap(dependency -> dependency.getModuleArtifacts().stream())
.forEach(artifact -> ignoreDependency(config, artifact)));
}

private void ignoreDependency(Configuration config, ModuleVersionIdentifier id) {
String dependencyId = BaselineExactDependencies.asString(id);
getLogger().info("Ignoring {} dependency: '{}'", config.getName(), dependencyId);
private void ignoreDependency(Configuration config, ResolvedArtifact artifact) {
String dependencyId = BaselineExactDependencies.asString(artifact);
getLogger().info("Ignoring {} dependency: {}", config.getName(), dependencyId);
ignore.add(dependencyId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ class BaselineExactDependenciesTest extends AbstractPluginTest {
result.task(':sub-project-no-deps:checkImplicitDependencies').getOutcome() == TaskOutcome.SUCCESS
}

def 'checkUnusedDependencies fails when a redundant project dep is present'() {
when:
setupMultiProject()

then:
BuildResult result = with(':checkUnusedDependencies', '--stacktrace').withDebug(true).buildAndFail()
result.output.contains "project(':sub-project-with-deps') (sub-project-with-deps.jar (project :sub-project-with-deps))"
result.output.contains "implementation project(':sub-project-no-deps')"
}

/**
* Sets up a multi-module project with 2 sub projects. The root project has a transitive dependency on sub-project-no-deps
* and so checkImplicitDependencies should fail on it.
Expand Down

0 comments on commit 60f04d6

Please sign in to comment.