Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ val errorPronePluginVersion = "4.2.0"
* @see <a href="https://github.com/google/protobuf-gradle-plugin/releases">
* Protobuf Gradle Plugins Releases</a>
*/
val protobufPluginVersion = "0.9.5"
val protobufPluginVersion = "0.9.4"

/**
* The version of Dokka Gradle Plugins.
Expand Down
3 changes: 3 additions & 0 deletions buildSrc/src/main/kotlin/DependencyResolution.kt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ fun NamedDomainObjectContainer<Configuration>.forceVersions() {

private fun ResolutionStrategy.forceProductionDependencies() {
@Suppress("DEPRECATION") // Force versions of SLF4J and Kotlin libs.
Protobuf.libs.forEach {
force(it)
}
force(
AnimalSniffer.lib,
AutoCommon.lib,
Expand Down
62 changes: 62 additions & 0 deletions buildSrc/src/main/kotlin/io/spine/dependency/boms/Boms.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2025, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.dependency.boms

import io.spine.dependency.kotlinx.Coroutines
import io.spine.dependency.lib.Jackson
import io.spine.dependency.lib.Kotlin
import io.spine.dependency.test.JUnit

/**
* The collection of references to BOMs applied by [BomsPlugin].
*/
object Boms {

/**
* The base production BOMs.
*/
val core = listOf(
Kotlin.bom,
Coroutines.bom
)

/**
* The BOMs for testing dependencies.
*/
val testing = listOf(
JUnit.bom
)

/**
* Technology-based BOMs.
*/
object Optional {
val jackson = listOf(
Jackson.bom
)
}
}
148 changes: 148 additions & 0 deletions buildSrc/src/main/kotlin/io/spine/dependency/boms/BomsPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright 2025, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.dependency.boms

import io.spine.dependency.lib.Kotlin
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration

/**
* The plugin which forces versions of platforms declared in the [Boms] object.
*
* Versions are enforced via the
* [org.gradle.api.artifacts.dsl.DependencyHandler.enforcedPlatform] call
* for configurations of the project to which the plugin is applied.
*
* The configurations are selected by the "kind" of BOM.
*
* [Boms.core] are applied to:
* 1. Production configurations, such as `api` or `implementation`.
* 2. Compilation configurations.
* 3. All `ksp` configurations.
*
* [Boms.testing] are applied to all testing configurations.
*
* In addition to forcing BOM-based dependencies,
* the plugin [forces][org.gradle.api.artifacts.ResolutionStrategy.force] the versions
* of [Kotlin.StdLib.artefacts] for all configurations because even through Kotlin
* artefacts are forced with BOM, the `variants` in the dependencies cannot be
* picked by Gradle.
*
* Run Gradle with the [INFO][org.gradle.api.logging.Logger.isInfoEnabled] logging level
* to see the dependencies forced by this plugin.
*/
class BomsPlugin : Plugin<Project> {

private val productionConfigs = listOf(
"api",
"implementation",
"compileOnly",
"runtimeOnly"
)

override fun apply(project: Project) = with(project) {

fun log(message: () -> String) {
val logger = project.logger
if (logger.isInfoEnabled) {
logger.info(message.invoke())
}
}

fun Configuration.applyBoms(boms: List<String>) {
boms.forEach { bom ->
withDependencies {
val platform = project.dependencies.enforcedPlatform(bom)
addLater(provider { platform })
log { "Applied BOM: `$bom` to the configuration: `${this@applyBoms.name}`." }
}
}
}

configurations.run {
matching { isCompilationConfig(it.name) }.all {
applyBoms(Boms.core)
}
matching { isKspConfig(it.name) }.all {
applyBoms(Boms.core)
}
matching { it.name in productionConfigs }.all {
applyBoms(Boms.core)
}
matching { isTestConfig(it.name) }.all {
applyBoms(Boms.core + Boms.testing)
}

fun Configuration.diagSuffix(): String =
"the configuration `$name` in the project: `${project.path}`."

matching { !supportsBom(it.name) }.all {
resolutionStrategy.eachDependency {
if (requested.group == Kotlin.group) {
val kotlinVersion = Kotlin.runtimeVersion
useVersion(kotlinVersion)
log { "Forced Kotlin version `$kotlinVersion` in " + this@all.diagSuffix() }
}
}
}

all {
resolutionStrategy {
// The versions for Kotlin are resoled above correctly.
// But that does not guarantees that Gradle picks up a correct `variant`.
Kotlin.StdLib.artefacts.forEach { artefact ->
force(artefact)
log { "Forced the version of `$artefact` in " + this@all.diagSuffix() }
}
}
}
}
}

private fun isCompilationConfig(name: String) =
name.contains("compile", ignoreCase = true) &&
// `comileProtoPath` or `compileTestProtoPath`.
!name.contains("ProtoPath", ignoreCase = true)

private fun isKspConfig(name: String) =
name.startsWith("ksp", ignoreCase = true)

private fun isTestConfig(name: String) =
name.startsWith("test", ignoreCase = true)

/**
* Tells if the configuration with the given [name] supports forcing
* versions via the BOM mechanism.
*
* Not all configurations supports forcing via BOM. E.g., the configurations created
* by Protobuf Gradle Plugin such as `compileProtoPath` or `extractIncludeProto` do
* not pick up versions of dependencies set via `enforcedPlatform(myBom)`.
*/
private fun supportsBom(name: String) =
(isCompilationConfig(name) || isKspConfig(name) || isTestConfig(name))
}
28 changes: 21 additions & 7 deletions buildSrc/src/main/kotlin/io/spine/dependency/lib/Kotlin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,30 @@ object Kotlin {
const val bom = "$group:kotlin-bom:$runtimeVersion"

const val scriptRuntime = "$group:kotlin-script-runtime"
const val stdLib = "$group:kotlin-stdlib"
const val stdLibCommon = "$group:kotlin-stdlib-common"

const val toolingCore = "$group:kotlin-tooling-core"
object StdLib {
private const val infix = "kotlin-stdlib"
const val itself = "$group:$infix"
const val common = "$group:$infix-common"
const val jdk7 = "$group:$infix-jdk7"
const val jdk8 = "$group:$infix-jdk8"

val artefacts = setOf(itself, common, jdk7, jdk8).map { "$it:$runtimeVersion" }
}

@Deprecated("Please use `StdLib.itself` instead.", ReplaceWith("StdLib.itself"))
const val stdLib = StdLib.itself

@Deprecated("Please use `stdLib` instead.")
const val stdLibJdk7 = "$group:kotlin-stdlib-jdk7"
@Deprecated("Please use `StdLib.common` instead.", ReplaceWith("StdLib.common"))
const val stdLibCommon = StdLib.common

@Deprecated("Please use `stdLib` instead.")
const val stdLibJdk8 = "$group:kotlin-stdlib-jdk8"
@Deprecated("Please use `StdLib.jdk7` instead.", ReplaceWith("StdLib.jdk7"))
const val stdLibJdk7 = StdLib.jdk7

@Deprecated("Please use `StdLib.jdk8` instead.")
const val stdLibJdk8 = StdLib.jdk8

const val toolingCore = "$group:kotlin-tooling-core"

const val reflect = "$group:kotlin-reflect"
const val testJUnit5 = "$group:kotlin-test-junit5"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ object Protobuf {
object GradlePlugin {
/**
* The version of this plugin is already specified in `buildSrc/build.gradle.kts` file.
* Thus, when applying the plugin to projects build files, only the [id] should be used.
* Thus, when applying the plugin to project build files, only the [id] should be used.
*
* When changing the version, also change the version used in the `build.gradle.kts`.
*/
Expand Down
3 changes: 3 additions & 0 deletions buildSrc/src/main/kotlin/io/spine/dependency/test/JUnit.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ object JUnit {
* testImplementation(enforcedPlatform(JUnit.bom))
* }
* ```
* The version of JUnit is forced automatically by
* the [BomsPlugin][io.spine.dependency.boms.BomsPlugin]
* when it is applied to the project.
*/
const val bom = "org.junit:junit-bom:$version"

Expand Down
25 changes: 2 additions & 23 deletions buildSrc/src/main/kotlin/jvm-module.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@ import BuildSettings.javaVersion
import io.spine.dependency.build.CheckerFramework
import io.spine.dependency.build.Dokka
import io.spine.dependency.build.ErrorProne
import io.spine.dependency.build.JSpecify
import io.spine.dependency.lib.Guava
import io.spine.dependency.lib.JavaX
import io.spine.dependency.lib.Protobuf
import io.spine.dependency.local.Logging
import io.spine.dependency.local.Reflect
import io.spine.dependency.local.TestLib
import io.spine.dependency.test.Jacoco
import io.spine.gradle.checkstyle.CheckStyleConfig
import io.spine.gradle.github.pages.updateGitHubPages
Expand All @@ -43,8 +41,6 @@ import io.spine.gradle.javadoc.JavadocConfig
import io.spine.gradle.kotlin.applyJvmToolchain
import io.spine.gradle.kotlin.setFreeCompilerArgs
import io.spine.gradle.report.license.LicenseReporter
import io.spine.gradle.testing.configureLogging
import io.spine.gradle.testing.registerTestTasks

plugins {
`java-library`
Expand All @@ -71,7 +67,6 @@ project.run {

val generatedDir = "$projectDir/generated"
setTaskDependencies(generatedDir)
setupTests()

configureGitHubPages()
}
Expand Down Expand Up @@ -134,12 +129,8 @@ fun Module.addDependencies() = dependencies {
api(Guava.lib)

compileOnlyApi(CheckerFramework.annotations)
compileOnlyApi(JavaX.annotations)
api(JSpecify.annotations)
ErrorProne.annotations.forEach { compileOnlyApi(it) }

implementation(Logging.lib)

testImplementation(TestLib.lib)
}

fun Module.forceConfigurations() {
Expand All @@ -157,18 +148,6 @@ fun Module.forceConfigurations() {
}
}

fun Module.setupTests() {
tasks {
registerTestTasks()
test.configure {
useJUnitPlatform {
includeEngines("junit-jupiter")
}
configureLogging()
}
}
}

fun Module.setTaskDependencies(generatedDir: String) {
tasks {
val cleanGenerated by registering(Delete::class) {
Expand Down
13 changes: 13 additions & 0 deletions buildSrc/src/main/kotlin/module-testing.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ import io.spine.dependency.test.Truth
import io.spine.gradle.testing.configureLogging
import io.spine.gradle.testing.registerTestTasks

/**
* This convention plugin applies test dependencies and configures test-related tasks.
*
* The version of the [JUnit] platform must be applied via the [BomsPlugin][io.spine.dependency.boms.BomsPlugin]:
*
* ```kotlin
* apply<BomsPlugin>()
* ```
*/
@Suppress("unused")
private val about = ""

plugins {
`java-library`
}
Expand All @@ -46,6 +58,7 @@ dependencies {
forceJunitPlatform()

testImplementation(Jupiter.api)
testImplementation(Jupiter.params)
testImplementation(JUnit.pioneer)

testImplementation(Guava.testLib)
Expand Down