Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KTOR-7743 Refactor Gradle configuration to use precompiled script plugins #4577

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
6 changes: 5 additions & 1 deletion build-logic/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
/*
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
`kotlin-dsl`
}

dependencies {
implementation(libs.kotlinx.atomicfu.gradlePlugin)
implementation(libs.kotlin.gradlePlugin)
implementation(libs.dokka.gradlePlugin)
implementation(libs.develocity)
implementation(libs.gradleDoctor)
implementation(libs.kotlinter)

// A hack to make version catalogs accessible from buildSrc sources
// https://github.com/gradle/gradle/issues/15383#issuecomment-779893192
Expand Down
15 changes: 15 additions & 0 deletions build-logic/src/main/kotlin/ktorbuild.codestyle.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

import ktorbuild.internal.ktorBuild

plugins {
id("org.jmailen.kotlinter")
}

kotlinter {
// Don't fail lint tasks on CI as we don't want TeamCity to show a build failure in addition to an actual lint report
ignoreLintFailures = ktorBuild.isCI.get()
reporters = arrayOf("checkstyle", "plain")
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/*
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

import ktorbuild.internal.ktorBuild
import org.gradle.api.services.internal.RegisteredBuildServiceProvider

plugins {
id("ktorbuild.base")
id("com.osacky.doctor")
}

Expand All @@ -21,8 +23,8 @@ doctor {

// Always monitor tasks on CI, but disable it locally by default with providing an option to opt-in.
// See 'doctor.enableTaskMonitoring' in gradle.properties for details.
val enableTasksMonitoring = CI ||
properties.getOrDefault("doctor.enableTaskMonitoring", "false").toString().toBoolean()
val enableTasksMonitoring = ktorBuild.isCI.get() ||
findProperty("doctor.enableTaskMonitoring")?.toString().toBoolean()

if (!enableTasksMonitoring) {
logger.info("Gradle Doctor task monitoring is disabled.")
Expand Down
19 changes: 17 additions & 2 deletions build-logic/src/main/kotlin/ktorbuild.kmp.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,31 @@
* Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

@file:OptIn(ExperimentalKotlinGradlePluginApi::class)

import ktorbuild.internal.*
import ktorbuild.internal.gradle.*
import ktorbuild.internal.ktorBuild
import ktorbuild.maybeNamed
import ktorbuild.targets.*
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi

plugins {
id("ktorbuild.base")
kotlin("multiplatform")
id("org.jetbrains.kotlinx.atomicfu")
id("ktorbuild.codestyle")
}

kotlin {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
explicitApi()

compilerOptions {
progressiveMode = true
apiVersion = ktorBuild.kotlinApiVersion
languageVersion = ktorBuild.kotlinLanguageVersion
freeCompilerArgs.addAll("-Xexpect-actual-classes")
}

applyHierarchyTemplate(KtorTargets.hierarchyTemplate)
addTargets(ktorBuild.targets)

Expand Down Expand Up @@ -55,3 +67,6 @@ if (targets.hasNative) {
onlyIf("run only on Windows") { ktorBuild.os.get().isWindows() }
}
}

setupTrain()
if (ktorBuild.isCI.get()) configureTestTasksOnCi()
12 changes: 12 additions & 0 deletions build-logic/src/main/kotlin/ktorbuild.project.internal.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
id("ktorbuild.kmp")
}

kotlin {
// Disable explicit API by default for internal projects
explicitApi = null
}
36 changes: 36 additions & 0 deletions build-logic/src/main/kotlin/ktorbuild/KtorBuildExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.gradle.kotlin.dsl.newInstance
import org.gradle.kotlin.dsl.property
import org.gradle.platform.BuildPlatform
import org.gradle.platform.OperatingSystem
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import javax.inject.Inject

@Suppress("UnstableApiUsage")
Expand All @@ -33,6 +34,15 @@ abstract class KtorBuildExtension(
buildPlatform: BuildPlatform,
) : this(objects, providers, buildPlatform, targets = objects.newInstance())

private val buildingOnTeamCity: Provider<Boolean> =
providers.environmentVariable("TEAMCITY_VERSION").map(String::isNotBlank)

val isCI: Provider<Boolean> =
providers.environmentVariable("CI")
.map(String::isNotBlank)
.orElse(buildingOnTeamCity)
.orElse(false)

/**
* The JDK version to be used to build the project.
* By default, the minimal supported JDK version is used.
Expand Down Expand Up @@ -60,6 +70,26 @@ abstract class KtorBuildExtension(
.orElse(providers.provider { JavaVersion.current().majorVersion })
.map(JavaLanguageVersion::of)

/**
* The Kotlin API version Ktor should be compatible with.
*
* DON'T change the property name as it is used in Kotlin Libraries train.
*/
val kotlinApiVersion: Provider<KotlinVersion> =
providers.gradleProperty("kotlin_api_version")
.map(KotlinVersion::fromVersion)
.orElse(DEFAULT_KOTLIN_API_VERSION)

/**
* The Kotlin Language version Ktor should be compatible with.
*
* DON'T change the property name as it is used in Kotlin Libraries train.
*/
val kotlinLanguageVersion: Provider<KotlinVersion> =
providers.gradleProperty("kotlin_language_version")
.map(KotlinVersion::fromVersion)
.orElse(DEFAULT_KOTLIN_LANGUAGE_VERSION)

/** Host operating system. */
val os: Provider<OperatingSystem> = providers.provider { buildPlatform.operatingSystem }

Expand All @@ -68,5 +98,11 @@ abstract class KtorBuildExtension(

/** The default (minimal) JDK version used for building the project. */
private val DEFAULT_JDK = JavaLanguageVersion.of(8)

/** The default (minimal) Kotlin version used as API version. */
private val DEFAULT_KOTLIN_API_VERSION = KotlinVersion.KOTLIN_2_0

/** The default Kotlin version used as Language version. */
private val DEFAULT_KOTLIN_LANGUAGE_VERSION = KotlinVersion.KOTLIN_2_1
}
}
75 changes: 75 additions & 0 deletions build-logic/src/main/kotlin/ktorbuild/internal/Train.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package ktorbuild.internal

import org.gradle.api.Project
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.withType

fun Project.setupTrain() {
if (!buildSnapshotTrain) return

if (name == "ktor-client") {
println("Manifest of kotlin-compiler-embeddable.jar")
printManifest()
}

configureVersion()
filterSnapshotTests()
}

// Hacking test tasks, removing stress and flaky tests"
private fun Project.filterSnapshotTests() {
tasks.withType<Test>().configureEach {
exclude("**/*ServerSocketTest*")
exclude("**/*NettyStressTest*")
exclude("**/*CIOMultithreadedTest*")
exclude("**/*testBlockingConcurrency*")
exclude("**/*testBigFile*")
exclude("**/*numberTest*")
exclude("**/*testWithPause*")
exclude("**/*WebSocketTest*")
exclude("**/*PostTest*")
exclude("**/*testCustomUrls*")
exclude("**/*testStaticServeFromDir*")
exclude("**/*testRedirect*")
exclude("**/*CIOHttpsTest*")
}
}

private fun Project.printManifest() {
configurations.matching { it.name == "kotlinCompilerClasspath" }.all {
resolvedConfiguration.files.filter { it.name.contains("kotlin-compiler-embeddable") }.forEach {
val manifest = zipTree(it).matching {
include("META-INF/MANIFEST.MF")
}.files.first()

manifest.readLines().forEach {
println(it)
}
}
}
}

private fun Project.configureVersion() {
version = findProperty("DeployVersion") ?: return

if (buildSnapshotTrain && !rootProject.hasProperty("skip_snapshot_checks")) {
check(version, rootProject.libs.versions.atomicfu, "atomicfu")
check(version, rootProject.libs.versions.coroutines, "coroutines")
check(version, rootProject.libs.versions.serialization, "serialization")
}
}

private val Project.buildSnapshotTrain: Boolean
get() = rootProject.findProperty("build_snapshot_train")?.toString().toBoolean()

private fun check(version: Any, libVersionProvider: Provider<String>, libName: String) {
val libVersion = libVersionProvider.get()
check(version == libVersion) {
"Current deploy version is $version, but $libName version is not overridden ($libVersion)"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

import com.gradle.develocity.agent.gradle.test.*
import org.gradle.api.*
import org.gradle.api.tasks.testing.*
import org.gradle.kotlin.dsl.*
import org.jetbrains.kotlin.gradle.targets.jvm.tasks.*
import org.jetbrains.kotlin.gradle.tasks.*
package ktorbuild.targets

val CI = System.getenv("TEAMCITY_VERSION") != null
import com.gradle.develocity.agent.gradle.test.DevelocityTestConfiguration
import com.gradle.develocity.agent.gradle.test.TestRetryConfiguration
import org.gradle.api.Project
import org.gradle.api.tasks.testing.AbstractTestTask
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.extra
import org.gradle.kotlin.dsl.getByName
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest
import org.jetbrains.kotlin.gradle.tasks.KotlinTest

/** Applies CI-specific configurations to test tasks. */
fun Project.configureTestTasksOnCi() {
internal fun Project.configureTestTasksOnCi() {
// Don't fail build on the CI:
// 1. To distinct builds failed because of failed tests and because of compilation errors or anything else.
// TeamCity parses test results to define build status, so the build won't be green.
Expand Down
35 changes: 5 additions & 30 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
import org.jetbrains.kotlin.konan.target.HostManager

extra["globalM2"] = "${project.file("build")}/m2"
Expand Down Expand Up @@ -33,9 +32,8 @@ extra["nonDefaultProjectStructure"] = mutableListOf(
apply(from = "gradle/compatibility.gradle")

plugins {
id("ktorbuild.base")
id("ktorbuild.doctor")
alias(libs.plugins.binaryCompatibilityValidator)
conventions.gradleDoctor
}

println("Build version: ${project.version}")
Expand All @@ -45,35 +43,18 @@ subprojects {

extra["hostManager"] = HostManager()

setupTrainForSubproject()

val nonDefaultProjectStructure: List<String> by rootProject.extra
if (nonDefaultProjectStructure.contains(project.name)) return@subprojects

apply(plugin = "ktorbuild.kmp")
apply(plugin = "atomicfu-conventions")

if (CI) configureTestTasksOnCi()

kotlin {
if (!internalProjects.contains(project.name)) explicitApi()

compilerOptions {
languageVersion = getKotlinLanguageVersion()
apiVersion = getKotlinApiVersion()
progressiveMode = true
}
}

if (!internalProjects.contains(project.name)) {
if (project.name !in internalProjects) {
apply(plugin = "ktorbuild.kmp")
configurePublication()
} else {
apply(plugin = "ktorbuild.project.internal")
}

configureCodestyle()
}

println("Using Kotlin compiler version: ${libs.versions.kotlin.get()}")
filterSnapshotTests()

fun configureDokka() {
allprojects {
Expand All @@ -86,9 +67,3 @@ fun configureDokka() {
}

configureDokka()

subprojects {
tasks.withType<KotlinCompilationTask<*>>().configureEach {
configureCompilerOptions()
}
}
7 changes: 1 addition & 6 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
Expand All @@ -10,11 +10,6 @@ dependencies {
implementation(libs.kotlin.gradlePlugin)
implementation(libs.kotlin.serialization)

implementation(libs.kotlinter)
implementation(libs.develocity)
implementation(libs.gradleDoctor)
implementation(libs.kotlinx.atomicfu.gradlePlugin)

// A hack to make version catalogs accessible from buildSrc sources
// https://github.com/gradle/gradle/issues/15383#issuecomment-779893192
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
Expand Down
21 changes: 0 additions & 21 deletions buildSrc/src/main/kotlin/Codestyle.kt

This file was deleted.

Loading
Loading