From 7f56d14587feeb6fe3c66fc97511b9da143ce753 Mon Sep 17 00:00:00 2001 From: forozco Date: Wed, 26 Feb 2020 16:02:45 -0500 Subject: [PATCH 01/13] checkUnusedDependencies ignores all sourceSets compileOnly configuration --- .../plugins/BaselineExactDependencies.java | 58 ++++++++++++------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java index 6108ccd63..5cb511ac0 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java @@ -45,6 +45,7 @@ import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskProvider; /** Validates that java projects declare exactly the dependencies they rely on, no more and no less. */ public final class BaselineExactDependencies implements Plugin { @@ -66,30 +67,43 @@ public void apply(Project project) { .getByName(SourceSet.MAIN_SOURCE_SET_NAME); Configuration compileClasspath = project.getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME); - Configuration compileOnly = - project.getConfigurations().getByName(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME); - Configuration justCompileOnlyResolvable = project.getConfigurations() - .create("baseline-exact-dependencies-compileOnly", conf -> { - conf.setVisible(false); - conf.setCanBeConsumed(false); - conf.extendsFrom(compileOnly); - // Important! this ensures we resolve 'compile' variants rather than 'runtime' - // This is the same attribute that's being set on compileClasspath - conf.getAttributes() - .attribute( - Usage.USAGE_ATTRIBUTE, - project.getObjects().named(Usage.class, Usage.JAVA_API)); - }); - project.getTasks().create("checkUnusedDependencies", CheckUnusedDependenciesTask.class, task -> { - task.dependsOn(JavaPlugin.CLASSES_TASK_NAME); - task.setSourceClasses(mainSourceSet.getOutput().getClassesDirs()); - task.dependenciesConfiguration(compileClasspath); - task.sourceOnlyConfiguration(justCompileOnlyResolvable); + TaskProvider checkUnusedDependencies = project.getTasks() + .register("checkUnusedDependencies", CheckUnusedDependenciesTask.class, task -> { + task.dependsOn(JavaPlugin.CLASSES_TASK_NAME); + task.setSourceClasses(mainSourceSet.getOutput().getClassesDirs()); + task.dependenciesConfiguration(compileClasspath); - // this is liberally applied to ease the Java8 -> 11 transition - task.ignore("javax.annotation", "javax.annotation-api"); - }); + // this is liberally applied to ease the Java8 -> 11 transition + task.ignore("javax.annotation", "javax.annotation-api"); + }); + + project.getConvention() + .getPlugin(JavaPluginConvention.class) + .getSourceSets() + .forEach(sourceSet -> { + Configuration compileOnly = + project.getConfigurations().getByName(sourceSet.getCompileOnlyConfigurationName()); + checkUnusedDependencies.configure(task -> { + task.sourceOnlyConfiguration(project.getConfigurations() + .create( + "baseline-exact-dependencies-" + + sourceSet.getCompileOnlyConfigurationName(), + conf -> { + conf.setVisible(false); + conf.setCanBeConsumed(false); + conf.extendsFrom(compileOnly); + // Important! this ensures we resolve 'compile' variants rather than + // 'runtime' + // This is the same attribute that's being set on compileClasspath + conf.getAttributes() + .attribute( + Usage.USAGE_ATTRIBUTE, + project.getObjects() + .named(Usage.class, Usage.JAVA_API)); + })); + }); + }); project.getTasks().create("checkImplicitDependencies", CheckImplicitDependenciesTask.class, task -> { task.dependsOn(JavaPlugin.CLASSES_TASK_NAME); From 000b180b6a722f3d8140f5e94b10d1fa1f3cfd81 Mon Sep 17 00:00:00 2001 From: forozco Date: Wed, 26 Feb 2020 16:29:43 -0500 Subject: [PATCH 02/13] on all source sets --- .../plugins/BaselineExactDependencies.java | 101 ++++++++++-------- .../BaselineExactDependenciesTest.groovy | 7 +- 2 files changed, 58 insertions(+), 50 deletions(-) diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java index 5cb511ac0..591521c9d 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java @@ -35,6 +35,7 @@ import org.apache.maven.shared.dependency.analyzer.asm.ASMDependencyAnalyzer; import org.gradle.api.Plugin; import org.gradle.api.Project; +import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ModuleVersionIdentifier; import org.gradle.api.artifacts.ResolvedArtifact; @@ -46,6 +47,7 @@ import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskProvider; +import org.gradle.util.GUtil; /** Validates that java projects declare exactly the dependencies they rely on, no more and no less. */ public final class BaselineExactDependencies implements Plugin { @@ -61,60 +63,65 @@ public final class BaselineExactDependencies implements Plugin { @Override public void apply(Project project) { project.getPluginManager().withPlugin("java", plugin -> { - SourceSet mainSourceSet = project.getConvention() - .getPlugin(JavaPluginConvention.class) - .getSourceSets() - .getByName(SourceSet.MAIN_SOURCE_SET_NAME); - Configuration compileClasspath = - project.getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME); - - TaskProvider checkUnusedDependencies = project.getTasks() - .register("checkUnusedDependencies", CheckUnusedDependenciesTask.class, task -> { - task.dependsOn(JavaPlugin.CLASSES_TASK_NAME); - task.setSourceClasses(mainSourceSet.getOutput().getClassesDirs()); - task.dependenciesConfiguration(compileClasspath); - - // this is liberally applied to ease the Java8 -> 11 transition - task.ignore("javax.annotation", "javax.annotation-api"); - }); + TaskProvider checkUnusedDependencies = project.getTasks().register("checkUnusedDependencies"); + TaskProvider checkImplicitDependencies = project.getTasks().register("checkImplicitDependencies"); project.getConvention() .getPlugin(JavaPluginConvention.class) .getSourceSets() - .forEach(sourceSet -> { - Configuration compileOnly = - project.getConfigurations().getByName(sourceSet.getCompileOnlyConfigurationName()); - checkUnusedDependencies.configure(task -> { - task.sourceOnlyConfiguration(project.getConfigurations() - .create( - "baseline-exact-dependencies-" - + sourceSet.getCompileOnlyConfigurationName(), - conf -> { - conf.setVisible(false); - conf.setCanBeConsumed(false); - conf.extendsFrom(compileOnly); - // Important! this ensures we resolve 'compile' variants rather than - // 'runtime' - // This is the same attribute that's being set on compileClasspath - conf.getAttributes() - .attribute( - Usage.USAGE_ATTRIBUTE, - project.getObjects() - .named(Usage.class, Usage.JAVA_API)); - })); - }); - }); - - project.getTasks().create("checkImplicitDependencies", CheckImplicitDependenciesTask.class, task -> { - task.dependsOn(JavaPlugin.CLASSES_TASK_NAME); - task.setSourceClasses(mainSourceSet.getOutput().getClassesDirs()); - task.dependenciesConfiguration(compileClasspath); - - task.ignore("org.slf4j", "slf4j-api"); - }); + .forEach(sourceSet -> + configureSourceSet(project, sourceSet, checkUnusedDependencies, checkImplicitDependencies)); }); } + private static void configureSourceSet( + Project project, + SourceSet sourceSet, + TaskProvider checkUnusedDependencies, + TaskProvider checkImplicitDependencies) { + Configuration compileClasspath = + project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName()); + Configuration compileOnly = project.getConfigurations().getByName(sourceSet.getCompileOnlyConfigurationName()); + Configuration resolvableCompileOnly = project.getConfigurations() + .create("baseline-exact-dependencies-" + sourceSet.getCompileOnlyConfigurationName(), conf -> { + conf.setVisible(false); + conf.setCanBeConsumed(false); + conf.extendsFrom(compileOnly); + // Important! this ensures we resolve 'compile' variants rather than + // 'runtime' + // This is the same attribute that's being set on compileClasspath + conf.getAttributes() + .attribute( + Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_API)); + }); + TaskProvider sourceSetUnusedDependencies = project.getTasks() + .register( + GUtil.toLowerCamelCase("checkUnusedDependencies " + sourceSet.getName()), + CheckUnusedDependenciesTask.class, + task -> { + task.dependsOn(JavaPlugin.CLASSES_TASK_NAME); + task.setSourceClasses(sourceSet.getOutput().getClassesDirs()); + task.dependenciesConfiguration(compileClasspath); + task.sourceOnlyConfiguration(resolvableCompileOnly); + + // this is liberally applied to ease the Java8 -> 11 transition + task.ignore("javax.annotation", "javax.annotation-api"); + }); + checkUnusedDependencies.configure(task -> task.dependsOn(sourceSetUnusedDependencies)); + TaskProvider sourceSetCheckImplicitDependencies = project.getTasks() + .register( + GUtil.toLowerCamelCase("checkImplicitDependencies " + sourceSet.getName()), + CheckImplicitDependenciesTask.class, + task -> { + task.dependsOn(JavaPlugin.CLASSES_TASK_NAME); + task.setSourceClasses(sourceSet.getOutput().getClassesDirs()); + task.dependenciesConfiguration(compileClasspath); + + task.ignore("org.slf4j", "slf4j-api"); + }); + checkImplicitDependencies.configure(task -> task.dependsOn(sourceSetCheckImplicitDependencies)); + } + /** Given a {@code com/palantir/product/Foo.class} file, what other classes does it import/reference. */ public static Stream referencedClasses(File classFile) { try { diff --git a/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineExactDependenciesTest.groovy b/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineExactDependenciesTest.groovy index 962497ba9..36ebfc772 100644 --- a/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineExactDependenciesTest.groovy +++ b/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineExactDependenciesTest.groovy @@ -87,7 +87,7 @@ class BaselineExactDependenciesTest extends AbstractPluginTest { then: BuildResult result = with('checkUnusedDependencies', '--stacktrace').buildAndFail() result.task(':classes').getOutcome() == TaskOutcome.SUCCESS - result.task(':checkUnusedDependencies').getOutcome() == TaskOutcome.FAILED + result.task(':checkUnusedDependenciesMain').getOutcome() == TaskOutcome.FAILED result.output.contains("Found 1 dependencies unused during compilation") } @@ -109,6 +109,7 @@ class BaselineExactDependenciesTest extends AbstractPluginTest { BuildResult result = with('checkUnusedDependencies', '--stacktrace').build() result.task(':classes').getOutcome() == TaskOutcome.SUCCESS result.task(':checkUnusedDependencies').getOutcome() == TaskOutcome.SUCCESS + result.task(':checkUnusedDependenciesMain').getOutcome() == TaskOutcome.SUCCESS } def 'checkImplicitDependencies fails when a class is imported without being declared as a dependency'() { @@ -134,7 +135,7 @@ class BaselineExactDependenciesTest extends AbstractPluginTest { then: BuildResult result = with('checkImplicitDependencies', '--stacktrace').buildAndFail() result.task(':classes').getOutcome() == TaskOutcome.SUCCESS - result.task(':checkImplicitDependencies').getOutcome() == TaskOutcome.FAILED + result.task(':checkImplicitDependenciesMain').getOutcome() == TaskOutcome.FAILED result.output.contains("Found 1 implicit dependencies") } @@ -156,7 +157,7 @@ class BaselineExactDependenciesTest extends AbstractPluginTest { then: BuildResult result = with('checkImplicitDependencies', '--stacktrace').withDebug(true).buildAndFail() result.task(':classes').getOutcome() == TaskOutcome.SUCCESS - result.task(':checkImplicitDependencies').getOutcome() == TaskOutcome.FAILED + result.task(':checkImplicitDependenciesMain').getOutcome() == TaskOutcome.FAILED result.output.contains("Found 1 implicit dependencies") result.output.contains("implementation project(':sub-project-no-deps')") } From d599b2dcdb8bced9eb982011cd6549850223fc30 Mon Sep 17 00:00:00 2001 From: forozco Date: Wed, 26 Feb 2020 21:29:43 +0000 Subject: [PATCH 03/13] Add generated changelog entries --- changelog/@unreleased/pr-1262.v2.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/@unreleased/pr-1262.v2.yml diff --git a/changelog/@unreleased/pr-1262.v2.yml b/changelog/@unreleased/pr-1262.v2.yml new file mode 100644 index 000000000..b9d0dcd18 --- /dev/null +++ b/changelog/@unreleased/pr-1262.v2.yml @@ -0,0 +1,5 @@ +type: improvement +improvement: + description: BaselineExactDependencies applies to all source sets + links: + - https://github.com/palantir/gradle-baseline/pull/1262 From df86f65647974064850ef5e5c0e1df643a546574 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Thu, 27 Feb 2020 12:18:21 +0000 Subject: [PATCH 04/13] source set classes --- .../palantir/baseline/plugins/BaselineExactDependencies.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java index 591521c9d..e3abb6502 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java @@ -43,7 +43,6 @@ import org.gradle.api.artifacts.component.ComponentIdentifier; import org.gradle.api.artifacts.component.ProjectComponentIdentifier; import org.gradle.api.attributes.Usage; -import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskProvider; @@ -99,7 +98,7 @@ private static void configureSourceSet( GUtil.toLowerCamelCase("checkUnusedDependencies " + sourceSet.getName()), CheckUnusedDependenciesTask.class, task -> { - task.dependsOn(JavaPlugin.CLASSES_TASK_NAME); + task.dependsOn(sourceSet.getClassesTaskName()); task.setSourceClasses(sourceSet.getOutput().getClassesDirs()); task.dependenciesConfiguration(compileClasspath); task.sourceOnlyConfiguration(resolvableCompileOnly); @@ -113,7 +112,7 @@ private static void configureSourceSet( GUtil.toLowerCamelCase("checkImplicitDependencies " + sourceSet.getName()), CheckImplicitDependenciesTask.class, task -> { - task.dependsOn(JavaPlugin.CLASSES_TASK_NAME); + task.dependsOn(sourceSet.getClassesTaskName()); task.setSourceClasses(sourceSet.getOutput().getClassesDirs()); task.dependenciesConfiguration(compileClasspath); From bc8e9e2cca856833a02d3d8775c9641378a8953f Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Thu, 27 Feb 2020 12:21:48 +0000 Subject: [PATCH 05/13] comment --- .../palantir/baseline/plugins/BaselineExactDependencies.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java index e3abb6502..0eca45df1 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java @@ -86,8 +86,7 @@ private static void configureSourceSet( conf.setVisible(false); conf.setCanBeConsumed(false); conf.extendsFrom(compileOnly); - // Important! this ensures we resolve 'compile' variants rather than - // 'runtime' + // Important! this ensures we resolve 'compile' variants rather than 'runtime' // This is the same attribute that's being set on compileClasspath conf.getAttributes() .attribute( From 3f16f85ada959a12cc2d14508605e6d4c70df705 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Thu, 27 Feb 2020 12:27:15 +0000 Subject: [PATCH 06/13] fail if trying to add unresolvable sourceOnlyConfiguration --- .../baseline/tasks/CheckUnusedDependenciesTask.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckUnusedDependenciesTask.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckUnusedDependenciesTask.java index 77e11d722..adbba4f8c 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckUnusedDependenciesTask.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckUnusedDependenciesTask.java @@ -16,6 +16,7 @@ package com.palantir.baseline.tasks; +import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import com.google.common.collect.Streams; import com.palantir.baseline.plugins.BaselineExactDependencies; @@ -190,6 +191,11 @@ public final Provider> getSourceOnlyConfigurations() { } public final void sourceOnlyConfiguration(Configuration configuration) { + Preconditions.checkNotNull(configuration); + Preconditions.checkArgument( + configuration.isCanBeResolved(), + "May only add sourceOnlyConfiguration if it is resolvable: %s", + configuration); this.sourceOnlyConfigurations.add(Objects.requireNonNull(configuration)); } From 82c3df450a6789f31a96ab59758f732d560461d8 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Thu, 27 Feb 2020 12:30:54 +0000 Subject: [PATCH 07/13] sigh --- .../palantir/baseline/tasks/CheckUnusedDependenciesTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckUnusedDependenciesTask.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckUnusedDependenciesTask.java index adbba4f8c..3deb81936 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckUnusedDependenciesTask.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckUnusedDependenciesTask.java @@ -191,7 +191,7 @@ public final Provider> getSourceOnlyConfigurations() { } public final void sourceOnlyConfiguration(Configuration configuration) { - Preconditions.checkNotNull(configuration); + Preconditions.checkNotNull(configuration, "This method requires a non-null configuration"); Preconditions.checkArgument( configuration.isCanBeResolved(), "May only add sourceOnlyConfiguration if it is resolvable: %s", From 4f1fae622f019512e9baadcc851b8f230ce3d6fb Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Thu, 27 Feb 2020 16:38:22 +0000 Subject: [PATCH 08/13] abstract out makeInternalCompileConfiguration --- .../plugins/BaselineExactDependencies.java | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java index 0eca45df1..940666574 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java @@ -81,17 +81,7 @@ private static void configureSourceSet( Configuration compileClasspath = project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName()); Configuration compileOnly = project.getConfigurations().getByName(sourceSet.getCompileOnlyConfigurationName()); - Configuration resolvableCompileOnly = project.getConfigurations() - .create("baseline-exact-dependencies-" + sourceSet.getCompileOnlyConfigurationName(), conf -> { - conf.setVisible(false); - conf.setCanBeConsumed(false); - conf.extendsFrom(compileOnly); - // Important! this ensures we resolve 'compile' variants rather than 'runtime' - // This is the same attribute that's being set on compileClasspath - conf.getAttributes() - .attribute( - Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_API)); - }); + Configuration resolvableCompileOnly = makeInternalCompileConfiguration(project, compileOnly); TaskProvider sourceSetUnusedDependencies = project.getTasks() .register( GUtil.toLowerCamelCase("checkUnusedDependencies " + sourceSet.getName()), @@ -99,6 +89,7 @@ private static void configureSourceSet( task -> { task.dependsOn(sourceSet.getClassesTaskName()); task.setSourceClasses(sourceSet.getOutput().getClassesDirs()); + // Note: want to exclude 'inherited' deps from other source sets task.dependenciesConfiguration(compileClasspath); task.sourceOnlyConfiguration(resolvableCompileOnly); @@ -120,6 +111,18 @@ private static void configureSourceSet( checkImplicitDependencies.configure(task -> task.dependsOn(sourceSetCheckImplicitDependencies)); } + private static Configuration makeInternalCompileConfiguration(Project project, Configuration compileOnly) { + return project.getConfigurations().create("baseline-exact-dependencies-" + compileOnly.getName(), conf -> { + conf.setVisible(false); + conf.setCanBeConsumed(false); + conf.extendsFrom(compileOnly); + // Important! this ensures we resolve 'compile' variants rather than 'runtime' + // This is the same attribute that's being set on compileClasspath + conf.getAttributes() + .attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_API)); + }); + } + /** Given a {@code com/palantir/product/Foo.class} file, what other classes does it import/reference. */ public static Stream referencedClasses(File classFile) { try { From d8473df44de9869f779e4cafbdf31f40d2296b80 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Fri, 28 Feb 2020 11:54:37 +0000 Subject: [PATCH 09/13] only explicit direct dependencies --- .../plugins/BaselineExactDependencies.java | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java index 940666574..dbf51cf68 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java @@ -17,7 +17,10 @@ package com.palantir.baseline.plugins; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.common.collect.Sets.SetView; import com.palantir.baseline.tasks.CheckImplicitDependenciesTask; import com.palantir.baseline.tasks.CheckUnusedDependenciesTask; import java.io.File; @@ -68,7 +71,7 @@ public void apply(Project project) { project.getConvention() .getPlugin(JavaPluginConvention.class) .getSourceSets() - .forEach(sourceSet -> + .all(sourceSet -> configureSourceSet(project, sourceSet, checkUnusedDependencies, checkImplicitDependencies)); }); } @@ -78,10 +81,47 @@ private static void configureSourceSet( SourceSet sourceSet, TaskProvider checkUnusedDependencies, TaskProvider checkImplicitDependencies) { + Configuration implementation = + project.getConfigurations().getByName(sourceSet.getImplementationConfigurationName()); + Configuration compile = project.getConfigurations().getByName(sourceSet.getCompileConfigurationName()); Configuration compileClasspath = project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName()); - Configuration compileOnly = project.getConfigurations().getByName(sourceSet.getCompileOnlyConfigurationName()); - Configuration resolvableCompileOnly = makeInternalCompileConfiguration(project, compileOnly); + + Configuration explicitCompile = project.getConfigurations() + .create("baseline-exact-dependencies-" + sourceSet.getName(), conf -> { + conf.setDescription(String.format( + "Tracks the explicit (not inherited) dependencies added to either %s or %s", + compile.toString(), implementation.toString())); + conf.setVisible(false); + conf.setCanBeConsumed(false); + // Important! this ensures we resolve 'compile' variants rather than 'runtime' + // This is the same attribute that's being set on compileClasspath + conf.getAttributes() + .attribute( + Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_API)); + }); + + // Figure out what our compile dependencies are while ignoring dependencies we've inherited from other source + // sets. For example, if we are `test`, some of our configurations extend from the `main` source set: + // testImplementation extendsFrom(implementation) + // \-- testCompile extendsFrom(compile) + // We therefore want to look at only the dependencies _directly_ declared in the implementation and compile + // configurations (belonging to our source set) + project.afterEvaluate(p -> { + Configuration implCopy = implementation.copy(); + Configuration compileCopy = compile.copy(); + explicitCompile.extendsFrom(implCopy, compileCopy); + // Mirror the transitive constraints form compileClasspath in order to pick up GCV locks. + // We should really do this with an addAllLater but that would require Gradle 6, or a hacky workaround. + explicitCompile.getDependencyConstraints().addAll(compileClasspath.getAllDependencyConstraints()); + // Inherit the excludes from compileClasspath too (that get aggregated from all its super-configurations). + compileClasspath + .getExcludeRules() + .forEach(rule -> explicitCompile.exclude(ImmutableMap.of( + "group", rule.getGroup(), + "module", rule.getModule()))); + }); + TaskProvider sourceSetUnusedDependencies = project.getTasks() .register( GUtil.toLowerCamelCase("checkUnusedDependencies " + sourceSet.getName()), @@ -89,9 +129,7 @@ private static void configureSourceSet( task -> { task.dependsOn(sourceSet.getClassesTaskName()); task.setSourceClasses(sourceSet.getOutput().getClassesDirs()); - // Note: want to exclude 'inherited' deps from other source sets - task.dependenciesConfiguration(compileClasspath); - task.sourceOnlyConfiguration(resolvableCompileOnly); + task.dependenciesConfiguration(explicitCompile); // this is liberally applied to ease the Java8 -> 11 transition task.ignore("javax.annotation", "javax.annotation-api"); From 55c0f2b9198654c8d87a42a111806ca21cef596e Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Fri, 28 Feb 2020 11:57:09 +0000 Subject: [PATCH 10/13] delete unused --- .../plugins/BaselineExactDependencies.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java index dbf51cf68..a61aacb5f 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java @@ -19,8 +19,6 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; -import com.google.common.collect.Sets.SetView; import com.palantir.baseline.tasks.CheckImplicitDependenciesTask; import com.palantir.baseline.tasks.CheckUnusedDependenciesTask; import java.io.File; @@ -149,18 +147,6 @@ private static void configureSourceSet( checkImplicitDependencies.configure(task -> task.dependsOn(sourceSetCheckImplicitDependencies)); } - private static Configuration makeInternalCompileConfiguration(Project project, Configuration compileOnly) { - return project.getConfigurations().create("baseline-exact-dependencies-" + compileOnly.getName(), conf -> { - conf.setVisible(false); - conf.setCanBeConsumed(false); - conf.extendsFrom(compileOnly); - // Important! this ensures we resolve 'compile' variants rather than 'runtime' - // This is the same attribute that's being set on compileClasspath - conf.getAttributes() - .attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_API)); - }); - } - /** Given a {@code com/palantir/product/Foo.class} file, what other classes does it import/reference. */ public static Stream referencedClasses(File classFile) { try { From 8e58b4a7f765064c5560b5ca7b7e25400065f7d1 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Fri, 28 Feb 2020 12:02:46 +0000 Subject: [PATCH 11/13] fix null pointer bug --- .../plugins/BaselineExactDependencies.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java index a61aacb5f..85ccedb03 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java @@ -18,6 +18,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; import com.google.common.collect.ImmutableSet; import com.palantir.baseline.tasks.CheckImplicitDependenciesTask; import com.palantir.baseline.tasks.CheckUnusedDependenciesTask; @@ -38,6 +39,7 @@ import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ExcludeRule; import org.gradle.api.artifacts.ModuleVersionIdentifier; import org.gradle.api.artifacts.ResolvedArtifact; import org.gradle.api.artifacts.ResolvedDependency; @@ -113,11 +115,7 @@ private static void configureSourceSet( // We should really do this with an addAllLater but that would require Gradle 6, or a hacky workaround. explicitCompile.getDependencyConstraints().addAll(compileClasspath.getAllDependencyConstraints()); // Inherit the excludes from compileClasspath too (that get aggregated from all its super-configurations). - compileClasspath - .getExcludeRules() - .forEach(rule -> explicitCompile.exclude(ImmutableMap.of( - "group", rule.getGroup(), - "module", rule.getModule()))); + compileClasspath.getExcludeRules().forEach(rule -> explicitCompile.exclude(excludeRuleAsMap(rule))); }); TaskProvider sourceSetUnusedDependencies = project.getTasks() @@ -147,6 +145,17 @@ private static void configureSourceSet( checkImplicitDependencies.configure(task -> task.dependsOn(sourceSetCheckImplicitDependencies)); } + private static Map excludeRuleAsMap(ExcludeRule rule) { + Builder excludeRule = ImmutableMap.builder(); + if (rule.getGroup() != null) { + excludeRule.put("group", rule.getGroup()); + } + if (rule.getModule() != null) { + excludeRule.put("module", rule.getModule()); + } + return excludeRule.build(); + } + /** Given a {@code com/palantir/product/Foo.class} file, what other classes does it import/reference. */ public static Stream referencedClasses(File classFile) { try { From 6bdc909f80b746aa03bfda5981ed2331ea07d630 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Fri, 28 Feb 2020 14:48:09 +0000 Subject: [PATCH 12/13] garbaggio --- .../baseline/plugins/BaselineExactDependencies.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java index 85ccedb03..db27baad9 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineExactDependencies.java @@ -107,9 +107,14 @@ private static void configureSourceSet( // \-- testCompile extendsFrom(compile) // We therefore want to look at only the dependencies _directly_ declared in the implementation and compile // configurations (belonging to our source set) - project.afterEvaluate(p -> { + explicitCompile.withDependencies(deps -> { Configuration implCopy = implementation.copy(); Configuration compileCopy = compile.copy(); + // Without these, explicitCompile will successfully resolve 0 files and you'll waste 1 hour trying + // to figure out why. + project.getConfigurations().add(implCopy); + project.getConfigurations().add(compileCopy); + explicitCompile.extendsFrom(implCopy, compileCopy); // Mirror the transitive constraints form compileClasspath in order to pick up GCV locks. // We should really do this with an addAllLater but that would require Gradle 6, or a hacky workaround. From 29f4c912d07d898374b20108cf45d1aa7a07fb49 Mon Sep 17 00:00:00 2001 From: Dan Sanduleac Date: Fri, 28 Feb 2020 15:43:08 +0000 Subject: [PATCH 13/13] test --- .../BaselineExactDependenciesTest.groovy | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineExactDependenciesTest.groovy b/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineExactDependenciesTest.groovy index 36ebfc772..e6ec79ced 100644 --- a/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineExactDependenciesTest.groovy +++ b/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineExactDependenciesTest.groovy @@ -112,6 +112,31 @@ class BaselineExactDependenciesTest extends AbstractPluginTest { result.task(':checkUnusedDependenciesMain').getOutcome() == TaskOutcome.SUCCESS } + def 'checkUnusedDependenciesTest passes if dependency from main source set is not referenced in test'() { + when: + buildFile << standardBuildFile + buildFile << """ + repositories { + mavenCentral() + } + dependencies { + compile 'com.google.guava:guava:28.0-jre' + } + """ + file('src/main/java/pkg/Foo.java') << ''' + package pkg; + public class Foo { + void foo() { + com.google.common.collect.ImmutableList.of(); + } + } + '''.stripIndent() + + then: + def result = with('checkUnusedDependencies', '--stacktrace').build() + result.task(':checkUnusedDependenciesTest').getOutcome() == TaskOutcome.SUCCESS + } + def 'checkImplicitDependencies fails when a class is imported without being declared as a dependency'() { when: buildFile << standardBuildFile