Defining common external dependencies for subprojects

Hi,

I am working on multiproject gradle application and I need to define dependencies which are required by multiple subproject.

For eg: lombok

From the documentation I understand that using subprojects {…} and allprojects {…} are not encouraged, instead buildSrc from Convention plugin is recommended. Although not sure how to use it’s different files i.e buildlogic.java-application-conventions.gradle.kts while I need only external dependencies that should be covered by build.gradle.kts.

Could you please suggest or share any reference for the same

Best Regards,
Manish

You can use a convention plugin for that, yes.
What exactly is unclear with using them?

Btw. if it is only about depending on multiple external libraries with one line,
I’d instead just use a “bundle” in a TOML version catalog.

Hello, Thanks for your response. My confusion was for 3 different files getting generated in buildSrc :- setting.gradle, build.gradle and buildlogic.java-application-conventions.gradle.kts. As per my understandig to define the external dependency on build.gradle is enough so what exactly buildlogic.java-application-conventions.gradle.kts fit s into my requirement was something not clear to me .

As you suggested, will check version catalog as I have only to define external dependencies with nexus repo config.

My confusion was for 3 different files getting generated in buildSrc :- setting.gradle, build.gradle and buildlogic.java-application-conventions.gradle.kts

buildSrc - similar to an included build which I prefer - is an own build that happens to be built before your main build and its result prepended to all build script classpaths.

Its settings.gradle defines the buildSrc build and its projects just like your root build’s settings script defines your root build.

Its build.gradle defines the dependencies that the code you developing in it, i. e. your convention plugin, needs.

The buildlogic.java-application-conventions.gradle.kts is a convention plugins implemented as precompiled script plugin. So if you do not use a version catalog bundle for whatever reason, here you would add the dependencies, and then apply that plugin to the projects where you cant that convention to be effective.


Btw. I highly recommend switching to Kotlin DSL. By now it is the default DSL, you immediately get type-safe build scripts, actually helpful error messages if you mess up the syntax, and an amazingly better IDE support if you use a good IDE like IntelliJ IDEA or Android Studio.

1 Like

Actually I am trying to migrate the existing root build.gradle.kts (gradle 6.7) to convention plugin (gradle 8.8).

import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
import nu.studer.gradle.credentials.domain.CredentialsContainer

plugins {
    java
    jacoco
    id("com.github.ben-manes.versions") version "0.36.0"
    id("nu.studer.credentials") version "2.1"
}

fun isNonStable(version: String): Boolean {
    val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.toUpperCase().contains(it) }
    val regex = "^[0-9,.v-]+(-r)?$".toRegex()
    val isStable = stableKeyword || regex.matches(version)
    return isStable.not()
}

tasks.named("dependencyUpdates", DependencyUpdatesTask::class.java).configure {
    rejectVersionIf {
        isNonStable(candidate.version) && !isNonStable(currentVersion)
    }
}

tasks.test {
    configure<JacocoTaskExtension> {
        excludes = listOf("reader.*", "compare.*")
        includes = listOf("com.myorg.*", "csv.*")
    }
}


val credentials: CredentialsContainer by project.extra
val repositoryUsername = credentials.getProperty("username") as String?
val repositoryPassword = credentials.getProperty("password") as String?

subprojects {
    apply(plugin = "application")
    apply(plugin = "java")
    apply(plugin = "jacoco")

    version = "0.1"
    group = "com.myorg"

    repositories {
        mavenCentral()
        jcenter()
        maven {
            name = "nexus"
            url = uri("https://nexus.dev.global.myorg.org/nexus/content/repositories/packages.myorg.com/")
            credentials {
                username = repositoryUsername
                password = repositoryPassword
            }
        }
    }

    dependencies {
        val lombokVersion = "1.18.16"
        val mockitoVersion = "3.7.0"
        val jsonAssertVersion = "1.5.0"
        compileOnly("org.projectlombok:lombok:$lombokVersion")
        // Ensure that this annotation processor is run before micronaut-data-processor
        annotationProcessor("org.projectlombok:lombok:$lombokVersion")
        implementation("org.xerial:sqlite-jdbc:3.34.0")
        testCompileOnly("org.projectlombok:lombok:$lombokVersion")
        testAnnotationProcessor("org.projectlombok:lombok:$lombokVersion")
        testImplementation("org.mockito:mockito-core:$mockitoVersion")
        testImplementation("org.mockito:mockito-junit-jupiter:$mockitoVersion")
        testImplementation("org.assertj:assertj-core:3.18.1")
        testImplementation("org.skyscreamer:jsonassert:$jsonAssertVersion")
    }

    tasks.test {
        useJUnitPlatform()  // use JUnit 5 platform
        finalizedBy(tasks.jacocoTestReport, tasks.jacocoTestCoverageVerification)
    }

    tasks.jacocoTestCoverageVerification {
        violationRules {
            rule {
                limit {
                    minimum = (
                            if (this@subprojects.name == "app1") {
                                "0.8"
                            } else {
                                "0.0"
                            }
                            ).toBigDecimal()
                }
            }
        }
    }

    tasks.jacocoTestReport {
        reports {
            xml.isEnabled = true
        }
    }

    java {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
}

// In order to run cbo-adaptor integration tests we need the event-publisher-mock to be assembled
childProjects["app1"]!!.tasks.named("test"){
    dependsOn(childProjects["app2"]!!.tasks.named("assemble"))
}

I have copied the entire code it’s code to myproject.java-conventions.gradle.kts file by copying the subprojects block content to root level. Additionally as suggested in docs I have created build.gradle.kts in buildSrc directory:

plugins {
    `kotlin-dsl`
}

repositories {
    mavenCentral()
}

while refreshing gradle config, getting error as I am defining version of credential plugin in myproject.java-conventions.gradle.kts.

Invalid plugin request [id: 'com.github.ben-manes.versions', version: '0.36.0']. Plugin requests from precompiled scripts must not include a version number. Please remove the version from the offending request and make sure the module containing the requested plugin 'com.github.ben-manes.versions' is an implementation dependency of project ':buildSrc'.
Invalid plugin request [id: 'nu.studer.credentials', version: '3.0']. Plugin requests from precompiled scripts must not include a version number. Please remove the version from the offending request and make sure the module containing the requested plugin 'nu.studer.credentials' is an implementation dependency of project ':buildSrc'.

Do I need to move it to build.gradle.kts ?

Yes, the error message is quite clear, isn’t it?

Also, copying over the whole build script and moving up the subprojects block to top-level is little helpful and will not at all do what you want.

More like copying only the content of subprojects block to the convention plugin, cleaning it up like using plugins { ... } block instead of legacy apply(...), and then then applying that plugin to your subprojects. (Of course directly, not via a subprojects block or you just replaced pest by cholera.