Defining dependencies

We discussed how to use dependency configurations to group together dependencies; we also saw how we must define repositories so that the dependencies can be resolved, but we haven't discussed how to define the actual dependencies yet. We define dependencies in our build project with the dependencies{} script block. We define a closure to pass to the dependencies{} script block with the configuration of the dependency.

We can define different types of dependencies. The following table shows the types that we can use:

Dependency type

Method

Description

External module dependency

-

This is a dependency on an external module or library in a repository.

Project dependency

project()

This is a dependency on another Gradle project.

File dependency

files()fileTree()

This is a dependency on a collection of files on the local computer.

Client module dependency

module()

This is a dependency on an external module, where the artifacts are stored in a repository, but the meta information about the module is in the build file. We can override meta information using this type of dependency.

Gradle API dependency

gradleApi()

This is a dependency on the Gradle API of the current Gradle version. We use this dependency when we develop Gradle plugins and tasks.

Local Groovy dependency

localGroovy()

This is a dependency on the Groovy libraries used by the current Gradle version. We use this dependency when we develop Gradle plugins and tasks.

Using external module dependencies

The most used dependency is the external module dependency. We can define a module dependency in different ways. For example, we can use arguments to set a group name, module name, and revision of the dependency. We can also use the String notation to set the group name, module name, and revision in a single string. We always assign a dependency to a specific dependency configuration. The dependency configuration must be defined by ourselves or a plugin that we have applied to our project.

In the following example build file, we will use the Java plugin so that we get a compile and runtime dependency configuration. We will also assign several external module dependencies to each configuration using the different syntax rules:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    // Use attributes for the group, name and 
    // version for the external module dependency. 
    compile(group: 'org.springframework', 
      name: 'spring-core', 
      version: '4.2.3.RELEASE') 
 
    // Use String notation with group, name and 
    // version in a single String. 
    runtime('org.springframework:spring-aop:4.2.3.RELEASE' 
} 

Remember that a Gradle build file is a Groovy script file, so we can define variables to set values and use them in the dependencies{} script block configuration closure. If we rewrite the previous build file, we get the following output:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
ext { 
    springVersion = '4.2.3.RELEASE' 
    springGroup = 'org.springframework' 
} 
 
dependencies { 
    // Use attributes to define dependency and 
    // refer to project properties. 
    compile(group: springGroup, 
      name: 'spring-core', 
      version: springVersion) 
 
    // Use String notation with expression support 
    // for variables. 
    runtime("$springGroup:spring-aop:$springVersion") 
} 

Gradle will look for the descriptor file in the JCenter repository. If the file is found, the artifact of the module and the dependencies of the module are downloaded and made available to the dependency configuration.

To see the dependencies and the transitive dependencies, we invoke the built-in dependencies task. We get the following output:

$ gradle -q dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
archives - Configuration for archive artifacts.
No dependencies
compile - Compile classpath for source set 'main'.
--- org.springframework:spring-core:4.2.3.RELEASE
    --- commons-logging:commons-logging:1.2
default - Configuration for default artifacts.
+--- org.springframework:spring-core:4.2.3.RELEASE
|    --- commons-logging:commons-logging:1.2
--- org.springframework:spring-aop:4.2.3.RELEASE
    +--- aopalliance:aopalliance:1.0
    +--- org.springframework:spring-beans:4.2.3.RELEASE
    |    --- org.springframework:spring-core:4.2.3.RELEASE (*)
    --- org.springframework:spring-core:4.2.3.RELEASE (*)
runtime - Runtime classpath for source set 'main'.
+--- org.springframework:spring-core:4.2.3.RELEASE
|    --- commons-logging:commons-logging:1.2
--- org.springframework:spring-aop:4.2.3.RELEASE
    +--- aopalliance:aopalliance:1.0
    +--- org.springframework:spring-beans:4.2.3.RELEASE
    |    --- org.springframework:spring-core:4.2.3.RELEASE (*)
    --- org.springframework:spring-core:4.2.3.RELEASE (*)
testCompile - Compile classpath for source set 'test'.
--- org.springframework:spring-core:4.2.3.RELEASE
    --- commons-logging:commons-logging:1.2
testRuntime - Runtime classpath for source set 'test'.
+--- org.springframework:spring-core:4.2.3.RELEASE
|    --- commons-logging:commons-logging:1.2
--- org.springframework:spring-aop:4.2.3.RELEASE
    +--- aopalliance:aopalliance:1.0
    +--- org.springframework:spring-beans:4.2.3.RELEASE
    |    --- org.springframework:spring-core:4.2.3.RELEASE (*)
    --- org.springframework:spring-core:4.2.3.RELEASE (*)
(*) - dependencies omitted (listed previously)

To download only the artifact of an external dependency and not the transitive dependencies, we can set the transitive property for the dependency to false. We can set the property with a closure or as an extra property in the argument list, as follows:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    // Configure transitive property with closure. 
    compile('org.slf4j:slf4j-simple:1.7.13') { 
        transitive = false 
    } 
 
    // Or we can use the transitive property 
    // as argument. 
    compile(group: 'org.slf4j', 
      name: 'slf4j-simple', 
      version: '1.7.13', 
      transitive: false) 
} 

We can also exclude some transitive dependencies with the exclude() method. Gradle will look at the descriptor file of the module and exclude any dependencies that we have added with the exclude() method.

For example, in the following build file, we exclude the org.slf4j:sl4j-api transitive dependency:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    // Configure transitive property with closure. 
    compile('org.slf4j:slf4j-simple:1.7.13') { 
        exclude 'org.slf4j:slf4j-api' 
    } 
} 

To get an artifact of only the external module dependency, we can use the artifact-only notation. We must also use this notation when a repository doesn't have a module descriptor file and we want to get the artifact. We must add an @ symbol before the extension of the artifact. Gradle will not look at the module descriptor file, if available, when we use this notation:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    // Use artifact-only notation with @ symbol. 
    runtime('org.slf4j:slf4j-simple:1.7.13@jar') 
 
    // Or we can use the ext property 
    // as method argument. 
    runtime(group: 'org.slf4j', 
      name: 'slf4j-simple', 
      version: '1.7.13', 
      ext: 'jar') 
} 

We can even set the transitive behavior on a complete configuration. Each configuration has a transitive property. We can set the value to true or false in order to change the transitive behavior for each dependency that we define in the configuration. In the following sample build file, we set the transitive property on the runtime configuration:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    compile('org.slf4j:slf4j-simple:1.7.13') 
} 
 
// Make all dependencies for the compile 
// configuration transitive. 
configurations.compile.transitive = false 

In a Maven repository, we can use classifiers for a dependency. For example, the module descriptor file defines the jdk16 and jdk15 classifiers for different JDK versions of the library. We can use classifier in a Gradle dependency definition to select the dependency with the given classifier, as follows:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    // Use artifact-only notation with @ symbol 
    // together with classifier jdk16. 
    compile('sample:simple:1.0:jdk16@jar') 
 
    // Or we can use the classifier property 
    // as method argument. 
    compile( 
      group: 'sample', 
      name: 'simple', 
      version: '1.0', 
      classifier: 'jdk16') 
} 

The module descriptor of a module in a Maven repository can only have one artifact; but in an Ivy repository, we can define multiple artifacts for a single module. Each set of artifacts is grouped together in a configuration. The default configuration contains all the artifacts belonging to the module. If we don't specify the configuration property when we define the dependency for an Ivy module, the default configuration is used. We must specify the configuration property if we want to use artifacts belonging to this specific configuration, as follows:

apply plugin: 'java' 
 
repositories { 
    ivy { 
        url = 'http://intranet/custom' 
        ivyPatterns '[module]/[revision]/ivy.xml' 
        artifactPatterns '[module]/[revision]/[artifact](.[ext])' 
    } 
} 
 
dependencies { 
    // Use configuration property in method arguments. 
    testCompile 
      group: 'sample', 
      name: 'logging', 
      version: '1.0', 
      configuration: 'test' 
 
    // Or we use a closure to set the property. 
    testCompile('sample:logging:1.0') { 
        configuration = 'test' 
    } 
} 

Using project dependencies

Gradle projects can be dependent on each other. To define such a dependency, we use the project() method and use the name of the other project as an argument. Gradle will look for a default dependency configuration in this project and use this dependency configuration. We can use the configuration property to use different dependency configurations as a dependency for each project:

apply plugin: 'java' 
 
dependencies { 
    // Define a dependency on projectA 
    // for the code in our project. 
    compile(project(':projectA')) 
 
    // Define a dependency on projectB, 
    // and use specifically the configuration 
    // compile. 
    compile(project(':projectB')) { 
        configuration = 'compile' 
    } 
} 

Using file dependencies

We can add dependencies using FileCollection. We can use the files() and fileTree()methods to add dependencies to a configuration. The dependency must be resolved to an actual artifact.

The following example uses file dependencies for the compile configuration:

apply plugin: 'java' 
 
dependencies { 
    compile files('spring-core.jar', 'spring-aop.jar') 
    compile fileTree(dir: 'deps', include: '*.jar') 
} 

Using client module dependencies

Normally, Gradle will use a descriptor XML file for dependencies found in the repository to see which artifacts and optional transitive dependencies need to be downloaded. However, these descriptor files can be misconfigured, and so, we may want to override the descriptors ourselves in order to ensure that the dependencies are correct. To do this, we must use the module() method to define the transitive dependencies of a dependency. Gradle will then use our configuration and not the one provided by the module in a repository, as follows:

apply plugin: 'java' 
 
ext { 
    springGroup = 'org.springframework' 
    springRelease = '4.2.3.RELEASE' 
} 
 
dependencies { 
    compile module("$springGroup:spring-context:$springRelease") { 
        dependency("$springGroup:spring-aop:$springRelease") { 
            transitive = false 
      } 
    } 
} 

Using Gradle and Groovy dependencies

When we develop Grails plugins and tasks, we can define a dependency on the Gradle API and Groovy libraries used by the current Gradle version. We can use the gradleApi() and localGroovy()methods to do this.

The following example defines the dependencies in the compile dependency configuration of a project:

apply plugin: 'groovy' 
 
// Dependency configuration for developing 
// Gradle plugins and tasks with Groovy. 
dependencies { 
    // Gradle API available for compile task. 
    compile gradleApi() 
 
    // Groovy libraries used by Gradle version. 
    groovy localGroovy() 
} 

Accessing configuration dependencies

We can access the dependencies for a dependency configuration in a build file or task through the configurations property of the Project object. We can use the dependencies() and allDependencies() methods to get a reference to the dependencies, as follows:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    runtime "org.springframework:spring-aop:4.2.3.RELEASE" 
} 
 
task dependencyInfo << { 
    println "-- Runtime dependencies --" 
    configurations.runtime.dependencies.each { 
        println "${it.group}:${it.name}:${it.version}" 
    } 
 
    println "-- Runtime allDependencies --" 
    configurations.runtime.allDependencies.each { 
        println "${it.group}:${it.name}:${it.version}" 
    } 
} 

Setting dynamic versions

Until now, we have explicitly set a version for a dependency with a complete version number. To set a minimum version number, we can use a special dynamic version syntax. For example, to set the dependency version to a minimum of 2.1 for a dependency, we use a version value 2.1.+. Gradle will resolve the dependency to the latest version after version 2.1 or to version 2.1 itself. In the following example, we will define a dependency on a spring-core version of 3.1 at least:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    compile(group: 'org.springframework', 
        name: 'spring-core', 
        version: '4.2.+') 
} 

We can also reference the latest released version of a module with latest.integration. We can also set a version range with a minimum and maximum version number. The following table shows the ranges that we can use:

Range

Description

[1.0, 2.0]

All versions greater than or equal to 1.0 and lower than or equal to 2.0

[1.0, 2.0[

All versions greater than or equal to 1.0 and lower than 2.0

]1.0, 2.0]

All versions greater than 1.0 and lower than or equal to 2.0

]1.0, 2.0[

All versions greater than 1.0 and lower than 2.0

[1.0, )

All versions greater than or equal to 1.0

]1.0, )

All versions greater than 1.0

(, 2.0]

All versions lower than or equal to 2.0

(, 2.0[

All versions lower than 2.0

The following example build file will use version 4.2.3.RELEASE as the latest release, which is greater than 4.2 and less than 4.3:

apply plugin: 'java' 
 
repositories { 
    jcenter() 
} 
 
dependencies { 
    compile(group: 'org.springframework', 
      name: 'spring-core', 
      version: '[4.2, 4.3[') 
} 

Resolving version conflicts

If we have a project with a lot of dependencies and these dependencies have transitive dependencies, version conflicts can easily arise. If one module has a dependency on sample:logging:1.0 and another on sample:logging:2.0, Gradle will use the newest version number by default.

To change the default behavior, we set the resolutionStrategy property of a dependency configuration. We can instruct Gradle to fail the build if a conflict arises. This is very useful for debugging version conflicts.

In the following example build file, we instruct Gradle to fail the build if a version conflicts arises for all configurations:

apply plugin: 'java' 
 
configurations.all { 
    resolutionStrategy { 
        failOnVersionConflict() 
    } 
} 

To force a certain version number to be used for all dependencies (even transitive dependencies), we can use the force() method of resolutionStrategy. With this method, we can make sure that the preferred version is always used for a given module:

apply plugin: 'java' 
 
configurations.all { 
    resolutionStrategy { 
        force('org.springframework:spring-core:4.2.3.RELEASE') 
    } 
} 

Adding optional ANT tasks

We can reuse existing Another Neat Tool (ANT) tasks in Gradle build files. Gradle uses Groovy's AntBuilder for ANT integration. However, if we want to use an optional ANT task, we must do something extra as the optional tasks and their dependencies are not in the Gradle classpath. Luckily, we only have to define our dependencies for the optional task in the build.gradle file and we can then define and use the optional ANT task.

In the following sample, we are using the scp ANT optional task. We define a new configuration with the name sshAntTask and assign the dependencies to this configuration. Then, we can define the task and set the classpath property to the classpath of the configuration. We use the asPath property to convert the configurations classpath for the ANT task. In the sample, we will also see how to ask for user input when the script is run. The passphrase for the SSH key file is a secret and we don't want to keep it in a file somewhere, so we ask the user for it. The System.console() Java method returns a reference to the console, and with the readPassword() method, we can get the value for the passphrase, as follows:

// We define a new configuration with the name 'sshAntTask'. 
// This configuration is used to define our dependencies. 
configurations { 
    sshAntTask 
} 
 
repositories { 
    jcenter() 
} 
 
// Assign dependencies to the sshAntTask configuration. 
dependencies { 
    sshAntTask('org.apache.ant:ant-jsch:1.7.1', 'jsch:jsch:0.1.29') 
} 
 
// Sample task which uses the scp ANT optional task. 
task update { 
    description = 'Update files on remote server.' 
 
    // Get passphrase from user input. 
    def console = System.console() 
 
    def passphrase = 
        console.readPassword( 
            '%s: ', 
             'Please enter the passphrase for the keyfile') 
 
    // Redefine scp ANT task, with the classpath 
    // property set to our newly defined 
    // sshAntTask configuration classpath. 
    ant.taskdef( 
      name: 'scp', 
      classname: 'org.apache.tools.ant.taskdefs.optional.ssh.Scp', 
      classpath: configurations.sshAntTask.asPath) 
 
    // Invoke the scp ANT task. 
    // (Use gradle -i update to see the output of the ANT task.) 
    ant.scp( 
      todir: 'mrhaki@servername:/home/mrhaki', 
      keyfile: '${user.home}/.ssh/id_rsa', 
      // Use phassphrase entered by the user. 
      passphrase: passphrase as String, 
      verbose: 'true') { 
      fileset(dir: 'work') { 
        include(name: '**/**') 
      } 
    } 
} 

Using dependency configurations as files

Each dependency configuration implements the FileCollection interface of Gradle. This means that we can use a configuration reference if we need a list of files somewhere. The files that make up the resolved dependency configuration are then used.

Let's create a new build file and use a dependency configuration as the value for the from() method. We create a new task of the Copy type and copy all the dependencies of a new configuration, springLibs, to a directory:

repositories { 
    jcenter() 
} 
 
configurations { 
    springLibs 
} 
 
dependencies { 
    springLibs('org.springframework:spring-web:4.2.3.RELEASE') 
} 
 
task copyCompileDeps(type: Copy) { 
    from configurations.springLibs 
    into "$buildDir/compileLibs" 
} 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.163.197