@@ -9,10 +9,13 @@ import org.gradle.api.*
9
9
import org.gradle.api.file.*
10
10
import org.gradle.api.internal.*
11
11
import org.gradle.api.plugins.*
12
+ import org.gradle.api.provider.Provider
13
+ import org.gradle.api.provider.ProviderFactory
12
14
import org.gradle.api.tasks.*
13
15
import org.gradle.api.tasks.compile.*
14
16
import org.gradle.api.tasks.testing.*
15
17
import org.gradle.jvm.tasks.*
18
+ import org.gradle.util.*
16
19
import org.jetbrains.kotlin.gradle.dsl.*
17
20
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
18
21
import org.jetbrains.kotlin.gradle.plugin.*
@@ -23,6 +26,7 @@ import org.jetbrains.kotlin.gradle.targets.js.*
23
26
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
24
27
import org.jetbrains.kotlin.gradle.tasks.*
25
28
import org.jetbrains.kotlinx.atomicfu.gradle.*
29
+ import javax.inject.Inject
26
30
27
31
private const val EXTENSION_NAME = " atomicfu"
28
32
private const val ORIGINAL_DIR_NAME = " originalClassesDir"
@@ -34,9 +38,12 @@ private const val TEST_IMPLEMENTATION_CONFIGURATION = "testImplementation"
34
38
private const val ENABLE_JS_IR_TRANSFORMATION_LEGACY = " kotlinx.atomicfu.enableIrTransformation"
35
39
private const val ENABLE_JS_IR_TRANSFORMATION = " kotlinx.atomicfu.enableJsIrTransformation"
36
40
private const val ENABLE_JVM_IR_TRANSFORMATION = " kotlinx.atomicfu.enableJvmIrTransformation"
41
+ private const val MIN_SUPPORTED_GRADLE_VERSION = " 7.0"
42
+ private const val MIN_SUPPORTED_KGP_VERSION = " 1.7.0"
37
43
38
44
open class AtomicFUGradlePlugin : Plugin <Project > {
39
45
override fun apply (project : Project ) = project.run {
46
+ checkCompatibility()
40
47
val pluginVersion = rootProject.buildscript.configurations.findByName(" classpath" )
41
48
?.allDependencies?.find { it.name == " atomicfu-gradle-plugin" }?.version
42
49
extensions.add(EXTENSION_NAME , AtomicFUPluginExtension (pluginVersion))
@@ -46,6 +53,24 @@ open class AtomicFUGradlePlugin : Plugin<Project> {
46
53
}
47
54
}
48
55
56
+ private fun Project.checkCompatibility () {
57
+ val currentGradleVersion = GradleVersion .current()
58
+ val kotlinVersion = getKotlinVersion()
59
+ val minSupportedVersion = GradleVersion .version(MIN_SUPPORTED_GRADLE_VERSION )
60
+ if (currentGradleVersion < minSupportedVersion) {
61
+ throw GradleException (
62
+ " The current Gradle version is not compatible with Atomicfu gradle plugin. " +
63
+ " Please use Gradle $MIN_SUPPORTED_GRADLE_VERSION or newer, or the previous version of Atomicfu gradle plugin."
64
+ )
65
+ }
66
+ if (! kotlinVersion.atLeast(1 , 7 , 0 )) {
67
+ throw GradleException (
68
+ " The current Kotlin gradle plugin version is not compatible with Atomicfu gradle plugin. " +
69
+ " Please use Kotlin $MIN_SUPPORTED_KGP_VERSION or newer, or the previous version of Atomicfu gradle plugin."
70
+ )
71
+ }
72
+ }
73
+
49
74
private fun Project.configureDependencies () {
50
75
withPluginWhenEvaluatedDependencies(" kotlin" ) { version ->
51
76
dependencies.add(
@@ -242,68 +267,79 @@ private fun Project.configureTransformationForTarget(target: KotlinTarget) {
242
267
? : return @compilations // skip unknown compilations
243
268
val classesDirs = compilation.output.classesDirs
244
269
// make copy of original classes directory
245
- val originalClassesDirs: FileCollection =
246
- project.files(classesDirs.from.toTypedArray()).filter { it.exists() }
270
+ @Suppress(" UNCHECKED_CAST" )
271
+ val compilationTask = compilation.compileTaskProvider as TaskProvider <KotlinCompileTool >
272
+ val originalDestinationDirectory = project.layout.buildDirectory
273
+ .dir(" classes/atomicfu-orig/${target.name} /${compilation.name} " )
274
+ compilationTask.configure {
275
+ if (it is Kotlin2JsCompile ) {
276
+ @Suppress(" INVISIBLE_REFERENCE" , " INVISIBLE_MEMBER" , " EXPOSED_PARAMETER_TYPE" )
277
+ it.defaultDestinationDirectory.value(originalDestinationDirectory)
278
+ } else {
279
+ it.destinationDirectory.value(originalDestinationDirectory)
280
+ }
281
+ }
282
+ val originalClassesDirs: FileCollection = project.objects.fileCollection().from(
283
+ compilationTask.flatMap { it.destinationDirectory }
284
+ )
247
285
originalDirsByCompilation[compilation] = originalClassesDirs
248
- val transformedClassesDir =
249
- project.buildDir.resolve (" classes/atomicfu/${target.name} /${compilation.name} " )
286
+ val transformedClassesDir = project.layout.buildDirectory
287
+ .dir (" classes/atomicfu/${target.name} /${compilation.name} " )
250
288
val transformTask = when (target.platformType) {
251
289
KotlinPlatformType .jvm, KotlinPlatformType .androidJvm -> {
252
290
// skip transformation task if transformation is turned off or ir transformation is enabled
253
291
if (! config.transformJvm || rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION )) return @compilations
254
- project.createJvmTransformTask(compilation).configureJvmTask(
255
- compilation.compileDependencyFiles,
256
- compilation.compileAllTaskName,
257
- transformedClassesDir,
258
- originalClassesDirs,
259
- config
260
- )
292
+ project.registerJvmTransformTask(compilation)
293
+ .configureJvmTask(
294
+ compilation.compileDependencyFiles,
295
+ compilation.compileAllTaskName,
296
+ transformedClassesDir,
297
+ originalClassesDirs,
298
+ config
299
+ )
300
+ .also {
301
+ compilation.defaultSourceSet.kotlin.compiledBy(it, AtomicFUTransformTask ::destinationDirectory)
302
+ }
261
303
}
262
304
KotlinPlatformType .js -> {
263
305
// skip when js transformation is not needed or when IR is transformed
264
306
if (! config.transformJs || (needsJsIrTransformation(target))) {
265
307
return @compilations
266
308
}
267
- project.createJsTransformTask(compilation).configureJsTask(
268
- compilation.compileAllTaskName,
269
- transformedClassesDir,
270
- originalClassesDirs,
271
- config
272
- )
309
+ project.registerJsTransformTask(compilation)
310
+ .configureJsTask(
311
+ compilation.compileAllTaskName,
312
+ transformedClassesDir,
313
+ originalClassesDirs,
314
+ config
315
+ )
316
+ .also {
317
+ compilation.defaultSourceSet.kotlin.compiledBy(it, AtomicFUTransformJsTask ::destinationDirectory)
318
+ }
273
319
}
274
320
else -> error(" Unsupported transformation platform '${target.platformType} '" )
275
321
}
276
322
// now transformTask is responsible for compiling this source set into the classes directory
323
+ compilation.defaultSourceSet.kotlin.destinationDirectory.value(transformedClassesDir)
277
324
classesDirs.setFrom(transformedClassesDir)
278
- classesDirs.builtBy(transformTask)
279
- (tasks.findByName(target.artifactsTaskName) as ? Jar )?.apply {
280
- setupJarManifest(multiRelease = config.jvmVariant.toJvmVariant() == JvmVariant .BOTH )
325
+ classesDirs.setBuiltBy(listOf (transformTask))
326
+ tasks.withType(Jar ::class .java).configureEach {
327
+ if (name == target.artifactsTaskName) {
328
+ it.setupJarManifest(multiRelease = config.jvmVariant.toJvmVariant() == JvmVariant .BOTH )
329
+ }
281
330
}
282
331
// test should compile and run against original production binaries
283
332
if (compilationType == CompilationType .TEST ) {
284
333
val mainCompilation =
285
334
compilation.target.compilations.getByName(KotlinCompilation .MAIN_COMPILATION_NAME )
286
- val originalMainClassesDirs = project.files(
287
- // use Callable because there is no guarantee that main is configured before test
288
- Callable { originalDirsByCompilation[mainCompilation]!! }
335
+ val originalMainClassesDirs = project.objects.fileCollection().from(
336
+ mainCompilation.compileTaskProvider.flatMap { (it as KotlinCompileTool ).destinationDirectory }
289
337
)
290
-
291
- // KGP >= 1.7.0 has breaking changes in task hierarchy:
292
- // https://youtrack.jetbrains.com/issue/KT-32805#focus=Comments-27-5915479.0-0
293
- val (majorVersion, minorVersion) = getKotlinPluginVersion()
294
- .split(' .' )
295
- .take(2 )
296
- .map { it.toInt() }
297
- if (majorVersion == 1 && minorVersion < 7 ) {
298
- (tasks.findByName(compilation.compileKotlinTaskName) as ? AbstractCompile )?.classpath =
299
- originalMainClassesDirs + compilation.compileDependencyFiles - mainCompilation.output.classesDirs
300
- } else {
301
- (tasks.findByName(compilation.compileKotlinTaskName) as ? AbstractKotlinCompileTool <* >)
302
- ?.libraries
303
- ?.setFrom(
304
- originalMainClassesDirs + compilation.compileDependencyFiles - mainCompilation.output.classesDirs
305
- )
306
- }
338
+ (tasks.findByName(compilation.compileKotlinTaskName) as ? AbstractKotlinCompileTool <* >)
339
+ ?.libraries
340
+ ?.setFrom(
341
+ originalMainClassesDirs + compilation.compileDependencyFiles
342
+ )
307
343
308
344
(tasks.findByName(" ${target.name}${compilation.name.capitalize()} " ) as ? Test )?.classpath =
309
345
originalMainClassesDirs + (compilation as KotlinCompilationToRunnableFiles ).runtimeDependencyFiles - mainCompilation.output.classesDirs
@@ -381,48 +417,49 @@ fun Project.configureMultiplatformPluginDependencies(version: String) {
381
417
382
418
fun String.toJvmVariant (): JvmVariant = enumValueOf(toUpperCase(Locale .US ))
383
419
384
- fun Project.createJvmTransformTask (compilation : KotlinCompilation <* >): AtomicFUTransformTask =
385
- tasks.create (
420
+ fun Project.registerJvmTransformTask (compilation : KotlinCompilation <* >): TaskProvider < AtomicFUTransformTask > =
421
+ tasks.register (
386
422
" transform${compilation.target.name.capitalize()}${compilation.name.capitalize()} Atomicfu" ,
387
423
AtomicFUTransformTask ::class .java
388
424
)
389
425
390
- fun Project.createJsTransformTask (compilation : KotlinCompilation <* >): AtomicFUTransformJsTask =
391
- tasks.create (
426
+ fun Project.registerJsTransformTask (compilation : KotlinCompilation <* >): TaskProvider < AtomicFUTransformJsTask > =
427
+ tasks.register (
392
428
" transform${compilation.target.name.capitalize()}${compilation.name.capitalize()} Atomicfu" ,
393
429
AtomicFUTransformJsTask ::class .java
394
430
)
395
431
396
- fun Project.createJvmTransformTask (sourceSet : SourceSet ): AtomicFUTransformTask =
397
- tasks.create(sourceSet.getTaskName(" transform" , " atomicfuClasses" ), AtomicFUTransformTask ::class .java)
398
-
399
- fun AtomicFUTransformTask.configureJvmTask (
432
+ fun TaskProvider<AtomicFUTransformTask>.configureJvmTask (
400
433
classpath : FileCollection ,
401
434
classesTaskName : String ,
402
- transformedClassesDir : File ,
435
+ transformedClassesDir : Provider < Directory > ,
403
436
originalClassesDir : FileCollection ,
404
437
config : AtomicFUPluginExtension
405
- ): ConventionTask =
438
+ ): TaskProvider < AtomicFUTransformTask > =
406
439
apply {
407
- dependsOn(classesTaskName)
408
- classPath = classpath
409
- inputFiles = originalClassesDir
410
- outputDir = transformedClassesDir
411
- jvmVariant = config.jvmVariant
412
- verbose = config.verbose
440
+ configure {
441
+ it.dependsOn(classesTaskName)
442
+ it.classPath = classpath
443
+ it.inputFiles = originalClassesDir
444
+ it.destinationDirectory.value(transformedClassesDir)
445
+ it.jvmVariant = config.jvmVariant
446
+ it.verbose = config.verbose
447
+ }
413
448
}
414
449
415
- fun AtomicFUTransformJsTask.configureJsTask (
450
+ fun TaskProvider< AtomicFUTransformJsTask> .configureJsTask (
416
451
classesTaskName : String ,
417
- transformedClassesDir : File ,
452
+ transformedClassesDir : Provider < Directory > ,
418
453
originalClassesDir : FileCollection ,
419
454
config : AtomicFUPluginExtension
420
- ): ConventionTask =
455
+ ): TaskProvider < AtomicFUTransformJsTask > =
421
456
apply {
422
- dependsOn(classesTaskName)
423
- inputFiles = originalClassesDir
424
- outputDir = transformedClassesDir
425
- verbose = config.verbose
457
+ configure {
458
+ it.dependsOn(classesTaskName)
459
+ it.inputFiles = originalClassesDir
460
+ it.destinationDirectory.value(transformedClassesDir)
461
+ it.verbose = config.verbose
462
+ }
426
463
}
427
464
428
465
fun Jar.setupJarManifest (multiRelease : Boolean ) {
@@ -445,13 +482,29 @@ class AtomicFUPluginExtension(pluginVersion: String?) {
445
482
}
446
483
447
484
@CacheableTask
448
- open class AtomicFUTransformTask : ConventionTask () {
485
+ abstract class AtomicFUTransformTask : ConventionTask () {
486
+ @get:Inject
487
+ internal abstract val providerFactory: ProviderFactory
488
+
489
+ @get:Inject
490
+ internal abstract val projectLayout: ProjectLayout
491
+
449
492
@PathSensitive(PathSensitivity .RELATIVE )
450
493
@InputFiles
451
494
lateinit var inputFiles: FileCollection
452
495
453
- @OutputDirectory
454
- lateinit var outputDir: File
496
+ @Suppress(" unused" )
497
+ @Deprecated(
498
+ message = " Replaced with 'destinationDirectory'" ,
499
+ replaceWith = ReplaceWith (" destinationDirectory" )
500
+ )
501
+ @get:Internal
502
+ var outputDir: File
503
+ get() = destinationDirectory.get().asFile
504
+ set(value) { destinationDirectory.value(projectLayout.dir(providerFactory.provider { value })) }
505
+
506
+ @get:OutputDirectory
507
+ abstract val destinationDirectory: DirectoryProperty
455
508
456
509
@Classpath
457
510
@InputFiles
@@ -467,7 +520,7 @@ open class AtomicFUTransformTask : ConventionTask() {
467
520
fun transform () {
468
521
val cp = classPath.files.map { it.absolutePath }
469
522
inputFiles.files.forEach { inputDir ->
470
- AtomicFUTransformer (cp, inputDir, outputDir ).let { t ->
523
+ AtomicFUTransformer (cp, inputDir, destinationDirectory.get().asFile ).let { t ->
471
524
t.jvmVariant = jvmVariant.toJvmVariant()
472
525
t.verbose = verbose
473
526
t.transform()
@@ -477,21 +530,38 @@ open class AtomicFUTransformTask : ConventionTask() {
477
530
}
478
531
479
532
@CacheableTask
480
- open class AtomicFUTransformJsTask : ConventionTask () {
533
+ abstract class AtomicFUTransformJsTask : ConventionTask () {
534
+
535
+ @get:Inject
536
+ internal abstract val providerFactory: ProviderFactory
537
+
538
+ @get:Inject
539
+ internal abstract val projectLayout: ProjectLayout
540
+
481
541
@PathSensitive(PathSensitivity .RELATIVE )
482
542
@InputFiles
483
543
lateinit var inputFiles: FileCollection
484
544
485
- @OutputDirectory
486
- lateinit var outputDir: File
545
+ @Suppress(" unused" )
546
+ @Deprecated(
547
+ message = " Replaced with 'destinationDirectory'" ,
548
+ replaceWith = ReplaceWith (" destinationDirectory" )
549
+ )
550
+ @get:Internal
551
+ var outputDir: File
552
+ get() = destinationDirectory.get().asFile
553
+ set(value) { destinationDirectory.value(projectLayout.dir(providerFactory.provider { value })) }
554
+
555
+ @get:OutputDirectory
556
+ abstract val destinationDirectory: DirectoryProperty
487
557
488
558
@Input
489
559
var verbose = false
490
560
491
561
@TaskAction
492
562
fun transform () {
493
563
inputFiles.files.forEach { inputDir ->
494
- AtomicFUTransformerJS (inputDir, outputDir ).let { t ->
564
+ AtomicFUTransformerJS (inputDir, destinationDirectory.get().asFile ).let { t ->
495
565
t.verbose = verbose
496
566
t.transform()
497
567
}
0 commit comments