gradledependency-managementdependency-resolution

How can I download and reference a single artifact in Gradle?


I am creating a Gradle task in which I want to download and reference the jar file for an artifact that exists in a Maven repository. Specifically, I plan to call an external script through the Gradle exec task using the location of that jar as an argument to the script. Using the exec task is straightforward; what I am trying to figure out is how to acquire the location of artifact's jar file on the filesystem.

As a simple yet concrete example, assume I have the following files in my Gradle project directory:

build.gradle:

task myScript(type: Exec) {
    executable './my_script.sh'
    args jarFilePath // How do I get this value?
}

my_script.sh:

#!/usr/bin/env bash
echo "Jar file location: $1"

How can I get the file path to the artifact jar, downloading it from the remote repository if necessary? For instance, it should be downloaded if it's not in my local artifact cache, or it's an updated snapshot version.


Solution

  • Let's assume that the jar file we want is guava-26.0-jre.jar. The following build.gradle file will retrieve the artifact (if necessary) and provide the file location as an argument to the script:

    import java.nio.file.Files
    import java.nio.file.Path
    import java.nio.file.StandardCopyOption
    
    repositories {
        mavenCentral()
    }
    
    configurations {
        myArtifact
    }
    
    dependencies {
        myArtifact group: 'com.google.guava', name: 'guava', 
                version: '26.0-jre', transitive: false
    }
    
    task myScript(type: Exec) {
        Path artifactPath = 
                temporaryDir.toPath().resolve('guava.jar').toAbsolutePath()
    
        doFirst {
            Files.copy(configurations.myArtifact.singleFile.toPath(), artifactPath,
                    StandardCopyOption.REPLACE_EXISTING)
        }
    
        executable './my_script.sh'
        args artifactPath.toString()
    }
    

    This creates a custom configuration named "myArtifact" with a single dependency on guava-26.0-jre. By marking it as transitive: false none of the artifact's dependencies will be included in the configuration, nor will they be downloaded if they do not yet exist in the local artifact cache.

    The call to configurations.myArtifact.singleFile returns a java.io.File reference to the artifact's jar file. This getSingleFile() method also ensures that there is exactly one file in the collection, throwing an IllegalStateException if there are zero or more than one. This guards against referencing the wrong artifact if the configuration gets incorrectly misconfigured in the future to having multiple artifacts (e.g. if somebody removes the "transitive" option from our script).

    The toAbsolutePath() method ensures that the path to the script is absolute, and therefore does not need to be resolved relative to any particular directory.

    Note that this is copying the artifact to the task's temporary directory in a doFirst block, rather than referencing the file's path directly. This is so that the artifact doesn't need to be resolved when loading the script. Otherwise, the artifact would need to be resolved (and possibly downloaded) even when doing unrelated tasks — including such basic tasks as clean and tasks — causing a build failure if the artifact could not be resolved.

    The StandardCopyOption.REPLACE_EXISTING option is used to overwrite any previous jar file from previous builds.