Gradle 5 Kotlin DSL: Common Tasks & Maven Artifacts in multi-modules projects
Asked Answered
M

1

8

I really would like to appreciate Gradle 5 especially in combination with the new Kotlin DSL, but I’m having a very hard time to get (in my eyes) a very, very simple and common build running with Gradle.

Task

Release a Java library with several interdependent submodules in Maven default directory layout as high-quality Maven artifacts/repository in a to-the-point, simple Gradle build (i.e. DRY).

Therefore: Have a root project as umbrella which defines & contains all the common configuration (practically all except the real dependencies).

My current struggles

I ported my current "results" to a sample project on Github and asked this question in the Gradle forum already.

Currently I'm failing to declare the necessary task to provide standard -sources and -javadoc artifacts in my central build.

For example these three "solutions" which you'll find on looking for a Kotlin DSL based solutions all do no (longer) work in a multi-module scenario:

Incomplete solution (/build.gradle.kts)

Complete example see on Github: https://github.com/bentolor/gradle-maven-multimodule-kotlindsl

subprojects {
    apply(plugin = "java-library")
    apply(plugin = "maven-publish")
    group = "de.bentolor.sampleproject"
    version = "0.1.0"

    repositories {
        jcenter()
    }

    dependencies {
        // Dependencies used in EVERY module
        "compile"("commons-logging:commons-logging:1.2")
        "testImplementation"("junit:junit:4.12")
    }

    tasks {
        // not working
        /*register("sourcesJar", Jar::class.java) {
            from(sourceSets.main.get().allJava)
            classifier = "sources"
        }*/

       // not working, eiher
       /* task<Jar>("sourcesJar") {
            from(sourceSets.main.get().allJava)
            classifier = "sources"
       } */
    }

    configure<JavaPluginExtension> {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }

    configure<PublishingExtension> {
        publications {
            create<MavenPublication>(project.name) {
                from(components["java"])
                // won't work, beause inaccessible declaration in `tasks{}`-Block
                //add("archives", javadocJar)
                //add("archives", sourcesJar)
            }
        }

        repositories {
            mavenLocal()
        }
    }
}

Example submodule /module2/build.gradle.kts

group = "de.bentolor.sampleproject.module2"

dependencies {
    compile(project(":module1"))
}
Mordant answered 5/12, 2018 at 11:56 Comment(0)
D
12

Try this:

subprojects {
    apply<JavaLibraryPlugin>()
    apply<MavenPublishPlugin>()

    group = "de.bentolor.sampleproject"
    version = "0.1.0"

    repositories {
        jcenter()
    }

    dependencies {
        val implementation by configurations
        val testImplementation by configurations

        implementation("commons-logging:commons-logging:1.2")
        testImplementation("junit:junit:4.12")
    }

    // This will work, but as long as these tasks are need only for publishing you can declare them inplace later where you need 
    // tasks {
    //     val sourcesJar by creating(Jar::class) {
    //         val sourceSets: SourceSetContainer by project
    //         from(sourceSets["main"].allJava)
    //         classifier = "sources"
    //     }
    //     val javadoc by getting(Javadoc::class)
    //     val javadocJar by creating(Jar::class) {
    //         from(javadoc)
    //         classifier = "javadoc"
    //     }
    // }

    configure<JavaPluginExtension> {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }

    configure<PublishingExtension> {
        publications {
            create<MavenPublication>(project.name) {
                from(components["java"])

                // If you configured them before
                // val sourcesJar by tasks.getting(Jar::class)
                // val javadocJar by tasks.getting(Jar::class)

                val sourcesJar by tasks.creating(Jar::class) {
                    val sourceSets: SourceSetContainer by project

                    from(sourceSets["main"].allJava)
                    classifier = "sources"
                }
                val javadocJar by tasks.creating(Jar::class) {
                    from(tasks.get("javadoc"))
                    classifier = "javadoc"
                }

                artifact(sourcesJar)
                artifact(javadocJar)
            }
        }
    }
}

A few notes:

  • Why use String-based apply, when you can do a type-safe apply<T>()?
  • Why use invokes on stings in dependencies, when you can use delegates, which is less hacky and better refactorable.
  • Consider using implementation instead of compile

Why sourceSets is not working in a multi-module project?

When you're using Kotlin DSL it generates accessors for projects based on the applied plugins. It's a two-step process: first Gradle processes plugins (that's why it's recommended to put them in plugins block) and generates accessors and then you can use them in your code (accessors are generated as Kotlin extensions for Project, NamedDomainObjectContainer and so on). But if you're configuring subprojects there are two issues:

  • Parent project evaluates before child, so the extensions for child are not known in parent.
  • The set of plugins applied to parent and child is different and you need to use children accessors in parent.

sourceSets is one of the accessors generated by Kotlin DSL for children. And it's just not available in parent. You can try it yourself: apply only java plugin in subprojects. sourceSets will be available in children build scripts, but not in parent.

This is also why you can use java in children, but have to use configure<JavaPluginExtension> when configuring it in parent.

But you can use delegates to get references for domain objects, like tasks, source sets, configuration and so on.

Disastrous answered 7/12, 2018 at 1:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.