diff --git a/build-logic/README.md b/build-logic/README.md new file mode 100644 index 00000000000..81e834808a0 --- /dev/null +++ b/build-logic/README.md @@ -0,0 +1,22 @@ +# build-logic + +Build logic shared between Ktor subprojects. + +This is similar to `buildSrc`, but uses [composite builds](https://docs.gradle.org/current/userguide/composite_builds.html) +to prevent projects from becoming out-of-date on any change in `buildSrc`. + +This project should be included in the root `settings.gradle.kts`: + +`/settings.gradle.kts` +```kotlin +includeBuild("build-logic") +``` + +`/build.gradle.kts` +```kotlin +plugins { + id("ktorbuild.base") +} +``` + +*The structure of this project is inspired by the structure used in [Dokka](https://github.com/Kotlin/dokka/tree/v2.0.0/build-logic/src/main/kotlin) and [Gradle](https://github.com/gradle/gradle/tree/v8.12.0/build-logic/jvm/src/main/kotlin).* diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 00000000000..f9f52af1e79 --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,24 @@ +/* + * 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) + + // 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)) +} + +// Should be synced with gradle/gradle-daemon-jvm.properties +kotlin { + jvmToolchain(21) +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 00000000000..6781de068a1 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,21 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +pluginManagement { + includeBuild("../build-settings-logic") +} + +plugins { + id("conventions-dependency-resolution-management") +} + +dependencyResolutionManagement { + // Additional repositories for build-logic + @Suppress("UnstableApiUsage") + repositories { + gradlePluginPortal() + } +} + +rootProject.name = "build-logic" diff --git a/build-logic/src/main/kotlin/ktorbuild.base.gradle.kts b/build-logic/src/main/kotlin/ktorbuild.base.gradle.kts new file mode 100644 index 00000000000..e5b2b6ec79a --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild.base.gradle.kts @@ -0,0 +1,10 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import ktorbuild.KtorBuildExtension +import ktorbuild.internal.resolveVersion + +version = resolveVersion() + +extensions.create(KtorBuildExtension.NAME) diff --git a/buildSrc/src/main/kotlin/conventions.gradleDoctor.gradle.kts b/build-logic/src/main/kotlin/ktorbuild.doctor.gradle.kts similarity index 80% rename from buildSrc/src/main/kotlin/conventions.gradleDoctor.gradle.kts rename to build-logic/src/main/kotlin/ktorbuild.doctor.gradle.kts index ebb77fc43bb..d2f7161f964 100644 --- a/buildSrc/src/main/kotlin/conventions.gradleDoctor.gradle.kts +++ b/build-logic/src/main/kotlin/ktorbuild.doctor.gradle.kts @@ -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") } @@ -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.") diff --git a/build-logic/src/main/kotlin/ktorbuild.dokka.gradle.kts b/build-logic/src/main/kotlin/ktorbuild.dokka.gradle.kts new file mode 100644 index 00000000000..86c45d0075b --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild.dokka.gradle.kts @@ -0,0 +1,26 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +import ktorbuild.internal.libs +import org.jetbrains.dokka.gradle.DokkaMultiModuleTask + +plugins { + id("org.jetbrains.dokka") +} + +dependencies { + dokkaPlugin(libs.dokka.plugin.versioning) +} + +if (project == rootProject) { + tasks.withType().configureEach { + val version = project.version + val dokkaOutputDir = "../versions" + val id = "org.jetbrains.dokka.versioning.VersioningPlugin" + val config = """{ "version": "$version", "olderVersionsDir":"$dokkaOutputDir" }""" + + outputDirectory = project.layout.projectDirectory.dir("$dokkaOutputDir/$version") + pluginsMapConfiguration = mapOf(id to config) + } +} diff --git a/build-logic/src/main/kotlin/ktorbuild.kmp.gradle.kts b/build-logic/src/main/kotlin/ktorbuild.kmp.gradle.kts new file mode 100644 index 00000000000..bd4a602687c --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild.kmp.gradle.kts @@ -0,0 +1,70 @@ +/* + * 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.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") +} + +kotlin { + explicitApi() + + compilerOptions { + progressiveMode = true + apiVersion = ktorBuild.kotlinApiVersion + languageVersion = ktorBuild.kotlinLanguageVersion + freeCompilerArgs.addAll("-Xexpect-actual-classes") + } + + applyHierarchyTemplate(KtorTargets.hierarchyTemplate) + addTargets(ktorBuild.targets) + + // Specify JVM toolchain later to prevent it from being evaluated before it was configured. + // TODO: Remove `afterEvaluate` when the BCV issue triggering JVM toolchain evaluation is fixed + // https://github.com/Kotlin/binary-compatibility-validator/issues/286 + afterEvaluate { + jvmToolchain { + languageVersion = ktorBuild.jvmToolchain + } + } +} + +val targets = ktorBuild.targets + +configureCommon() +if (targets.hasJvm) configureJvm() +if (targets.hasJs) configureJs() +if (targets.hasWasmJs) configureWasmJs() + +if (targets.hasJsOrWasmJs) { + tasks.configureEach { + if (name == "compileJsAndWasmSharedMainKotlinMetadata") enabled = false + } +} + +// Run native tests only on matching host. +// There is no need to configure `onlyIf` for Darwin targets as they're configured by KGP. +@Suppress("UnstableApiUsage") +if (targets.hasNative) { + tasks.maybeNamed("linkDebugTestLinuxX64") { + onlyIf("run only on Linux") { ktorBuild.os.get().isLinux() } + } + tasks.maybeNamed("linkDebugTestLinuxArm64") { + onlyIf("run only on Linux") { ktorBuild.os.get().isLinux() } + } + tasks.maybeNamed("linkDebugTestMingwX64") { + onlyIf("run only on Windows") { ktorBuild.os.get().isWindows() } + } +} + +if (ktorBuild.isCI.get()) configureTestTasksOnCi() diff --git a/build-logic/src/main/kotlin/ktorbuild.project.internal.gradle.kts b/build-logic/src/main/kotlin/ktorbuild.project.internal.gradle.kts new file mode 100644 index 00000000000..507a672ea06 --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild.project.internal.gradle.kts @@ -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 +} diff --git a/build-logic/src/main/kotlin/ktorbuild/CInterop.kt b/build-logic/src/main/kotlin/ktorbuild/CInterop.kt new file mode 100644 index 00000000000..739e5b77ff6 --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/CInterop.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package ktorbuild + +import ktorbuild.targets.KtorTargets +import org.gradle.kotlin.dsl.assign +import org.gradle.kotlin.dsl.getValue +import org.gradle.kotlin.dsl.provideDelegate +import org.jetbrains.kotlin.gradle.ExternalKotlinTargetApi +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultCInteropSettings +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.external.project + +/** + * Creates a CInterop configuration for all Native targets using the given [sourceSet] + * in a Kotlin Multiplatform project. + * + * The [name] defines the CInterop configuration name. Definition file is expected to be located + * at `[sourceSet]/interop/[name].def` by default, but can be customized via [definitionFilePath]. + * Additional configuration can be provided through [configure] block. + * + * Simple usage: + * ``` + * kotlin { + * createCInterop("ssl", "posix") + * } + * ``` + * + * Advanced usage with a separate definition for each target and additional configuration: + * ``` + * kotlin { + * createCInterop( + * name = "ssl", + * sourceSet = "posix", + * definitionFilePath = { target -> "$target/interop/ssl.def" }, + * configure = { target -> + * includeDirs("$target/interop/include") + * compilerOpts("-DUSE_SSL") + * } + * ) + * } + * ``` + */ +@Suppress("UnstableApiUsage") +fun KotlinMultiplatformExtension.createCInterop( + name: String, + sourceSet: String, + definitionFilePath: (String) -> String = { "$sourceSet/interop/$name.def" }, + configure: DefaultCInteropSettings.(String) -> Unit = {} +) { + val cinteropTargets = KtorTargets.resolveTargets(sourceSet) + @OptIn(ExternalKotlinTargetApi::class) + val projectDirectory = project.isolated.projectDirectory + + targets.named { it in cinteropTargets } + .all { + check(this is KotlinNativeTarget) { "Can't create cinterop for non-native target $targetName" } + + val main by compilations + main.cinterops.create(name) { + definitionFile = projectDirectory.file(definitionFilePath(targetName)) + configure(targetName) + } + } +} diff --git a/build-logic/src/main/kotlin/ktorbuild/KtorBuildExtension.kt b/build-logic/src/main/kotlin/ktorbuild/KtorBuildExtension.kt new file mode 100644 index 00000000000..ca2d2eb5a56 --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/KtorBuildExtension.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package ktorbuild + +import ktorbuild.internal.gradle.finalizedOnRead +import ktorbuild.targets.KtorTargets +import org.gradle.api.JavaVersion +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.gradle.jvm.toolchain.JavaLanguageVersion +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") +abstract class KtorBuildExtension( + objects: ObjectFactory, + providers: ProviderFactory, + buildPlatform: BuildPlatform, + val targets: KtorTargets, +) { + + @Inject + constructor( + objects: ObjectFactory, + providers: ProviderFactory, + buildPlatform: BuildPlatform, + ) : this(objects, providers, buildPlatform, targets = objects.newInstance()) + + private val buildingOnTeamCity: Provider = + providers.environmentVariable("TEAMCITY_VERSION").map(String::isNotBlank) + + val isCI: Provider = + 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. + */ + val jvmToolchain: Property = + objects.property() + .convention(DEFAULT_JDK) + .finalizedOnRead() + + fun jvmToolchain(version: Int) { + jvmToolchain.set(JavaLanguageVersion.of(version)) + } + + /** + * The JDK version to be used for testing. + * + * The value is determined from the Gradle property "test.jdk". + * If the property is not specified, it defaults to the current JDK used by Gradle. + * + * For example, to run tests against JDK 8, run a test task with flag "-Ptest.jdk=8" + * or put this property to `gradle.properties`. + */ + val jvmTestToolchain: Provider = + providers.gradleProperty("test.jdk") + .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 = + providers.gradleProperty("kotlin_api_version") + .map(KotlinVersion::fromVersion) + .orElse(DEFAULT_KOTLIN_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 = + providers.gradleProperty("kotlin_language_version") + .map(KotlinVersion::fromVersion) + .orElse(DEFAULT_KOTLIN_VERSION) + + /** Host operating system. */ + val os: Provider = providers.provider { buildPlatform.operatingSystem } + + companion object { + const val NAME = "ktorBuild" + + /** 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 and Language version. */ + private val DEFAULT_KOTLIN_VERSION = KotlinVersion.KOTLIN_2_0 + } +} diff --git a/build-logic/src/main/kotlin/ktorbuild/NamedDomainObjectCollection.kt b/build-logic/src/main/kotlin/ktorbuild/NamedDomainObjectCollection.kt new file mode 100644 index 00000000000..31ed54dcef7 --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/NamedDomainObjectCollection.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package ktorbuild + +import org.gradle.api.NamedDomainObjectCollection + +internal fun NamedDomainObjectCollection.maybeNamed(name: String, configure: T.() -> Unit) { + if (name in names) named(name).configure(configure) +} diff --git a/build-logic/src/main/kotlin/ktorbuild/internal/Accessors.kt b/build-logic/src/main/kotlin/ktorbuild/internal/Accessors.kt new file mode 100644 index 00000000000..7e6c85f8168 --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/internal/Accessors.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package ktorbuild.internal + +import ktorbuild.KtorBuildExtension +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension + +/* + * Gradle doesn't generate accessors for plugins defined in this module, so we have to declare them manually. + */ + +internal val Project.ktorBuild: KtorBuildExtension get() = extensions.getByType() + +internal fun Project.kotlin(configure: KotlinMultiplatformExtension.() -> Unit) = + extensions.configure("kotlin", configure) diff --git a/build-logic/src/main/kotlin/ktorbuild/internal/String.kt b/build-logic/src/main/kotlin/ktorbuild/internal/String.kt new file mode 100644 index 00000000000..b9c7db1cf9b --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/internal/String.kt @@ -0,0 +1,7 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package ktorbuild.internal + +internal fun String.capitalized() = replaceFirstChar { it.uppercase() } diff --git a/build-logic/src/main/kotlin/ktorbuild/internal/TrackedKotlinHierarchy.kt b/build-logic/src/main/kotlin/ktorbuild/internal/TrackedKotlinHierarchy.kt new file mode 100644 index 00000000000..d2630a71726 --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/internal/TrackedKotlinHierarchy.kt @@ -0,0 +1,201 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:OptIn(ExperimentalKotlinGradlePluginApi::class) + +package ktorbuild.internal + +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyBuilder +import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyTemplate +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree +import org.jetbrains.kotlin.konan.target.REMOVED_TARGET_MESSAGE + +private typealias GroupedSourceSets = MutableMap> + +/** Constructs a new [KotlinHierarchyTemplate] tracking added targets and groups using the provided [tracker]. */ +@Suppress("FunctionName") +fun TrackedKotlinHierarchyTemplate( + tracker: KotlinHierarchyTracker, + describe: KotlinHierarchyBuilder.Root.() -> Unit, +): KotlinHierarchyTemplate { + val trackedRoot = KotlinHierarchyTrackedRoot(tracker as KotlinHierarchyTrackerImpl) + trackedRoot.apply(describe) + return KotlinHierarchyTemplate(describe) +} + +interface KotlinHierarchyTracker { + val targetSourceSets: Map> + val groups: Map> +} + +fun KotlinHierarchyTracker(): KotlinHierarchyTracker = KotlinHierarchyTrackerImpl.getOrCreate( + name = null, + targetSourceSets = mutableMapOf(), + groups = mutableMapOf(), +) + +@Suppress("DeprecatedCallableAddReplaceWith") +private class KotlinHierarchyTrackerImpl( + private val groupName: String?, + override val targetSourceSets: GroupedSourceSets, + override val groups: GroupedSourceSets, +) : KotlinHierarchyBuilder, KotlinHierarchyTracker { + + private var targetsFrozen = false + + override fun withCompilations(predicate: (KotlinCompilation<*>) -> Boolean) {} + override fun excludeCompilations(predicate: (KotlinCompilation<*>) -> Boolean) {} + + override fun group(name: String, build: KotlinHierarchyBuilder.() -> Unit) { + val groupTracker = getOrCreate(name, targetSourceSets, groups).also(build) + groupTracker.targetsFrozen = true + groups.getValue(name).forEach(::addTarget) + } + + //region Groups + override fun withNative() { + withApple() + withLinux() + withMingw() + withAndroidNative() + } + + override fun withApple() { + withIos() + withWatchos() + withMacos() + withTvos() + } + + override fun withIos() { + withIosArm64() + withIosX64() + withIosSimulatorArm64() + } + + override fun withWatchos() { + withWatchosArm32() + withWatchosArm64() + withWatchosX64() + withWatchosDeviceArm64() + withWatchosSimulatorArm64() + } + + override fun withMacos() { + withMacosArm64() + withMacosX64() + } + + override fun withTvos() { + withTvosArm64() + withTvosX64() + withTvosSimulatorArm64() + } + + override fun withMingw() { + withMingwX64() + } + + override fun withLinux() { + withLinuxArm64() + withLinuxX64() + } + + override fun withAndroidNative() { + withAndroidNativeX64() + withAndroidNativeX86() + withAndroidNativeArm32() + withAndroidNativeArm64() + } + //endregion + + //region Actual targets + override fun withJs() = addTarget("js") + override fun withJvm() = addTarget("jvm") + + @Deprecated("Renamed to 'withWasmJs'", replaceWith = ReplaceWith("withWasmJs()")) + override fun withWasm() = withWasmJs() + override fun withWasmJs() = addTarget("wasmJs") + override fun withWasmWasi() = addTarget("wasmWasi") + + @Deprecated("Renamed to 'withAndroidTarget'", replaceWith = ReplaceWith("withAndroidTarget()")) + override fun withAndroid() = withAndroidTarget() + override fun withAndroidTarget() = addTarget("android") + override fun withAndroidNativeX64() = addTarget("androidNativeX64") + override fun withAndroidNativeX86() = addTarget("androidNativeX86") + override fun withAndroidNativeArm32() = addTarget("androidNativeArm32") + override fun withAndroidNativeArm64() = addTarget("androidNativeArm64") + + override fun withIosArm64() = addTarget("iosArm64") + override fun withIosX64() = addTarget("iosX64") + override fun withIosSimulatorArm64() = addTarget("iosSimulatorArm64") + override fun withWatchosArm32() = addTarget("watchosArm32") + override fun withWatchosArm64() = addTarget("watchosArm64") + override fun withWatchosX64() = addTarget("watchosX64") + override fun withWatchosSimulatorArm64() = addTarget("watchosSimulatorArm64") + override fun withWatchosDeviceArm64() = addTarget("watchosDeviceArm64") + override fun withTvosArm64() = addTarget("tvosArm64") + override fun withTvosX64() = addTarget("tvosX64") + override fun withTvosSimulatorArm64() = addTarget("tvosSimulatorArm64") + + override fun withLinuxArm64() = addTarget("linuxArm64") + override fun withLinuxX64() = addTarget("linuxX64") + override fun withMacosArm64() = addTarget("macosArm64") + override fun withMacosX64() = addTarget("macosX64") + override fun withMingwX64() = addTarget("mingwX64") + //endregion + + //region Removed targets + @Deprecated(REMOVED_TARGET_MESSAGE, level = DeprecationLevel.ERROR) + override fun withIosArm32() = error("Target removed") + + @Deprecated(REMOVED_TARGET_MESSAGE, level = DeprecationLevel.ERROR) + override fun withWatchosX86() = error("Target removed") + + @Deprecated(REMOVED_TARGET_MESSAGE, level = DeprecationLevel.ERROR) + override fun withMingwX86() = error("Target removed") + + @Deprecated(REMOVED_TARGET_MESSAGE, level = DeprecationLevel.ERROR) + override fun withLinuxArm32Hfp() = error("Target removed") + + @Deprecated(REMOVED_TARGET_MESSAGE, level = DeprecationLevel.ERROR) + override fun withLinuxMips32() = error("Target removed") + + @Deprecated(REMOVED_TARGET_MESSAGE, level = DeprecationLevel.ERROR) + override fun withLinuxMipsel32() = error("Target removed") + + @Deprecated(REMOVED_TARGET_MESSAGE, level = DeprecationLevel.ERROR) + override fun withWasm32() = error("Target removed") + //endregion + + private fun addTarget(name: String) { + if (groupName == null) return + check(!targetsFrozen) { "Can't add targets to already declared group: $groupName" } + + targetSourceSets.getOrPut(name) { mutableSetOf(name) }.add(groupName) + groups.getOrPut(groupName) { mutableSetOf() }.add(name) + } + + companion object { + private val builders = hashMapOf() + + fun getOrCreate( + name: String?, + targetSourceSets: GroupedSourceSets, + groups: GroupedSourceSets, + ): KotlinHierarchyTrackerImpl = builders.getOrPut(name) { + KotlinHierarchyTrackerImpl(name, targetSourceSets, groups) + } + } +} + +private class KotlinHierarchyTrackedRoot( + tracker: KotlinHierarchyTrackerImpl, +) : KotlinHierarchyBuilder.Root, KotlinHierarchyBuilder by tracker { + override fun excludeSourceSetTree(vararg tree: KotlinSourceSetTree) {} + override fun sourceSetTrees(vararg tree: KotlinSourceSetTree) {} + override fun withSourceSetTree(vararg tree: KotlinSourceSetTree) {} +} diff --git a/build-logic/src/main/kotlin/ktorbuild/internal/Version.kt b/build-logic/src/main/kotlin/ktorbuild/internal/Version.kt new file mode 100644 index 00000000000..6440b3bc449 --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/internal/Version.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2014-2024 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 + +/** + * Resolves the version for the current project based on the defined properties. + * Properties "releaseVersion" and "eapVersion" are passed on CI as build parameters: + * ``` + * ./gradlew build -PreleaseVersion=3.0.0 + * ``` + */ +internal fun Project.resolveVersion(): String { + val projectVersion = version.toString().removeSuffix("-SNAPSHOT") + val releaseVersion = findProperty("releaseVersion")?.toString() + val eapVersion = findProperty("eapVersion")?.toString() + + return when { + releaseVersion != null -> releaseVersion + eapVersion != null -> "$projectVersion-eap-$eapVersion" + else -> projectVersion + } +} diff --git a/build-logic/src/main/kotlin/ktorbuild/internal/VersionCatalogs.kt b/build-logic/src/main/kotlin/ktorbuild/internal/VersionCatalogs.kt new file mode 100644 index 00000000000..41c0616bea5 --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/internal/VersionCatalogs.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2014-2024 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.accessors.dm.LibrariesForLibs +import org.gradle.api.Project +import org.gradle.kotlin.dsl.the + +/** + * Accessor to make version catalog available in build-logic. + * See: https://github.com/gradle/gradle/issues/15383#issuecomment-779893192 + */ +internal val Project.libs: LibrariesForLibs + get() = rootProject.the() diff --git a/build-logic/src/main/kotlin/ktorbuild/internal/gradle/OperatingSystem.kt b/build-logic/src/main/kotlin/ktorbuild/internal/gradle/OperatingSystem.kt new file mode 100644 index 00000000000..df5388aa486 --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/internal/gradle/OperatingSystem.kt @@ -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. + */ + +@file:Suppress("UnstableApiUsage") + +package ktorbuild.internal.gradle + +import org.gradle.platform.OperatingSystem + +internal fun OperatingSystem.isLinux() = this == OperatingSystem.LINUX +internal fun OperatingSystem.isWindows() = this == OperatingSystem.WINDOWS diff --git a/build-logic/src/main/kotlin/ktorbuild/internal/gradle/ProjectGradleProperties.kt b/build-logic/src/main/kotlin/ktorbuild/internal/gradle/ProjectGradleProperties.kt new file mode 100644 index 00000000000..7eb47812a24 --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/internal/gradle/ProjectGradleProperties.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package ktorbuild.internal.gradle + +import org.gradle.api.Project +import org.gradle.api.file.ProjectLayout +import org.gradle.initialization.Environment +import java.io.File +import javax.inject.Inject + +/** + * A utility class providing access to project properties declared in `gradle.properties` + * files from the project directory up to the root project. The properties closer to the + * current project directory take precedence over properties in parent directories. + * + * It is a workaround for the issue when `ProviderFactory.gradleProperty` and `GradleProperties` + * take into account only root `gradle.properties`. + * TODO: Remove when the issue is fixed + * https://github.com/gradle/gradle/issues/23572 + */ +internal abstract class ProjectGradleProperties( + private val layout: ProjectLayout, + private val environment: Environment, + private val rootDir: File, +) { + + @Suppress("unused") + @Inject + constructor( + layout: ProjectLayout, + environment: Environment, + project: Project, + ) : this(layout, environment, project.rootDir) + + private val properties: Map by lazy { + val properties = mutableMapOf() + layout.projectDirectory.asFile + .walkUpToRoot(rootDir) + .map { it.resolve(Project.GRADLE_PROPERTIES) } + .mapNotNull(environment::propertiesFile) + // Properties closer to the current project take precedence + .forEach(properties::putAllAbsent) + + properties + } + + fun byNamePrefix(prefix: String): Map { + return properties.filterKeys { it.startsWith(prefix) } + .mapKeys { (key, _) -> key.removePrefix(prefix) } + } +} + +//region Utils +private fun File.walkUpToRoot(rootDir: File): Sequence = sequence { + var current = absoluteFile + while(true) { + yield(current) + if (current == rootDir || current.parent == null) break + current = current.parentFile + } +} + +private fun MutableMap.putAllAbsent(other: Map) { + for ((key, value) in other) { + if (key !in this) this[key] = value + } +} +//endregion diff --git a/build-logic/src/main/kotlin/ktorbuild/internal/gradle/Property.kt b/build-logic/src/main/kotlin/ktorbuild/internal/gradle/Property.kt new file mode 100644 index 00000000000..3a9430f52af --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/internal/gradle/Property.kt @@ -0,0 +1,9 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package ktorbuild.internal.gradle + +import org.gradle.api.provider.Property + +internal fun Property.finalizedOnRead(): Property = apply { finalizeValueOnRead() } diff --git a/buildSrc/src/main/kotlin/CI.kt b/build-logic/src/main/kotlin/ktorbuild/targets/CiConfig.kt similarity index 84% rename from buildSrc/src/main/kotlin/CI.kt rename to build-logic/src/main/kotlin/ktorbuild/targets/CiConfig.kt index 8b27caedf2d..f09b1f56934 100644 --- a/buildSrc/src/main/kotlin/CI.kt +++ b/build-logic/src/main/kotlin/ktorbuild/targets/CiConfig.kt @@ -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. diff --git a/build-logic/src/main/kotlin/ktorbuild/targets/CommonConfig.kt b/build-logic/src/main/kotlin/ktorbuild/targets/CommonConfig.kt new file mode 100644 index 00000000000..d776cb7efd1 --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/targets/CommonConfig.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package ktorbuild.targets + +import ktorbuild.internal.kotlin +import ktorbuild.internal.libs +import org.gradle.api.Project +import org.gradle.kotlin.dsl.invoke + +internal fun Project.configureCommon() { + kotlin { + sourceSets { + commonMain.dependencies { + api(libs.kotlinx.coroutines.core) + } + + commonTest.dependencies { + implementation(libs.kotlin.test) + } + } + } +} diff --git a/build-logic/src/main/kotlin/ktorbuild/targets/JsConfig.kt b/build-logic/src/main/kotlin/ktorbuild/targets/JsConfig.kt new file mode 100644 index 00000000000..d8fa7e81beb --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/targets/JsConfig.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package ktorbuild.targets + +import ktorbuild.internal.capitalized +import ktorbuild.internal.kotlin +import ktorbuild.internal.libs +import ktorbuild.maybeNamed +import org.gradle.api.Project +import org.gradle.kotlin.dsl.invoke +import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetDsl +import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl + +internal fun KotlinJsTargetDsl.addSubTargets(targets: KtorTargets) { + if (targets.isEnabled("${targetName}.nodeJs")) nodejs { useMochaForTests() } + if (targets.isEnabled("${targetName}.browser")) browser { useKarmaForTests() } +} + +private fun KotlinJsSubTargetDsl.useMochaForTests() { + testTask { + useMocha { + // Disable timeout as we use individual timeouts for tests + timeout = "0" + } + } +} + +private fun KotlinJsSubTargetDsl.useKarmaForTests() { + testTask { + useKarma { + useChromeHeadless() + useConfigDirectory(project.rootProject.file("karma")) + } + } +} + +internal fun Project.configureJs() { + kotlin { + js { binaries.library() } + + sourceSets { + jsTest.dependencies { + implementation(npm("puppeteer", libs.versions.puppeteer.get())) + } + } + } + + configureJsTestTasks(target = "js") +} + + +internal fun Project.configureWasmJs() { + kotlin { + sourceSets { + wasmJsMain.dependencies { + implementation(libs.kotlinx.browser) + } + wasmJsTest.dependencies { + implementation(npm("puppeteer", libs.versions.puppeteer.get())) + } + } + } + + configureJsTestTasks(target = "wasmJs") +} + + +internal fun Project.configureJsTestTasks(target: String) { + val shouldRunJsBrowserTest = !hasProperty("teamcity") || hasProperty("enable-js-tests") + if (shouldRunJsBrowserTest) return + + tasks.maybeNamed("clean${target.capitalized()}BrowserTest") { onlyIf { false } } + tasks.maybeNamed("${target}BrowserTest") { onlyIf { false } } +} diff --git a/buildSrc/src/main/kotlin/JvmConfig.kt b/build-logic/src/main/kotlin/ktorbuild/targets/JvmConfig.kt similarity index 54% rename from buildSrc/src/main/kotlin/JvmConfig.kt rename to build-logic/src/main/kotlin/ktorbuild/targets/JvmConfig.kt index 351345000f6..fcc94dd45b7 100644 --- a/buildSrc/src/main/kotlin/JvmConfig.kt +++ b/build-logic/src/main/kotlin/ktorbuild/targets/JvmConfig.kt @@ -2,8 +2,13 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ -import internal.libs +package ktorbuild.targets + +import ktorbuild.internal.kotlin +import ktorbuild.internal.ktorBuild +import ktorbuild.internal.libs import org.gradle.api.Project +import org.gradle.api.provider.Provider import org.gradle.api.tasks.testing.Test import org.gradle.jvm.tasks.Jar import org.gradle.jvm.toolchain.JavaLanguageVersion @@ -11,56 +16,36 @@ import org.gradle.jvm.toolchain.JavaToolchainService import org.gradle.kotlin.dsl.* import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest -fun Project.configureJvm() { - val compileJdk = project.requiredJdkVersion - +internal fun Project.configureJvm() { kotlin { - jvm() - sourceSets { - jvmMain { - dependencies { - api(libs.slf4j.api) - } + jvmMain.dependencies { + api(libs.slf4j.api) } - jvmTest { - dependencies { - implementation(libs.kotlin.test.junit5) - implementation(libs.junit) - implementation(libs.kotlinx.coroutines.debug) - } + jvmTest.dependencies { + implementation(libs.kotlin.test.junit5) + implementation(libs.junit) + implementation(libs.kotlinx.coroutines.debug) } } } - tasks.register("jarTest") { - dependsOn(tasks.named("jvmTestClasses")) - archiveClassifier = "test" - from(kotlin.jvm().compilations["test"].output) - } - - configurations { - val testCompile = findByName("testCompile") ?: return@configurations - - val testOutput by creating { - extendsFrom(testCompile) - } - val boot by creating { - } - } + configureTests() + configureJarManifest() +} - val testJdk = project.testJdk +private fun Project.configureTests() { val jvmTest = tasks.named("jvmTest") { maxHeapSize = "2g" exclude("**/*StressTest*") useJUnitPlatform() - configureJavaToolchain(compileJdk, testJdk) + configureJavaToolchain(ktorBuild.jvmToolchain, ktorBuild.jvmTestToolchain) } tasks.register("stressTest") { - classpath = files(jvmTest.get().classpath) - testClassesDirs = files(jvmTest.get().testClassesDirs) + classpath = files(jvmTest.map { it.classpath }) + testClassesDirs = files(jvmTest.map { it.testClassesDirs }) maxHeapSize = "2g" jvmArgs("-XX:+HeapDumpOnOutOfMemoryError") @@ -68,35 +53,36 @@ fun Project.configureJvm() { systemProperty("enable.stress.tests", "true") include("**/*StressTest*") useJUnitPlatform() - configureJavaToolchain(compileJdk, testJdk) + configureJavaToolchain(ktorBuild.jvmToolchain, ktorBuild.jvmTestToolchain) } +} - val configuredVersion: String by rootProject.extra +private fun Project.configureJarManifest() { tasks.named("jvmJar") { manifest { attributes( - "Implementation-Title" to name, - "Implementation-Version" to configuredVersion + "Implementation-Title" to project.name, + "Implementation-Version" to project.version, + "Automatic-Module-Name" to project.javaModuleName(), ) - val name = project.javaModuleName() - attributes("Automatic-Module-Name" to name) } } } /** Configure tests against different JDK versions. */ -private fun Test.configureJavaToolchain(compileJdk: Int, testJdk: Int) { - if (testJdk < compileJdk) { - enabled = false - return - } +private fun Test.configureJavaToolchain( + compileJdk: Provider, + testJdk: Provider, +) { + val testJdkVersion = testJdk.get().asInt() + onlyIf("only if testJdk is not lower than compileJdk") { testJdkVersion >= compileJdk.get().asInt() } val javaToolchains = project.the() javaLauncher = javaToolchains.launcherFor { - languageVersion = JavaLanguageVersion.of(testJdk) + languageVersion = testJdk } - if (testJdk >= 16) { + if (testJdkVersion >= 16) { // Allow reflective access from tests jvmArgs( "--add-opens=java.base/java.net=ALL-UNNAMED", @@ -105,7 +91,7 @@ private fun Test.configureJavaToolchain(compileJdk: Int, testJdk: Int) { ) } - if (testJdk >= 21) { + if (testJdkVersion >= 21) { // coroutines-debug use dynamic agent loading under the hood. // Remove as soon as the issue is fixed: https://youtrack.jetbrains.com/issue/KT-62096/ jvmArgs("-XX:+EnableDynamicAgentLoading") diff --git a/build-logic/src/main/kotlin/ktorbuild/targets/KtorTargets.kt b/build-logic/src/main/kotlin/ktorbuild/targets/KtorTargets.kt new file mode 100644 index 00000000000..1b894fd4b2a --- /dev/null +++ b/build-logic/src/main/kotlin/ktorbuild/targets/KtorTargets.kt @@ -0,0 +1,264 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package ktorbuild.targets + +import ktorbuild.internal.KotlinHierarchyTracker +import ktorbuild.internal.TrackedKotlinHierarchyTemplate +import ktorbuild.internal.gradle.ProjectGradleProperties +import org.gradle.api.file.ProjectLayout +import org.gradle.api.model.ObjectFactory +import org.gradle.kotlin.dsl.newInstance +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyBuilder +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.konan.target.KonanTarget +import javax.inject.Inject + +/** + * Represents configuration for Kotlin Multiplatform targets in a Ktor project, enabling + * or disabling specific targets based on the project structure or Gradle properties. + * + * By default, a target is enabled if its source set directory exists in the project, + * unless explicitly disabled in properties. + * + * Targets can be enabled or disabled using `gradle.properties` file with properties starting with `target.` prefix: + * ```properties + * # Enable/disable specific target + * target.jvm=true + * target.watchosDeviceArm64=false + * + * # Enable/disable group of targets + * target.posix=true + * target.androidNative=false + * ``` + * + * There are two sub-targets available for targets `js` and `wasmJs`: `browser` and `nodeJs`. + * Sub-targets inherit their parent target's state but can be individually configured using properties. + * ```properties + * # Disable specific sub-targets + * target.js.browser=false + * target.wasmJs.browser=false + * ``` + * + * See the full list of targets and target groups in [KtorTargets.hierarchyTemplate]. + */ +abstract class KtorTargets internal constructor( + private val layout: ProjectLayout, + properties: ProjectGradleProperties, +) { + + @Inject + internal constructor( + layout: ProjectLayout, + objects: ObjectFactory, + ) : this(layout, properties = objects.newInstance()) + + private val targetStates: MutableMap by lazy { loadDefaults(properties) } + + private val directories: Set by lazy { + layout.projectDirectory.asFile.walk() + .maxDepth(1) + .filter { it.isDirectory } + .map { it.name } + .toSet() + } + + val hasJvm: Boolean get() = isEnabled("jvm") + val hasJs: Boolean get() = isEnabled("js") + val hasWasmJs: Boolean get() = isEnabled("wasmJs") + + val hasJsOrWasmJs: Boolean get() = hasJs || hasWasmJs + val hasNative: Boolean get() = resolveTargets("posix").any(::isEnabled) + + /** + * Determines if the specified [target] is enabled. + * + * The target is considered enabled if: + * - It wasn't explicitly disabled in `gradle.properties`, and + * - The project has at least one source set used by this target, or + * - The target is explicitly enabled in `gradle.properties` + * + * For sub-targets (e.g., 'js.browser'), the state is inherited from the parent target + * unless explicitly configured in `gradle.properties`. + */ + fun isEnabled(target: String): Boolean = targetStates.getOrPut(target) { + // Sub-targets inherit parent state + if (target.contains(".")) { + isEnabled(target.substringBefore(".")) + } else { + hierarchyTracker.targetSourceSets.getValue(target).any { it in directories } + } + } + + private fun loadDefaults(properties: ProjectGradleProperties): MutableMap { + val defaults = mutableMapOf() + for ((key, rawValue) in properties.byNamePrefix("target.")) { + val value = rawValue.toBoolean() + for (target in resolveTargets(key)) defaults[target] = value + } + return defaults + } + + companion object { + private val hierarchyTracker = KotlinHierarchyTracker() + + @OptIn(ExperimentalKotlinGradlePluginApi::class) + internal val hierarchyTemplate = TrackedKotlinHierarchyTemplate(hierarchyTracker) { + withSourceSetTree(KotlinSourceSetTree.main, KotlinSourceSetTree.test) + + common { + group("posix") { + group("windows") { withMingw() } + + group("nix") { + group("linux") { withLinux() } + + group("darwin") { + group("ios") { withIos() } + group("tvos") { withTvos() } + group("watchos") { withWatchos() } + group("macos") { withMacos() } + } + + group("androidNative") { + group("androidNative64") { + withAndroidNativeX64() + withAndroidNativeArm64() + } + + group("androidNative32") { + withAndroidNativeX86() + withAndroidNativeArm32Fixed() + } + } + } + } + + group("jsAndWasmShared") { + withJs() + withWasmJs() + } + + group("jvmAndPosix") { + withJvm() + group("posix") + } + + group("desktop") { + group("linux") + group("windows") + group("macos") + } + + group("nonJvm") { + group("posix") + group("jsAndWasmShared") + } + } + } + + /** Returns targets corresponding to the provided [sourceSet]. */ + fun resolveTargets(sourceSet: String): Set = hierarchyTracker.groups[sourceSet] ?: setOf(sourceSet) + } +} + +/** + * Original `withAndroidNativeArm32` has a bug and matches to `X86` actually. + * TODO: Remove after the bug is fixed + * https://youtrack.jetbrains.com/issue/KT-71866/ + */ +@OptIn(ExperimentalKotlinGradlePluginApi::class) +private fun KotlinHierarchyBuilder.withAndroidNativeArm32Fixed() { + if (this is KotlinHierarchyTracker) return withAndroidNativeArm32() + + withCompilations { + val target = it.target + target is KotlinNativeTarget && target.konanTarget == KonanTarget.ANDROID_ARM32 + } +} + +internal fun KotlinMultiplatformExtension.addTargets(targets: KtorTargets) { + if (targets.hasJvm) jvm() + + if (targets.hasJs) js { addSubTargets(targets) } + @OptIn(ExperimentalWasmDsl::class) + if (targets.hasWasmJs) wasmJs { addSubTargets(targets) } + + // Native targets + // See: https://kotlinlang.org/docs/native-target-support.html + + // Tier 1 + if (targets.isEnabled("macosX64")) macosX64() + if (targets.isEnabled("macosArm64")) macosArm64() + if (targets.isEnabled("iosArm64")) iosArm64() + if (targets.isEnabled("iosX64")) iosX64() + if (targets.isEnabled("iosSimulatorArm64")) iosSimulatorArm64() + + // Tier 2 + if (targets.isEnabled("linuxArm64")) linuxArm64() + if (targets.isEnabled("linuxX64")) linuxX64() + if (targets.isEnabled("watchosArm32")) watchosArm32() + if (targets.isEnabled("watchosArm64")) watchosArm64() + if (targets.isEnabled("watchosX64")) watchosX64() + if (targets.isEnabled("watchosSimulatorArm64")) watchosSimulatorArm64() + if (targets.isEnabled("tvosArm64")) tvosArm64() + if (targets.isEnabled("tvosX64")) tvosX64() + if (targets.isEnabled("tvosSimulatorArm64")) tvosSimulatorArm64() + + // Tier 3 + if (targets.isEnabled("androidNativeArm32")) androidNativeArm32() + if (targets.isEnabled("androidNativeArm64")) androidNativeArm64() + if (targets.isEnabled("androidNativeX86")) androidNativeX86() + if (targets.isEnabled("androidNativeX64")) androidNativeX64() + if (targets.isEnabled("mingwX64")) mingwX64() + if (targets.isEnabled("watchosDeviceArm64")) watchosDeviceArm64() + + flattenSourceSetsStructure() +} + +/** + * Changes the source sets structure to a more concise format. + * + * Transforms from the default Kotlin Multiplatform structure: + * ``` + * + * - src/ + * - commonMain/ + * - kotlin/ + * - jvmMain/ + * - kotlin/ + * - resources/ + * - jvmTest/ + * - kotlin/ + * - resources/ + * ``` + * + * To a flattened platform-centric structure: + * ``` + * + * - common/ + * - src/ # commonMain kotlin sources + * - jvm/ + * - src/ # jvmMain kotlin sources + * - resources/ # jvmMain resources + * - test/ # jvmTest kotlin sources + * - test-resources/ # jvmTest resources + * ``` + */ +private fun KotlinMultiplatformExtension.flattenSourceSetsStructure() { + sourceSets + .matching { it.name !in listOf("main", "test") } + .all { + val srcDir = if (name.endsWith("Main")) "src" else "test" + val resourcesPrefix = if (name.endsWith("Test")) "test-" else "" + val platform = name.dropLast(4) + + kotlin.setSrcDirs(listOf("$platform/$srcDir")) + resources.setSrcDirs(listOf("$platform/${resourcesPrefix}resources")) + } +} diff --git a/gradle-settings-conventions/build.gradle.kts b/build-settings-logic/build.gradle.kts similarity index 77% rename from gradle-settings-conventions/build.gradle.kts rename to build-settings-logic/build.gradle.kts index d5fd0a8bbe0..000686af2f1 100644 --- a/gradle-settings-conventions/build.gradle.kts +++ b/build-settings-logic/build.gradle.kts @@ -15,3 +15,8 @@ dependencies { implementation(libs.develocity) implementation(libs.develocity.commonCustomUserData) } + +// Should be synced with gradle/gradle-daemon-jvm.properties +kotlin { + jvmToolchain(21) +} diff --git a/gradle-settings-conventions/settings.gradle.kts b/build-settings-logic/settings.gradle.kts similarity index 85% rename from gradle-settings-conventions/settings.gradle.kts rename to build-settings-logic/settings.gradle.kts index 3b1dcfb46be..cb55d60d598 100644 --- a/gradle-settings-conventions/settings.gradle.kts +++ b/build-settings-logic/settings.gradle.kts @@ -10,4 +10,4 @@ dependencyResolutionManagement { } } -rootProject.name = "gradle-settings-conventions" +rootProject.name = "build-settings-logic" diff --git a/gradle-settings-conventions/src/main/kotlin/conventions-dependency-resolution-management.settings.gradle.kts b/build-settings-logic/src/main/kotlin/conventions-dependency-resolution-management.settings.gradle.kts similarity index 100% rename from gradle-settings-conventions/src/main/kotlin/conventions-dependency-resolution-management.settings.gradle.kts rename to build-settings-logic/src/main/kotlin/conventions-dependency-resolution-management.settings.gradle.kts diff --git a/gradle-settings-conventions/src/main/kotlin/conventions-develocity.settings.gradle.kts b/build-settings-logic/src/main/kotlin/conventions-develocity.settings.gradle.kts similarity index 100% rename from gradle-settings-conventions/src/main/kotlin/conventions-develocity.settings.gradle.kts rename to build-settings-logic/src/main/kotlin/conventions-develocity.settings.gradle.kts diff --git a/gradle-settings-conventions/src/main/kotlin/customization.kt b/build-settings-logic/src/main/kotlin/customization.kt similarity index 100% rename from gradle-settings-conventions/src/main/kotlin/customization.kt rename to build-settings-logic/src/main/kotlin/customization.kt diff --git a/gradle-settings-conventions/src/main/kotlin/execute.kt b/build-settings-logic/src/main/kotlin/execute.kt similarity index 100% rename from gradle-settings-conventions/src/main/kotlin/execute.kt rename to build-settings-logic/src/main/kotlin/execute.kt diff --git a/gradle-settings-conventions/src/main/kotlin/params.kt b/build-settings-logic/src/main/kotlin/params.kt similarity index 100% rename from gradle-settings-conventions/src/main/kotlin/params.kt rename to build-settings-logic/src/main/kotlin/params.kt diff --git a/build.gradle.kts b/build.gradle.kts index 0763684337e..d30a09ffe6f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,29 +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 org.jetbrains.dokka.gradle.DokkaMultiModuleTask -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask import org.jetbrains.kotlin.konan.target.HostManager -val releaseVersion: String? by extra -val eapVersion: String? by extra -val version = (project.version as String).let { if (it.endsWith("-SNAPSHOT")) it.dropLast("-SNAPSHOT".length) else it } - -extra["configuredVersion"] = when { - releaseVersion != null -> releaseVersion - eapVersion != null -> "$version-eap-$eapVersion" - else -> project.version -} - -println("The build version is ${extra["configuredVersion"]}") - extra["globalM2"] = "${project.file("build")}/m2" extra["publishLocal"] = project.hasProperty("publishLocal") -val configuredVersion: String by extra - apply(from = "gradle/verifier.gradle") extra["skipPublish"] = mutableListOf( @@ -42,26 +25,19 @@ extra["nonDefaultProjectStructure"] = mutableListOf( "ktor-java-modules-test", ) -val disabledExplicitApiModeProjects = listOf( - "ktor-client-tests", - "ktor-server-test-base", - "ktor-server-test-suites", - "ktor-server-tests", - "ktor-client-content-negotiation-tests", - "ktor-test-base" -) - apply(from = "gradle/compatibility.gradle") plugins { - alias(libs.plugins.dokka) apply false + id("ktorbuild.doctor") alias(libs.plugins.binaryCompatibilityValidator) - conventions.gradleDoctor } +println("Build version: ${project.version}") + subprojects { + apply(plugin = "ktorbuild.base") + group = "io.ktor" - version = configuredVersion extra["hostManager"] = HostManager() setupTrainForSubproject() @@ -69,27 +45,7 @@ subprojects { val nonDefaultProjectStructure: List by rootProject.extra if (nonDefaultProjectStructure.contains(project.name)) return@subprojects - apply(plugin = "kotlin-multiplatform") - apply(plugin = "atomicfu-conventions") - - configureTargets() - if (CI) configureTestTasksOnCi() - - configurations { - maybeCreate("testOutput") - } - - kotlin { - if (!disabledExplicitApiModeProjects.contains(project.name)) explicitApi() - - configureSourceSets() - setupJvmToolchain() - - compilerOptions { - languageVersion = getKotlinLanguageVersion() - apiVersion = getKotlinApiVersion() - } - } + apply(plugin = "ktorbuild.kmp") val skipPublish: List by rootProject.extra if (!skipPublish.contains(project.name)) { @@ -104,23 +60,7 @@ filterSnapshotTests() fun configureDokka() { allprojects { - plugins.apply("org.jetbrains.dokka") - - val dokkaPlugin by configurations - dependencies { - dokkaPlugin(rootProject.libs.dokka.plugin.versioning) - } - } - - val dokkaOutputDir = "../versions" - - tasks.withType().configureEach { - val id = "org.jetbrains.dokka.versioning.VersioningPlugin" - val config = """{ "version": "$configuredVersion", "olderVersionsDir":"$dokkaOutputDir" }""" - val mapOf = mapOf(id to config) - - outputDirectory.set(file(projectDir.toPath().resolve(dokkaOutputDir).resolve(configuredVersion))) - pluginsMapConfiguration.set(mapOf) + plugins.apply("ktorbuild.dokka") } rootProject.plugins.withType { @@ -129,32 +69,3 @@ fun configureDokka() { } configureDokka() - -fun Project.setupJvmToolchain() { - kotlin { - jvmToolchain(project.requiredJdkVersion) - } -} - -subprojects { - tasks.withType>().configureEach { - configureCompilerOptions() - } -} - -fun KotlinMultiplatformExtension.configureSourceSets() { - sourceSets - .matching { it.name !in listOf("main", "test") } - .all { - val srcDir = if (name.endsWith("Main")) "src" else "test" - val resourcesPrefix = if (name.endsWith("Test")) "test-" else "" - val platform = name.dropLast(4) - - kotlin.srcDir("$platform/$srcDir") - resources.srcDir("$platform/${resourcesPrefix}resources") - - languageSettings.apply { - progressiveMode = true - } - } -} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 6c505a91a44..7678470ae4c 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -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 { @@ -11,9 +11,6 @@ dependencies { 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 diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index 6461f16acd9..c906077ad91 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -3,7 +3,7 @@ */ pluginManagement { - includeBuild("../gradle-settings-conventions") + includeBuild("../build-settings-logic") } plugins { diff --git a/buildSrc/src/main/kotlin/CommonConfig.kt b/buildSrc/src/main/kotlin/CommonConfig.kt deleted file mode 100644 index 6c18de36b46..00000000000 --- a/buildSrc/src/main/kotlin/CommonConfig.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -import internal.* -import org.gradle.api.* -import org.gradle.kotlin.dsl.* - -fun Project.configureCommon() { - kotlin { - sourceSets { - commonMain { - dependencies { - api(libs.kotlinx.coroutines.core) - } - } - - commonTest { - dependencies { - implementation(libs.kotlin.test) - } - } - } - } -} diff --git a/buildSrc/src/main/kotlin/Compilations.kt b/buildSrc/src/main/kotlin/Compilations.kt deleted file mode 100644 index 5befd7687e9..00000000000 --- a/buildSrc/src/main/kotlin/Compilations.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2014-2024 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.* - -fun KotlinCompilationTask<*>.configureCompilerOptions() { - compilerOptions { - progressiveMode.set(true) - freeCompilerArgs.addAll("-Xexpect-actual-classes") - } -} diff --git a/buildSrc/src/main/kotlin/JsConfig.kt b/buildSrc/src/main/kotlin/JsConfig.kt deleted file mode 100644 index e7f5dfe772e..00000000000 --- a/buildSrc/src/main/kotlin/JsConfig.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -import internal.capitalized -import internal.libs -import org.gradle.api.Project -import org.gradle.kotlin.dsl.invoke -import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsSubTargetDsl - -fun Project.configureJs() { - kotlin { - js { - if (project.targetIsEnabled("js.nodeJs")) nodejs { useMochaForTests() } - if (project.targetIsEnabled("js.browser")) browser { useKarmaForTests() } - - binaries.library() - } - - sourceSets { - jsTest { - dependencies { - implementation(npm("puppeteer", libs.versions.puppeteer.get())) - } - } - } - } - - configureJsTestTasks(target = "js") -} - -internal fun KotlinJsSubTargetDsl.useMochaForTests() { - testTask { - useMocha { - // Disable timeout as we use individual timeouts for tests - timeout = "0" - } - } -} - -internal fun KotlinJsSubTargetDsl.useKarmaForTests() { - testTask { - useKarma { - useChromeHeadless() - useConfigDirectory(project.rootProject.file("karma")) - } - } -} - -internal fun Project.configureJsTestTasks(target: String) { - val shouldRunJsBrowserTest = !hasProperty("teamcity") || hasProperty("enable-js-tests") - if (shouldRunJsBrowserTest) return - - tasks.maybeNamed("clean${target.capitalized()}BrowserTest") { onlyIf { false } } - tasks.maybeNamed("${target}BrowserTest") { onlyIf { false } } -} diff --git a/buildSrc/src/main/kotlin/KotlinExtensions.kt b/buildSrc/src/main/kotlin/KotlinExtensions.kt index 72c6def8fb1..99b51f30f88 100644 --- a/buildSrc/src/main/kotlin/KotlinExtensions.kt +++ b/buildSrc/src/main/kotlin/KotlinExtensions.kt @@ -1,12 +1,14 @@ /* * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ -import org.gradle.api.* -import org.gradle.kotlin.dsl.* -import org.jetbrains.kotlin.gradle.dsl.* -import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.plugin.mpp.* -import org.jmailen.gradle.kotlinter.* + +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.the +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jmailen.gradle.kotlinter.KotlinterExtension fun Project.kotlin(block: KotlinMultiplatformExtension.() -> Unit) { configure(block) @@ -16,17 +18,6 @@ val Project.kotlin: KotlinMultiplatformExtension get() = the() val Project.kotlinter: KotlinterExtension get() = the() -fun KotlinMultiplatformExtension.createCInterop( - name: String, - cinteropTargets: List, - block: DefaultCInteropSettings.() -> Unit -) { - cinteropTargets.mapNotNull { targets.findByName(it) }.filterIsInstance().forEach { - val main by it.compilations - main.cinterops.create(name, block) - } -} - fun NamedDomainObjectContainer.commonMain(block: KotlinSourceSet.() -> Unit) { val sourceSet = getByName("commonMain") block(sourceSet) diff --git a/buildSrc/src/main/kotlin/KtorBuildProperties.kt b/buildSrc/src/main/kotlin/KtorBuildProperties.kt index 8450b18ace6..361c4d1c4b3 100644 --- a/buildSrc/src/main/kotlin/KtorBuildProperties.kt +++ b/buildSrc/src/main/kotlin/KtorBuildProperties.kt @@ -2,11 +2,6 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ -import org.gradle.api.JavaVersion -import org.gradle.api.Project - -val IDEA_ACTIVE: Boolean = System.getProperty("idea.active") == "true" - val OS_NAME = System.getProperty("os.name").lowercase() val HOST_NAME = when { @@ -15,43 +10,3 @@ val HOST_NAME = when { OS_NAME.startsWith("mac") -> "macos" else -> error("Unknown os name `$OS_NAME`") } - -private var _testJdk = 0 - -/** - * Retrieves the JDK version for running tests. - * - * Takes the version from property "test.jdk" or uses Gradle JDK by default. - * For example, to run tests against JDK 8, run test task with flag "-Ptest.jdk=8" - * or put this property to `gradle.properties`. - */ -val Project.testJdk: Int - get() { - if (_testJdk == 0) { - _testJdk = rootProject.properties["test.jdk"]?.toString()?.toInt() - ?: JavaVersion.current().majorVersion.toInt() - logger.info("Running tests against JDK $_testJdk") - } - return _testJdk - } - -val Project.requiredJdkVersion: Int - get() = when { - name in jdk17Modules -> 17 - name in jdk11Modules -> 11 - else -> 8 - } - -private val jdk17Modules = setOf( - "ktor-server-jte", -) - -private val jdk11Modules = setOf( - "ktor-client-java", - "ktor-client-jetty-jakarta", - "ktor-server-jetty-jakarta", - "ktor-server-jetty-test-http2-jakarta", - "ktor-server-openapi", - "ktor-server-servlet-jakarta", - "ktor-server-tomcat-jakarta", -) diff --git a/buildSrc/src/main/kotlin/NativeUtils.kt b/buildSrc/src/main/kotlin/NativeUtils.kt deleted file mode 100644 index 8f1917be847..00000000000 --- a/buildSrc/src/main/kotlin/NativeUtils.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -import org.gradle.api.* - -fun Project.posixTargets(): List = nixTargets() + windowsTargets() - -fun Project.nixTargets(): List = darwinTargets() + linuxTargets() + androidNativeTargets() - -fun Project.androidNativeTargets(): List = with(kotlin) { - if (project.targetIsEnabled("androidNative")) listOf( - androidNativeArm32(), - androidNativeArm64(), - androidNativeX86(), - androidNativeX64(), - ) else emptyList() -}.map { it.name } - -fun Project.linuxTargets(): List = with(kotlin) { - listOf( - linuxX64(), - linuxArm64(), - ) -}.map { it.name } - -fun Project.darwinTargets(): List = macosTargets() + iosTargets() + watchosTargets() + tvosTargets() - -fun Project.macosTargets(): List = with(kotlin) { - listOf( - macosX64(), - macosArm64() - ).map { it.name } -} - -fun Project.iosTargets(): List = with(kotlin) { - listOf( - iosX64(), - iosArm64(), - iosSimulatorArm64(), - ).map { it.name } -} - -fun Project.watchosTargets(): List = with(kotlin) { - listOfNotNull( - watchosX64(), - watchosArm32(), - watchosArm64(), - watchosSimulatorArm64(), - // ktor-server-config-yaml: because of dependency on YAML library: https://github.com/Him188/yamlkt/issues/67 - if (project.name != "ktor-server-config-yaml") { - watchosDeviceArm64() - } else { - null - }, - ).map { it.name } -} - -fun Project.tvosTargets(): List = with(kotlin) { - listOf( - tvosX64(), - tvosArm64(), - tvosSimulatorArm64(), - ).map { it.name } -} - -fun Project.desktopTargets(): List = with(kotlin) { - listOf( - macosX64(), - macosArm64(), - linuxX64(), - linuxArm64(), - mingwX64() - ).map { it.name } -} - -fun Project.windowsTargets(): List = with(kotlin) { - listOf( - mingwX64() - ).map { it.name } -} diff --git a/buildSrc/src/main/kotlin/TargetsConfig.kt b/buildSrc/src/main/kotlin/TargetsConfig.kt index 3780f03a3c5..72c12a18669 100644 --- a/buildSrc/src/main/kotlin/TargetsConfig.kt +++ b/buildSrc/src/main/kotlin/TargetsConfig.kt @@ -4,20 +4,15 @@ @file:OptIn(ExperimentalKotlinGradlePluginApi::class) -import org.gradle.api.* -import org.gradle.kotlin.dsl.* -import org.jetbrains.kotlin.gradle.* -import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.plugin.mpp.* -import org.jetbrains.kotlin.konan.target.* -import java.io.* +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import java.io.File private val Project.files: Array get() = project.projectDir.listFiles() ?: emptyArray() val Project.hasCommon: Boolean get() = files.any { it.name == "common" } val Project.hasNonJvm: Boolean get() = files.any { it.name == "nonJvm" } val Project.hasJvmAndPosix: Boolean get() = hasCommon || files.any { it.name == "jvmAndPosix" } val Project.hasPosix: Boolean get() = hasCommon || hasNonJvm || hasJvmAndPosix || files.any { it.name == "posix" } -val Project.hasDesktop: Boolean get() = hasPosix || files.any { it.name == "desktop" } val Project.hasNix: Boolean get() = hasPosix || files.any { it.name == "nix" } val Project.hasLinux: Boolean get() = hasNix || files.any { it.name == "linux" } val Project.hasDarwin: Boolean get() = hasNix || files.any { it.name == "darwin" } @@ -27,122 +22,3 @@ val Project.hasJsAndWasmShared: Boolean get() = hasCommon || hasNonJvm || files. val Project.hasJs: Boolean get() = hasJsAndWasmShared || files.any { it.name == "js" } val Project.hasWasmJs: Boolean get() = hasJsAndWasmShared || files.any { it.name == "wasmJs" } val Project.hasJvm: Boolean get() = hasCommon || hasJvmAndPosix || files.any { it.name == "jvm" } - -val Project.hasExplicitNative: Boolean - get() = hasNix || hasPosix || hasLinux || hasAndroidNative || hasDarwin || hasDesktop || hasWindows -val Project.hasNative: Boolean - get() = hasCommon || hasExplicitNative - -fun Project.configureTargets() { - kotlin { - configureCommon() - - if (hasJvm) configureJvm() - - if (hasJs) configureJs() - if (hasWasmJs) configureWasm() - - if (hasPosix) posixTargets() - if (hasNix) nixTargets() - if (hasDarwin) darwinTargets() - if (hasLinux) linuxTargets() - if (hasAndroidNative) androidNativeTargets() - if (hasDesktop) desktopTargets() - if (hasWindows) windowsTargets() - - applyHierarchyTemplate(hierarchyTemplate) - } - - if (hasExplicitNative) extra["hasNative"] = true - if (hasNative) { - tasks.maybeNamed("linkDebugTestLinuxX64") { onlyIf { HOST_NAME == "linux" } } - tasks.maybeNamed("linkDebugTestLinuxArm64") { onlyIf { HOST_NAME == "linux" } } - tasks.maybeNamed("linkDebugTestMingwX64") { onlyIf { HOST_NAME == "windows" } } - } - - if (hasJsAndWasmShared) { - tasks.configureEach { - if (name == "compileJsAndWasmSharedMainKotlinMetadata") { - enabled = false - } - } - } -} - -private val hierarchyTemplate = KotlinHierarchyTemplate { - withSourceSetTree(KotlinSourceSetTree.main, KotlinSourceSetTree.test) - - common { - group("posix") { - group("windows") { withMingw() } - - group("nix") { - group("linux") { withLinux() } - - group("darwin") { - group("ios") { withIos() } - group("tvos") { withTvos() } - group("watchos") { withWatchos() } - group("macos") { withMacos() } - } - - group("androidNative") { - group("androidNative64") { - withAndroidNativeX64() - withAndroidNativeArm64() - } - - group("androidNative32") { - withAndroidNativeX86() - withAndroidNativeArm32Fixed() - } - } - } - } - - group("jsAndWasmShared") { - withJs() - withWasmJs() - } - - group("jvmAndPosix") { - withJvm() - group("posix") - } - - group("desktop") { - group("linux") - group("windows") - group("macos") - } - - group("nonJvm") { - group("posix") - group("jsAndWasmShared") - } - } -} - -/** - * By default, all targets are enabled. To disable specific target, - * disable the corresponding flag in `gradle.properties` of the target project. - * - * Targets that could be disabled: - * - `target.js.nodeJs` - * - `target.js.browser` - * - `target.wasmJs.browser` - * - `target.androidNative` - */ -internal fun Project.targetIsEnabled(target: String): Boolean { - return findProperty("target.$target") != "false" -} - -/** - * Original `withAndroidNativeArm32` has a bug and matches to `X86` actually. - * TODO: Remove after the bug is fixed - * https://youtrack.jetbrains.com/issue/KT-71866/ - */ -private fun KotlinHierarchyBuilder.withAndroidNativeArm32Fixed() = withCompilations { - val target = it.target - target is KotlinNativeTarget && target.konanTarget == KonanTarget.ANDROID_ARM32 -} diff --git a/buildSrc/src/main/kotlin/Train.kt b/buildSrc/src/main/kotlin/Train.kt index 3b3d4698ea5..a91a830f333 100644 --- a/buildSrc/src/main/kotlin/Train.kt +++ b/buildSrc/src/main/kotlin/Train.kt @@ -1,12 +1,11 @@ /* * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ -import internal.* -import org.gradle.api.* -import org.gradle.api.provider.* -import org.gradle.api.tasks.testing.* -import org.gradle.kotlin.dsl.* -import org.jetbrains.kotlin.gradle.dsl.* +import internal.libs +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.filterSnapshotTests() { if (!buildSnapshotTrain) return @@ -67,31 +66,3 @@ private fun check(version: Any, libVersionProvider: Provider, libName: S "Current deploy version is $version, but $libName version is not overridden ($libVersion)" } } - -private var resolvedKotlinApiVersion: KotlinVersion? = null - -fun Project.getKotlinApiVersion(): KotlinVersion = - resolvedKotlinApiVersion ?: resolveKotlinApiVersion().also { resolvedKotlinApiVersion = it } - -private fun Project.resolveKotlinApiVersion(): KotlinVersion { - val apiVersion = rootProject.findProperty("kotlin_api_version") - ?.let { KotlinVersion.fromVersion(it.toString()) } - ?: KotlinVersion.KOTLIN_2_0 - logger.info("Kotlin API version: $apiVersion") - - return apiVersion -} - -private var resolvedKotlinLanguageVersion: KotlinVersion? = null - -fun Project.getKotlinLanguageVersion(): KotlinVersion = - resolvedKotlinLanguageVersion ?: resolveKotlinLanguageVersion().also { resolvedKotlinLanguageVersion = it } - -private fun Project.resolveKotlinLanguageVersion(): KotlinVersion { - val languageVersion = rootProject.findProperty("kotlin_language_version") - ?.let { KotlinVersion.fromVersion(it.toString()) } - ?: KotlinVersion.KOTLIN_2_0 - logger.info("Kotlin language version: $languageVersion") - - return languageVersion -} diff --git a/buildSrc/src/main/kotlin/WasmConfig.kt b/buildSrc/src/main/kotlin/WasmConfig.kt deleted file mode 100644 index fc9d23fcb1f..00000000000 --- a/buildSrc/src/main/kotlin/WasmConfig.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -import internal.* -import org.gradle.api.* -import org.gradle.kotlin.dsl.* -import org.jetbrains.kotlin.gradle.* -import org.jetbrains.kotlin.gradle.targets.js.ir.* -import java.io.* - -fun Project.configureWasm() { - kotlin { - @OptIn(ExperimentalWasmDsl::class) - wasmJs { - nodejs { useMochaForTests() } - if (project.targetIsEnabled("wasmJs.browser")) browser { useKarmaForTests() } - } - - sourceSets { - wasmJsMain { - dependencies { - implementation(libs.kotlinx.browser) - } - } - wasmJsTest { - dependencies { - implementation(npm("puppeteer", libs.versions.puppeteer.get())) - } - } - } - } - - configureJsTestTasks(target = "wasmJs") -} diff --git a/buildSrc/src/main/kotlin/atomicfu-conventions.gradle.kts b/buildSrc/src/main/kotlin/atomicfu-conventions.gradle.kts deleted file mode 100644 index 7e82657a393..00000000000 --- a/buildSrc/src/main/kotlin/atomicfu-conventions.gradle.kts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -plugins { - id("org.jetbrains.kotlinx.atomicfu") -} - -// Workaround for KT-71203. Can be removed after https://github.com/Kotlin/kotlinx-atomicfu/issues/431 -atomicfu { - transformJs = false -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 67919882e4a..10c882f6486 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,6 @@ netty-tcnative = "2.0.69.Final" jetty = "9.4.56.v20240826" jetty-jakarta = "11.0.24" jetty-alpn-api = "1.1.3.v20160715" -jetty-alpn-boot = "8.1.13.v20181017" jetty-alpn-openjdk8 = "9.4.56.v20240826" tomcat = "9.0.98" @@ -128,6 +127,7 @@ kotlinx-io-core = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.re kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser", version.ref = "kotlinx-browser" } +dokka-gradlePlugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } dokka-plugin-versioning = { module = "org.jetbrains.dokka:versioning-plugin", version.ref = "dokka" } netty-handler = { module = "io.netty:netty-handler", version.ref = "netty" } @@ -155,7 +155,6 @@ jetty-alpn-java-server = { module = "org.eclipse.jetty:jetty-alpn-java-server", jetty-alpn-java-client = { module = "org.eclipse.jetty:jetty-alpn-java-client", version.ref = "jetty" } jetty-alpn-api = { module = "org.eclipse.jetty.alpn:alpn-api", version.ref = "jetty-alpn-api" } -jetty-alpn-boot = { module = "org.mortbay.jetty.alpn:alpn-boot", version.ref = "jetty-alpn-boot" } jetty-alpn-openjdk8-server = { module = "org.eclipse.jetty:jetty-alpn-openjdk8-server", version.ref = "jetty-alpn-openjdk8" } jetty-alpn-openjdk8-client = { module = "org.eclipse.jetty:jetty-alpn-openjdk8-client", version.ref = "jetty-alpn-openjdk8" } @@ -233,7 +232,6 @@ gradleDoctor = { module = "com.osacky.doctor:doctor-plugin", version.ref = "grad [plugins] binaryCompatibilityValidator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binaryCompatibilityValidator" } -dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } doctor = { id = "com.osacky.doctor", version.ref = "gradleDoctor" } diff --git a/jvm/.gitkeep b/jvm/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/ktor-client/ktor-client-curl/build.gradle.kts b/ktor-client/ktor-client-curl/build.gradle.kts index 9816989729a..b1509832427 100644 --- a/ktor-client/ktor-client-curl/build.gradle.kts +++ b/ktor-client/ktor-client-curl/build.gradle.kts @@ -2,6 +2,8 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +import ktorbuild.createCInterop + val paths = listOf( "/opt/homebrew/opt/curl/include/", "/opt/local/include/", @@ -25,20 +27,21 @@ plugins { } kotlin { - createCInterop("libcurl", listOf("macosX64", "linuxX64", "mingwX64")) { - definitionFile = File(projectDir, "desktop/interop/libcurl.def") - includeDirs.headerFilterOnly(paths) - } - - createCInterop("libcurl", listOf("macosArm64")) { - definitionFile = File(projectDir, "desktop/interop/libcurl_arm64.def") - includeDirs.headerFilterOnly(paths) - } - - createCInterop("libcurl", listOf("linuxArm64")) { - definitionFile = File(projectDir, "desktop/interop/libcurl_linux_arm64.def") - includeDirs.headerFilterOnly(listOf("desktop/interop/linuxArm64/include/")) - } + createCInterop( + "libcurl", + sourceSet = "desktop", + definitionFilePath = { target -> + when (target) { + "macosArm64" -> "desktop/interop/libcurl_arm64.def" + "linuxArm64" -> "desktop/interop/libcurl_linux_arm64.def" + else -> "desktop/interop/libcurl.def" + } + }, + configure = { target -> + val included = if (target == "linuxArm64") listOf("desktop/interop/linuxArm64/include/") else paths + includeDirs.headerFilterOnly(included) + } + ) sourceSets { desktopMain { diff --git a/ktor-client/ktor-client-java/build.gradle.kts b/ktor-client/ktor-client-java/build.gradle.kts index 2bddd751e54..eab68d05a91 100644 --- a/ktor-client/ktor-client-java/build.gradle.kts +++ b/ktor-client/ktor-client-java/build.gradle.kts @@ -6,6 +6,11 @@ plugins { id("test-server") } +ktorBuild { + // Package java.net.http was introduced in Java 11 + jvmToolchain(11) +} + kotlin.sourceSets { jvmMain { dependencies { diff --git a/ktor-client/ktor-client-jetty-jakarta/build.gradle.kts b/ktor-client/ktor-client-jetty-jakarta/build.gradle.kts index 617250617d8..5f79685d0df 100644 --- a/ktor-client/ktor-client-jetty-jakarta/build.gradle.kts +++ b/ktor-client/ktor-client-jetty-jakarta/build.gradle.kts @@ -1,5 +1,14 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + description = "Jetty based client engine" +ktorBuild { + // The minimal JVM version required for Jetty 10+ + jvmToolchain(11) +} + kotlin.sourceSets { jvmMain { dependencies { diff --git a/ktor-client/ktor-client-plugins/ktor-client-content-negotiation/ktor-client-content-negotiation-tests/build.gradle.kts b/ktor-client/ktor-client-plugins/ktor-client-content-negotiation/ktor-client-content-negotiation-tests/build.gradle.kts index 45bd658f8b1..8a173f16968 100644 --- a/ktor-client/ktor-client-plugins/ktor-client-content-negotiation/ktor-client-content-negotiation-tests/build.gradle.kts +++ b/ktor-client/ktor-client-plugins/ktor-client-content-negotiation/ktor-client-content-negotiation-tests/build.gradle.kts @@ -5,6 +5,7 @@ description = "Common tests for client content negotiation" plugins { + id("ktorbuild.project.internal") id("kotlinx-serialization") } diff --git a/ktor-client/ktor-client-tests/build.gradle.kts b/ktor-client/ktor-client-tests/build.gradle.kts index b4359213383..5c776b80bea 100644 --- a/ktor-client/ktor-client-tests/build.gradle.kts +++ b/ktor-client/ktor-client-tests/build.gradle.kts @@ -5,6 +5,7 @@ description = "Common tests for client" plugins { + id("ktorbuild.project.internal") id("kotlinx-serialization") id("test-server") } @@ -54,9 +55,7 @@ kotlin.sourceSets { api(project(":ktor-client:ktor-client-apache5")) runtimeOnly(project(":ktor-client:ktor-client-android")) runtimeOnly(project(":ktor-client:ktor-client-okhttp")) - if (testJdk >= 11) { - runtimeOnly(project(":ktor-client:ktor-client-java")) - } + runtimeOnly(project(":ktor-client:ktor-client-java")) implementation(project(":ktor-client:ktor-client-plugins:ktor-client-logging")) implementation(libs.kotlinx.coroutines.slf4j) implementation(libs.junit) diff --git a/ktor-client/ktor-client-winhttp/build.gradle.kts b/ktor-client/ktor-client-winhttp/build.gradle.kts index 55654510259..896a0656365 100644 --- a/ktor-client/ktor-client-winhttp/build.gradle.kts +++ b/ktor-client/ktor-client-winhttp/build.gradle.kts @@ -2,15 +2,15 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +import ktorbuild.createCInterop + plugins { id("kotlinx-serialization") id("test-server") } kotlin { - createCInterop("winhttp", windowsTargets()) { - definitionFile = File(projectDir, "windows/interop/winhttp.def") - } + createCInterop("winhttp", sourceSet = "windows") sourceSets { windowsMain { diff --git a/ktor-io/build.gradle.kts b/ktor-io/build.gradle.kts index e3cfe2f033e..1ab3071d4ec 100644 --- a/ktor-io/build.gradle.kts +++ b/ktor-io/build.gradle.kts @@ -2,15 +2,14 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +import ktorbuild.createCInterop plugins { alias(libs.plugins.kover) } kotlin { - createCInterop("mutex", posixTargets()) { - definitionFile = File(projectDir, "posix/interop/mutex.def") - } + createCInterop("mutex", sourceSet = "posix") sourceSets { commonMain { diff --git a/ktor-io/src/jsAndWasmSharedMain/kotlin/io/ktor/utils/io/ByteChannel.jsAndWasmShared.kt b/ktor-io/jsAndWasmShared/src/io/ktor/utils/io/ByteChannel.jsAndWasmShared.kt similarity index 100% rename from ktor-io/src/jsAndWasmSharedMain/kotlin/io/ktor/utils/io/ByteChannel.jsAndWasmShared.kt rename to ktor-io/jsAndWasmShared/src/io/ktor/utils/io/ByteChannel.jsAndWasmShared.kt diff --git a/ktor-java-modules-test/build.gradle.kts b/ktor-java-modules-test/build.gradle.kts index 6899d006313..e1b9e1e5f96 100644 --- a/ktor-java-modules-test/build.gradle.kts +++ b/ktor-java-modules-test/build.gradle.kts @@ -2,6 +2,8 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +import ktorbuild.targets.javaModuleName + plugins { id("java-library") } diff --git a/ktor-network/build.gradle.kts b/ktor-network/build.gradle.kts index df8caca958d..2bad75c3c9c 100644 --- a/ktor-network/build.gradle.kts +++ b/ktor-network/build.gradle.kts @@ -2,12 +2,12 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +import ktorbuild.createCInterop + description = "Ktor network utilities" kotlin { - createCInterop("network", nixTargets()) { - definitionFile = projectDir.resolve("nix/interop/network.def") - } + createCInterop("network", sourceSet = "nix") sourceSets { jvmAndPosixMain { diff --git a/ktor-server/build.gradle.kts b/ktor-server/build.gradle.kts index fd3410a4791..6f3cdc7ba8f 100644 --- a/ktor-server/build.gradle.kts +++ b/ktor-server/build.gradle.kts @@ -34,7 +34,3 @@ kotlin.sourceSets { } } } - -artifacts { - add("testOutput", tasks.named("jarTest")) -} diff --git a/ktor-server/ktor-server-cio/build.gradle.kts b/ktor-server/ktor-server-cio/build.gradle.kts index c7b84585d68..3a550b8bd03 100644 --- a/ktor-server/ktor-server-cio/build.gradle.kts +++ b/ktor-server/ktor-server-cio/build.gradle.kts @@ -23,7 +23,6 @@ kotlin.sourceSets { jvmTest { dependencies { api(project(":ktor-server:ktor-server-test-base")) - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) api(libs.logback.classic) implementation(libs.kotlinx.coroutines.debug) } diff --git a/ktor-server/ktor-server-config-yaml/gradle.properties b/ktor-server/ktor-server-config-yaml/gradle.properties index fdcdb8525c3..0b307118ca4 100644 --- a/ktor-server/ktor-server-config-yaml/gradle.properties +++ b/ktor-server/ktor-server-config-yaml/gradle.properties @@ -4,3 +4,4 @@ # Because of dependency on YAML library: https://github.com/Him188/yamlkt/issues/67 target.androidNative=false +target.watchosDeviceArm64=false diff --git a/ktor-server/ktor-server-core/build.gradle.kts b/ktor-server/ktor-server-core/build.gradle.kts index e12ecb52dbe..6dc2be9dd29 100644 --- a/ktor-server/ktor-server-core/build.gradle.kts +++ b/ktor-server/ktor-server-core/build.gradle.kts @@ -2,12 +2,12 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +import ktorbuild.createCInterop + description = "" kotlin { - createCInterop("host_common", posixTargets()) { - definitionFile = projectDir.resolve("posix/interop/host_common.def") - } + createCInterop("host_common", sourceSet = "posix") sourceSets { commonMain { @@ -48,7 +48,3 @@ kotlin { } } } - -artifacts { - add("testOutput", tasks.named("jarTest")) -} diff --git a/ktor-server/ktor-server-jetty-jakarta/build.gradle.kts b/ktor-server/ktor-server-jetty-jakarta/build.gradle.kts index ff8be93a183..ed425ffc547 100644 --- a/ktor-server/ktor-server-jetty-jakarta/build.gradle.kts +++ b/ktor-server/ktor-server-jetty-jakarta/build.gradle.kts @@ -4,6 +4,11 @@ description = "" +ktorBuild { + // The minimal JVM version required for Jetty 10+ + jvmToolchain(11) +} + kotlin { sourceSets { jvmMain { @@ -26,17 +31,8 @@ kotlin { api(project(":ktor-server:ktor-server-test-suites")) api(libs.jetty.servlet.jakarta) - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) api(libs.logback.classic) } } } } - -val jetty_alpn_boot_version: String? by extra - -dependencies { - if (jetty_alpn_boot_version != null) { - add("boot", libs.jetty.alpn.boot) - } -} diff --git a/ktor-server/ktor-server-jetty-jakarta/ktor-server-jetty-test-http2-jakarta/build.gradle.kts b/ktor-server/ktor-server-jetty-jakarta/ktor-server-jetty-test-http2-jakarta/build.gradle.kts index 25c645859a2..7a773823b70 100644 --- a/ktor-server/ktor-server-jetty-jakarta/ktor-server-jetty-test-http2-jakarta/build.gradle.kts +++ b/ktor-server/ktor-server-jetty-jakarta/ktor-server-jetty-test-http2-jakarta/build.gradle.kts @@ -4,6 +4,11 @@ import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest +ktorBuild { + // The minimal JVM version required for Jetty 10+ + jvmToolchain(11) +} + kotlin.sourceSets { jvmTest { dependencies { @@ -12,25 +17,12 @@ kotlin.sourceSets { api(libs.jetty.servlet.jakarta) api(project(":ktor-server:ktor-server-core")) api(project(":ktor-server:ktor-server-jetty-jakarta")) - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) api(libs.logback.classic) } } } -val jetty_alpn_boot_version: String? by extra -dependencies { - if (jetty_alpn_boot_version != null) { - add("boot", libs.jetty.alpn.boot) - } -} - tasks.named("jvmTest") { systemProperty("enable.http2", "true") - - if (jetty_alpn_boot_version != null && JavaVersion.current() == JavaVersion.VERSION_1_8) { - val bootClasspath = configurations.named("boot").get().files - jvmArgs(bootClasspath.map { "-Xbootclasspath/p:${it.absolutePath}" }.iterator()) - } } diff --git a/ktor-server/ktor-server-jetty/build.gradle.kts b/ktor-server/ktor-server-jetty/build.gradle.kts index 8550d5dbd80..ca4dd05be77 100644 --- a/ktor-server/ktor-server-jetty/build.gradle.kts +++ b/ktor-server/ktor-server-jetty/build.gradle.kts @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + description = "" kotlin { @@ -21,17 +25,8 @@ kotlin { api(project(":ktor-server:ktor-server-test-suites")) api(libs.jetty.servlet) - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) api(libs.logback.classic) } } } } - -val jetty_alpn_boot_version: String? by extra - -dependencies { - if (jetty_alpn_boot_version != null) { - add("boot", libs.jetty.alpn.boot) - } -} diff --git a/ktor-server/ktor-server-jetty/ktor-server-jetty-test-http2/build.gradle.kts b/ktor-server/ktor-server-jetty/ktor-server-jetty-test-http2/build.gradle.kts index 8f510fde5ef..2981a415d47 100644 --- a/ktor-server/ktor-server-jetty/ktor-server-jetty-test-http2/build.gradle.kts +++ b/ktor-server/ktor-server-jetty/ktor-server-jetty-test-http2/build.gradle.kts @@ -12,25 +12,12 @@ kotlin.sourceSets { api(libs.jetty.servlet) api(project(":ktor-server:ktor-server-core")) api(project(":ktor-server:ktor-server-jetty")) - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) api(libs.logback.classic) } } } -val jetty_alpn_boot_version: String? by extra -dependencies { - if (jetty_alpn_boot_version != null) { - add("boot", libs.jetty.alpn.boot) - } -} - tasks.named("jvmTest") { systemProperty("enable.http2", "true") - - if (jetty_alpn_boot_version != null && JavaVersion.current() == JavaVersion.VERSION_1_8) { - val bootClasspath = configurations.named("boot").get().files - jvmArgs(bootClasspath.map { "-Xbootclasspath/p:${it.absolutePath}" }.iterator()) - } } diff --git a/ktor-server/ktor-server-netty/build.gradle.kts b/ktor-server/ktor-server-netty/build.gradle.kts index 1b57fdad89e..77f0396aeec 100644 --- a/ktor-server/ktor-server-netty/build.gradle.kts +++ b/ktor-server/ktor-server-netty/build.gradle.kts @@ -6,8 +6,6 @@ import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest description = "" -val jetty_alpn_api_version: String by extra - val enableAlpnProp = project.hasProperty("enableAlpn") val osName = System.getProperty("os.name").lowercase() val nativeClassifier: String? = if (enableAlpnProp) { @@ -46,8 +44,6 @@ kotlin.sourceSets { api(libs.netty.tcnative.boringssl.static) api(libs.mockk) api(libs.logback.classic) - - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) } } } diff --git a/ktor-server/ktor-server-plugins/build.gradle.kts b/ktor-server/ktor-server-plugins/build.gradle.kts index 9aaff437fc9..ec3d49282f3 100644 --- a/ktor-server/ktor-server-plugins/build.gradle.kts +++ b/ktor-server/ktor-server-plugins/build.gradle.kts @@ -18,8 +18,6 @@ subprojects { jvmTest { dependencies { - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) - // Version catalogs aren't accessible directly inside subprojects block // https://github.com/gradle/gradle/issues/16634#issuecomment-809345790 api(rootProject.libs.logback.classic) diff --git a/ktor-server/ktor-server-plugins/ktor-server-default-headers/build.gradle.kts b/ktor-server/ktor-server-plugins/ktor-server-default-headers/build.gradle.kts index b1ac14591d0..bbee1c61878 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-default-headers/build.gradle.kts +++ b/ktor-server/ktor-server-plugins/ktor-server-default-headers/build.gradle.kts @@ -2,7 +2,7 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ -import java.time.* +import java.time.Year description = "" @@ -16,10 +16,8 @@ kotlin { } } -val configuredVersion: String by rootProject.extra - val generateKtorVersionFile by tasks.registering { - val ktorVersion = configuredVersion + val ktorVersion = project.version inputs.property("ktor_version", ktorVersion) val year = Year.now().toString() diff --git a/ktor-server/ktor-server-plugins/ktor-server-jte/build.gradle.kts b/ktor-server/ktor-server-plugins/ktor-server-jte/build.gradle.kts index f0278a2b799..6ea2aee51d2 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-jte/build.gradle.kts +++ b/ktor-server/ktor-server-plugins/ktor-server-jte/build.gradle.kts @@ -2,6 +2,11 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +ktorBuild { + // The minimal JDK version required for jte 3.0+ + jvmToolchain(17) +} + kotlin.sourceSets { jvmMain { dependencies { diff --git a/ktor-server/ktor-server-plugins/ktor-server-openapi/build.gradle.kts b/ktor-server/ktor-server-plugins/ktor-server-openapi/build.gradle.kts index b62e1b29814..0de1216a879 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-openapi/build.gradle.kts +++ b/ktor-server/ktor-server-plugins/ktor-server-openapi/build.gradle.kts @@ -1,7 +1,12 @@ /* - * Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +ktorBuild { + // The minimal JDK version required for Swagger Codegen + jvmToolchain(11) +} + kotlin { sourceSets { jvmMain { diff --git a/ktor-server/ktor-server-servlet-jakarta/build.gradle.kts b/ktor-server/ktor-server-servlet-jakarta/build.gradle.kts index 3e29eca3039..628c11c45f1 100644 --- a/ktor-server/ktor-server-servlet-jakarta/build.gradle.kts +++ b/ktor-server/ktor-server-servlet-jakarta/build.gradle.kts @@ -1,5 +1,13 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + description = "" +ktorBuild { + jvmToolchain(11) +} + kotlin.sourceSets { jvmMain { dependencies { @@ -11,7 +19,6 @@ kotlin.sourceSets { jvmTest { dependencies { - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) api(project(":ktor-server:ktor-server-config-yaml")) implementation(libs.mockk) implementation(libs.jakarta.servlet) diff --git a/ktor-server/ktor-server-servlet/build.gradle.kts b/ktor-server/ktor-server-servlet/build.gradle.kts index cc3035d6acc..8f593b6dbed 100644 --- a/ktor-server/ktor-server-servlet/build.gradle.kts +++ b/ktor-server/ktor-server-servlet/build.gradle.kts @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + description = "" kotlin.sourceSets { @@ -10,7 +14,6 @@ kotlin.sourceSets { jvmTest { dependencies { - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) api(project(":ktor-server:ktor-server-config-yaml")) implementation(libs.mockk) implementation(libs.javax.servlet) diff --git a/ktor-server/ktor-server-test-base/build.gradle.kts b/ktor-server/ktor-server-test-base/build.gradle.kts index 9f9c3a183f4..bc587ae3abf 100644 --- a/ktor-server/ktor-server-test-base/build.gradle.kts +++ b/ktor-server/ktor-server-test-base/build.gradle.kts @@ -4,7 +4,9 @@ description = "" -val jetty_alpn_boot_version: String? by extra +plugins { + id("ktorbuild.project.internal") +} kotlin.sourceSets { commonMain { @@ -22,10 +24,6 @@ kotlin.sourceSets { api(project(":ktor-network:ktor-network-tls:ktor-network-tls-certificates")) api(project(":ktor-server:ktor-server-plugins:ktor-server-call-logging")) api(project(":ktor-shared:ktor-test-base")) - - if (jetty_alpn_boot_version != null) { - api(libs.jetty.alpn.boot) - } } } } diff --git a/ktor-server/ktor-server-test-host/build.gradle.kts b/ktor-server/ktor-server-test-host/build.gradle.kts index 0602940f9cb..eeb822af28c 100644 --- a/ktor-server/ktor-server-test-host/build.gradle.kts +++ b/ktor-server/ktor-server-test-host/build.gradle.kts @@ -4,8 +4,6 @@ description = "" -val jetty_alpn_boot_version: String? by extra - kotlin.sourceSets { commonMain { dependencies { @@ -33,10 +31,6 @@ kotlin.sourceSets { // so shouldn"t increase the size of the final artifact. api(project(":ktor-server:ktor-server-plugins:ktor-server-websockets")) - if (jetty_alpn_boot_version != null) { - api(libs.jetty.alpn.boot) - } - api(libs.kotlin.test) api(libs.junit) implementation(libs.kotlinx.coroutines.debug) @@ -45,7 +39,6 @@ kotlin.sourceSets { jvmTest { dependencies { - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) api(project(":ktor-server:ktor-server-config-yaml")) api(libs.kotlin.test) } diff --git a/ktor-server/ktor-server-test-suites/build.gradle.kts b/ktor-server/ktor-server-test-suites/build.gradle.kts index 59e18b03417..3e02dea950c 100644 --- a/ktor-server/ktor-server-test-suites/build.gradle.kts +++ b/ktor-server/ktor-server-test-suites/build.gradle.kts @@ -4,6 +4,10 @@ description = "" +plugins { + id("ktorbuild.project.internal") +} + kotlin.sourceSets { commonMain { dependencies { @@ -29,8 +33,6 @@ kotlin.sourceSets { jvmTest { dependencies { - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) - api(libs.logback.classic) } } diff --git a/ktor-server/ktor-server-tests/build.gradle.kts b/ktor-server/ktor-server-tests/build.gradle.kts index 2f7e66c2917..ca724ea3590 100644 --- a/ktor-server/ktor-server-tests/build.gradle.kts +++ b/ktor-server/ktor-server-tests/build.gradle.kts @@ -1,6 +1,11 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + description = "" plugins { + id("ktorbuild.project.internal") id("kotlinx-serialization") } @@ -16,7 +21,6 @@ kotlin.sourceSets { dependencies { implementation(libs.jansi) implementation(project(":ktor-client:ktor-client-plugins:ktor-client-encoding")) - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) api(libs.logback.classic) api(project(":ktor-server:ktor-server-plugins:ktor-server-sse")) } diff --git a/ktor-server/ktor-server-tomcat-jakarta/build.gradle.kts b/ktor-server/ktor-server-tomcat-jakarta/build.gradle.kts index 3b0a81e9df5..df51c3dbce2 100644 --- a/ktor-server/ktor-server-tomcat-jakarta/build.gradle.kts +++ b/ktor-server/ktor-server-tomcat-jakarta/build.gradle.kts @@ -1,5 +1,14 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + description = "" +ktorBuild { + // The minimal JVM version required for Tomcat 10 + jvmToolchain(11) +} + kotlin.sourceSets { jvmMain { dependencies { @@ -14,7 +23,6 @@ kotlin.sourceSets { api(project(":ktor-server:ktor-server-test-base")) api(project(":ktor-server:ktor-server-test-suites")) api(project(":ktor-server:ktor-server-core")) - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) api(libs.logback.classic) } } diff --git a/ktor-server/ktor-server-tomcat/build.gradle.kts b/ktor-server/ktor-server-tomcat/build.gradle.kts index 36ecff8a64f..857b232e56b 100644 --- a/ktor-server/ktor-server-tomcat/build.gradle.kts +++ b/ktor-server/ktor-server-tomcat/build.gradle.kts @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + description = "" kotlin.sourceSets { @@ -14,7 +18,6 @@ kotlin.sourceSets { api(project(":ktor-server:ktor-server-test-base")) api(project(":ktor-server:ktor-server-test-suites")) api(project(":ktor-server:ktor-server-core")) - api(project(":ktor-server:ktor-server-core", configuration = "testOutput")) api(libs.logback.classic) } } diff --git a/ktor-shared/ktor-test-base/build.gradle.kts b/ktor-shared/ktor-test-base/build.gradle.kts index d7e45cbac83..da479ab470c 100644 --- a/ktor-shared/ktor-test-base/build.gradle.kts +++ b/ktor-shared/ktor-test-base/build.gradle.kts @@ -4,6 +4,10 @@ description = "Common extensions for testing Ktor" +plugins { + id("ktorbuild.project.internal") +} + kotlin.sourceSets { commonMain { dependencies { diff --git a/ktor-test-server/settings.gradle.kts b/ktor-test-server/settings.gradle.kts index 735c50882a1..3e474f7face 100644 --- a/ktor-test-server/settings.gradle.kts +++ b/ktor-test-server/settings.gradle.kts @@ -3,7 +3,7 @@ */ pluginManagement { - includeBuild("../gradle-settings-conventions") + includeBuild("../build-settings-logic") } plugins { diff --git a/ktor-utils/build.gradle.kts b/ktor-utils/build.gradle.kts index b7caba18221..db2748dbd96 100644 --- a/ktor-utils/build.gradle.kts +++ b/ktor-utils/build.gradle.kts @@ -2,19 +2,25 @@ * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. */ +import ktorbuild.createCInterop + plugins { id("kotlinx-serialization") } kotlin { - createCInterop("threadUtils", nixTargets() - androidNativeTargets()) { - definitionFile = File(projectDir, "nix/interop/threadUtils.def") - } - // we create an empty cinterop for androidNative targets - // to overcome an issue with cinterop Gradle dependencies resolution - createCInterop("threadUtils", androidNativeTargets()) { - definitionFile = File(projectDir, "androidNative/interop/threadUtils.def") - } + createCInterop( + "threadUtils", + sourceSet = "nix", + definitionFilePath = { target -> + // We create an empty cinterop for androidNative targets + // to overcome an issue with cinterop Gradle dependencies resolution + when { + target.startsWith("androidNative") -> "androidNative/interop/threadUtils.def" + else -> "nix/interop/threadUtils.def" + } + } + ) sourceSets { commonMain { diff --git a/settings.gradle.kts b/settings.gradle.kts index 90f8c89fe09..558ef752c06 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,7 +3,7 @@ */ pluginManagement { - includeBuild("gradle-settings-conventions") + includeBuild("build-settings-logic") } plugins { @@ -14,6 +14,7 @@ plugins { rootProject.name = "ktor" +includeBuild("build-logic") includeBuild("ktor-test-server") include(":ktor-server")