Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Move the logic from koltin repo atomicfu gradle plugin to the gradle plugin in the library. #406

Merged
merged 18 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,18 @@

## Requirements

Starting from version `0.23.2` of the library your project is required to use:
Starting from version `0.24.0` of the library your project is required to use:

* Gradle `7.0` or newer
fzhinkin marked this conversation as resolved.
Show resolved Hide resolved

* Kotlin `1.7.0` or newer
* Kotlin `1.9.0` or newer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

## Atomicfu compiler plugin sections seems to be obsolete as it describes how the plugin should be configured for older versions.

As there's a suggestion to use an older atomicfu version with older Kotlin version, it does make sense to move everything related to older versions to a dedicated page (or leave it here, but stress that it only applies to older atomicfu versions).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've fixed this section, added a note about Kotlin version requirements for the current atomicfu version and moved old configurations to the hidden section.


> **Note on Kotlin version:** Currently, the `kotlinx-atomicfu` Gradle plugin only relies on the version of Kotlin Gradle Plugin (KGP) present in the user's project.
> It's important to note this constraint if your project configures the custom Kotlin compiler version or modifies the Kotlin Native compiler version using `kotlin.native.version` property.

>**In case, you cannot upgrade the Kotlin version to 1.9.0 or newer** in your project,
you can downgrade `kotlinx-atomicfu` plugin version to `0.22.0`.
> Please note, though, that using the latest version of the plugin and upgrading the Kotlin version is the more recommended approach.

## Features

Expand Down Expand Up @@ -105,8 +112,6 @@ operations. They can be also atomically modified via `+=` and `-=` operators.
### Apply plugin
#### Gradle configuration

Gradle configuration is supported for all platforms, minimal version is Gradle 6.8.

In top-level build file:

<details open>
Expand Down
4 changes: 0 additions & 4 deletions atomicfu-gradle-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ dependencies {
// Atomicfu compiler plugin dependency will be loaded to kotlinCompilerPluginClasspath
// Atomicfu plugin will only be applied if the flag is set kotlinx.atomicfu.enableJsIrTransformation=true
compileOnly "org.jetbrains.kotlin:atomicfu:$kotlin_version"
// This runtimeOnly dependency is added as a temporary WA for the problem #384.
// The version 1.6.21 was chosen as the lowest valid version of this artifact,
// and it's expected to be resolved to the highest existing version from the user's classpath.
runtimeOnly "org.jetbrains.kotlin:atomicfu:1.6.21"

testImplementation gradleTestKit()
testImplementation 'org.jetbrains.kotlin:kotlin-test'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.atomicfu.plugin.gradle
Expand All @@ -20,33 +20,33 @@ import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.targets.js.*
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlinx.atomicfu.gradle.*
import java.io.*
import java.util.*
import javax.inject.Inject

private const val EXTENSION_NAME = "atomicfu"
private const val ORIGINAL_DIR_NAME = "originalClassesDir"
private const val COMPILE_ONLY_CONFIGURATION = "compileOnly"
private const val IMPLEMENTATION_CONFIGURATION = "implementation"
private const val TEST_IMPLEMENTATION_CONFIGURATION = "testImplementation"

// If the project uses KGP <= 1.6.20, only JS IR compiler plugin is available, and it is turned on via setting this property.
// The property is supported for backwards compatibility.
private const val ENABLE_JS_IR_TRANSFORMATION_LEGACY = "kotlinx.atomicfu.enableIrTransformation"
private const val ENABLE_JS_IR_TRANSFORMATION = "kotlinx.atomicfu.enableJsIrTransformation"
private const val ENABLE_JVM_IR_TRANSFORMATION = "kotlinx.atomicfu.enableJvmIrTransformation"
private const val ENABLE_NATIVE_IR_TRANSFORMATION = "kotlinx.atomicfu.enableNativeIrTransformation"
internal const val ENABLE_JS_IR_TRANSFORMATION = "kotlinx.atomicfu.enableJsIrTransformation"
internal const val ENABLE_JVM_IR_TRANSFORMATION = "kotlinx.atomicfu.enableJvmIrTransformation"
internal const val ENABLE_NATIVE_IR_TRANSFORMATION = "kotlinx.atomicfu.enableNativeIrTransformation"
private const val MIN_SUPPORTED_GRADLE_VERSION = "7.0"
private const val MIN_SUPPORTED_KGP_VERSION = "1.7.0"

open class AtomicFUGradlePlugin : Plugin<Project> {
override fun apply(project: Project) = project.run {
checkCompatibility()
// atomicfu version is stored at build time in atomicfu.properties file
// Atomicfu version is stored at build time in atomicfu.properties file
// located in atomicfu-gradle-plugin resources
val pluginVersion = loadPropertyFromResources("atomicfu.properties", "atomicfu.version")
extensions.add(EXTENSION_NAME, AtomicFUPluginExtension(pluginVersion))
applyAtomicfuCompilerPlugin()
val afuPluginVersion = loadPropertyFromResources("atomicfu.properties", "atomicfu.version")
checkCompatibility(afuPluginVersion)
extensions.add(EXTENSION_NAME, AtomicFUPluginExtension(afuPluginVersion))
// Apply Atomicfu compiler plugin
plugins.apply(AtomicfuKotlinCompilerPluginInternal::class.java)
configureDependencies()
configureTasks()
}
Expand All @@ -55,59 +55,40 @@ open class AtomicFUGradlePlugin : Plugin<Project> {
private fun loadPropertyFromResources(propFileName: String, property: String): String {
val props = Properties()
val inputStream = AtomicFUGradlePlugin::class.java.classLoader!!.getResourceAsStream(propFileName)
?: throw FileNotFoundException("You are applying `kotlinx-atomicfu` plugin of version 0.23.3 or newer, yet we were unable to determine the specific version of the plugin.\". \n" +
"Starting from version 0.23.3 of `kotlinx-atomicfu`, the plugin version is extracted from the `atomicfu.properties` file, which resides within the atomicfu-gradle-plugin-{version}.jar. \n" +
"However, this file couldn't be found. Please ensure that there are no atomicfu-gradle-plugin-{version}.jar with version older than 0.23.3 present on the classpath.\n" +
"If the problem is not resolved, please submit the issue: https://github.com/Kotlin/kotlinx-atomicfu/" )
?: throw FileNotFoundException(
"You are applying `kotlinx-atomicfu` plugin of version 0.24.0 or newer, yet we were unable to determine the specific version of the plugin.\". \n" +
"Starting from version 0.24.0 of `kotlinx-atomicfu`, the plugin version is extracted from the `atomicfu.properties` file, which resides within the atomicfu-gradle-plugin-{version}.jar. \n" +
"However, this file couldn't be found. Please ensure that there are no atomicfu-gradle-plugin-{version}.jar with version older than 0.24.0 present on the classpath.\n" +
"If the problem is not resolved, please submit the issue: https://github.com/Kotlin/kotlinx-atomicfu/issues"
)
inputStream.use { props.load(it) }
return props[property] as String
}

private fun Project.checkCompatibility() {
private fun Project.checkCompatibility(afuPluginVersion: String) {
val currentGradleVersion = GradleVersion.current()
val kotlinVersion = getKotlinVersion()
val minSupportedVersion = GradleVersion.version(MIN_SUPPORTED_GRADLE_VERSION)
if (currentGradleVersion < minSupportedVersion) {
throw GradleException(
"The current Gradle version is not compatible with Atomicfu gradle plugin. " +
"Please use Gradle $MIN_SUPPORTED_GRADLE_VERSION or newer, or the previous version of Atomicfu gradle plugin."
"Please use Gradle $MIN_SUPPORTED_GRADLE_VERSION or newer, or the previous version of Atomicfu gradle plugin."
)
}
if (!kotlinVersion.atLeast(1, 7, 0)) {
throw GradleException(
"The current Kotlin gradle plugin version is not compatible with Atomicfu gradle plugin. " +
"Please use Kotlin $MIN_SUPPORTED_KGP_VERSION or newer, or the previous version of Atomicfu gradle plugin."
if (!kotlinVersion.atLeast(1, 9, 0)) {
// Since Kotlin 1.9.0 the logic of the Gradle plugin from the Kotlin repo (AtomicfuKotlinGradleSubplugin)
// may be moved to the Gradle plugin in the library. The sources of the compiler plugin
// are published as `kotlin-atomicfu-compiler-plugin-embeddable` since Kotlin 1.9.0 and may be accessed out of the Kotlin repo.
error(
"You are applying `kotlinx-atomicfu` plugin of version $afuPluginVersion. " +
"However, this version of the plugin is only compatible with Kotlin versions newer than 1.9.0.\n" +
"If you wish to use this version of the plugin, please upgrade your Kotlin version to 1.9.0 or newer.\n" +
"In case you can not upgrade the Kotlin version, please read further instructions in the README: https://github.com/Kotlin/kotlinx-atomicfu/blob/master/README.md#requirements \n" +
"If you encounter any problems, please submit the issue: https://github.com/Kotlin/kotlinx-atomicfu/issues"
)
}
}

private fun Project.applyAtomicfuCompilerPlugin() {
val kotlinVersion = getKotlinVersion()
// for KGP >= 1.7.20:
// compiler plugin for JS IR is applied via the property `kotlinx.atomicfu.enableJsIrTransformation`
// compiler plugin for JVM IR is applied via the property `kotlinx.atomicfu.enableJvmIrTransformation`
if (kotlinVersion.atLeast(1, 7, 20)) {
plugins.apply(AtomicfuKotlinGradleSubplugin::class.java)
extensions.getByType(AtomicfuKotlinGradleSubplugin.AtomicfuKotlinGradleExtension::class.java).apply {
isJsIrTransformationEnabled = rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION)
isJvmIrTransformationEnabled = rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION)
if (kotlinVersion.atLeast(1, 9, 20)) {
// Native IR transformation is available since Kotlin 1.9.20
isNativeIrTransformationEnabled = rootProject.getBooleanProperty(ENABLE_NATIVE_IR_TRANSFORMATION)
}
}
} else {
// for KGP >= 1.6.20 && KGP <= 1.7.20:
// compiler plugin for JS IR is applied via the property `kotlinx.atomicfu.enableIrTransformation`
// compiler plugin for JVM IR is not supported yet
if (kotlinVersion.atLeast(1, 6, 20)) {
if (rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION_LEGACY)) {
plugins.apply(AtomicfuKotlinGradleSubplugin::class.java)
}
}
}
}

private fun Project.configureDependencies() {
withPluginWhenEvaluatedDependencies("kotlin") { version ->
dependencies.add(
Expand All @@ -131,7 +112,8 @@ private fun Project.configureDependencies() {
}

private fun Project.configureMultiplatformPluginDependencies(version: String) {
val multiplatformExtension = kotlinExtension as? KotlinMultiplatformExtension ?: error("Expected kotlin multiplatform extension")
val multiplatformExtension =
kotlinExtension as? KotlinMultiplatformExtension ?: error("Expected kotlin multiplatform extension")
val atomicfuDependency = "org.jetbrains.kotlinx:atomicfu:$version"
multiplatformExtension.sourceSets.getByName("commonMain").dependencies {
compileOnly(atomicfuDependency)
Expand All @@ -152,7 +134,6 @@ private fun Project.configureMultiplatformPluginDependencies(version: String) {
}
}
}

// atomicfu should also appear in apiElements config for native targets,
// otherwise the warning is triggered, see: KT-64109
multiplatformExtension.targets
Expand Down Expand Up @@ -186,7 +167,7 @@ private fun KotlinVersion.atLeast(major: Int, minor: Int, patch: Int) =
// kotlinx-atomicfu compiler plugin is available for KGP >= 1.6.20
private fun Project.isCompilerPluginAvailable() = getKotlinVersion().atLeast(1, 6, 20)

private fun Project.getBooleanProperty(name: String) =
internal fun Project.getBooleanProperty(name: String) =
rootProject.findProperty(name)?.toString()?.toBooleanStrict() ?: false

private fun String.toBooleanStrict(): Boolean = when (this) {
Expand All @@ -195,30 +176,29 @@ private fun String.toBooleanStrict(): Boolean = when (this) {
else -> throw IllegalArgumentException("The string doesn't represent a boolean value: $this")
}

private fun Project.needsJsIrTransformation(target: KotlinTarget): Boolean =
internal fun Project.needsJsIrTransformation(target: KotlinTarget): Boolean =
(rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION) || rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION_LEGACY))
&& target.isJsIrTarget()

private fun Project.needsJvmIrTransformation(target: KotlinTarget): Boolean =
internal fun Project.needsJvmIrTransformation(target: KotlinTarget): Boolean =
rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION) &&
(target.platformType == KotlinPlatformType.jvm || target.platformType == KotlinPlatformType.androidJvm)
(target.platformType == KotlinPlatformType.jvm || target.platformType == KotlinPlatformType.androidJvm)

private fun Project.needsNativeIrTransformation(target: KotlinTarget): Boolean =
internal fun Project.needsNativeIrTransformation(target: KotlinTarget): Boolean =
rootProject.getBooleanProperty(ENABLE_NATIVE_IR_TRANSFORMATION) &&
(target.platformType == KotlinPlatformType.native)

(target.platformType == KotlinPlatformType.native)

private fun KotlinTarget.isJsIrTarget() =
(this is KotlinJsTarget && this.irTarget != null) ||
(this is KotlinJsIrTarget && this.platformType != KotlinPlatformType.wasm)
(this is KotlinJsIrTarget && this.platformType != KotlinPlatformType.wasm)

private fun Project.isTransitiveAtomicfuDependencyRequired(target: KotlinTarget): Boolean {
val platformType = target.platformType
return !config.transformJvm && (platformType == KotlinPlatformType.jvm || platformType == KotlinPlatformType.androidJvm) ||
!config.transformJs && platformType == KotlinPlatformType.js ||
platformType == KotlinPlatformType.wasm ||
// Always add the transitive atomicfu dependency for native targets, see #379
platformType == KotlinPlatformType.native
(!config.transformJs && platformType == KotlinPlatformType.js) ||
platformType == KotlinPlatformType.wasm ||
// Always add the transitive atomicfu dependency for native targets, see #379
platformType == KotlinPlatformType.native
}

// Adds kotlinx-atomicfu-runtime as an implementation dependency to the JS IR target:
Expand Down Expand Up @@ -319,7 +299,7 @@ private fun Project.configureJvmTransformation() {
if (kotlinExtension is KotlinJvmProjectExtension || kotlinExtension is KotlinAndroidProjectExtension) {
val target = (kotlinExtension as KotlinSingleTargetExtension<*>).target
if (!needsJvmIrTransformation(target)) {
configureTransformationForTarget(target)
configureTransformationForTarget(target)
}
}
}
Expand All @@ -334,11 +314,11 @@ private fun Project.configureJsTransformation() {
private fun Project.configureMultiplatformTransformation() =
withKotlinTargets { target ->
// Skip transformation for common, native and wasm targets or in case IR transformation by the compiler plugin is enabled (for JVM or JS targets)
if (target.platformType == KotlinPlatformType.common ||
if (target.platformType == KotlinPlatformType.common ||
target.platformType == KotlinPlatformType.native ||
target.platformType == KotlinPlatformType.wasm ||
needsJvmIrTransformation(target) || needsJsIrTransformation(target)
) {
) {
return@withKotlinTargets
}
configureTransformationForTarget(target)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.atomicfu.plugin.gradle
fzhinkin marked this conversation as resolved.
Show resolved Hide resolved

import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.plugin.*

/**
* This Gradle plugin applies compiler transformations to the project, it was copied from the kotlin repo (org.jetbrains.kotlinx.atomicfu.gradle.AtomicfuKotlinGradleSubplugin).
*
* As the sources of the compiler plugin are published as `org.jetbrains.kotlin.kotlin-atomicfu-compiler-plugin-embeddable` starting from Kotlin 1.9.0,
* the Gradle plugin can access this artifact from the library and apply the transformations.
*
* NOTE: The version of KGP may differ from the version of Kotlin compiler, and kotlin.native.version may override the version of Kotlin native compiler.
fzhinkin marked this conversation as resolved.
Show resolved Hide resolved
* So, the right behavior for the Gradle plugin would be to obtain compiler versions and apply compiler transformations separately to JVM/JS and Native targets.
* This was postponed as a separate task (#408).
*/
internal class AtomicfuKotlinCompilerPluginInternal : KotlinCompilerPluginSupportPlugin {

companion object {
const val ATOMICFU_ARTIFACT_NAME = "kotlin-atomicfu-compiler-plugin-embeddable"
}

override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean {
val target = kotlinCompilation.target
val project = target.project
return project.needsJvmIrTransformation(target) || project.needsJsIrTransformation(target) || project.needsNativeIrTransformation(target)
}

override fun applyToCompilation(
kotlinCompilation: KotlinCompilation<*>
): Provider<List<SubpluginOption>> = kotlinCompilation.target.project.provider { emptyList() }

override fun getCompilerPluginId() = "org.jetbrains.kotlinx.atomicfu"

// Gets "org.jetbrains.kotlin:kotlin-atomicfu-compiler-plugin-embeddable:{KGP version}"
override fun getPluginArtifact(): SubpluginArtifact {
return JetBrainsSubpluginArtifact(ATOMICFU_ARTIFACT_NAME)
}
}
13 changes: 13 additions & 0 deletions integration-testing/examples/mpp-version-catalog/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile

plugins {
// this is necessary to avoid the plugins to be loaded multiple times
// in each subproject's classloader
alias(libs.plugins.kotlinMultiplatform) apply false
}

tasks.withType<KotlinCompile<*>>().configureEach {
kotlinOptions {
freeCompilerArgs += listOf("-Xskip-prerelease-check")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=2g
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[versions]
junit = "4.13.2"
kotlin = "1.9.21"
atomicfu = "0.23.2-SNAPSHOT"
ktor = "2.3.8"
logback = "1.5.0"

[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty-jvm", version.ref = "ktor" }
ktor-server-tests = { module = "io.ktor:ktor-server-tests-jvm", version.ref = "ktor" }
atomicfuGradlePlugin = { module = "org.jetbrains.kotlinx:atomicfu-gradle-plugin", version.ref = "atomicfu" }

[plugins]
kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
ktor = { id = "io.ktor.plugin", version.ref = "ktor" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
rootProject.name = "mpp-version-catalog"
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")

pluginManagement {
repositories {

google()
gradlePluginPortal()
mavenCentral()
mavenLocal()
}
}

dependencyResolutionManagement {
repositories {
google()
mavenCentral()
mavenLocal()
}
}

include(":shared")
Loading