Search

Dark theme | Light theme
Showing posts with label GradleGoodness:Dependencies. Show all posts
Showing posts with label GradleGoodness:Dependencies. Show all posts

March 26, 2023

Gradle Goodness: Publish Version Catalog For Sharing Between Projects

A version catalog in Gradle is a central place in our project where we can define dependency references with their version or version rules. We can define a version catalog using an API in our build file, but we can also create an external file where we define our dependencies and version. In our dependencies section we can refer to the names in the version catalog using a type-safe accessor (if we use Kotlin for writing our build script) with code completion in a supported IDE (IntelliJ IDEA). If we want to share a version catalog between projects we can publish a version catalog to a Maven repository with a groupId, artifactId and version.

In order to do this we need to create a new Gradle project that will only contain the definitions for our version catalog. We must add two gradle plugins: version-catalog and maven-publish. The version-catalog plugin adds a new extension versionCatalog to our build file. Here we define the content of the version catalog we want to share. We can refer to an external version catalog file written in the TOML format that is dictated by Gradle. But we can also use an API provided by VersionCatalogBuilder to define our versions, plugins, libraries and bundles. The publication outcome of this project is a Maven POM file and generated version catalog file in TOML format. Using the maven-publish plugin we can publish our version catalog to a Maven repository. Other projects can then refer to this published version catalog in their Gradle settings file. In the build script we can use the dependencies using the type-safe accessor we alread know for a project version catalog.

In the following example we first look at the Gradle project that has all the data to publish a version catalog. We first create an external version catalog file:

# File: gradle/libs.versions.toml
[versions]
junit5 = "5.9.1"

[libraries]
junit-api = {
    module = "org.junit.jupiter:junit-jupiter-api",
    version.ref = "junit5"
}
junit-engine = {
    module = "org.junit.jupiter:junit-jupiter-engine",
    version.ref = "junit5"
}

helidon-deps = "io.helidon:helidon-dependencies:3.2.0"

We define the rootProject name as this will be the artifact identifier of the version catalog we will publish:

// File: settings.gradle.kts
rootProject.name = "version-catalog"

Finally we have a build file where we define the version catalog and Maven publish plugins. Furthermore we configure our version catalog with data from the external file and we use the API. Lastly we define the publishing configuration:

// File: build.gradle.kts
plugins {
    // Version catalog plugin will add the catalog extension
    // to our build file where we can define the version
    // catalog contents.
    `version-catalog`

    // Maven publish plugin so we can publish our version catalog
    // to a Maven repository.
    `maven-publish`
}

group = "mrhaki.shared"
version = "1.1.0"

// catalog extension added by version-catalog plugin.
catalog {
    versionCatalog {
        // We can refer to an external version catalog file.
        // This could even be a published version catalog as well.
        from(files("gradle/libs.versions.toml"))

        // But also use the methods of the VersionCatalogBuilder.
        library("helidon-deps", "io.helidon:helidon-dependencies:3.2.0")
    }
}

publishing {
    publications {
        create<MavenPublication>("maven") {
            // The version-catalog plugin adds a new component
            // "versionCatalog" that we can use a publication.
            // We will get a POM file and a generated version catalog
            // TOML file that are part of the publication.
            from(components["versionCatalog"])
        }
    }

    repositories {
        // Configuration for Maven repo to publish our version catalog to.
        maven {
            url = uri("https://intranet.repo/repository/maven-releases")

            credentials {
                val mavenRepoUsername: String by project
                val mavenRepoPassword: String by project
                username = mavenRepoUsername
                password = mavenRepoPassword
            }
        }
    }
}

Now we create the Gradle project build files that will use our published version catalog. First we must refer to the published version catalog in our settings file. We define the Maven repository where we published the catalog to and then refer to the artifact to assign it to the sharedLibs version catalog accessor:

// File: settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        // Configuration for Maven repo to get our
        // published version catalog from.
        maven {
            url = uri("https://intranet.repo/repository/maven-public")

            credentials {
                val mavenRepoUsername: String by settings
                val mavenRepoPassword: String by settings
                username = mavenRepoUsername
                password = mavenRepoPassword
            }
        }
    }

    versionCatalogs {
        // We create a new version catalog with the
        // given name sharedLibs.
        // We are free to use any name, Gradle will
        // create Kotlin accessors we can use in our build file.
        create("sharedLibs") {
            from("mrhaki.shared:version-catalog:1.1.0")
        }
    }
}

Now we are all setup and in our build file we can use the type-safe accessors to get the dependencies in our project dependencies section:

// File: build.gradle.kts
plugins {
    java // We want to craete a Java Helidon app.
}

repositories {
    // Repository for downloading the dependencies.
    mavenCentral()
}

dependencies {
    // We can reference sharedLibs.helidon.deps
    // from our shared version catalog.
    implementation(platform(sharedLibs.helidon.deps))

    // List of dependencies for Helidon where the version
    // can be left out as we use platform(libs.helidon.deps)
    // to include our Bill of Materials (BOM).
    implementation("io.helidon.webserver:helidon-webserver")
    implementation("io.helidon.config:helidon-config-yaml")
    implementation("io.helidon.media:helidon-media-jsonp")

    // We can reference sharedLibs.junit.api and
    // sharedLibs.junit.engine from our shared version catalog.
    testImplementation(sharedLibs.junit.api)
    testImplementation(sharedLibs.junit.engine)

    testImplementation("io.helidon.webclient:helidon-webclient")
}

Written with Gradle 8.0.2

November 13, 2022

Gradle Goodness: Grouping Version Catalog Dependencies Into Bundles

The version catalog in Gradle is very useful to define a list of dependencies in one single place. In our build script we references dependencies from the version catalog using type safe accessors when we define a dependency for a configuration. Sometimes multiple dependencies belong to each other and are used in combination with each other. In the version catalog we can define bundles of such dependency groups. Instead of referencing each dependency individually we can reference a bundle from the version catalog in our build script. This keeps our build script cleaner and updating a bundle only needs a change in the version catalog.

In the following example version catalog we have several Log4j2 dependencies. We create two bundles that each contain a set of the dependencies for Log4j2:

# File: gradle/libs.versions.toml
[versions]
# We define the log4j2 version for our dependencies.
log4j2 = "2.19.0"

[libraries]
# The api and core modules from log4j2 we need in our project.
# We can use version.ref to refer to version defined in the [versions] section.
log4j2-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j2" }
log4j2-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j2" }

# Bridge to have Java Util Logging (JUL) processed by log4j2.
log4j2-jul = { module = "org.apache.logging.log4j:log4j-jul", version.ref = "log4j2" }
# JSON layout following ECS (Elastic Search) standard.
log4j2-ecs-layout = "co.elastic.logging:log4j2-ecs-layout:1.5.0"
# Support async logging for log4j2.
disruptor = "com.lmax:disruptor:3.4.4"

[bundles]
# Bundle with log4j2 dependencies needed to compile.
log4j2-impl = ["log4j2-api", "log4j2-core"]

# Bundle with log4j2 dependencies only needed at runtime.
log4j2-runtime = ["log4j2-jul", "log4j2-ecs-layout", "disruptor"]    

With the version catalog in place we can now reference the bundles from our build script. In the following example build script we use both bundles for the configurations implementation and runtimeOnly:

plugins {
    java
}

repositories {
    mavenCentral()
}

dependencies {
    // Use log4j2-impl bundle from version catalog.
    implementation(libs.bundles.log4j2.impl)

    // Use log4j2-runtime bundle from version catalog.
    runtimeOnly(libs.bundles.log4j2.runtime)
}

Bundles are very useful to define dependencies that are used together. We can reference than multiple dependencies with one bundle reference in our build scripts.

Written with Gradle 7.5.1.

April 19, 2019

Gradle Goodness: Use bill of materials (BOM) As Dependency Constraints

Since Gradle 5 we can easily use a bill of materials (BOM) in our build file to get recommended dependency versions. The dependency versions defined in the BOM are dependency constraints in Gradle. This means the dependencies we define in our build that are part of the BOM don't need a version, because the version is resolved via the dependency constraint that is defined in the BOM. Also transitive dependency versions are resolved using the BOM if applicable. We use the dependency handler method platform to define the BOM we want to import. The versions in the BOM are recommendations. We can override the recommendation by specifying the version for a dependency found in the BOM with an explicit version.

In the following example build file we import the BOM for Spring Boot 1.2.4.RELEASE. We also add two dependencies that are defined in the BOM: commons-codec:commons-codec and org.yaml:snakeyaml. One without a version and one with an explicit version to override the version defined in the BOM:

// File: build.gradle.kts
plugins {
    java
}

repositories {
    jcenter()
}

dependencies {
    // Load bill of materials (BOM) for Spring Boot.
    // The dependencies in the BOM will be 
    // dependency constraints in our build.
    implementation(platform("org.springframework.boot:spring-boot-dependencies:2.1.4.RELEASE"))

    // Use dependency defined in BOM.
    // Version is not needed, because the version
    // defined in the BOM is a dependency constraint
    // that is used.
    implementation("commons-codec:commons-codec")

    // Override version for dependency in the BOM.
    // Version in BOM is 1.23.
    implementation(group = "org.yaml", 
                   name = "snakeyaml", 
                   version = "1.24")
}

When we run the dependencies task for the configuration compileClasspath we can see how the dependencies are resolved. Notice that version 1.24 for snakeyaml is used, while the version in the BOM is 1.23:

$ gradle -q dependencies --configuration compileClasspath
------------------------------------------------------------
Root project
------------------------------------------------------------

compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-dependencies:2.1.4.RELEASE
|    +--- commons-codec:commons-codec:1.11 (c)
|    \--- org.yaml:snakeyaml:1.23 -> 1.24 (c)
+--- commons-codec:commons-codec -> 1.11
\--- org.yaml:snakeyaml:1.24

(c) - dependency constraint
A web-based, searchable dependency report is available by adding the --scan option.
$

The dependency handler method enforcedPlatform is also available. When we use this method to import a BOM in our build the versions of dependencies we use are forced for the dependencies we use. Even if we define an explicit version on a dependency found in the BOM, the version is forced that is described in the BOM.

// File: build.gradle.kts
plugins {
    java
}

repositories {
    jcenter()
}

dependencies {
    // Load bill of materials (BOM) for Spring Boot.
    // The dependencies in the BOM will be 
    // dependency constraints in our build, but
    // the versions in the BOM are forced for
    // used dependencies.
    implementation(enforcedPlatform("org.springframework.boot:spring-boot-dependencies:2.1.4.RELEASE"))

    // Use dependency defined in BOM.
    // Version is not needed, because the version
    // defined in the BOM is a dependency constraint
    // that is used.
    implementation("commons-codec:commons-codec")

    // Version in BOM is 1.23 and because
    // we use enforcedPlatform the version
    // will be 1.23 once the dependency is resolved,
    // even though we define a newer version explicitly.
    implementation(group = "org.yaml", 
                   name = "snakeyaml", 
                   version = "1.24")
}

Now we look at the resolved dependencies and see how the snakeyaml dependency is forced to use version 1.23:

$ gradle -q dependencies --configuration compileClasspath
------------------------------------------------------------
Root project
------------------------------------------------------------

compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-dependencies:2.1.4.RELEASE
|    +--- commons-codec:commons-codec:1.11 (c)
|    \--- org.yaml:snakeyaml:1.23 (c)
+--- commons-codec:commons-codec -> 1.11
\--- org.yaml:snakeyaml:1.24 -> 1.23

(c) - dependency constraint
A web-based, searchable dependency report is available by adding the --scan option.
$

Read more about the Gradle BOM support on the Gradle website.

Written with Gradle 5.4.

Gradle Goodness: Manage Dependency Versions With Dependency Constraints

From Maven builds we know the dependencyManagement section in our POM file. In the section we can describe dependencies with their version and later in the dependencies section we can refer to the dependency without the version. We can use dependency constraints in Gradle to do the same thing. A dependency constraint can be used to define the version or version range for a dependency defined in our scripts or a transitive dependency. Just like a dependency the dependency constraint is defined for a configuration, so we can fine tune the constraints to the correct configuration.

Using dependency constraints in a multi-project build allows us to define the dependency versions in the root build file and define project dependencies per project without a version. The version will then be used from the dependency constraint we defined in the root build file.

In the following example build script we define two dependency constraints for different configurations:

// File: build.gradle.kts
plugins {
    groovy
}

repositories {
    jcenter()
}

// In a multi-project build, this dependencies
// block with constraints could be in the 
// root build file, so all versions are 
// defined in one place.
dependencies {
    constraints {
        // Define dependency with version to be used.
        // This version is used when we define a dependency
        // for guava without a version.
        implementation("com.google.guava:guava:27.1-jre")

        // Constraints are scoped to configurations,
        // so we can make specific constraints for a configuration.
        // In this case we want the dependency on Spock defined
        // in the testImplementation configuration to be a specific version.
        // Here we use named arguments to define the dependency constraint.
        testImplementation(group = "org.spockframework", 
                           name = "spock-core", 
                           version = "1.3-groovy-2.5")
    }
}

// In a multi-project build this dependencies block
// could be in subprojects, where the dependency
// declarations do not need a version, because the
// versions are defined in the root build file using 
// constraints.
dependencies {
    // Because of the dependency constraint, 
    // we don't have to specify the dependency version here.
    implementation("com.google.guava:guava")

    // Another dependency without version for the 
    // testImplementation configuration.
    testImplementation("org.spockframework:spock-core")
}

Let's run the dependencies task for the configuration compileClasspath to see how the dependencies are resolved:

$ gradle -q dependencies --configuration compileClasspath
------------------------------------------------------------
Root project
------------------------------------------------------------

compileClasspath - Compile classpath for source set 'main'.
+--- com.google.guava:guava -> 27.1-jre
|    +--- com.google.guava:failureaccess:1.0.1
|    +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|    +--- com.google.code.findbugs:jsr305:3.0.2
|    +--- org.checkerframework:checker-qual:2.5.2
|    +--- com.google.errorprone:error_prone_annotations:2.2.0
|    +--- com.google.j2objc:j2objc-annotations:1.1
|    \--- org.codehaus.mojo:animal-sniffer-annotations:1.17
\--- com.google.guava:guava:27.1-jre (c)

(c) - dependency constraint
A web-based, searchable dependency report is available by adding the --scan option.
$

We see that the Guava dependency is resolved for version 27.1-jre. In the output is also shown with (c) that a dependency constraint is used to resolve the dependency.

When we look at the testCompileClasspath configuration we see the following output:

$ gradle -q dependencies --configuration testCompileClasspath
> Task :dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

testCompileClasspath - Compile classpath for source set 'test'.
+--- com.google.guava:guava -> 27.1-jre
|    +--- com.google.guava:failureaccess:1.0.1
|    +--- com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|    +--- com.google.code.findbugs:jsr305:3.0.2
|    +--- org.checkerframework:checker-qual:2.5.2
|    +--- com.google.errorprone:error_prone_annotations:2.2.0
|    +--- com.google.j2objc:j2objc-annotations:1.1
|    \--- org.codehaus.mojo:animal-sniffer-annotations:1.17
+--- com.google.guava:guava:27.1-jre (c)
+--- org.spockframework:spock-core:1.3-groovy-2.5
|    +--- org.codehaus.groovy:groovy:2.5.4
|    +--- org.codehaus.groovy:groovy-json:2.5.4
|    |    \--- org.codehaus.groovy:groovy:2.5.4
|    +--- org.codehaus.groovy:groovy-nio:2.5.4
|    |    \--- org.codehaus.groovy:groovy:2.5.4
|    +--- org.codehaus.groovy:groovy-macro:2.5.4
|    |    \--- org.codehaus.groovy:groovy:2.5.4
|    +--- org.codehaus.groovy:groovy-templates:2.5.4
|    |    +--- org.codehaus.groovy:groovy:2.5.4
|    |    \--- org.codehaus.groovy:groovy-xml:2.5.4
|    |         \--- org.codehaus.groovy:groovy:2.5.4
|    +--- org.codehaus.groovy:groovy-test:2.5.4
|    |    +--- org.codehaus.groovy:groovy:2.5.4
|    |    \--- junit:junit:4.12
|    |         \--- org.hamcrest:hamcrest-core:1.3
|    +--- org.codehaus.groovy:groovy-sql:2.5.4
|    |    \--- org.codehaus.groovy:groovy:2.5.4
|    +--- org.codehaus.groovy:groovy-xml:2.5.4 (*)
|    \--- junit:junit:4.12 (*)
\--- org.spockframework:spock-core -> 1.3-groovy-2.5 (*)

(c) - dependency constraint
(*) - dependencies omitted (listed previously)

A web-based, searchable dependency report is available by adding the --scan option.
$

Read more about Gradle dependency constraints on the Gradle website.

Written with Gradle 5.4.

September 19, 2014

Gradle Goodness: Adding Dependencies Only for Packaging to War

My colleague, Tom Wetjens, wrote a blog post Package-only dependencies in Maven. He showed a Maven solution when we want to include dependencies in the WAR file, which are not used in any other scopes. In this blog post we will see how we solve this in Gradle.

Suppose we use the SLF4J Logging API in our project. We use the API as a compile dependency, because our code uses this API. But in our test runtime we want to use the SLF4J Simple implementation of this API. And in our WAR file we want to include the Logback implementation of the API. The Logback dependency is only needed to be included in the WAR file and shouldn't exist in any other dependency configuration.

We first add the War plugin to our project. The war task uses the runtime dependency configuration to determine which files are added to the WEB-INF/lib directory in our WAR file. We add a new dependency configuration warLib that extends the runtime configuration in our project.

apply plugin: 'war'

repositories.jcenter()

configurations {
    // Create new dependency configuration
    // for dependencies to be added in 
    // WAR file.
    warLib.extendsFrom runtime
}

dependencies {
    // API dependency for Slf4j.
    compile 'org.slf4j:slf4j-api:1.7.7'

    testCompile 'junit:junit:4.11'

    // Slf4j implementation used for tests.
    testRuntime 'org.slf4j:slf4j-simple:1.7.7'

    // Slf4j implementation to be packaged
    // in WAR file.
    warLib 'ch.qos.logback:logback-classic:1.1.2'
}

war {
    // Add warLib dependency configuration
    classpath configurations.warLib

    // We remove all duplicate files
    // with this assignment.
    // geFiles() method return a unique
    // set of File objects, removing
    // any duplicates from configurations
    // added by classpath() method.
    classpath = classpath.files
}

We can now run the build task and we get a WAR file with the following contents:

$ gradle build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:war
:assemble
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

Total time: 6.18 secs
$ jar tvf build/libs/package-only-dep-example.war
     0 Fri Sep 19 05:59:54 CEST 2014 META-INF/
    25 Fri Sep 19 05:59:54 CEST 2014 META-INF/MANIFEST.MF
     0 Fri Sep 19 05:59:54 CEST 2014 WEB-INF/
     0 Fri Sep 19 05:59:54 CEST 2014 WEB-INF/lib/
 29257 Thu Sep 18 14:36:24 CEST 2014 WEB-INF/lib/slf4j-api-1.7.7.jar
270750 Thu Sep 18 14:36:24 CEST 2014 WEB-INF/lib/logback-classic-1.1.2.jar
427729 Thu Sep 18 14:36:26 CEST 2014 WEB-INF/lib/logback-core-1.1.2.jar
   115 Wed Sep 03 09:24:40 CEST 2014 WEB-INF/web.xml

Also when we run the dependencies task we can see how the implementations of the SLF4J API relate to the dependency configurations:

$ gradle dependencies
:dependencies

------------------------------------------------------------
Root project
------------------------------------------------------------

archives - Configuration for archive artifacts.
No dependencies

compile - Compile classpath for source set 'main'.
\--- org.slf4j:slf4j-api:1.7.7

default - Configuration for default artifacts.
\--- org.slf4j:slf4j-api:1.7.7

providedCompile - Additional compile classpath for libraries that should not be part of the WAR archive.
No dependencies

providedRuntime - Additional runtime classpath for libraries that should not be part of the WAR archive.
No dependencies

runtime - Runtime classpath for source set 'main'.
\--- org.slf4j:slf4j-api:1.7.7

testCompile - Compile classpath for source set 'test'.
+--- org.slf4j:slf4j-api:1.7.7
\--- junit:junit:4.11
     \--- org.hamcrest:hamcrest-core:1.3

testRuntime - Runtime classpath for source set 'test'.
+--- org.slf4j:slf4j-api:1.7.7
+--- junit:junit:4.11
|    \--- org.hamcrest:hamcrest-core:1.3
\--- org.slf4j:slf4j-simple:1.7.7
     \--- org.slf4j:slf4j-api:1.7.7

warLib
+--- org.slf4j:slf4j-api:1.7.7
\--- ch.qos.logback:logback-classic:1.1.2
     +--- ch.qos.logback:logback-core:1.1.2
     \--- org.slf4j:slf4j-api:1.7.6 -> 1.7.7

(*) - dependencies omitted (listed previously)

BUILD SUCCESSFUL

Total time: 6.274 secs

Code written with Gradle 2.1.

August 11, 2014

Gradle Goodness: Getting More Dependency Insight

In most of our projects we have dependencies on other code, like libraries or other projects. Gradle has a nice DSL to define dependencies. Dependencies are grouped in dependency configurations. These configuration can be created by ourselves or added via a plugin. Once we have defined our dependencies we get a nice overview of all dependencies in our project with the dependencies task. We can add the optional argument --configuration to only see dependencies for the given configuration. But we can even check for a specific dependency where it is used, any transitive dependencies and how the version is resolved.

In the following sample build we define a compile dependency on Spring Boot and SLF4J API. The SLF4J API is also a transitive dependency for the Spring Boot dependency, so we can see how the dependencyInsight tasks shows a version conflict.

apply plugin: 'java'

// Set Bintray JCenter as repository.
repositories.jcenter()

dependencies {
    // Set dependency for Spring Boot
    compile "org.springframework.boot:spring-boot-starter-web:1.1.5.RELEASE"
    
    // Set dependency for SLF4J with conflicting version.
    compile 'org.slf4j:slf4j-api:1.7.1'
}

Now let's run the dependencyInsight task for the dependency SLF4J API in the compile configuration:

$ gradle -q dependencyInsight --configuration compile --dependency slf4j-api
org.slf4j:slf4j-api:1.7.7 (conflict resolution)
+--- org.slf4j:jcl-over-slf4j:1.7.7
|    \--- org.springframework.boot:spring-boot-starter-logging:1.1.5.RELEASE
|         \--- org.springframework.boot:spring-boot-starter:1.1.5.RELEASE
|              \--- org.springframework.boot:spring-boot-starter-web:1.1.5.RELEASE
|                   \--- compile
+--- org.slf4j:jul-to-slf4j:1.7.7
|    \--- org.springframework.boot:spring-boot-starter-logging:1.1.5.RELEASE (*)
\--- org.slf4j:log4j-over-slf4j:1.7.7
     \--- org.springframework.boot:spring-boot-starter-logging:1.1.5.RELEASE (*)

org.slf4j:slf4j-api:1.7.1 -> 1.7.7
\--- compile

org.slf4j:slf4j-api:1.7.6 -> 1.7.7
\--- ch.qos.logback:logback-classic:1.1.2
     \--- org.springframework.boot:spring-boot-starter-logging:1.1.5.RELEASE
          \--- org.springframework.boot:spring-boot-starter:1.1.5.RELEASE
               \--- org.springframework.boot:spring-boot-starter-web:1.1.5.RELEASE
                    \--- compile

(*) - dependencies omitted (listed previously)

In the output we can see slf4j-api is referenced three times, once as a transitive dependency for jcl-over-slf4j, jul-to-slf4j and log4j-over-slf4j, once as transitive dependency for logback-classic and once as a direct dependency for the compile configuration. We also see the version is bumped to 1.7.7 where necessary, because the transitive dependency of jcl-over-slf4j defines the newest version.

The value we use for the --dependency option is used to do partial matching in the group, name or version properties of the dependencies. For example to see an insight in all dependencies with logging we can invoke $ gradle dependencyInsight --dependency logging.

We can also get an HTML report page with an overview of all dependencies. To get dependency insight we must click on the desired dependency from the HTML page and we get a similar output as via the command-line. First we must add the project-report plugin to our project. Next we invoke the dependencyReport task. When the task is finished we can open build/reports/project/dependencies/index.html in our web browser. When we navigate to the compile configuration and click on the slf4j-api dependency we get the following output:

Written with Gradle 2.0.