Gradle Dependencies and Dependency Configurations

From NovaOrdis Knowledge Base
Revision as of 23:29, 7 February 2022 by Ovidiu (talk | contribs) (→‎Forced Cache Update)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

External

Internal

TO DEPLETE

Gradle Dependencies and Dependency Configurations TODEPLETE

TODO

    configurations.all {
        // break Java 9 modularization constraints, includes a "javax.xml.namespace" package
        exclude group: 'xml-apis', module: 'xml-apis'
    }

Overview

The vast majority of Java projects rely on external libraries so managing a project's dependencies is an important part of building a Java project.

Specifying a dependency for a Java project requires just three pieces of information:

  • Which dependency - specified by the library name and version. This is specified in the dependencies {} block.
  • What is needed for - compilation or running. This is specified in the dependencies {} block.
  • Where to look for it. This is specified in the repositories {...} block.

Dependencies for a project can be managed with Project.dependencies(Closure), Project.getDependencies() and the "dependencies" build.gradle DSL element:

dependencies {
  // configuration closure
}

Configurations for a project can be managed with Project.configurations(Closure), Project.getConfigurations() and the "configurations" build.gradle DSL element:

configurations {
  // configuration closure
}

Concepts

Dependency

Configuration

DSL Reference https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.Configuration.html

A configuration represents a named collection of artifacts and their dependencies, grouped together for a specific goal such as compiling and running the code from a source set. A configuration is similar to a Maven scope.


Configuration, in this context, does not refer to a configuration file of any sort, as in a settings configuration settings.gradle or a build configuration build.gradle. It refers to named set of artifacts.

Each source set points to several configurations required to compile and run the code included in the source set. Also see:

Source Set

Default Configurations added by the Java Plugin

https://docs.gradle.org/current/userguide/java_plugin.html#tab:configurations

The Java plugin adds the following configurations:

compileOnly

The Java plugin "compileOnly" configuration defines dependencies that are necessary to compile the main source set code, but that shouldn't be part of the runtime classpath.

runtimeOnly

The Java plugin "runtimeOnly" configuration defines dependencies that are necessary to run the main source set code, but not for compilation.

implementation

The Java plugin "implementation" configuration defines dependencies that are necessary to compile and run the main source set code that are not part of the API exposed by the project. "implementation" supersedes the "compile" configuration. Also see the api configuration.

testCompileOnly

The Java plugin "testCompileOnly" configuration defines dependencies that are necessary to compile the test source set code, but that shouldn't be part of the runtime classpath.

testRuntimeOnly

The Java plugin "testRuntimeOnly" configuration defines dependencies that are necessary to run the test source set code, but not for compilation. Extends runtimeOnly, everything available on runtimeOnly will also be available on "testRuntimeOnly". This configuration is used by the test Java plugin task.

testImplementation

The Java plugin "testImplementation" configuration defines dependencies that are necessary to compile and run the test source set code.

Deprecated Configurations

compile

This is a configuration that was used for dependencies that are required to both compile and run the main source set code. It is deprecated and it was replaced by implementation. It will issue a warning when it is used. It does not distinguish between dependencies that impact the public API of a java library project.

runtime

A deprecated configuration, replaced by runtimeOnly. "runtime" has a special signification when it comes to artifact publishing. If the project is used as a library, the "runtime" configuration is used to declare the artifacts of the library and their dependencies. The dependencies declared here are assumed to be the dependencies of the published artifact.

testCompile

A deprecated configuration, replaced by testImplementation.

testRuntime

A deprecated configuration that includes all dependencies required to compile the project and tests and then run the tests. It is replaced by testImplementation.

Default Configurations added by the Java Library Plugin

The Java Library plugin adds the the following additional configurations:

api

A Java Library plugin configuration. This dependency configuration defines dependencies required to compile the production sources of the project which are part of the API exposed by the project.

compileOnlyApi

A Java Library plugin configuration.

Dependencies Reported by the 'dependencies' Built-in Task

These configurations are displayed when executing the built in dependencies task:

annotationProcessor

Annotation processors used during compilation and their dependencies, for the source set "main".

api

See api above.

apiElements

archives

The "archives" configuration is the standard configuration to assign artifacts to. The Java plugin automatically assigns the default JAR to this configuration, as the only element. If a Maven plugin is present, it associates its "install" task with this configuration.

compileClasspath

The compilation classpath for the main source set, used by the compileJava task. Extends compile, compileOnly and implementation.

compileOnly

See compileOnly above.

compileOnlyApi

See compileOnlyApi above.

default

Configuration for default artifacts.

implementation

See implementation above.

runtimeClasspath

Runtime classpath of source set 'main'.

runtimeElements

runtimeOnly

testAnnotationProcessor

testCompileClasspath

testCompileOnly

testImplementation

testRuntimeClasspath

testRuntimeOnly

build.gradle Syntax

dependencies {
  implementation 'io.someproject:someartifact:0.1.0'
}

Operations

Display the Dependency Tree

./gradlew dependencies

The Dependency Cache

https://docs.gradle.org/current/userguide/dependency_cache.html

Once a dependency is resolved as result of the dependency resolution, Gradle stores the associated artifacts in a local cache, known as the dependency cache. One of the goals of storing artifacts locally in the cache is to minimize the number of remote requests made during the dependency resolution process.

The dependency cache is located under $GRADLE_USER_HOME/caches (typically ~/.gradle/caches). The directory contains two storage types: file-based storage for downloaded artifacts and raw metadata files, and a binary store of resolved module meta-data. This cache is also known as the metadata cache. The metadata cache contains, among other things, the result of resolving dynamic versions to concrete versions, the resolved module metadata, including module artifacts and module dependencies, the resolved artifact metadata, including a pointer to the downloaded file in the file-based storage, the absence of a particular module or artifact, eliminating repeated attempts to access a resource that does not exist.

For a specific dependency version, the files are maintained under .gradle/caches/modules-2/files-* and the corresponding binary metadata is maintained in .gradle/caches/modules-2/metadata-*. For example, the binary artifacts for SLF4J API 1.7.25, where "org.slf4j" is the group ID and "slf4j-api" is the artifact ID, are stored as follows:

.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25
                                                         |
                                                         + df51c4a85dd6acf8b6cdc9323596766b3d577c28/slf4j-api-1.7.25.pom
                                                         |
                                                         + da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar
                                                         |
                                                         + 962153db4a9ea71b79d047dfd1b2a0d80d8f4739/slf4j-api-1.7.25-sources.jar

The corresponding binary metadata is stored as:

.gradle/caches/modules-2/metadata-2.58/descriptors/org.slf4j/slf4j-api/1.7.25
                                                                         |
                                                                         + 1dd858de07b774d6be9d3e38c5646087/descriptor.bin
                                                                         |
                                                                         + 33ddd5448b9e1258ce46034f20c899a8/descriptor.bin
                                                                         |
                                                                         + f8e6315c37eb56998f7a5ba08e30db71/descriptor.bin


Dependency resolution will fail if the required artifacts are not available in any repository specified in the build, even if the local cache has a copy of the artifact that was retrieved from a different repository.

Before downloading an artifact from a repository, Gradle tries to get its checksum first by attempting to download the SHA file associated with the artifact. If the checksum can be retrieved, and Gradle determines that a file with the same id and checksum exists in the cache. If the checksum cannot be retrieved, the artifact will be downloaded (and ignored if it matches an existing artifact).

The cache is periodically scanned (at most every 24 hours) for artifacts that have not been used for more than 30 days and obsolete artifacts are deleted, to ensure the cache does not grow indefinitely.

Forced Cache Update

Gradle can be told to ignore all cached entries for resolved modules and artifacts with the --refresh-dependencies command line option. When used, a fresh resolve will be performed against all configured repositories, with dynamic versions recalculated, modules refreshed, and artifacts downloaded. However, where possible Gradle will check if the previously downloaded artifacts are valid before downloading again. This is done by comparing published SHA1 values in the repository with the SHA1 values for existing downloaded artifacts. A new snapshot repository will produce the following log:

gradle clean build --refresh-dependencies
...
Download s3://my-repo/snapshots/com/example/my-artifact/1.0.0-SNAPSHOT/maven-metadata.xml
...
Download s3://my-repo/snapshots/com/example/my-artifact/1.0.0-SNAPSHOT/my-artifact-1.0.0-20181008.231746-18.jar

Low-Level Cache Manipulation

It is sometimes necessary to remove individual dependency from cache. TODO.