
Developer Advocate at Gradle

Developer Advocate at Gradle

Senior Software Engineer at Gradle
All things Dependency Management
API and implementation separation
Dependency constraints and platforms
Publishing and the Gradle Module Metadata format
Test fixtures and feature variants
Enrich existing Metadata with Rules
Java, Groovy, and Scala improvements
New features for plugin authors

Use java-library or application plugin

API and implementation separation (doc)
Deprecated: compile and runtime
Declare dependencies: api, implementation, compileOnly, runtimeOnly
Resolved dependencies: compileClasspath or runtimeClasspath
"of each dependency, give me the variant required for compilation"
"of each dependency, give me the variant required to run the application"
Create a separate project and apply java-platform plugin (doc)

Use dependency constraints block
Use rich versions, including strict versions, if required (doc)
dependencies {
  constraints {
    api("com.google.guava:guava:24.1.1-jre!!")
    api("com.google.inject:guice") {
      version {
        strictly("[4.0, 5.0[")
        // require("[4.0, 5.0[")
        prefer("4.2.0")
        reject("4.2.1")
        // rejectAll()
      }
      because("Only version 4 of Guice has all DI features we need.")
    }
  }
}Local project with java-platform plugin
dependencies {
  api(platform(project(":my-platform")))
}Published platform (BOM can serve as platform)
dependencies {
  api(platform("com.fasterxml.jackson:jackson-bom:2.9.8"))
}

Create Javadoc and/or sources jars for a java-library
java {
    withJavadocJar()
    withSourcesJar()
}sourcesJar and javadocJar run as part of assemble
If maven-publish or ivy-publish are applied, jars are published with publish
Publication of the data-0.1 component of the demo project
data/0.1/
├── data-0.1.pom
      <!-- do_not_remove: published-with-gradle-metadata -->
├── data-0.1.module
      variants [
        { "name": "apiElements",     "files": [...], ... }
        { "name": "runtimeElements", "files": [...], ... }
        { "name": "javadocElements", "files": [...], ... }
        { "name": "sourcesElements", "files": [...], ... }
      ]
├── data-0.1.jar
├── data-0.1-javadoc.jar
├── data-0.1-sources.jarPublication of the platform-0.1 component of the demo project
platform/0.1/
├── platform-0.1.pom
      <!-- do_not_remove: published-with-gradle-metadata -->
├── platform-0.1.module
      variants [
      {
        "dependencyConstraints": [
          { "group": "com.google.guava", "module": "guava",
            "version": { "strictly": "24.1.1-jre" }
          },
          { "group": "com.google.inject", "module": "guice",
            "version": { "requires": "4.2.2" }
          },
          ...Apply java-test-fixtures plugin in addition to java-library (doc)
Use src/testFixtures/java (or src/testFixtures/<other-jvm-language>)

Depend on the test fixture variant of another project/component (doc)
dependencies {
  // Use test fixtures of local project
  testImplementation(testFixtures(project(":data")))
  // Use test fixture of Guava (if it would publish test fixtures)
  testImplementation(testFixtures("com.google.guava:guava")))
}Functionality of java-library plugin (doc)
val loud: SourceSet by sourceSets.creating
java {
  registerFeature("loud") {
    // code isolated: separate implementation and dependencies
    usingSourceSet(loud)
    // code not isolated: use for "optional dependencies"
    usingSourceSet(sourceSets.main)
  }
}
dependencies {
  "loudApi"(project(":data"))
  "loudImplementation"("org.apache.commons:commons-lang3")
}Depend on a feature variant of another project/component (doc)
dependencies {
  implementation(project(":hello-java-service")) {
    capabilities {
      // Use feature variant by requesting the corresponding capability
      requireCapability("org.gradle.hello6:hello-java-service-loud")
    }
  }
  implementation("com.google.inject:guice") {
    capabilities {
      // Select no_aop feature of Guice (not published, added by rule)
      requireCapability("com.google.inject:guice-no_aop")
    }
  }
}Add additional jdk${version}Api and jdk${version}Runtime variants and
set org.gradle.jvm.version for different variants (6 or 8)
Gradle uses targetCompatibility to select the best variant (best jar)
subprojects {
  dependencies {
    components {
      withModule<GuavaRule>("com.google.guava:guava")
    }
  }
}
Use in one project
java {
    sourceCompatibility = JavaVersion.VERSION_13
    targetCompatibility = JavaVersion.VERSION_13
}Or use in all subprojects
subprojects {
    plugins.withType<JavaPlugin> {
        extensions.configure<JavaPluginExtension> {
            sourceCompatibility = JavaVersion.VERSION_13
            targetCompatibility = JavaVersion.VERSION_13
        }
    }
}Turn on support for Java 13 preview features like text blocks
// for compilation
tasks.withType<JavaCompile> {
    options.compilerArgs.add("--enable-preview")
}
// to run tests
tasks.withType<Test>  {
    jvmArgs = listOf("--enable-preview")
}
// to run an application
application {
    applicationDefaultJvmArgs = listOf("--enable-preview")
}// to generate Javadoc
tasks.withType<Javadoc>  {
    val javadocOptions = options as CoreJavadocOptions
    javadocOptions.addStringOption("source", "13")
    javadocOptions.addBooleanOption("-enable-preview", true)
}Turn on incremental compilation for Groovy compile tasks (doc)
tasks.withType<GroovyCompile> { options.isIncremental = true }
Turn on compilation avoidance between projects in settings.gradle(.kts)
enableFeaturePreview("GROOVY_COMPILATION_AVOIDANCE") (doc)
Combine groovy and java-library plugins
For example to make Groovy an implementation detail
Gradle now uses org.scala-sbt:zinc (doc)
Configure Zinc compiler version scala { zincVersion.set("1.3.1") }
Combine scala and java-library plugins
For example to make Scala an implementation detail
$ gradle initSelect type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4]Select implementation language:
  1: Groovy
  2: Java
  3: Kotlin
Enter selection (default: Java) [1..3]Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2]├── build.gradle.kts
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
└── src
    ├── functionalTest
    │   └── java
    │       └── my
    │           └── MyPluginFunctionalTest.java
    ├── main
    │   ├── java
    │   │   └── my
    │   │       └── MyPlugin.java
    │   └── resources
    └── test
        ├── java
        │   └── my
        │       └── MyPluginTest.java
        └── resourcesManaged properties: Lazy properties with less boilerplate (doc)
buildSrc/src/main/java/ATask.java
abstract class ATask extends DefaultTask {
    abstract Property<String> getInput();
    @TaskAction
    void doAction() {
        System.out.println(input.get());
    }
}build.gradle.kts
tasks.create<ATask>("a") {
    input.set("something")
    // input.set(provider { ... })
}Worker API: Execute work in parallel and/or isolation (separate JVM) (doc)
New in Gradle 6: FileSystemOperations (work with files)
New in Gradle 6: ExecOperations (execute extra processes)
private final FileSystemOperations fileSystemOperations
@Inject
public ReverseFile(FileSystemOperations fileSystemOperations) {
    this.fileSystemOperations = fileSystemOperations
}
@Override
public void execute() {
    fileSystemOperations.copy {
        from parameters.fileToReverse
        into parameters.destinationDir
        filter { String line -> line.reverse() }
    }
}The previously shown variant-aware features can be leveraged in plugins
Examples of plugins that make heavy use of it already
Android (will add publishing in 3.6.0)
Kotlin Native (already publishes Gradle Module Metadata)
Gradle User Manual with more content on dependency management
Recorded Dependency Management webcasts
Regular online intro and advanced trainings - gradle.com/training
Declared Dependencies

Resolved Dependencies



junit-jupiter-api-5.6.0.module (excerpt)
"variants": [
  { "name": "apiElements" ... },
  { "name": "runtimeElements" ... },
  { "name": "javadocElements",
    "attributes": {
      "org.gradle.category": "documentation",
      "org.gradle.docstype": "javadoc",
    },
    "files": [
      { "url": "junit-jupiter-api-5.6.0-javadoc.jar" ... }
    ]},
  { "name": "sourcesElements" ... },build.gradle.kts (consumer example)
val javadoc by configurations.creating {
    attributes.attribute(CATEGORY_ATTRIBUTE, objects.named(DOCUMENTATION))
    attributes.attribute(DOCS_TYPE_ATTRIBUTE, objects.named(JAVADOC))
}
dependencies { javadoc("org.junit.jupiter:junit-jupiter-api:5.6.0") }
tasks.create("doSomethingWithJavadocs") { doLast { javadoc.files } }junit-jupiter-api-5.6.0.module (excerpt)
"variants": [
  {
    "name": "apiElements",
    "attributes": { "org.gradle.category": "library", ... },
    "dependencies": [
      {
        "group": "org.junit",
        "module": "junit-bom",
        "version": { "requires": "5.6.0" },
        "attributes": { "org.gradle.category": "platform" },
      }
    ]
  },junit-bom-5.6.0.module (excerpt)
"variants": [
  {
    "name": "apiElements",
    "attributes": { "org.gradle.category": "platform", ... },
    "dependencyConstraints": [
      {
        "group": "org.junit.jupiter",
        "module": "junit-jupiter-api",
        "version": { "requires": "5.6.0" }
      },
      {
        "group": "org.junit.jupiter",
        "module": "junit-jupiter-engine",
        "version": { "requires": "5.6.0" }
      }
      ...
val variantVersion = ctx.details.id.version
val version = variantVersion.substring(0, variantVersion.indexOf("-"))
val artifactName = variantVersion.substring(variantVersion.indexOf("-") + 1)
val otherArtifactName = if (artifactName == "android") "jre" else "android"
val jdkVersion = if (artifactName == "android") 6 else 8
val otherJdkVersion = if (artifactName == "android") 8 else 6
ctx.details.allVariants {
  attributes {
    if (!contains(TARGET_JVM_VERSION_ATTRIBUTE)) {
      attribute(TARGET_JVM_VERSION_ATTRIBUTE, jdkVersion)
    }
  }
  withCapabilities {
    if (!capabilities.any { it.name == "google-collections" }) {
      addCapability("com.google.collections",
          "google-collections", variantVersion)
    }
  }
}
listOf("compile", "runtime").forEach { base ->
  ctx.details.addVariant("jdk${otherJdkVersion}${base.capitalize()}", base) {
    attributes {
      attribute(TARGET_JVM_VERSION_ATTRIBUTE, otherJdkVersion)
    }
    withFiles {
      removeAllFiles()
      addFile("guava-$version-$otherArtifactName.jar",
          "../$version-$otherArtifactName/guava-$version-$otherArtifactName.jar")
    }
  }
}

hello-java-service/gradle.kts defines com.google.guava:guava:24.1.1-android!!
> Cannot find version of 'guava' that satisfies version constraints:
  Dependency path 'app' --> 'guice:4.2.2' --> 'guava:25.1-android'
  Dependency path 'app' --> 'hello-java-service' --> 'guava:{strictly 24.1.1-android}'app/gradle.kts defines com.google.guava:guava:24.1.1-android!!
\--- guice:4.2.2
     \--- guava:25.1-android -> 24.1.1-android