# Declaring dependencies

version 7.6.4

Before looking at dependency declarations themselves, the concept of dependency configuration needs to be defined.

## What are dependency configurations

Every dependency declared for a Gradle project applies to a specific scope. For example some dependencies should be used for compiling source code whereas others only need to be available at runtime. Gradle represents the scope of a dependency with the help of a Configuration. Every configuration can be identified by a unique name.

Many Gradle plugins add pre-defined configurations to your project. The Java plugin, for example, adds configurations to represent the various classpaths it needs for source code compilation, executing tests and the like. See the Java plugin chapter for an example.

Figure 1. Configurations use declared dependencies for specific purposes

For more examples on the usage of configurations to navigate, inspect and post-process metadata and artifacts of assigned dependencies, have a look at the resolution result APIs.

### Configuration inheritance and composition

A configuration can extend other configurations to form an inheritance hierarchy. Child configurations inherit the whole set of dependencies declared for any of its superconfigurations.

Configuration inheritance is heavily used by Gradle core plugins like the Java plugin. For example the `testImplementation` configuration extends the `implementation` configuration. The configuration hierarchy has a practical purpose: compiling tests requires the dependencies of the source code under test on top of the dependencies needed write the test class. A Java project that uses JUnit to write and execute test code also needs Guava if its classes are imported in the production source code.

Figure 2. Configuration inheritance provided by the Java plugin

Under the covers the `testImplementation` and `implementation` configurations form an inheritance hierarchy by calling the method Configuration.extendsFrom(org.gradle.api.artifacts.Configuration[]). A configuration can extend any other configuration irrespective of its definition in the build script or a plugin.

Let’s say you wanted to write a suite of smoke tests. Each smoke test makes a HTTP call to verify a web service endpoint. As the underlying test framework the project already uses JUnit. You can define a new configuration named `smokeTest` that extends from the `testImplementation` configuration to reuse the existing test framework dependency.

Example 1. Extending a configuration from another configuration
build.gradle

    configurations {
        smokeTest.extendsFrom testImplementation
    }

    dependencies {
        testImplementation 'junit:junit:4.13'
        smokeTest 'org.apache.httpcomponents:httpclient:4.5.5'
    }

build.gradle.kts

    val smokeTest by configurations.creating {
        extendsFrom(configurations.testImplementation.get())
    }

    dependencies {
        testImplementation("junit:junit:4.13")
        smokeTest("org.apache.httpcomponents:httpclient:4.5.5")
    }

## Resolvable and consumable configurations

Configurations are a fundamental part of dependency resolution in Gradle. In the context of dependency resolution, it is useful to distinguish between a consumer and a producer. Along these lines, configurations have at least 3 different roles:

1. to declare dependencies

2. as a consumer, to resolve a set of dependencies to files

3. as a producer, to expose artifacts and their dependencies for consumption by other projects (such consumable configurations usually represent the variants the producer offers to its consumers)

For example, to express that an application `app` depends on library `lib`, at least one configuration is required:

Example 2. Configurations are used to declare dependencies
build.gradle

    configurations {
        // declare a "configuration" named "someConfiguration"
        someConfiguration
    }
    dependencies {
        // add a project dependency to the "someConfiguration" configuration
        someConfiguration project(":lib")
    }

build.gradle.kts

    // declare a "configuration" named "someConfiguration"
    val someConfiguration by configurations.creating

    dependencies {
        // add a project dependency to the "someConfiguration" configuration
        someConfiguration(project(":lib"))
    }

Configurations can inherit dependencies from other configurations by extending from them. Now, notice that the code above doesn’t tell us anything about the intended consumer of this configuration. In particular, it doesn’t tell us how the configuration is meant to be used. Let’s say that `lib` is a Java library: it might expose different things, such as its API, implementation, or test fixtures. It might be necessary to change how we resolve the dependencies of `app` depending upon the task we’re performing (compiling against the API of `lib`, executing the application, compiling tests, etc.). To address this problem, you’ll often find companion configurations, which are meant to unambiguously declare the usage:

Example 3. Configurations representing concrete dependency graphs
build.gradle

    configurations {
        // declare a configuration that is going to resolve the compile classpath of the application
        compileClasspath.extendsFrom(someConfiguration)

        // declare a configuration that is going to resolve the runtime classpath of the application
        runtimeClasspath.extendsFrom(someConfiguration)
    }

build.gradle.kts

    configurations {
        // declare a configuration that is going to resolve the compile classpath of the application
        compileClasspath.extendsFrom(someConfiguration)

        // declare a configuration that is going to resolve the runtime classpath of the application
        runtimeClasspath.extendsFrom(someConfiguration)
    }

At this point, we have 3 different configurations with different roles:

- `someConfiguration` declares the dependencies of my application. It’s just a bucket that can hold a list of dependencies.

- `compileClasspath` and `runtimeClasspath` are configurations meant to be resolved: when resolved they should contain the compile classpath, and the runtime classpath of the application respectively.

This distinction is represented by the `canBeResolved` flag in the `Configuration` type. A configuration that can be resolved is a configuration for which we can compute a dependency graph, because it contains all the necessary information for resolution to happen. That is to say we’re going to compute a dependency graph, resolve the components in the graph, and eventually get artifacts. A configuration which has `canBeResolved` set to `false` is not meant to be resolved. Such a configuration is there only to declare dependencies. The reason is that depending on the usage (compile classpath, runtime classpath), it can resolve to different graphs. It is an error to try to resolve a configuration which has `canBeResolved` set to `false`. To some extent, this is similar to an abstract class (`canBeResolved`=false) which is not supposed to be instantiated, and a concrete class extending the abstract class (`canBeResolved`=true). A resolvable configuration will extend at least one non-resolvable configuration (and may extend more than one).

On the other end, at the library project side (the producer), we also use configurations to represent what can be consumed. For example, the library may expose an API or a runtime, and we would attach artifacts to either one, the other, or both. Typically, to compile against `lib`, we need the API of `lib`, but we don’t need its runtime dependencies. So the `lib` project will expose an `apiElements` configuration, which is aimed at consumers looking for its API. Such a configuration is consumable, but is not meant to be resolved. This is expressed via the canBeConsumed flag of a `Configuration`:

Example 4. Setting up configurations
build.gradle

    configurations {
        // A configuration meant for consumers that need the API of this component
        exposedApi {
            // This configuration is an "outgoing" configuration, it's not meant to be resolved
            canBeResolved = false
            // As an outgoing configuration, explain that consumers may want to consume it
            canBeConsumed = true
        }
        // A configuration meant for consumers that need the implementation of this component
        exposedRuntime {
            canBeResolved = false
            canBeConsumed = true
        }
    }

build.gradle.kts

    configurations {
        // A configuration meant for consumers that need the API of this component
        create("exposedApi") {
            // This configuration is an "outgoing" configuration, it's not meant to be resolved
            isCanBeResolved = false
            // As an outgoing configuration, explain that consumers may want to consume it
            isCanBeConsumed = true
        }
        // A configuration meant for consumers that need the implementation of this component
        create("exposedRuntime") {
            isCanBeResolved = false
            isCanBeConsumed = true
        }
    }

In short, a configuration’s role is determined by the `canBeResolved` and `canBeConsumed` flag combinations:

Table 1. Configuration roles
Configuration role | can be resolved | can be consumed
Bucket of dependencies | false | false
Resolve for certain usage | true | false
Exposed to consumers | false | true
Legacy, don’t use | true | true

For backwards compatibility, both flags have a default value of `true`, but as a plugin author, you should always determine the right values for those flags, or you might accidentally introduce resolution errors.

## Choosing the right configuration for dependencies

The choice of the configuration where you declare a dependency is important. However there is no fixed rule into which configuration a dependency must go. It mostly depends on the way the configurations are organised, which is most often a property of the applied plugin(s).

For example, in the `java` plugin, the created configuration are documented and should serve as the basis for determining where to declare a dependency, based on its role for your code.

As a recommendation, plugins should clearly document the way their configurations are linked together and should strive as much as possible to isolate their roles.

## Defining custom configurations

You can define configurations yourself, so-called custom configurations. A custom configuration is useful for separating the scope of dependencies needed for a dedicated purpose.

Let’s say you wanted to declare a dependency on the Jasper Ant task for the purpose of pre-compiling JSP files that should not end up in the classpath for compiling your source code. It’s fairly simple to achieve that goal by introducing a custom configuration and using it in a task.

Example 5. Declaring and using a custom configuration
build.gradle

    configurations {
        jasper
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        jasper 'org.apache.tomcat.embed:tomcat-embed-jasper:9.0.2'
    }

    tasks.register('preCompileJsps') {
        doLast {
            ant.taskdef(classname: 'org.apache.jasper.JspC',
                        name: 'jasper',
                        classpath: configurations.jasper.asPath)
            ant.jasper(validateXml: false,
                       uriroot: file('src/main/webapp'),
                       outputDir: file("$buildDir/compiled-jsps"))
        }
    }

build.gradle.kts

    val jasper by configurations.creating

    repositories {
        mavenCentral()
    }

    dependencies {
        jasper("org.apache.tomcat.embed:tomcat-embed-jasper:9.0.2")
    }

    tasks.register("preCompileJsps") {
        doLast {
            ant.withGroovyBuilder {
                "taskdef"("classname" to "org.apache.jasper.JspC",
                          "name" to "jasper",
                          "classpath" to jasper.asPath)
                "jasper"("validateXml" to false,
                         "uriroot" to file("src/main/webapp"),
                         "outputDir" to file("$buildDir/compiled-jsps"))
            }
        }
    }

You can manage project configurations with a `configurations` object. Configurations have a name and can extend each other. To learn more about this API have a look at ConfigurationContainer.

## Different kinds of dependencies

### Module dependencies

Module dependencies are the most common dependencies. They refer to a module in a repository.

Example 6. Module dependencies
build.gradle

    dependencies {
        runtimeOnly group: 'org.springframework', name: 'spring-core', version: '2.5'
        runtimeOnly 'org.springframework:spring-core:2.5',
                'org.springframework:spring-aop:2.5'
        runtimeOnly(
            [group: 'org.springframework', name: 'spring-core', version: '2.5'],
            [group: 'org.springframework', name: 'spring-aop', version: '2.5']
        )
        runtimeOnly('org.hibernate:hibernate:3.0.5') {
            transitive = true
        }
        runtimeOnly group: 'org.hibernate', name: 'hibernate', version: '3.0.5', transitive: true
        runtimeOnly(group: 'org.hibernate', name: 'hibernate', version: '3.0.5') {
            transitive = true
        }
    }

build.gradle.kts

    dependencies {
        runtimeOnly(group = "org.springframework", name = "spring-core", version = "2.5")
        runtimeOnly("org.springframework:spring-aop:2.5")
        runtimeOnly("org.hibernate:hibernate:3.0.5") {
            isTransitive = true
        }
        runtimeOnly(group = "org.hibernate", name = "hibernate", version = "3.0.5") {
            isTransitive = true
        }
    }

See the DependencyHandler class in the API documentation for more examples and a complete reference.

Gradle provides different notations for module dependencies. There is a string notation and a map notation. A module dependency has an API which allows further configuration. Have a look at ExternalModuleDependency to learn all about the API. This API provides properties and configuration methods. Via the string notation you can define a subset of the properties. With the map notation you can define all properties. To have access to the complete API, either with the map or with the string notation, you can assign a single dependency to a configuration together with a closure.

| If you declare a module dependency, Gradle looks for a module metadata file (`.module`, `.pom` or `ivy.xml`) in the repositories. If such a module metadata file exists, it is parsed and the artifacts of this module (e.g. `hibernate-3.0.5.jar`) as well as its dependencies (e.g. `cglib`) are downloaded. If no such module metadata file exists, as of Gradle 6.0, you need to configure metadata sources definitions to look for an artifact file called `hibernate-3.0.5.jar` directly.

| In Maven, a module can have one and only one artifact. In Gradle and Ivy, a module can have multiple artifacts. Each artifact can have a different set of dependencies.

### File dependencies

Projects sometimes do not rely on a binary repository product e.g. JFrog Artifactory or Sonatype Nexus for hosting and resolving external dependencies. It’s common practice to host those dependencies on a shared drive or check them into version control alongside the project source code. Those dependencies are referred to as file dependencies, the reason being that they represent a file without any metadata (like information about transitive dependencies, the origin or its author) attached to them.

Figure 3. Resolving file dependencies from the local file system and a shared drive

The following example resolves file dependencies from the directories `ant`, `libs` and `tools`.

Example 7. Declaring multiple file dependencies
build.gradle

    configurations {
        antContrib
        externalLibs
        deploymentTools
    }

    dependencies {
        antContrib files('ant/antcontrib.jar')
        externalLibs files('libs/commons-lang.jar', 'libs/log4j.jar')
        deploymentTools(fileTree('tools') { include '*.exe' })
    }

build.gradle.kts

    configurations {
        create("antContrib")
        create("externalLibs")
        create("deploymentTools")
    }

    dependencies {
        "antContrib"(files("ant/antcontrib.jar"))
        "externalLibs"(files("libs/commons-lang.jar", "libs/log4j.jar"))
        "deploymentTools"(fileTree("tools") { include("*.exe") })
    }

As you can see in the code example, every dependency has to define its exact location in the file system. The most prominent methods for creating a file reference are Project.files(java.lang.Object…​), ProjectLayout.files(java.lang.Object…​) and Project.fileTree(java.lang.Object) Alternatively, you can also define the source directory of one or many file dependencies in the form of a flat directory repository.

| The order of the files in a `FileTree` is not stable, even on a single computer. It means that dependency configuration seeded with such a construct may produce a resolution result which has a different ordering, possibly impacting the cacheability of tasks using the result as an input. Using the simpler `files` instead is recommended where possible.

File dependencies allow you to directly add a set of files to a configuration, without first adding them to a repository. This can be useful if you cannot, or do not want to, place certain files in a repository. Or if you do not want to use any repositories at all for storing your dependencies.

To add some files as a dependency for a configuration, you simply pass a file collection as a dependency:

Example 8. File dependencies
build.gradle

    dependencies {
        runtimeOnly files('libs/a.jar', 'libs/b.jar')
        runtimeOnly fileTree('libs') { include '*.jar' }
    }

build.gradle.kts

    dependencies {
        runtimeOnly(files("libs/a.jar", "libs/b.jar"))
        runtimeOnly(fileTree("libs") { include("*.jar") })
    }

File dependencies are not included in the published dependency descriptor for your project. However, file dependencies are included in transitive project dependencies within the same build. This means they cannot be used outside the current build, but they can be used within the same build.

| The order of the files in a `FileTree` is not stable, even on a single computer. It means that dependency configuration seeded with such a construct may produce a resolution result which has a different ordering, possibly impacting the cacheability of tasks using the result as an input. Using the simpler `files` instead is recommended where possible.

You can declare which tasks produce the files for a file dependency. You might do this when, for example, the files are generated by the build.

Example 9. Generated file dependencies
build.gradle

    dependencies {
        implementation files(layout.buildDirectory.dir('classes')) {
            builtBy 'compile'
        }
    }

    tasks.register('compile') {
        doLast {
            println 'compiling classes'
        }
    }

    tasks.register('list') {
        dependsOn configurations.compileClasspath
        doLast {
            println "classpath = ${configurations.compileClasspath.collect { File file -> file.name }}"
        }
    }

build.gradle.kts

    dependencies {
        implementation(files(layout.buildDirectory.dir("classes")) {
            builtBy("compile")
        })
    }

    tasks.register("compile") {
        doLast {
            println("compiling classes")
        }
    }

    tasks.register("list") {
        dependsOn(configurations["compileClasspath"])
        doLast {
            println("classpath = ${configurations["compileClasspath"].map { file: File -> file.name }}")
        }
    }

    $ gradle -q list
    compiling classes
    classpath = [classes]

#### Versioning of file dependencies

It is recommended to clearly express the intention and a concrete version for file dependencies. File dependencies are not considered by Gradle’s version conflict resolution. Therefore, it is extremely important to assign a version to the file name to indicate the distinct set of changes shipped with it. For example `commons-beanutils-1.3.jar` lets you track the changes of the library by the release notes.

As a result, the dependencies of the project are easier to maintain and organize. It is much easier to uncover potential API incompatibilities by the assigned version.

### Project dependencies

Software projects often break up software components into modules to improve maintainability and prevent strong coupling. Modules can define dependencies between each other to reuse code within the same project.

Figure 4. Dependencies between projects

Gradle can model dependencies between modules. Those dependencies are called project dependencies because each module is represented by a Gradle project.

Example 10. Project dependencies
build.gradle

    dependencies {
        implementation project(':shared')
    }

build.gradle.kts

    dependencies {
        implementation(project(":shared"))
    }

At runtime, the build automatically ensures that project dependencies are built in the correct order and added to the classpath for compilation. The chapter Authoring Multi-Project Builds discusses how to set up and configure multi-project builds in more detail.

For more information see the API documentation for ProjectDependency.

The following example declares the dependencies on the `utils` and `api` project from the `web-service` project. The method Project.project(java.lang.String) creates a reference to a specific subproject by path.

Example 11. Declaring project dependencies
web-service/build.gradle

    dependencies {
        implementation project(':utils')
        implementation project(':api')
    }

web-service/build.gradle.kts

    dependencies {
        implementation(project(":utils"))
        implementation(project(":api"))
    }

#### Type-safe project dependencies

| Type-safe project accessors are an incubating feature which must be enabled explicitly. Implementation may change at any time. To add support for type-safe project accessors, add this to your `settings.gradle(.kts)` file:
    enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")

One issue with the `project(":some:path")` notation is that you have to remember the path to every project you want to depend on. In addition, changing a project path requires you to change all places where the project dependency is used, but it is easy to miss one or more occurrences (because you have to rely on search and replace).

Since Gradle 7, Gradle offers an experimental type-safe API for project dependencies. The same example as above can now be rewritten as:

Example 12. Declaring project dependencies using the type-safe API
web-service/build.gradle

    dependencies {
        implementation projects.utils
        implementation projects.api
    }

web-service/build.gradle.kts

    dependencies {
        implementation(projects.utils)
        implementation(projects.api)
    }

The type-safe API has the advantage of providing IDE completion so you don’t need to figure out the actual names of the projects.

If you add or remove a project that uses the Kotlin DSL, build script compilation fails if you forget to update a dependency.

The project accessors are mapped from the project path. For example, if a project path is `:commons:utils:some:lib` then the project accessor will be `projects.commons.utils.some.lib` (which is the short-hand notation for `projects.getCommons().getUtils().getSome().getLib()`).

A project name with kebab case (`some-lib`) or snake case (`some_lib`) will be converted to camel case in accessors: `projects.someLib`.

### Local forks of module dependencies

A module dependency can be substituted by a dependency to a local fork of the sources of that module, if the module itself is built with Gradle. This can be done by utilising composite builds. This allows you, for example, to fix an issue in a library you use in an application by using, and building, a locally patched version instead of the published binary version. The details of this are described in the section on composite builds.

### Gradle distribution-specific dependencies

#### Gradle API dependency

You can declare a dependency on the API of the current version of Gradle by using the DependencyHandler.gradleApi() method. This is useful when you are developing custom Gradle tasks or plugins.

Example 13. Gradle API dependencies
build.gradle

    dependencies {
        implementation gradleApi()
    }

build.gradle.kts

    dependencies {
        implementation(gradleApi())
    }

#### Gradle TestKit dependency

You can declare a dependency on the TestKit API of the current version of Gradle by using the DependencyHandler.gradleTestKit() method. This is useful for writing and executing functional tests for Gradle plugins and build scripts.

Example 14. Gradle TestKit dependencies
build.gradle

    dependencies {
        testImplementation gradleTestKit()
    }

build.gradle.kts

    dependencies {
        testImplementation(gradleTestKit())
    }

The TestKit chapter explains the use of TestKit by example.

#### Local Groovy dependency

You can declare a dependency on the Groovy that is distributed with Gradle by using the DependencyHandler.localGroovy() method. This is useful when you are developing custom Gradle tasks or plugins in Groovy.

Example 15. Gradle’s Groovy dependencies
build.gradle

    dependencies {
        implementation localGroovy()
    }

build.gradle.kts

    dependencies {
        implementation(localGroovy())
    }

## Documenting dependencies

When you declare a dependency or a dependency constraint, you can provide a custom reason for the declaration. This makes the dependency declarations in your build script and the dependency insight report easier to interpret.

Example 16. Giving a reason for choosing a certain module version in a dependency declaration
build.gradle

    plugins {
        id 'java-library'
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        implementation('org.ow2.asm:asm:7.1') {
            because 'we require a JDK 9 compatible bytecode generator'
        }
    }

build.gradle.kts

    plugins {
        `java-library`
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        implementation("org.ow2.asm:asm:7.1") {
            because("we require a JDK 9 compatible bytecode generator")
        }
    }

### Example: Using the dependency insight report with custom reasons

Output of `gradle -q dependencyInsight --dependency asm`

    > gradle -q dependencyInsight --dependency asm
    org.ow2.asm:asm:7.1
      Variant compile:
        | Attribute Name                 | Provided | Requested    |
        |--------------------------------|----------|--------------|
        | org.gradle.status              | release  |              |
        | org.gradle.category            | library  | library      |
        | org.gradle.libraryelements     | jar      | classes      |
        | org.gradle.usage               | java-api | java-api     |
        | org.gradle.dependency.bundling |          | external     |
        | org.gradle.jvm.environment     |          | standard-jvm |
        | org.gradle.jvm.version         |          | 11           |
       Selection reasons:
          - Was requested: we require a JDK 9 compatible bytecode generator

    org.ow2.asm:asm:7.1
    \--- compileClasspath

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

## Resolving specific artifacts from a module dependency

Whenever Gradle tries to resolve a module from a Maven or Ivy repository, it looks for a metadata file and the default artifact file, a JAR. The build fails if none of these artifact files can be resolved. Under certain conditions, you might want to tweak the way Gradle resolves artifacts for a dependency.

- The dependency only provides a non-standard artifact without any metadata e.g. a ZIP file.

- The module metadata declares more than one artifact e.g. as part of an Ivy dependency descriptor.

- You only want to download a specific artifact without any of the transitive dependencies declared in the metadata.

Gradle is a polyglot build tool and not limited to just resolving Java libraries. Let’s assume you wanted to build a web application using JavaScript as the client technology. Most projects check in external JavaScript libraries into version control. An external JavaScript library is no different than a reusable Java library so why not download it from a repository instead?

Google Hosted Libraries is a distribution platform for popular, open-source JavaScript libraries. With the help of the artifact-only notation you can download a JavaScript library file e.g. JQuery. The `@` character separates the dependency’s coordinates from the artifact’s file extension.

Example 17. Resolving a JavaScript artifact for a declared dependency
build.gradle

    repositories {
        ivy {
            url 'https://ajax.googleapis.com/ajax/libs'
            patternLayout {
                artifact '[organization]/[revision]/[module].[ext]'
            }
            metadataSources {
                artifact()
            }
        }
    }

    configurations {
        js
    }

    dependencies {
        js 'jquery:jquery:3.2.1@js'
    }

build.gradle.kts

    repositories {
        ivy {
            url = uri("https://ajax.googleapis.com/ajax/libs")
            patternLayout {
                artifact("[organization]/[revision]/[module].[ext]")
            }
            metadataSources {
                artifact()
            }
        }
    }

    configurations {
        create("js")
    }

    dependencies {
        "js"("jquery:jquery:3.2.1@js")
    }

Some modules ship different "flavors" of the same artifact or they publish multiple artifacts that belong to a specific module version but have a different purpose. It’s common for a Java library to publish the artifact with the compiled class files, another one with just the source code in it and a third one containing the Javadocs.

In JavaScript, a library may exist as uncompressed or minified artifact. In Gradle, a specific artifact identifier is called classifier, a term generally used in Maven and Ivy dependency management.

Let’s say we wanted to download the minified artifact of the JQuery library instead of the uncompressed file. You can provide the classifier `min` as part of the dependency declaration.

Example 18. Resolving a JavaScript artifact with classifier for a declared dependency
build.gradle

    repositories {
        ivy {
            url 'https://ajax.googleapis.com/ajax/libs'
            patternLayout {
                artifact '[organization]/[revision]/[module](.[classifier]).[ext]'
            }
            metadataSources {
                artifact()
            }
        }
    }

    configurations {
        js
    }

    dependencies {
        js 'jquery:jquery:3.2.1:min@js'
    }

build.gradle.kts

    repositories {
        ivy {
            url = uri("https://ajax.googleapis.com/ajax/libs")
            patternLayout {
                artifact("[organization]/[revision]/[module](.[classifier]).[ext]")
            }
            metadataSources {
                artifact()
            }
        }
    }

    configurations {
        create("js")
    }

    dependencies {
        "js"("jquery:jquery:3.2.1:min@js")
    }

## Supported Metadata formats

External module dependencies require module metadata (so that, typically, Gradle can figure out the transitive dependencies of a module). To do so, Gradle supports different metadata formats.

You can also tweak which format will be looked up in the repository definition.

### Gradle Module Metadata files

Gradle Module Metadata has been specifically designed to support all features of Gradle’s dependency management model and is hence the preferred format. You can find its specification here.

### POM files

Gradle natively supports Maven POM files. It’s worth noting that by default Gradle will first look for a POM file, but if this file contains a special marker, Gradle will use Gradle Module Metadata instead.

### Ivy files

Similarly, Gradle supports Apache Ivy metadata files. Again, Gradle will first look for an `ivy.xml` file, but if this file contains a special marker, Gradle will use Gradle Module Metadata instead.