aboutsummaryrefslogtreecommitdiff
path: root/atomicfu-gradle-plugin
diff options
context:
space:
mode:
Diffstat (limited to 'atomicfu-gradle-plugin')
-rw-r--r--atomicfu-gradle-plugin/build.gradle35
-rw-r--r--atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt311
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/BaseKotlinGradleTest.kt29
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/EmptyProjectTest.kt14
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/JsProjectTest.kt43
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/JvmProjectTest.kt63
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/MppProjectTest.kt55
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/Project.kt99
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/Assert.kt32
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/TestDsl.kt139
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/utils.kt13
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/BaseKotlinGradleTest.kt108
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JsProjectTest.kt49
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JvmProjectTest.kt108
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/MppProjectTest.kt219
-rw-r--r--atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/utils.kt36
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/empty/build.gradle10
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/js-simple/build.gradle18
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/js-simple/js-simple.gradle.kts38
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/js-simple/settings.gradle.kts1
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/build.gradle30
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/gradle.properties1
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/jvm-simple.gradle.kts41
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/settings.gradle.kts1
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/build.gradle73
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_both3
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_js2
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_jvm1
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/mpp-simple.gradle.kts90
-rw-r--r--atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/settings.gradle.kts1
30 files changed, 1080 insertions, 583 deletions
diff --git a/atomicfu-gradle-plugin/build.gradle b/atomicfu-gradle-plugin/build.gradle
index 28091b6..5312551 100644
--- a/atomicfu-gradle-plugin/build.gradle
+++ b/atomicfu-gradle-plugin/build.gradle
@@ -14,26 +14,41 @@ if (rootProject.ext.jvm_ir_enabled) {
// Gradle plugin must be compiled targeting the same Kotlin version as used by Gradle
kotlin.sourceSets.all {
languageSettings {
- apiVersion = "1.3"
- languageVersion = "1.3"
+ apiVersion = "1.4"
+ languageVersion = "1.4"
}
}
dependencies {
- compile gradleApi()
- compile project(":atomicfu-transformer")
- compile 'org.jetbrains.kotlin:kotlin-stdlib'
+ implementation(project(":atomicfu-transformer")) {
+ exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib'
+ }
+ compileOnly gradleApi()
+ compileOnly 'org.jetbrains.kotlin:kotlin-stdlib'
compileOnly "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ // atomicfu compiler plugin dependency will be loaded to kotlinCompilerPluginClasspath
+ implementation "org.jetbrains.kotlin:atomicfu:$kotlin_version"
- testCompile gradleTestKit()
- testCompile 'org.jetbrains.kotlin:kotlin-test'
- testCompile 'org.jetbrains.kotlin:kotlin-test-junit'
- testCompile 'junit:junit:4.12'
+ testImplementation gradleTestKit()
+ testImplementation 'org.jetbrains.kotlin:kotlin-test'
+ testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
+ testImplementation 'junit:junit:4.12'
}
configurations {
- testPluginClasspath
+ testPluginClasspath {
+ attributes {
+ attribute(
+ Usage.USAGE_ATTRIBUTE,
+ project.objects.named(Usage.class, Usage.JAVA_RUNTIME)
+ )
+ attribute(
+ Category.CATEGORY_ATTRIBUTE,
+ project.objects.named(Category.class, Category.LIBRARY)
+ )
+ }
+ }
}
dependencies {
diff --git a/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt b/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt
index bb619a5..b77e95b 100644
--- a/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt
+++ b/atomicfu-gradle-plugin/src/main/kotlin/kotlinx/atomicfu/plugin/gradle/AtomicFUGradlePlugin.kt
@@ -14,22 +14,33 @@ import org.gradle.api.tasks.compile.*
import org.gradle.api.tasks.testing.*
import org.gradle.jvm.tasks.*
import org.jetbrains.kotlin.gradle.dsl.*
+import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.plugin.*
import java.io.*
import java.util.*
import java.util.concurrent.*
+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.*
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"
open class AtomicFUGradlePlugin : Plugin<Project> {
override fun apply(project: Project) = project.run {
val pluginVersion = rootProject.buildscript.configurations.findByName("classpath")
?.allDependencies?.find { it.name == "atomicfu-gradle-plugin" }?.version
extensions.add(EXTENSION_NAME, AtomicFUPluginExtension(pluginVersion))
+ applyAtomicfuCompilerPlugin()
configureDependencies()
configureTasks()
}
@@ -43,12 +54,13 @@ private fun Project.configureDependencies() {
)
dependencies.add(TEST_IMPLEMENTATION_CONFIGURATION, getAtomicfuDependencyNotation(Platform.JVM, version))
}
- withPluginWhenEvaluatedDependencies("kotlin2js") { version ->
+ withPluginWhenEvaluatedDependencies("org.jetbrains.kotlin.js") { version ->
dependencies.add(
if (config.transformJs) COMPILE_ONLY_CONFIGURATION else IMPLEMENTATION_CONFIGURATION,
getAtomicfuDependencyNotation(Platform.JS, version)
)
dependencies.add(TEST_IMPLEMENTATION_CONFIGURATION, getAtomicfuDependencyNotation(Platform.JS, version))
+ addCompilerPluginDependency()
}
withPluginWhenEvaluatedDependencies("kotlin-multiplatform") { version ->
configureMultiplatformPluginDependencies(version)
@@ -59,7 +71,9 @@ private fun Project.configureTasks() {
val config = config
withPluginWhenEvaluated("kotlin") {
if (config.transformJvm) {
- configureTransformTasks("compileTestKotlin") { sourceSet, transformedDir, originalDir ->
+ // skip transformation task if ir transformation is enabled
+ if (rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION)) return@withPluginWhenEvaluated
+ configureJvmTransformation("compileTestKotlin") { sourceSet, transformedDir, originalDir ->
createJvmTransformTask(sourceSet).configureJvmTask(
sourceSet.compileClasspath,
sourceSet.classesTaskName,
@@ -70,20 +84,88 @@ private fun Project.configureTasks() {
}
}
}
- withPluginWhenEvaluated("kotlin2js") {
- if (config.transformJs) {
- configureTransformTasks("compileTestKotlin2Js") { sourceSet, transformedDir, originalDir ->
- createJsTransformTask(sourceSet).configureJsTask(
- sourceSet.classesTaskName,
- transformedDir,
- originalDir,
- config
- )
+ withPluginWhenEvaluated("org.jetbrains.kotlin.js") {
+ if (config.transformJs) configureJsTransformation()
+ }
+ withPluginWhenEvaluated("kotlin-multiplatform") {
+ configureMultiplatformTransformation()
+ }
+}
+
+private data class KotlinVersion(val major: Int, val minor: Int, val patch: Int)
+
+private fun Project.getKotlinVersion(): KotlinVersion {
+ val kotlinVersion = getKotlinPluginVersion()
+ val (major, minor) = kotlinVersion
+ .split('.')
+ .take(2)
+ .map { it.toInt() }
+ val patch = kotlinVersion.substringAfterLast('.').substringBefore('-').toInt()
+ return KotlinVersion(major, minor, patch)
+}
+
+private fun KotlinVersion.atLeast(major: Int, minor: Int, patch: Int) =
+ this.major == major && (this.minor == minor && this.patch >= patch || this.minor > minor) || this.major > major
+
+// kotlinx-atomicfu compiler plugin is available for KGP >= 1.6.20
+private fun Project.isCompilerPluginAvailable() = getKotlinVersion().atLeast(1, 6, 20)
+
+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)
+ }
+ } 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)
}
}
}
- withPluginWhenEvaluated("kotlin-multiplatform") {
- configureMultiplatformPluginTasks()
+}
+
+private fun Project.getBooleanProperty(name: String) =
+ rootProject.findProperty(name)?.toString()?.toBooleanStrict() ?: false
+
+private fun String.toBooleanStrict(): Boolean = when (this) {
+ "true" -> true
+ "false" -> false
+ else -> throw IllegalArgumentException("The string doesn't represent a boolean value: $this")
+}
+
+private fun Project.needsJsIrTransformation(target: KotlinTarget): Boolean =
+ (rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION) || rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION_LEGACY))
+ && target.isJsIrTarget()
+
+private fun KotlinTarget.isJsIrTarget() = (this is KotlinJsTarget && this.irTarget != null) || this is KotlinJsIrTarget
+
+private fun Project.addCompilerPluginDependency() {
+ if (isCompilerPluginAvailable()) {
+ withKotlinTargets { target ->
+ if (needsJsIrTransformation(target)) {
+ target.compilations.forEach { kotlinCompilation ->
+ kotlinCompilation.dependencies {
+ if (getKotlinVersion().atLeast(1, 7, 10)) {
+ // since Kotlin 1.7.10 we can add `atomicfu-runtime` dependency directly
+ implementation("org.jetbrains.kotlin:kotlinx-atomicfu-runtime:${getKotlinPluginVersion()}")
+ } else {
+ // add atomicfu compiler plugin dependency
+ // to provide the `atomicfu-runtime` library used during compiler plugin transformation
+ implementation("org.jetbrains.kotlin:atomicfu:${getKotlinPluginVersion()}")
+ }
+ }
+ }
+ }
+ }
}
}
@@ -135,88 +217,112 @@ fun Project.withPluginWhenEvaluatedDependencies(plugin: String, fn: Project.(ver
}
fun Project.withKotlinTargets(fn: (KotlinTarget) -> Unit) {
- extensions.findByType(KotlinProjectExtension::class.java)?.let { kotlinExtension ->
- val targetsExtension = (kotlinExtension as? ExtensionAware)?.extensions?.findByName("targets")
- @Suppress("UNCHECKED_CAST")
- val targets = targetsExtension as NamedDomainObjectContainer<KotlinTarget>
+ extensions.findByType(KotlinTargetsContainer::class.java)?.let { kotlinExtension ->
// find all compilations given sourceSet belongs to
- targets.all { target -> fn(target) }
+ kotlinExtension.targets
+ .all { target -> fn(target) }
}
}
-private fun KotlinCommonOptions.addFriendPaths(friendPathsFileCollection: FileCollection) {
- val argName = when (this) {
- is KotlinJvmOptions -> "-Xfriend-paths"
- is KotlinJsOptions -> "-Xfriend-modules"
- else -> return
+private fun KotlinCompile<*>.setFriendPaths(friendPathsFileCollection: FileCollection) {
+ val (majorVersion, minorVersion) = project.getKotlinPluginVersion()
+ .split('.')
+ .take(2)
+ .map { it.toInt() }
+ if (majorVersion == 1 && minorVersion < 7) {
+ (this as? AbstractKotlinCompile<*>)?.friendPaths?.from(friendPathsFileCollection)
+ } else {
+ // See KT-KT-54167 (works only for KGP 1.7.0+)
+ (this as BaseKotlinCompile).friendPaths.from(friendPathsFileCollection)
}
- freeCompilerArgs = freeCompilerArgs + "$argName=${friendPathsFileCollection.joinToString(",")}"
}
-fun Project.configureMultiplatformPluginTasks() {
- val originalDirsByCompilation = hashMapOf<KotlinCompilation<*>, FileCollection>()
- val config = config
+fun Project.configureJsTransformation() =
+ configureTransformationForTarget((kotlinExtension as KotlinJsProjectExtension).js())
+
+fun Project.configureMultiplatformTransformation() =
withKotlinTargets { target ->
if (target.platformType == KotlinPlatformType.common || target.platformType == KotlinPlatformType.native) {
return@withKotlinTargets // skip the common & native targets -- no transformation for them
}
- target.compilations.all compilations@{ compilation ->
- val compilationType = compilation.name.compilationNameToType()
- ?: return@compilations // skip unknown compilations
- val classesDirs = compilation.output.classesDirs
- // make copy of original classes directory
- val originalClassesDirs: FileCollection =
- project.files(classesDirs.from.toTypedArray()).filter { it.exists() }
- originalDirsByCompilation[compilation] = originalClassesDirs
- val transformedClassesDir =
- project.buildDir.resolve("classes/atomicfu/${target.name}/${compilation.name}")
- val transformTask = when (target.platformType) {
- KotlinPlatformType.jvm, KotlinPlatformType.androidJvm -> {
- if (!config.transformJvm) return@compilations // skip when transformation is turned off
- project.createJvmTransformTask(compilation).configureJvmTask(
- compilation.compileDependencyFiles,
- compilation.compileAllTaskName,
- transformedClassesDir,
- originalClassesDirs,
- config
- )
- }
- KotlinPlatformType.js -> {
- if (!config.transformJs) return@compilations // skip when transformation is turned off
- project.createJsTransformTask(compilation).configureJsTask(
- compilation.compileAllTaskName,
- transformedClassesDir,
- originalClassesDirs,
- config
- )
- }
- else -> error("Unsupported transformation platform '${target.platformType}'")
- }
- //now transformTask is responsible for compiling this source set into the classes directory
- classesDirs.setFrom(transformedClassesDir)
- classesDirs.builtBy(transformTask)
- (tasks.findByName(target.artifactsTaskName) as? Jar)?.apply {
- setupJarManifest(multiRelease = config.variant.toVariant() == Variant.BOTH)
+ configureTransformationForTarget(target)
+ }
+
+private fun Project.configureTransformationForTarget(target: KotlinTarget) {
+ val originalDirsByCompilation = hashMapOf<KotlinCompilation<*>, FileCollection>()
+ val config = config
+ target.compilations.all compilations@{ compilation ->
+ val compilationType = compilation.name.compilationNameToType()
+ ?: return@compilations // skip unknown compilations
+ val classesDirs = compilation.output.classesDirs
+ // make copy of original classes directory
+ val originalClassesDirs: FileCollection =
+ project.files(classesDirs.from.toTypedArray()).filter { it.exists() }
+ originalDirsByCompilation[compilation] = originalClassesDirs
+ val transformedClassesDir =
+ project.buildDir.resolve("classes/atomicfu/${target.name}/${compilation.name}")
+ val transformTask = when (target.platformType) {
+ KotlinPlatformType.jvm, KotlinPlatformType.androidJvm -> {
+ // skip transformation task if transformation is turned off or ir transformation is enabled
+ if (!config.transformJvm || rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION)) return@compilations
+ project.createJvmTransformTask(compilation).configureJvmTask(
+ compilation.compileDependencyFiles,
+ compilation.compileAllTaskName,
+ transformedClassesDir,
+ originalClassesDirs,
+ config
+ )
}
- // test should compile and run against original production binaries
- if (compilationType == CompilationType.TEST) {
- val mainCompilation =
- compilation.target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
- val originalMainClassesDirs = project.files(
- // use Callable because there is no guarantee that main is configured before test
- Callable { originalDirsByCompilation[mainCompilation]!! }
+ KotlinPlatformType.js -> {
+ // skip when js transformation is not needed or when IR is transformed
+ if (!config.transformJs || (needsJsIrTransformation(target))) {
+ return@compilations
+ }
+ project.createJsTransformTask(compilation).configureJsTask(
+ compilation.compileAllTaskName,
+ transformedClassesDir,
+ originalClassesDirs,
+ config
)
+ }
+ else -> error("Unsupported transformation platform '${target.platformType}'")
+ }
+ //now transformTask is responsible for compiling this source set into the classes directory
+ classesDirs.setFrom(transformedClassesDir)
+ classesDirs.builtBy(transformTask)
+ (tasks.findByName(target.artifactsTaskName) as? Jar)?.apply {
+ setupJarManifest(multiRelease = config.jvmVariant.toJvmVariant() == JvmVariant.BOTH)
+ }
+ // test should compile and run against original production binaries
+ if (compilationType == CompilationType.TEST) {
+ val mainCompilation =
+ compilation.target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
+ val originalMainClassesDirs = project.files(
+ // use Callable because there is no guarantee that main is configured before test
+ Callable { originalDirsByCompilation[mainCompilation]!! }
+ )
+ // KGP >= 1.7.0 has breaking changes in task hierarchy:
+ // https://youtrack.jetbrains.com/issue/KT-32805#focus=Comments-27-5915479.0-0
+ val (majorVersion, minorVersion) = getKotlinPluginVersion()
+ .split('.')
+ .take(2)
+ .map { it.toInt() }
+ if (majorVersion == 1 && minorVersion < 7) {
(tasks.findByName(compilation.compileKotlinTaskName) as? AbstractCompile)?.classpath =
originalMainClassesDirs + compilation.compileDependencyFiles - mainCompilation.output.classesDirs
+ } else {
+ (tasks.findByName(compilation.compileKotlinTaskName) as? AbstractKotlinCompileTool<*>)
+ ?.libraries
+ ?.setFrom(
+ originalMainClassesDirs + compilation.compileDependencyFiles - mainCompilation.output.classesDirs
+ )
+ }
- (tasks.findByName("${target.name}${compilation.name.capitalize()}") as? Test)?.classpath =
- originalMainClassesDirs + (compilation as KotlinCompilationToRunnableFiles).runtimeDependencyFiles - mainCompilation.output.classesDirs
+ (tasks.findByName("${target.name}${compilation.name.capitalize()}") as? Test)?.classpath =
+ originalMainClassesDirs + (compilation as KotlinCompilationToRunnableFiles).runtimeDependencyFiles - mainCompilation.output.classesDirs
- compilation.compileKotlinTask.doFirst {
- compilation.kotlinOptions.addFriendPaths(originalMainClassesDirs)
- }
- }
+ compilation.compileKotlinTask.setFriendPaths(originalMainClassesDirs)
}
}
}
@@ -234,15 +340,16 @@ fun Project.sourceSetsByCompilation(): Map<KotlinSourceSet, List<KotlinCompilati
}
fun Project.configureMultiplatformPluginDependencies(version: String) {
- if (rootProject.findProperty("kotlin.mpp.enableGranularSourceSetsMetadata").toString().toBoolean()) {
+ if (rootProject.getBooleanProperty("kotlin.mpp.enableGranularSourceSetsMetadata")) {
+ addCompilerPluginDependency()
val mainConfigurationName = project.extensions.getByType(KotlinMultiplatformExtension::class.java).sourceSets
- .getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
- .compileOnlyConfigurationName
+ .getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
+ .compileOnlyConfigurationName
dependencies.add(mainConfigurationName, getAtomicfuDependencyNotation(Platform.MULTIPLATFORM, version))
val testConfigurationName = project.extensions.getByType(KotlinMultiplatformExtension::class.java).sourceSets
- .getByName(KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME)
- .implementationConfigurationName
+ .getByName(KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME)
+ .implementationConfigurationName
dependencies.add(testConfigurationName, getAtomicfuDependencyNotation(Platform.MULTIPLATFORM, version))
// For each source set that is only used in Native compilations, add an implementation dependency so that it
@@ -258,20 +365,21 @@ fun Project.configureMultiplatformPluginDependencies(version: String) {
}
} else {
sourceSetsByCompilation().forEach { (sourceSet, compilations) ->
+ addCompilerPluginDependency()
val platformTypes = compilations.map { it.platformType }.toSet()
val compilationNames = compilations.map { it.compilationName }.toSet()
if (compilationNames.size != 1)
error("Source set '${sourceSet.name}' of project '$name' is part of several compilations $compilationNames")
val compilationType = compilationNames.single().compilationNameToType()
- ?: return@forEach // skip unknown compilations
+ ?: return@forEach // skip unknown compilations
val platform =
- if (platformTypes.size > 1) Platform.MULTIPLATFORM else // mix of platform types -> "common"
- when (platformTypes.single()) {
- KotlinPlatformType.common -> Platform.MULTIPLATFORM
- KotlinPlatformType.jvm, KotlinPlatformType.androidJvm -> Platform.JVM
- KotlinPlatformType.js -> Platform.JS
- KotlinPlatformType.native -> Platform.NATIVE
- }
+ if (platformTypes.size > 1) Platform.MULTIPLATFORM else // mix of platform types -> "common"
+ when (platformTypes.single()) {
+ KotlinPlatformType.common -> Platform.MULTIPLATFORM
+ KotlinPlatformType.jvm, KotlinPlatformType.androidJvm -> Platform.JVM
+ KotlinPlatformType.js -> Platform.JS
+ KotlinPlatformType.native, KotlinPlatformType.wasm -> Platform.NATIVE
+ }
val configurationName = when {
// impl dependency for native (there is no transformation)
platform == Platform.NATIVE -> sourceSet.implementationConfigurationName
@@ -285,7 +393,7 @@ fun Project.configureMultiplatformPluginDependencies(version: String) {
}
}
-fun Project.configureTransformTasks(
+fun Project.configureJvmTransformation(
testTaskName: String,
createTransformTask: (sourceSet: SourceSet, transformedDir: File, originalDir: FileCollection) -> Task
) {
@@ -305,7 +413,7 @@ fun Project.configureTransformTasks(
//now transformTask is responsible for compiling this source set into the classes directory
sourceSet.compiledBy(transformTask)
(tasks.findByName(sourceSet.jarTaskName) as? Jar)?.apply {
- setupJarManifest(multiRelease = config.variant.toVariant() == Variant.BOTH)
+ setupJarManifest(multiRelease = config.jvmVariant.toJvmVariant() == JvmVariant.BOTH)
}
// test should compile and run against original production binaries
if (compilationType == CompilationType.TEST) {
@@ -319,9 +427,7 @@ fun Project.configureTransformTasks(
classpath =
originalMainClassesDirs + sourceSet.compileClasspath - mainSourceSet.output.classesDirs
- (this as? KotlinCompile<*>)?.doFirst {
- kotlinOptions.addFriendPaths(originalMainClassesDirs)
- }
+ (this as? KotlinCompile<*>)?.setFriendPaths(originalMainClassesDirs)
}
// todo: fix test runtime classpath for JS?
@@ -331,7 +437,7 @@ fun Project.configureTransformTasks(
}
}
-fun String.toVariant(): Variant = enumValueOf(toUpperCase(Locale.US))
+fun String.toJvmVariant(): JvmVariant = enumValueOf(toUpperCase(Locale.US))
fun Project.createJvmTransformTask(compilation: KotlinCompilation<*>): AtomicFUTransformTask =
tasks.create(
@@ -348,9 +454,6 @@ fun Project.createJsTransformTask(compilation: KotlinCompilation<*>): AtomicFUTr
fun Project.createJvmTransformTask(sourceSet: SourceSet): AtomicFUTransformTask =
tasks.create(sourceSet.getTaskName("transform", "atomicfuClasses"), AtomicFUTransformTask::class.java)
-fun Project.createJsTransformTask(sourceSet: SourceSet): AtomicFUTransformJsTask =
- tasks.create(sourceSet.getTaskName("transform", "atomicfuJsFiles"), AtomicFUTransformJsTask::class.java)
-
fun AtomicFUTransformTask.configureJvmTask(
classpath: FileCollection,
classesTaskName: String,
@@ -363,7 +466,7 @@ fun AtomicFUTransformTask.configureJvmTask(
classPath = classpath
inputFiles = originalClassesDir
outputDir = transformedClassesDir
- variant = config.variant
+ jvmVariant = config.jvmVariant
verbose = config.verbose
}
@@ -395,7 +498,7 @@ class AtomicFUPluginExtension(pluginVersion: String?) {
var dependenciesVersion = pluginVersion
var transformJvm = true
var transformJs = true
- var variant: String = "FU"
+ var jvmVariant: String = "FU"
var verbose: Boolean = false
}
@@ -413,7 +516,8 @@ open class AtomicFUTransformTask : ConventionTask() {
lateinit var classPath: FileCollection
@Input
- var variant = "FU"
+ var jvmVariant = "FU"
+
@Input
var verbose = false
@@ -422,7 +526,7 @@ open class AtomicFUTransformTask : ConventionTask() {
val cp = classPath.files.map { it.absolutePath }
inputFiles.files.forEach { inputDir ->
AtomicFUTransformer(cp, inputDir, outputDir).let { t ->
- t.variant = variant.toVariant()
+ t.jvmVariant = jvmVariant.toJvmVariant()
t.verbose = verbose
t.transform()
}
@@ -438,6 +542,7 @@ open class AtomicFUTransformJsTask : ConventionTask() {
@OutputDirectory
lateinit var outputDir: File
+
@Input
var verbose = false
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/BaseKotlinGradleTest.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/BaseKotlinGradleTest.kt
deleted file mode 100644
index b81a0c9..0000000
--- a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/BaseKotlinGradleTest.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.atomicfu.plugin.gradle
-
-import org.gradle.internal.impldep.com.google.common.io.Files
-import org.junit.After
-import org.junit.Before
-import java.io.File
-
-abstract class BaseKotlinGradleTest {
- private lateinit var workingDir: File
-
- fun project(name: String, suffix: String = "", fn: Project.() -> Unit) {
- workingDir = File("build${File.separator}test-$name$suffix").absoluteFile
- workingDir.deleteRecursively()
- workingDir.mkdirs()
- val testResources = File("src/test/resources")
- val originalProjectDir = testResources.resolve("projects/$name").apply { checkExists() }
- val projectDir = workingDir.resolve(name).apply { mkdirs() }
- originalProjectDir.listFiles().forEach { it.copyRecursively(projectDir.resolve(it.name)) }
-
- // Add an empty setting.gradle
- projectDir.resolve("settings.gradle").writeText("// this file is intentionally left empty")
-
- Project(projectDir = projectDir).fn()
- }
-} \ No newline at end of file
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/EmptyProjectTest.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/EmptyProjectTest.kt
deleted file mode 100644
index b542f2b..0000000
--- a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/EmptyProjectTest.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.atomicfu.plugin.gradle
-
-import org.junit.Test
-
-class EmptyProjectTest : BaseKotlinGradleTest() {
- @Test
- fun testEmpty() = project("empty") {
- build("build") {}
- }
-}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/JsProjectTest.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/JsProjectTest.kt
deleted file mode 100644
index 2201199..0000000
--- a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/JsProjectTest.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.atomicfu.plugin.gradle
-
-import org.gradle.testkit.runner.TaskOutcome
-import org.junit.Test
-import java.io.File
-
-class JsProjectTest : BaseKotlinGradleTest() {
- @Test
- fun testKotlin2JsPlugin() = project("js-simple") {
- val tasksToCheck = arrayOf(
- ":compileKotlin2Js",
- ":compileTestKotlin2Js",
- ":transformAtomicfuJsFiles",
- ":transformTestAtomicfuJsFiles"
- )
-
- build("build") {
- checkOutcomes(TaskOutcome.SUCCESS, *tasksToCheck)
-
- val testCompileClasspathFiles = projectDir.resolve("build/test_compile_classpath.txt")
- .readLines().asSequence().flatMap { File(it).walk().filter(File::isFile) }.toHashSet()
-
- projectDir.resolve("build/classes/kotlin/main/js-simple.js").let {
- it.checkExists()
- check(it in testCompileClasspathFiles) { "Original '$it' is missing from test compile classpath" }
- // todo: check test runtime classpath when js test tasks are supported in plugin
- }
-
- projectDir.resolve("build/classes/atomicfu/main/js-simple.js").let {
- it.checkExists()
- check(it !in testCompileClasspathFiles) { "Transformed '$it' is present in test compile classpath" }
- }
- }
-
- build("build") {
- checkOutcomes(TaskOutcome.UP_TO_DATE, *tasksToCheck)
- }
- }
-}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/JvmProjectTest.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/JvmProjectTest.kt
deleted file mode 100644
index e017f05..0000000
--- a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/JvmProjectTest.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.atomicfu.plugin.gradle
-
-import org.gradle.testkit.runner.TaskOutcome
-import org.junit.Test
-import java.io.File
-
-class JvmProjectTest : BaseKotlinGradleTest() {
- @Test
- fun testKotlinPlugin() =
- project("jvm-simple") {
- doSimpleTest()
- }
-
- @Test
- fun testKotlinPlatformJvmPlugin() =
- project("jvm-simple", "-platform") {
- projectDir.resolve("build.gradle").modify {
- it.checkedReplace("apply plugin: 'kotlin'", "apply plugin: 'kotlin-platform-jvm'")
- }
- doSimpleTest()
- }
-
- private fun Project.doSimpleTest() {
- val tasksToCheck = arrayOf(
- ":compileKotlin",
- ":compileTestKotlin",
- ":transformAtomicfuClasses",
- ":transformTestAtomicfuClasses"
- )
-
- build("build") {
- checkOutcomes(TaskOutcome.SUCCESS, *tasksToCheck)
-
- val testCompileClasspathFiles = filesFrom("build/test_compile_classpath.txt")
- val testRuntimeClasspathFiles = filesFrom("build/test_runtime_classpath.txt")
-
- projectDir.resolve("build/classes/kotlin/main/IntArithmetic.class").let {
- it.checkExists()
- check(it in testCompileClasspathFiles) { "Original '$it' is missing from test compile classpath" }
- check(it in testRuntimeClasspathFiles) { "Original '$it' is missing from test runtime classpath" }
- }
-
- projectDir.resolve("build/classes/atomicfu/main/IntArithmetic.class").let {
- it.checkExists()
- check(it !in testCompileClasspathFiles) { "Transformed '$it' is present in test compile classpath" }
- check(it !in testRuntimeClasspathFiles) { "Transformed '$it' is present in test runtime classpath" }
- }
- }
-
- build("build") {
- checkOutcomes(TaskOutcome.UP_TO_DATE, *tasksToCheck)
- }
- }
-
- private fun Project.filesFrom(name: String) = projectDir.resolve(name)
- .readLines().asSequence().flatMap { listFiles(it) }.toHashSet()
-
- private fun listFiles(dir: String): Sequence<File> = File(dir).walk().filter { it.isFile }
-}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/MppProjectTest.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/MppProjectTest.kt
deleted file mode 100644
index 6dd7fa1..0000000
--- a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/MppProjectTest.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.atomicfu.plugin.gradle
-
-import org.gradle.testkit.runner.TaskOutcome
-import org.junit.Test
-import java.io.File
-
-class MppProjectTest : BaseKotlinGradleTest() {
- @Test
- fun testKotlinMultiplatformPlugin() = project("mpp-simple") {
- val tasksToCheck = arrayOf(
- ":compileKotlinJvm",
- ":compileTestKotlinJvm",
- ":transformJvmMainAtomicfu",
- ":transformJvmTestAtomicfu",
- ":compileKotlinJs",
- ":transformJsMainAtomicfu"
- )
-
- build("build") {
- checkOutcomes(TaskOutcome.SUCCESS, *tasksToCheck)
-
- fun checkPlatform(platform: String, fileInMainName: String) {
- val isJs = platform == "js"
- val testCompileClasspathFiles = projectDir.resolve("build/classpath/$platform/test_compile.txt")
- .readLines().asSequence().flatMapTo(HashSet()) { File(it).walk().filter(File::isFile) }
- val testRuntimeClasspathFiles = if (isJs) emptySet<File>() else projectDir.resolve("build/classpath/$platform/test_runtime.txt")
- .readLines().asSequence().flatMapTo(HashSet()) { File(it).walk().filter(File::isFile) }
-
- projectDir.resolve("build/classes/kotlin/$platform/main/$fileInMainName").let {
- it.checkExists()
- check(it in testCompileClasspathFiles) { "Original '$it' is missing from $platform test compile classpath" }
- if (!isJs) check(it in testRuntimeClasspathFiles) { "Original '$it' is missing from $platform test runtime classpath" }
- }
-
- projectDir.resolve("build/classes/atomicfu/jvm/main/IntArithmetic.class").let {
- it.checkExists()
- check(it !in testCompileClasspathFiles) { "Transformed '$it' is present in $platform test compile classpath" }
- if (!isJs) check(it !in testRuntimeClasspathFiles) { "Transformed '$it' is present in $platform test runtime classpath" }
- }
-
- }
-
- checkPlatform("jvm", "IntArithmetic.class")
- checkPlatform("js", "mpp-simple.js")
- }
-
- build("build") {
- checkOutcomes(TaskOutcome.UP_TO_DATE, *tasksToCheck)
- }
- }
-}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/Project.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/Project.kt
deleted file mode 100644
index f3f49e1..0000000
--- a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/Project.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.atomicfu.plugin.gradle
-
-import org.gradle.testkit.runner.BuildResult
-import org.gradle.testkit.runner.GradleRunner
-import java.io.File
-
-class Project(val projectDir: File) {
- init {
- projectDir.resolve("build.gradle").modify {
- buildScript + "\n\n" + it
- }
- }
-
- private var isDebug = false
- private var printStdout = false
-
- @Deprecated("Should be used for debug only!")
- @Suppress("unused")
- fun debug() {
- isDebug = true
- }
-
- /**
- * Redirects Gradle runner output to stdout. Useful for debugging.
- */
- @Deprecated("Should be used for debug only!")
- @Suppress("unused")
- fun printStdout() {
- printStdout = true
- }
-
- fun gradle(vararg tasks: String): GradleRunner =
- GradleRunner.create()
- .withDebug(isDebug)
- .withProjectDir(projectDir)
- .withArguments(*(defaultArguments() + tasks))
- .run {
- if (printStdout) {
- forwardStdOutput(System.out.bufferedWriter())
- } else {
- this
- }
- }
-
- fun build(vararg tasks: String, fn: BuildResult.() -> Unit = {}) {
- val gradle = gradle(*tasks)
- val buildResult = gradle.build()
- buildResult.fn()
- }
-
- @Suppress("unused")
- fun buildAndFail(vararg tasks: String, fn: BuildResult.() -> Unit = {}) {
- val gradle = gradle(*tasks)
- val buildResult = gradle.buildAndFail()
- buildResult.fn()
- }
-
- private fun defaultArguments(): Array<String> =
- arrayOf("--stacktrace")
-
- companion object {
- private fun readFileList(fileName: String): String {
- val resource = Project::class.java.classLoader.getResource(fileName)
- ?: throw IllegalStateException("Could not find resource '$fileName'")
- val files = File(resource.toURI())
- .readLines()
- .map { File(it).absolutePath.replace("\\", "\\\\") } // escape backslashes in Windows paths
- return files.joinToString(", ") { "'$it'" }
- }
-
- private val buildScript = run {
- """
- buildscript {
- dependencies {
- classpath files(${readFileList("plugin-classpath.txt")})
- }
- }
-
- repositories {
- jcenter()
- mavenCentral()
- maven { url 'https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev' }
- maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
- maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' }
- maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev' }
- maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
- }
-
- def atomicfuJvm = files(${readFileList("atomicfu-jvm.txt")})
- def atomicfuJs = files(${readFileList("atomicfu-js.txt")})
- def atomicfuMetadata = files(${readFileList("atomicfu-metadata.txt")})
- """.trimIndent()
- }
- }
-}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/Assert.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/Assert.kt
new file mode 100644
index 0000000..f55e38a
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/Assert.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016-2022 JetBrains s.r.o.
+ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
+ */
+
+package kotlinx.atomicfu.plugin.gradle.internal
+
+import org.gradle.testkit.runner.BuildResult
+import org.gradle.testkit.runner.TaskOutcome
+import kotlin.test.assertEquals
+
+/**
+ * Helper `fun` for asserting a [TaskOutcome] to be equal to [TaskOutcome.SUCCESS]
+ */
+internal fun BuildResult.assertTaskSuccess(task: String) {
+ assertTaskOutcome(TaskOutcome.SUCCESS, task)
+}
+
+/**
+ * Helper `fun` for asserting a [TaskOutcome] to be equal to [TaskOutcome.FAILED]
+ */
+internal fun BuildResult.assertTaskFailure(task: String) {
+ assertTaskOutcome(TaskOutcome.FAILED, task)
+}
+
+internal fun BuildResult.assertTaskUpToDate(task: String) {
+ assertTaskOutcome(TaskOutcome.UP_TO_DATE, task)
+}
+
+private fun BuildResult.assertTaskOutcome(taskOutcome: TaskOutcome, taskName: String) {
+ assertEquals(taskOutcome, task(taskName)?.outcome)
+}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/TestDsl.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/TestDsl.kt
new file mode 100644
index 0000000..2541b41
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/TestDsl.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2016-2022 JetBrains s.r.o.
+ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
+ */
+
+package kotlinx.atomicfu.plugin.gradle.internal
+
+import kotlinx.atomicfu.plugin.gradle.test.*
+import org.gradle.testkit.runner.*
+import java.io.*
+
+internal fun BaseKotlinGradleTest.test(fn: BaseKotlinScope.() -> Unit): GradleRunner {
+ val baseKotlinScope = BaseKotlinScope()
+ fn(baseKotlinScope)
+
+ baseKotlinScope.files.forEach { scope ->
+ val fileWriteTo = rootProjectDir.resolve(scope.filePath)
+ .apply {
+ parentFile.mkdirs()
+ createNewFile()
+ }
+
+ scope.files.forEach {
+ val fileContent = readFileList(it)
+ fileWriteTo.appendText(fileContent)
+ }
+ }
+
+ return GradleRunner.create()
+ .withProjectDir(rootProjectDir)
+ .withArguments(baseKotlinScope.runner.arguments)
+ .withPluginClasspath()
+ .addPluginTestRuntimeClasspath()
+}
+
+/**
+ * same as [file][FileContainer.file], but prepends "src/${sourceSet}/kotlin" before given `classFileName`
+ */
+internal fun FileContainer.kotlin(classFileName: String, sourceSet: String = "main", fn: AppendableScope.() -> Unit) {
+ require(classFileName.endsWith(".kt")) {
+ "ClassFileName must end with '.kt'"
+ }
+
+ val fileName = "src/${sourceSet}/kotlin/$classFileName"
+ file(fileName, fn)
+}
+
+/**
+ * Shortcut for creating a `build.gradle.kts` by using [file][FileContainer.file]
+ */
+internal fun FileContainer.buildGradleKts(fn: AppendableScope.() -> Unit) {
+ val fileName = "build.gradle.kts"
+ file(fileName, fn)
+}
+
+/**
+ * Shortcut for creating a `settings.gradle.kts` by using [file][FileContainer.file]
+ */
+internal fun FileContainer.settingsGradleKts(fn: AppendableScope.() -> Unit) {
+ val fileName = "settings.gradle.kts"
+ file(fileName, fn)
+}
+
+/**
+ * Shortcut for creating a `gradle.properties` by using [file][FileContainer.file]
+ */
+internal fun FileContainer.gradleProperties(fn: AppendableScope.() -> Unit) {
+ val fileName = "gradle.properties"
+ file(fileName, fn)
+}
+
+/**
+ * Declares a directory with the given [dirName] inside the current container.
+ * All calls creating files within this scope will create the files nested in this directory.
+ *
+ * Note that it is valid to call this method multiple times at the same level with the same [dirName].
+ * Files declared within 2 independent calls to [dir] will be added to the same directory.
+ */
+internal fun FileContainer.dir(dirName: String, fn: DirectoryScope.() -> Unit) {
+ DirectoryScope(dirName, this).fn()
+}
+
+internal fun BaseKotlinScope.runner(fn: Runner.() -> Unit) {
+ val runner = Runner()
+ fn(runner)
+
+ this.runner = runner
+}
+
+internal fun AppendableScope.resolve(fileName: String) {
+ this.files.add(fileName)
+}
+
+internal interface FileContainer {
+ fun file(fileName: String, fn: AppendableScope.() -> Unit)
+}
+
+internal class BaseKotlinScope : FileContainer {
+ var files: MutableList<AppendableScope> = mutableListOf()
+ var runner: Runner = Runner()
+
+ override fun file(fileName: String, fn: AppendableScope.() -> Unit) {
+ val appendableScope = AppendableScope(fileName)
+ fn(appendableScope)
+ files.add(appendableScope)
+ }
+}
+
+internal class DirectoryScope(
+ val dirPath: String,
+ val parent: FileContainer
+): FileContainer {
+
+ override fun file(fileName: String, fn: AppendableScope.() -> Unit) {
+ parent.file("$dirPath/$fileName", fn)
+ }
+}
+
+internal class AppendableScope(val filePath: String) {
+ val files: MutableList<String> = mutableListOf()
+}
+
+internal class Runner {
+ val arguments: MutableList<String> = mutableListOf()
+}
+
+internal fun readFileList(fileName: String): String =
+ getFile(fileName).readText()
+
+internal fun getFile(fileName: String): File {
+ val resource = BaseKotlinGradleTest::class.java.classLoader.getResource(fileName)
+ ?: throw IllegalStateException("Could not find resource '$fileName'")
+ return File(resource.toURI())
+}
+
+internal fun GradleRunner.addPluginTestRuntimeClasspath() = apply {
+ val pluginClasspath = getFile("plugin-classpath.txt").readLines().map { File(it) }
+ withPluginClasspath(pluginClasspath)
+}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/utils.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/utils.kt
new file mode 100644
index 0000000..49856f2
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/internal/utils.kt
@@ -0,0 +1,13 @@
+package kotlinx.atomicfu.plugin.gradle.internal
+
+import java.io.*
+import kotlin.test.*
+
+fun File.checkExists() {
+ assertTrue(exists(), "File does not exist: $canonicalPath")
+}
+
+fun File.filesFrom(relative: String) = resolve(relative)
+ .readLines().asSequence().flatMap { listFiles(it) }.toHashSet()
+
+fun listFiles(dir: String): Sequence<File> = File(dir).walk().filter { it.isFile } \ No newline at end of file
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/BaseKotlinGradleTest.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/BaseKotlinGradleTest.kt
new file mode 100644
index 0000000..e9ff7bb
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/BaseKotlinGradleTest.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.atomicfu.plugin.gradle.test
+
+import kotlinx.atomicfu.plugin.gradle.internal.*
+import org.objectweb.asm.*
+import java.io.File
+import kotlin.test.*
+
+abstract class BaseKotlinGradleTest(private val projectName: String) {
+ internal val rootProjectDir: File
+ private val ATOMIC_FU_REF = "Lkotlinx/atomicfu/".toByteArray()
+ private val KOTLIN_METADATA_DESC = "Lkotlin/Metadata;"
+
+ init {
+ rootProjectDir = File("build${File.separator}test-$projectName").absoluteFile
+ rootProjectDir.deleteRecursively()
+ rootProjectDir.mkdirs()
+ }
+
+ internal abstract fun BaseKotlinScope.createProject()
+
+ val runner = test {
+ createProject()
+ runner {
+ arguments.add(":build")
+ }
+ }
+
+ fun checkTaskOutcomes(executedTasks: List<String>, excludedTasks: List<String>) {
+ runner.build().apply {
+ val tasks = tasks.map { it.path }
+ excludedTasks.forEach {
+ check(it !in tasks) { "Post-compilation transformation task $it was added in the compiler plugin mode" }
+ }
+ executedTasks.forEach {
+ assertTaskSuccess(it)
+ }
+ }
+ // check that task outcomes are cached for the second build
+ runner.build().apply {
+ executedTasks.forEach {
+ assertTaskUpToDate(it)
+ }
+ }
+ }
+
+ fun checkJvmCompilationClasspath(originalClassFile: String, transformedClassFile: String) {
+ // check that test compile and runtime classpath does not contain original non-transformed classes
+ val testCompileClasspathFiles = rootProjectDir.filesFrom("build/test_compile_jvm_classpath.txt")
+ val testRuntimeClasspathFiles = rootProjectDir.filesFrom("build/test_runtime_jvm_classpath.txt")
+
+ rootProjectDir.resolve(transformedClassFile).let {
+ it.checkExists()
+ check(it in testCompileClasspathFiles) { "Transformed '$it' is missing from test compile classpath" }
+ check(it in testRuntimeClasspathFiles) { "Transformed '$it' is missing from test runtime classpath" }
+ }
+
+ rootProjectDir.resolve(originalClassFile).let {
+ it.checkExists()
+ check(it !in testCompileClasspathFiles) { "Original '$it' is present in test compile classpath" }
+ check(it !in testRuntimeClasspathFiles) { "Original '$it' is present in test runtime classpath" }
+ }
+ }
+
+ fun checkJsCompilationClasspath() {
+ // check that test compilation depends on transformed main sources
+ val testCompileClasspathFiles = rootProjectDir.filesFrom("build/test_compile_js_classpath.txt")
+
+ rootProjectDir.resolve("build/classes/atomicfu/js/main/$projectName.js").let {
+ it.checkExists()
+ check(it in testCompileClasspathFiles) { "Transformed '$it' is missing from test compile classpath" }
+ }
+ }
+
+ fun checkBytecode(classFilePath: String) {
+ rootProjectDir.resolve(classFilePath).let {
+ it.checkExists()
+ assertFalse(it.readBytes().findAtomicfuRef(), "Found 'Lkotlinx/atomicfu/' reference in $it" )
+ }
+ }
+
+ private fun ByteArray.findAtomicfuRef(): Boolean {
+ val bytes = this.eraseMetadata()
+ loop@for (i in 0 until bytes.size - ATOMIC_FU_REF.size) {
+ for (j in 0 until ATOMIC_FU_REF.size) {
+ if (bytes[i + j] != ATOMIC_FU_REF[j]) continue@loop
+ }
+ return true
+ }
+ return false
+ }
+
+ // The atomicfu compiler plugin does not remove atomic properties from metadata,
+ // so for now we check that there are no ATOMIC_FU_REF left in the class bytecode excluding metadata.
+ // This may be reverted after the fix in the compiler plugin transformer (See #254).
+ private fun ByteArray.eraseMetadata(): ByteArray {
+ val cw = ClassWriter(ClassWriter.COMPUTE_MAXS or ClassWriter.COMPUTE_FRAMES)
+ ClassReader(this).accept(object : ClassVisitor(Opcodes.ASM9, cw) {
+ override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
+ return if (descriptor == KOTLIN_METADATA_DESC) null else super.visitAnnotation(descriptor, visible)
+ }
+ }, ClassReader.SKIP_FRAMES)
+ return cw.toByteArray()
+ }
+}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JsProjectTest.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JsProjectTest.kt
new file mode 100644
index 0000000..0b34955
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JsProjectTest.kt
@@ -0,0 +1,49 @@
+package kotlinx.atomicfu.plugin.gradle.test
+
+import kotlinx.atomicfu.plugin.gradle.internal.*
+import kotlinx.atomicfu.plugin.gradle.internal.BaseKotlinScope
+import org.junit.Test
+
+/**
+ * Test that ensures correctness of `atomicfu-gradle-plugin` application to the JS project:
+ * - post-compilation js transformation tasks are created
+ * (legacy transformation is tested here, compiler plugin is not applied).
+ * - original non-transformed classes are not left in compile/runtime classpath.
+ */
+class JsLegacyTransformationTest : BaseKotlinGradleTest("js-simple") {
+
+ override fun BaseKotlinScope.createProject() {
+ buildGradleKts {
+ resolve("projects/js-simple/js-simple.gradle.kts")
+ }
+ settingsGradleKts {
+ resolve("projects/js-simple/settings.gradle.kts")
+ }
+ dir("src/main/kotlin") {}
+ kotlin("IntArithmetic.kt", "main") {
+ resolve("projects/js-simple/src/main/kotlin/IntArithmetic.kt")
+ }
+ dir("src/test/kotlin") {}
+ kotlin("ArithmeticTest.kt", "test") {
+ resolve("projects/js-simple/src/test/kotlin/ArithmeticTest.kt")
+ }
+ }
+
+ @Test
+ fun testPluginApplication() =
+ checkTaskOutcomes(
+ executedTasks = listOf(
+ ":compileKotlinJs",
+ ":transformJsMainAtomicfu",
+ ":compileTestKotlinJs",
+ ":transformJsTestAtomicfu"
+ ),
+ excludedTasks = emptyList()
+ )
+
+ @Test
+ fun testClasspath() {
+ runner.build()
+ checkJsCompilationClasspath()
+ }
+}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JvmProjectTest.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JvmProjectTest.kt
new file mode 100644
index 0000000..2545e0e
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/JvmProjectTest.kt
@@ -0,0 +1,108 @@
+package kotlinx.atomicfu.plugin.gradle.test
+
+import kotlinx.atomicfu.plugin.gradle.internal.*
+import kotlinx.atomicfu.plugin.gradle.internal.BaseKotlinScope
+import org.junit.Test
+
+/**
+ * Test that ensures correctness of `atomicfu-gradle-plugin` application to the JVM project:
+ * - post-compilation bytecode transformation tasks are created
+ * (legacy transformation is tested here, compiler plugin is not applied).
+ * - original non-transformed classes are not left in compile/runtime classpath.
+ * - no `kotlinx/atomicfu` references are left in the transformed bytecode.
+ */
+class JvmLegacyTransformationTest : BaseKotlinGradleTest("jvm-simple") {
+
+ override fun BaseKotlinScope.createProject() {
+ buildGradleKts {
+ resolve("projects/jvm-simple/jvm-simple.gradle.kts")
+ }
+ settingsGradleKts {
+ resolve("projects/jvm-simple/settings.gradle.kts")
+ }
+ dir("src/main/kotlin") {}
+ kotlin("IntArithmetic.kt", "main") {
+ resolve("projects/jvm-simple/src/main/kotlin/IntArithmetic.kt")
+ }
+ dir("src/test/kotlin") {}
+ kotlin("ArithmeticTest.kt", "test") {
+ resolve("projects/jvm-simple/src/test/kotlin/ArithmeticTest.kt")
+ }
+ }
+
+ @Test
+ fun testPluginApplication() =
+ checkTaskOutcomes(
+ executedTasks = listOf(
+ ":compileKotlin",
+ ":transformAtomicfuClasses",
+ ":compileTestKotlin",
+ ":transformTestAtomicfuClasses"
+ ),
+ excludedTasks = emptyList()
+ )
+
+ @Test
+ fun testClasspath() {
+ runner.build()
+ checkJvmCompilationClasspath(
+ originalClassFile = "build/classes/kotlin/main/IntArithmetic.class",
+ transformedClassFile = "build/classes/atomicfu/main/IntArithmetic.class"
+ )
+ }
+
+ @Test
+ fun testAtomicfuReferences() {
+ runner.build()
+ checkBytecode("build/classes/atomicfu/main/IntArithmetic.class")
+ }
+}
+
+/**
+ * Test that ensures correctness of `atomicfu-gradle-plugin` application to the JVM project,
+ * - JVM IR compiler plugin transformation (kotlinx.atomicfu.enableJvmIrTransformation=true)
+ * - no post-compilation bytecode transforming tasks created
+ * - no `kotlinx/atomicfu` references are left in the resulting bytecode after IR transformation.
+ */
+class JvmIrTransformationTest : BaseKotlinGradleTest("jvm-simple") {
+
+ override fun BaseKotlinScope.createProject() {
+ buildGradleKts {
+ resolve("projects/jvm-simple/jvm-simple.gradle.kts")
+ }
+ settingsGradleKts {
+ resolve("projects/jvm-simple/settings.gradle.kts")
+ }
+ // set kotlinx.atomicfu.enableJvmIrTransformation=true to apply compiler plugin
+ gradleProperties {
+ resolve("projects/jvm-simple/gradle.properties")
+ }
+ dir("src/main/kotlin") {}
+ kotlin("IntArithmetic.kt", "main") {
+ resolve("projects/jvm-simple/src/main/kotlin/IntArithmetic.kt")
+ }
+ dir("src/test/kotlin") {}
+ kotlin("ArithmeticTest.kt", "test") {
+ resolve("projects/jvm-simple/src/test/kotlin/ArithmeticTest.kt")
+ }
+ }
+
+ @Test
+ fun testPluginApplication() =
+ checkTaskOutcomes(
+ executedTasks = listOf(
+ ":compileKotlin",
+ ":compileTestKotlin"
+ ),
+ excludedTasks = listOf(
+ ":transformAtomicfuClasses",
+ ":transformTestAtomicfuClasses"
+ )
+ )
+
+ @Test
+ fun testAtomicfuReferences() {
+ runner.build()
+ checkBytecode("build/classes/kotlin/main/IntArithmetic.class")
+ }
+}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/MppProjectTest.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/MppProjectTest.kt
new file mode 100644
index 0000000..e95b091
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/test/MppProjectTest.kt
@@ -0,0 +1,219 @@
+package kotlinx.atomicfu.plugin.gradle.test
+
+import kotlinx.atomicfu.plugin.gradle.internal.*
+import org.junit.*
+
+/**
+ * Test that ensures correctness of `atomicfu-gradle-plugin` application to the MPP project:
+ * - post-compilation bytecode transformation tasks are created
+ * (legacy transformation is tested here, compiler plugin is not applied).
+ * - original non-transformed classes are not left in compile/runtime classpath.
+ * - no `kotlinx/atomicfu` references are left in the transformed bytecode.
+ */
+class MppLegacyTransformationTest : BaseKotlinGradleTest("mpp-simple") {
+
+ override fun BaseKotlinScope.createProject() {
+ buildGradleKts {
+ resolve("projects/mpp-simple/mpp-simple.gradle.kts")
+ }
+ settingsGradleKts {
+ resolve("projects/mpp-simple/settings.gradle.kts")
+ }
+ dir("src/commonMain/kotlin") {}
+ kotlin("IntArithmetic.kt", "commonMain") {
+ resolve("projects/mpp-simple/src/commonMain/kotlin/IntArithmetic.kt")
+ }
+ dir("src/commonTest/kotlin") {}
+ kotlin("ArithmeticTest.kt", "commonTest") {
+ resolve("projects/mpp-simple/src/commonTest/kotlin/ArithmeticTest.kt")
+ }
+ }
+
+ @Test
+ fun testPluginApplication() =
+ checkTaskOutcomes(
+ executedTasks = listOf(
+ ":compileKotlinJvm",
+ ":compileTestKotlinJvm",
+ ":transformJvmMainAtomicfu",
+ ":transformJvmTestAtomicfu",
+ ":compileKotlinJs",
+ ":transformJsMainAtomicfu"
+ ),
+ excludedTasks = emptyList()
+ )
+
+ @Test
+ fun testClasspath() {
+ runner.build()
+ checkJvmCompilationClasspath(
+ originalClassFile = "build/classes/kotlin/jvm/main/IntArithmetic.class",
+ transformedClassFile = "build/classes/atomicfu/jvm/main/IntArithmetic.class"
+ )
+ checkJsCompilationClasspath()
+ }
+
+ @Test
+ fun testAtomicfuReferences() {
+ runner.build()
+ checkBytecode("build/classes/atomicfu/jvm/main/IntArithmetic.class")
+ }
+}
+
+/**
+ * Test that ensures correctness of `atomicfu-gradle-plugin` application to the MPP project,
+ * - JVM IR compiler plugin transformation (kotlinx.atomicfu.enableJvmIrTransformation=true)
+ * - no post-compilation bytecode transformation tasks are created
+ * - post-compilation js file transformation task created (as only JVM IR transformation applied, js is transformed in legacy mode)
+ * - no `kotlinx/atomicfu` references are left in the transformed bytecode.
+ */
+class MppJvmIrTransformationTest : BaseKotlinGradleTest("mpp-simple") {
+
+ override fun BaseKotlinScope.createProject() {
+ buildGradleKts {
+ resolve("projects/mpp-simple/mpp-simple.gradle.kts")
+ }
+ settingsGradleKts {
+ resolve("projects/mpp-simple/settings.gradle.kts")
+ }
+ gradleProperties {
+ resolve("projects/mpp-simple/gradle.properties_jvm")
+ }
+ dir("src/commonMain/kotlin") {}
+ kotlin("IntArithmetic.kt", "commonMain") {
+ resolve("projects/mpp-simple/src/commonMain/kotlin/IntArithmetic.kt")
+ }
+ dir("src/commonTest/kotlin") {}
+ kotlin("ArithmeticTest.kt", "commonTest") {
+ resolve("projects/mpp-simple/src/commonTest/kotlin/ArithmeticTest.kt")
+ }
+ }
+
+ @Test
+ fun testPluginApplication() =
+ checkTaskOutcomes(
+ executedTasks = listOf(
+ ":compileKotlinJvm",
+ ":compileTestKotlinJvm",
+ ":compileKotlinJs",
+ // legacy JS transformation
+ ":transformJsMainAtomicfu",
+ ":transformJsTestAtomicfu"
+ ),
+ excludedTasks = listOf(
+ ":transformJvmMainAtomicfu",
+ ":transformJvmTestAtomicfu"
+ )
+ )
+
+ @Test
+ fun testAtomicfuReferences() {
+ runner.build()
+ checkBytecode("build/classes/kotlin/jvm/main/IntArithmetic.class")
+ }
+}
+
+/**
+ * Test that ensures correctness of `atomicfu-gradle-plugin` application to the MPP project,
+ * - JS IR compiler plugin transformation (kotlinx.atomicfu.enableJsIrTransformation=true)
+ * - post-compilation bytecode transformation tasks are created (only JS IR transformation is applied, jvm is transformed in legacy mode)
+ * - no post-compilation js file transformation tasks are created
+ * - no `kotlinx/atomicfu` references are left in the transformed bytecode.
+ */
+class MppJsIrTransformationTest : BaseKotlinGradleTest("mpp-simple") {
+
+ override fun BaseKotlinScope.createProject() {
+ buildGradleKts {
+ resolve("projects/mpp-simple/mpp-simple.gradle.kts")
+ }
+ settingsGradleKts {
+ resolve("projects/mpp-simple/settings.gradle.kts")
+ }
+ gradleProperties {
+ resolve("projects/mpp-simple/gradle.properties_js")
+ }
+ dir("src/commonMain/kotlin") {}
+ kotlin("IntArithmetic.kt", "commonMain") {
+ resolve("projects/mpp-simple/src/commonMain/kotlin/IntArithmetic.kt")
+ }
+ dir("src/commonTest/kotlin") {}
+ kotlin("ArithmeticTest.kt", "commonTest") {
+ resolve("projects/mpp-simple/src/commonTest/kotlin/ArithmeticTest.kt")
+ }
+ }
+
+ @Test
+ fun testPluginApplication() =
+ checkTaskOutcomes(
+ executedTasks = listOf(
+ ":compileKotlinJvm",
+ ":compileTestKotlinJvm",
+ ":compileKotlinJs",
+ // legacy JVM transformation
+ ":transformJvmMainAtomicfu",
+ ":transformJvmTestAtomicfu"
+ ),
+ excludedTasks = listOf(
+ ":transformJsMainAtomicfu",
+ ":transformJsTestAtomicfu"
+ )
+ )
+
+ @Test
+ fun testAtomicfuReferences() {
+ runner.build()
+ checkBytecode("build/classes/atomicfu/jvm/main/IntArithmetic.class")
+ }
+}
+
+/**
+ * Test that ensures correctness of `atomicfu-gradle-plugin` application to the MPP project,
+ * - JS IR and JVM IR compiler plugin transformation
+ * - no post-compilation bytecode transformation tasks are created (only JS IR transformation is applied, jvm is transformed in legacy mode)
+ * - no post-compilation js file transformation tasks are created
+ * - no `kotlinx/atomicfu` references are left in the transformed bytecode.
+ */
+class MppBothIrTransformationTest : BaseKotlinGradleTest("mpp-simple") {
+
+ override fun BaseKotlinScope.createProject() {
+ buildGradleKts {
+ resolve("projects/mpp-simple/mpp-simple.gradle.kts")
+ }
+ settingsGradleKts {
+ resolve("projects/mpp-simple/settings.gradle.kts")
+ }
+ gradleProperties {
+ resolve("projects/mpp-simple/gradle.properties_both")
+ }
+ dir("src/commonMain/kotlin") {}
+ kotlin("IntArithmetic.kt", "commonMain") {
+ resolve("projects/mpp-simple/src/commonMain/kotlin/IntArithmetic.kt")
+ }
+ dir("src/commonTest/kotlin") {}
+ kotlin("ArithmeticTest.kt", "commonTest") {
+ resolve("projects/mpp-simple/src/commonTest/kotlin/ArithmeticTest.kt")
+ }
+ }
+
+ @Test
+ fun testPluginApplication() =
+ checkTaskOutcomes(
+ executedTasks = listOf(
+ ":compileKotlinJvm",
+ ":compileTestKotlinJvm",
+ ":compileKotlinJs"
+ ),
+ excludedTasks = listOf(
+ ":transformJvmMainAtomicfu",
+ ":transformJvmTestAtomicfu",
+ ":transformJsMainAtomicfu",
+ ":transformJsTestAtomicfu"
+ )
+ )
+
+ @Test
+ fun testAtomicfuReferences() {
+ runner.build()
+ checkBytecode("build/classes/kotlin/jvm/main/IntArithmetic.class")
+ }
+}
diff --git a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/utils.kt b/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/utils.kt
deleted file mode 100644
index 2a8d0f7..0000000
--- a/atomicfu-gradle-plugin/src/test/kotlin/kotlinx/atomicfu/plugin/gradle/utils.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.atomicfu.plugin.gradle
-
-import org.gradle.testkit.runner.BuildResult
-import org.gradle.testkit.runner.TaskOutcome
-import java.io.File
-import kotlin.test.assertTrue
-
-fun BuildResult.checkOutcomes(expected: TaskOutcome, vararg tasks: String) {
- val unexpectedOutcomes = tasks
- .map { it to task(it)?.outcome }
- .filter { (_, outcome) -> outcome != expected }
- if (unexpectedOutcomes.isNotEmpty()) {
- throw AssertionError("Unexpected outcomes for tasks." +
- "\nExpected: $expected." +
- "\nGot:" +
- "\n${unexpectedOutcomes.joinToString("\n") { (task, outcome) -> "* $task -> $outcome" }}")
-
- }
-}
-
-fun File.checkExists() {
- assertTrue(exists(), "File does not exist: $canonicalPath")
-}
-
-fun File.modify(fn: (String) -> String) {
- writeText(fn(readText()))
-}
-
-fun String.checkedReplace(oldValue: String, newValue: String, ignoreCase: Boolean = false): String {
- check(contains(oldValue, ignoreCase)) { "String must contain '$oldValue'" }
- return replace(oldValue, newValue, ignoreCase)
-} \ No newline at end of file
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/empty/build.gradle b/atomicfu-gradle-plugin/src/test/resources/projects/empty/build.gradle
deleted file mode 100644
index f4add41..0000000
--- a/atomicfu-gradle-plugin/src/test/resources/projects/empty/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kotlinx-atomicfu'
-apply plugin: 'base'
-
-repositories {
- maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
-} \ No newline at end of file
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/js-simple/build.gradle b/atomicfu-gradle-plugin/src/test/resources/projects/js-simple/build.gradle
deleted file mode 100644
index 31dbec6..0000000
--- a/atomicfu-gradle-plugin/src/test/resources/projects/js-simple/build.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kotlinx-atomicfu'
-apply plugin: 'kotlin2js'
-
-dependencies {
- compileOnly atomicfuJs
- testRuntime atomicfuJs
-
- compile 'org.jetbrains.kotlin:kotlin-stdlib-js'
- testCompile 'org.jetbrains.kotlin:kotlin-test-js'
-}
-
-compileTestKotlin2Js.doLast {
- file("$buildDir/test_compile_classpath.txt").text = classpath.join("\n")
-} \ No newline at end of file
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/js-simple/js-simple.gradle.kts b/atomicfu-gradle-plugin/src/test/resources/projects/js-simple/js-simple.gradle.kts
new file mode 100644
index 0000000..37a41e5
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/js-simple/js-simple.gradle.kts
@@ -0,0 +1,38 @@
+import kotlinx.atomicfu.plugin.gradle.*
+
+buildscript {
+ dependencies {
+ classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.17.0")
+ }
+}
+
+plugins {
+ kotlin("js")
+}
+
+apply(plugin = "kotlinx-atomicfu")
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+}
+
+dependencies {
+ implementation(kotlin("stdlib-js"))
+ implementation(kotlin("test-junit"))
+ implementation("org.jetbrains.kotlin:kotlin-test-js")
+}
+
+kotlin {
+ js {
+ nodejs()
+ }
+
+ tasks.named("compileTestKotlinJs") {
+ doLast {
+ file("$buildDir/test_compile_js_classpath.txt").writeText(
+ target.compilations["test"].compileDependencyFiles.joinToString("\n")
+ )
+ }
+ }
+}
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/js-simple/settings.gradle.kts b/atomicfu-gradle-plugin/src/test/resources/projects/js-simple/settings.gradle.kts
new file mode 100644
index 0000000..bd39e74
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/js-simple/settings.gradle.kts
@@ -0,0 +1 @@
+rootProject.name = "js-simple" \ No newline at end of file
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/build.gradle b/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/build.gradle
deleted file mode 100644
index 7e21215..0000000
--- a/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/build.gradle
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kotlinx-atomicfu'
-apply plugin: 'kotlin'
-
-// This flag is enabled to be able using JVM IR compiled dependencies (when build is ran with -Penable_jvm_ir)
-kotlin.target.compilations.all {
- kotlinOptions.freeCompilerArgs += '-Xallow-jvm-ir-dependencies'
-}
-
-dependencies {
- compileOnly atomicfuJvm
- testRuntime atomicfuJvm
-
- compile 'org.jetbrains.kotlin:kotlin-stdlib'
-
- testCompile 'org.jetbrains.kotlin:kotlin-test'
- testCompile 'org.jetbrains.kotlin:kotlin-test-junit'
- testCompile 'junit:junit:4.12'
-}
-
-compileTestKotlin.doLast {
- file("$buildDir/test_compile_classpath.txt").text = classpath.join("\n")
-}
-
-test.doLast {
- file("$buildDir/test_runtime_classpath.txt").text = classpath.join("\n")
-} \ No newline at end of file
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/gradle.properties b/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/gradle.properties
new file mode 100644
index 0000000..fa37a2c
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/gradle.properties
@@ -0,0 +1 @@
+kotlinx.atomicfu.enableJvmIrTransformation=true
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/jvm-simple.gradle.kts b/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/jvm-simple.gradle.kts
new file mode 100644
index 0000000..db644ef
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/jvm-simple.gradle.kts
@@ -0,0 +1,41 @@
+import org.gradle.api.tasks.compile.*
+import org.jetbrains.kotlin.gradle.plugin.*
+
+buildscript {
+ dependencies {
+ classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.17.0")
+ }
+}
+
+plugins {
+ kotlin("jvm")
+}
+
+apply(plugin = "kotlinx-atomicfu")
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ implementation(kotlin("test-junit"))
+}
+
+kotlin {
+ tasks.compileTestKotlin {
+ doLast {
+ file("$buildDir/test_compile_jvm_classpath.txt").writeText(
+ target.compilations["test"].compileDependencyFiles.joinToString("\n")
+ )
+ }
+ }
+
+ tasks.test {
+ doLast {
+ file("$buildDir/test_runtime_jvm_classpath.txt").writeText(
+ (target.compilations["test"] as KotlinCompilationToRunnableFiles<*>).runtimeDependencyFiles.joinToString("\n")
+ )
+ }
+ }
+}
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/settings.gradle.kts b/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/settings.gradle.kts
new file mode 100644
index 0000000..2f5327f
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/jvm-simple/settings.gradle.kts
@@ -0,0 +1 @@
+rootProject.name = "jvm-simple" \ No newline at end of file
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/build.gradle b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/build.gradle
deleted file mode 100644
index fc95366..0000000
--- a/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/build.gradle
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kotlinx-atomicfu'
-apply plugin: 'kotlin-multiplatform'
-
-kotlin {
- // This flag is enabled to be able using JVM IR compiled dependencies (when build is ran with -Penable_jvm_ir)
- jvm() {
- compilations.all {
- kotlinOptions.freeCompilerArgs += '-Xallow-jvm-ir-dependencies'
- }
- }
- js()
-
- sourceSets {
- commonMain.dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
- compileOnly atomicfuMetadata
-
- }
- commonTest.dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-test-common'
- implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common'
- runtimeOnly atomicfuMetadata
-
- }
- jsMain.dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-stdlib-js'
- compileOnly atomicfuJs
-
- }
- jsTest.dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-test-js'
- runtimeOnly atomicfuJs
- }
- jvmMain.dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-stdlib'
- compileOnly atomicfuJvm
- }
- jvmTest.dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-test'
- implementation 'org.jetbrains.kotlin:kotlin-test-junit'
- implementation "junit:junit:4.12"
- runtimeOnly atomicfuJvm
- }
- }
-}
-
-def File classpathFile(String platform, String fileName) {
- def dir = file("$buildDir/classpath/$platform")
- dir.mkdirs()
- return file("$dir/$fileName")
-}
-
-
-compileTestKotlinJvm.doLast {
- classpathFile("jvm", "test_compile.txt").text = classpath.files.join("\n")
-}
-
-jvmTest.doLast {
- classpathFile("jvm", "test_runtime.txt").text = classpath.files.join("\n")
-}
-
-
-compileTestKotlinJs.doLast {
- classpathFile("js", "test_compile.txt").text = classpath.files.join("\n")
-}
-
-jsTest.dependsOn(":compileTestKotlinJs")
-jsTest.dependsOn(":transformJsTestAtomicfu")
-check.dependsOn(":jsTest") \ No newline at end of file
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_both b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_both
new file mode 100644
index 0000000..5d28ccd
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_both
@@ -0,0 +1,3 @@
+kotlin.js.compiler=ir
+kotlinx.atomicfu.enableJvmIrTransformation=true
+kotlinx.atomicfu.enableJsIrTransformation=true
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_js b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_js
new file mode 100644
index 0000000..b57875b
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_js
@@ -0,0 +1,2 @@
+kotlin.js.compiler=ir
+kotlinx.atomicfu.enableJsIrTransformation=true
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_jvm b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_jvm
new file mode 100644
index 0000000..fa37a2c
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/gradle.properties_jvm
@@ -0,0 +1 @@
+kotlinx.atomicfu.enableJvmIrTransformation=true
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/mpp-simple.gradle.kts b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/mpp-simple.gradle.kts
new file mode 100644
index 0000000..ed15d3d
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/mpp-simple.gradle.kts
@@ -0,0 +1,90 @@
+import org.jetbrains.kotlin.gradle.plugin.*
+
+buildscript {
+ dependencies {
+ classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.17.0")
+ }
+}
+
+plugins {
+ kotlin("multiplatform")
+}
+
+apply(plugin = "kotlinx-atomicfu")
+
+repositories {
+ mavenCentral()
+}
+
+kotlin {
+ targets {
+ jvm {
+ compilations.all {
+ kotlinOptions.jvmTarget = "1.8"
+ }
+ testRuns["test"].executionTask.configure {
+ useJUnit()
+ }
+ }
+ js {
+ nodejs()
+ }
+ }
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation("org.jetbrains.kotlin:kotlin-stdlib-common")
+ }
+ }
+ val commonTest by getting {
+ dependencies {
+ implementation(kotlin("test-common"))
+ implementation(kotlin("test-annotations-common"))
+ }
+ }
+ val jvmMain by getting {
+ dependencies {
+ implementation(kotlin("stdlib"))
+ }
+ }
+ val jvmTest by getting {
+ dependencies {
+ implementation(kotlin("test-junit"))
+ }
+ }
+ val jsMain by getting {
+ dependencies {
+ implementation("org.jetbrains.kotlin:kotlin-stdlib-js")
+ }
+ }
+ val jsTest by getting {
+ dependencies {
+ implementation("org.jetbrains.kotlin:kotlin-test-js")
+ }
+ }
+ }
+
+ tasks.named("compileTestKotlinJvm") {
+ doLast {
+ file("$buildDir/test_compile_jvm_classpath.txt").writeText(
+ targets["jvm"].compilations["test"].compileDependencyFiles.joinToString("\n")
+ )
+ }
+ }
+
+ tasks.named("jvmTest") {
+ doLast {
+ file("$buildDir/test_runtime_jvm_classpath.txt").writeText(
+ (targets["jvm"].compilations["test"] as KotlinCompilationToRunnableFiles).runtimeDependencyFiles.joinToString("\n")
+ )
+ }
+ }
+
+ tasks.named("compileTestKotlinJs") {
+ doLast {
+ file("$buildDir/test_compile_js_classpath.txt").writeText(
+ targets["js"].compilations["test"].compileDependencyFiles.joinToString("\n")
+ )
+ }
+ }
+}
diff --git a/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/settings.gradle.kts b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/settings.gradle.kts
new file mode 100644
index 0000000..5a4e5ab
--- /dev/null
+++ b/atomicfu-gradle-plugin/src/test/resources/projects/mpp-simple/settings.gradle.kts
@@ -0,0 +1 @@
+rootProject.name = "mpp-simple" \ No newline at end of file