1 /*
<lambda>null2 * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
4
5 package kotlinx.atomicfu.plugin.gradle
6
7 import kotlinx.atomicfu.transformer.*
8 import org.gradle.api.*
9 import org.gradle.api.file.*
10 import org.gradle.api.internal.*
11 import org.gradle.api.provider.Provider
12 import org.gradle.api.provider.ProviderFactory
13 import org.gradle.api.tasks.*
14 import org.gradle.api.tasks.testing.*
15 import org.gradle.jvm.tasks.*
16 import org.gradle.util.*
17 import org.jetbrains.kotlin.gradle.dsl.*
18 import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
19 import org.jetbrains.kotlin.gradle.plugin.*
20 import java.io.*
21 import java.util.*
22 import org.jetbrains.kotlin.gradle.targets.js.*
23 import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
24 import org.jetbrains.kotlin.gradle.tasks.*
25 import org.jetbrains.kotlinx.atomicfu.gradle.*
26 import javax.inject.Inject
27
28 private const val EXTENSION_NAME = "atomicfu"
29 private const val ORIGINAL_DIR_NAME = "originalClassesDir"
30 private const val COMPILE_ONLY_CONFIGURATION = "compileOnly"
31 private const val IMPLEMENTATION_CONFIGURATION = "implementation"
32 private const val TEST_IMPLEMENTATION_CONFIGURATION = "testImplementation"
33 // If the project uses KGP <= 1.6.20, only JS IR compiler plugin is available, and it is turned on via setting this property.
34 // The property is supported for backwards compatibility.
35 private const val ENABLE_JS_IR_TRANSFORMATION_LEGACY = "kotlinx.atomicfu.enableIrTransformation"
36 private const val ENABLE_JS_IR_TRANSFORMATION = "kotlinx.atomicfu.enableJsIrTransformation"
37 private const val ENABLE_JVM_IR_TRANSFORMATION = "kotlinx.atomicfu.enableJvmIrTransformation"
38 private const val ENABLE_NATIVE_IR_TRANSFORMATION = "kotlinx.atomicfu.enableNativeIrTransformation"
39 private const val MIN_SUPPORTED_GRADLE_VERSION = "7.0"
40 private const val MIN_SUPPORTED_KGP_VERSION = "1.7.0"
41
42 open class AtomicFUGradlePlugin : Plugin<Project> {
43 override fun apply(project: Project) = project.run {
44 checkCompatibility()
45 val pluginVersion = rootProject.buildscript.configurations.findByName("classpath")
46 ?.allDependencies?.find { it.name == "atomicfu-gradle-plugin" }?.version
47 extensions.add(EXTENSION_NAME, AtomicFUPluginExtension(pluginVersion))
48 applyAtomicfuCompilerPlugin()
49 configureDependencies()
50 configureTasks()
51 }
52 }
53
Projectnull54 private fun Project.checkCompatibility() {
55 val currentGradleVersion = GradleVersion.current()
56 val kotlinVersion = getKotlinVersion()
57 val minSupportedVersion = GradleVersion.version(MIN_SUPPORTED_GRADLE_VERSION)
58 if (currentGradleVersion < minSupportedVersion) {
59 throw GradleException(
60 "The current Gradle version is not compatible with Atomicfu gradle plugin. " +
61 "Please use Gradle $MIN_SUPPORTED_GRADLE_VERSION or newer, or the previous version of Atomicfu gradle plugin."
62 )
63 }
64 if (!kotlinVersion.atLeast(1, 7, 0)) {
65 throw GradleException(
66 "The current Kotlin gradle plugin version is not compatible with Atomicfu gradle plugin. " +
67 "Please use Kotlin $MIN_SUPPORTED_KGP_VERSION or newer, or the previous version of Atomicfu gradle plugin."
68 )
69 }
70 }
71
Projectnull72 private fun Project.applyAtomicfuCompilerPlugin() {
73 val kotlinVersion = getKotlinVersion()
74 // for KGP >= 1.7.20:
75 // compiler plugin for JS IR is applied via the property `kotlinx.atomicfu.enableJsIrTransformation`
76 // compiler plugin for JVM IR is applied via the property `kotlinx.atomicfu.enableJvmIrTransformation`
77 if (kotlinVersion.atLeast(1, 7, 20)) {
78 plugins.apply(AtomicfuKotlinGradleSubplugin::class.java)
79 extensions.getByType(AtomicfuKotlinGradleSubplugin.AtomicfuKotlinGradleExtension::class.java).apply {
80 isJsIrTransformationEnabled = rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION)
81 isJvmIrTransformationEnabled = rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION)
82 if (kotlinVersion.atLeast(1, 9, 20)) {
83 // Native IR transformation is available since Kotlin 1.9.20
84 isNativeIrTransformationEnabled = rootProject.getBooleanProperty(ENABLE_NATIVE_IR_TRANSFORMATION)
85 }
86 }
87 } else {
88 // for KGP >= 1.6.20 && KGP <= 1.7.20:
89 // compiler plugin for JS IR is applied via the property `kotlinx.atomicfu.enableIrTransformation`
90 // compiler plugin for JVM IR is not supported yet
91 if (kotlinVersion.atLeast(1, 6, 20)) {
92 if (rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION_LEGACY)) {
93 plugins.apply(AtomicfuKotlinGradleSubplugin::class.java)
94 }
95 }
96 }
97 }
98
Projectnull99 private fun Project.configureDependencies() {
100 withPluginWhenEvaluatedDependencies("kotlin") { version ->
101 dependencies.add(
102 if (config.transformJvm) COMPILE_ONLY_CONFIGURATION else IMPLEMENTATION_CONFIGURATION,
103 getAtomicfuDependencyNotation(Platform.JVM, version)
104 )
105 dependencies.add(TEST_IMPLEMENTATION_CONFIGURATION, getAtomicfuDependencyNotation(Platform.JVM, version))
106 }
107 withPluginWhenEvaluatedDependencies("org.jetbrains.kotlin.js") { version ->
108 dependencies.add(
109 if (config.transformJs) COMPILE_ONLY_CONFIGURATION else IMPLEMENTATION_CONFIGURATION,
110 getAtomicfuDependencyNotation(Platform.JS, version)
111 )
112 dependencies.add(TEST_IMPLEMENTATION_CONFIGURATION, getAtomicfuDependencyNotation(Platform.JS, version))
113 addJsCompilerPluginRuntimeDependency()
114 }
115 withPluginWhenEvaluatedDependencies("kotlin-multiplatform") { version ->
116 addJsCompilerPluginRuntimeDependency()
117 configureMultiplatformPluginDependencies(version)
118 }
119 }
120
Projectnull121 private fun Project.configureMultiplatformPluginDependencies(version: String) {
122 val multiplatformExtension = kotlinExtension as? KotlinMultiplatformExtension ?: error("Expected kotlin multiplatform extension")
123 val atomicfuDependency = "org.jetbrains.kotlinx:atomicfu:$version"
124 multiplatformExtension.sourceSets.getByName("commonMain").dependencies {
125 compileOnly(atomicfuDependency)
126 }
127 multiplatformExtension.sourceSets.getByName("commonTest").dependencies {
128 implementation(atomicfuDependency)
129 }
130 // Include atomicfu as a dependency for publication when transformation for the target is disabled
131 multiplatformExtension.targets.all { target ->
132 if (isTransformationDisabled(target)) {
133 target.compilations.all { compilation ->
134 compilation
135 .defaultSourceSet
136 .dependencies {
137 implementation(atomicfuDependency)
138 }
139 }
140 }
141 }
142 }
143
144 private data class KotlinVersion(val major: Int, val minor: Int, val patch: Int)
145
Projectnull146 private fun Project.getKotlinVersion(): KotlinVersion {
147 val kotlinVersion = getKotlinPluginVersion()
148 val (major, minor) = kotlinVersion
149 .split('.')
150 .take(2)
151 .map { it.toInt() }
152 val patch = kotlinVersion.substringAfterLast('.').substringBefore('-').toInt()
153 return KotlinVersion(major, minor, patch)
154 }
155
KotlinVersionnull156 private fun KotlinVersion.atLeast(major: Int, minor: Int, patch: Int) =
157 this.major == major && (this.minor == minor && this.patch >= patch || this.minor > minor) || this.major > major
158
159 // kotlinx-atomicfu compiler plugin is available for KGP >= 1.6.20
160 private fun Project.isCompilerPluginAvailable() = getKotlinVersion().atLeast(1, 6, 20)
161
162 private fun Project.getBooleanProperty(name: String) =
163 rootProject.findProperty(name)?.toString()?.toBooleanStrict() ?: false
164
165 private fun String.toBooleanStrict(): Boolean = when (this) {
166 "true" -> true
167 "false" -> false
168 else -> throw IllegalArgumentException("The string doesn't represent a boolean value: $this")
169 }
170
Projectnull171 private fun Project.needsJsIrTransformation(target: KotlinTarget): Boolean =
172 (rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION) || rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION_LEGACY))
173 && target.isJsIrTarget()
174
175 private fun Project.needsJvmIrTransformation(target: KotlinTarget): Boolean =
176 rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION) &&
177 (target.platformType == KotlinPlatformType.jvm || target.platformType == KotlinPlatformType.androidJvm)
178
179 private fun Project.needsNativeIrTransformation(target: KotlinTarget): Boolean =
180 rootProject.getBooleanProperty(ENABLE_NATIVE_IR_TRANSFORMATION) &&
181 (target.platformType == KotlinPlatformType.native)
182
183
184 private fun KotlinTarget.isJsIrTarget() =
185 (this is KotlinJsTarget && this.irTarget != null) ||
186 (this is KotlinJsIrTarget && this.platformType != KotlinPlatformType.wasm)
187
188 private fun Project.isTransformationDisabled(target: KotlinTarget): Boolean {
189 val platformType = target.platformType
190 return !config.transformJvm && (platformType == KotlinPlatformType.jvm || platformType == KotlinPlatformType.androidJvm) ||
191 !config.transformJs && platformType == KotlinPlatformType.js ||
192 platformType == KotlinPlatformType.wasm ||
193 !needsNativeIrTransformation(target) && platformType == KotlinPlatformType.native
194 }
195
196 // Adds kotlinx-atomicfu-runtime as an implementation dependency to the JS IR target:
197 // it provides inline methods that replace atomic methods from the library and is needed at runtime.
Projectnull198 private fun Project.addJsCompilerPluginRuntimeDependency() {
199 if (isCompilerPluginAvailable()) {
200 withKotlinTargets { target ->
201 if (target.isJsIrTarget()) {
202 target.compilations.forEach { kotlinCompilation ->
203 kotlinCompilation.dependencies {
204 if (getKotlinVersion().atLeast(1, 7, 10)) {
205 // since Kotlin 1.7.10 `kotlinx-atomicfu-runtime` is published and should be added directly
206 implementation("org.jetbrains.kotlin:kotlinx-atomicfu-runtime:${getKotlinPluginVersion()}")
207 } else {
208 implementation("org.jetbrains.kotlin:atomicfu:${getKotlinPluginVersion()}")
209 }
210 }
211 }
212 }
213 }
214 }
215 }
216
217 private enum class Platform(val suffix: String) {
218 JVM("-jvm"),
219 JS("-js"),
220 NATIVE(""),
221 MULTIPLATFORM("")
222 }
223
224 private enum class CompilationType { MAIN, TEST }
225
compilationNameToTypenull226 private fun String.compilationNameToType(): CompilationType? = when (this) {
227 KotlinCompilation.MAIN_COMPILATION_NAME -> CompilationType.MAIN
228 KotlinCompilation.TEST_COMPILATION_NAME -> CompilationType.TEST
229 else -> null
230 }
231
232 private val Project.config: AtomicFUPluginExtension
233 get() = extensions.findByName(EXTENSION_NAME) as? AtomicFUPluginExtension ?: AtomicFUPluginExtension(null)
234
getAtomicfuDependencyNotationnull235 private fun getAtomicfuDependencyNotation(platform: Platform, version: String): String =
236 "org.jetbrains.kotlinx:atomicfu${platform.suffix}:$version"
237
238 // Note "afterEvaluate" does nothing when the project is already in executed state, so we need
239 // a special check for this case
240 private fun <T> Project.whenEvaluated(fn: Project.() -> T) {
241 if (state.executed) {
242 fn()
243 } else {
244 afterEvaluate { fn() }
245 }
246 }
247
Projectnull248 private fun Project.withPluginWhenEvaluated(plugin: String, fn: Project.() -> Unit) {
249 pluginManager.withPlugin(plugin) { whenEvaluated(fn) }
250 }
251
Projectnull252 private fun Project.withPluginWhenEvaluatedDependencies(plugin: String, fn: Project.(version: String) -> Unit) {
253 withPluginWhenEvaluated(plugin) {
254 config.dependenciesVersion?.let { fn(it) }
255 }
256 }
257
Projectnull258 private fun Project.withKotlinTargets(fn: (KotlinTarget) -> Unit) {
259 extensions.findByType(KotlinTargetsContainer::class.java)?.let { kotlinExtension ->
260 // find all compilations given sourceSet belongs to
261 kotlinExtension.targets
262 .all { target -> fn(target) }
263 }
264 }
265
setFriendPathsnull266 private fun KotlinCompile<*>.setFriendPaths(friendPathsFileCollection: FileCollection) {
267 val (majorVersion, minorVersion) = project.getKotlinPluginVersion()
268 .split('.')
269 .take(2)
270 .map { it.toInt() }
271 if (majorVersion == 1 && minorVersion < 7) {
272 (this as? AbstractKotlinCompile<*>)?.friendPaths?.from(friendPathsFileCollection)
273 } else {
274 // See KT-KT-54167 (works only for KGP 1.7.0+)
275 (this as BaseKotlinCompile).friendPaths.from(friendPathsFileCollection)
276 }
277 }
278
configureTasksnull279 private fun Project.configureTasks() {
280 val config = config
281 withPluginWhenEvaluated("kotlin") {
282 if (config.transformJvm) configureJvmTransformation()
283 }
284 withPluginWhenEvaluated("org.jetbrains.kotlin.js") {
285 if (config.transformJs) configureJsTransformation()
286 }
287 withPluginWhenEvaluated("kotlin-multiplatform") {
288 configureMultiplatformTransformation()
289 }
290 }
291
Projectnull292 private fun Project.configureJvmTransformation() {
293 if (kotlinExtension is KotlinJvmProjectExtension || kotlinExtension is KotlinAndroidProjectExtension) {
294 val target = (kotlinExtension as KotlinSingleTargetExtension<*>).target
295 if (!needsJvmIrTransformation(target)) {
296 configureTransformationForTarget(target)
297 }
298 }
299 }
300
Projectnull301 private fun Project.configureJsTransformation() {
302 val target = (kotlinExtension as KotlinJsProjectExtension).js()
303 if (!needsJsIrTransformation(target)) {
304 configureTransformationForTarget(target)
305 }
306 }
307
Projectnull308 private fun Project.configureMultiplatformTransformation() =
309 withKotlinTargets { target ->
310 // Skip transformation for common, native and wasm targets or in case IR transformation by the compiler plugin is enabled (for JVM or JS targets)
311 if (target.platformType == KotlinPlatformType.common ||
312 target.platformType == KotlinPlatformType.native ||
313 target.platformType == KotlinPlatformType.wasm ||
314 needsJvmIrTransformation(target) || needsJsIrTransformation(target)
315 ) {
316 return@withKotlinTargets
317 }
318 configureTransformationForTarget(target)
319 }
320
Projectnull321 private fun Project.configureTransformationForTarget(target: KotlinTarget) {
322 val originalDirsByCompilation = hashMapOf<KotlinCompilation<*>, FileCollection>()
323 val config = config
324 target.compilations.all compilations@{ compilation ->
325 val compilationType = compilation.name.compilationNameToType()
326 ?: return@compilations // skip unknown compilations
327 val classesDirs = compilation.output.classesDirs
328 // make copy of original classes directory
329 @Suppress("UNCHECKED_CAST")
330 val compilationTask = compilation.compileTaskProvider as TaskProvider<KotlinCompileTool>
331 val originalDestinationDirectory = project.layout.buildDirectory
332 .dir("classes/atomicfu-orig/${target.name}/${compilation.name}")
333 compilationTask.configure {
334 if (it is Kotlin2JsCompile) {
335 @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "EXPOSED_PARAMETER_TYPE")
336 it.defaultDestinationDirectory.value(originalDestinationDirectory)
337 } else {
338 it.destinationDirectory.value(originalDestinationDirectory)
339 }
340 }
341 val originalClassesDirs: FileCollection = project.objects.fileCollection().from(
342 compilationTask.flatMap { it.destinationDirectory }
343 )
344 originalDirsByCompilation[compilation] = originalClassesDirs
345 val transformedClassesDir = project.layout.buildDirectory
346 .dir("classes/atomicfu/${target.name}/${compilation.name}")
347 val transformTask = when (target.platformType) {
348 KotlinPlatformType.jvm, KotlinPlatformType.androidJvm -> {
349 // create transformation task only if transformation is required and JVM IR compiler transformation is not enabled
350 if (config.transformJvm) {
351 project.registerJvmTransformTask(compilation)
352 .configureJvmTask(
353 compilation.compileDependencyFiles,
354 compilation.compileAllTaskName,
355 transformedClassesDir,
356 originalClassesDirs,
357 config
358 )
359 .also {
360 compilation.defaultSourceSet.kotlin.compiledBy(it, AtomicFUTransformTask::destinationDirectory)
361 }
362 } else null
363 }
364 KotlinPlatformType.js -> {
365 // create transformation task only if transformation is required and JS IR compiler transformation is not enabled
366 if (config.transformJs && !needsJsIrTransformation(target)) {
367 project.registerJsTransformTask(compilation)
368 .configureJsTask(
369 compilation.compileAllTaskName,
370 transformedClassesDir,
371 originalClassesDirs,
372 config
373 )
374 .also {
375 compilation.defaultSourceSet.kotlin.compiledBy(it, AtomicFUTransformJsTask::destinationDirectory)
376 }
377 } else null
378 }
379 else -> error("Unsupported transformation platform '${target.platformType}'")
380 }
381 if (transformTask != null) {
382 //now transformTask is responsible for compiling this source set into the classes directory
383 compilation.defaultSourceSet.kotlin.destinationDirectory.value(transformedClassesDir)
384 classesDirs.setFrom(transformedClassesDir)
385 classesDirs.setBuiltBy(listOf(transformTask))
386 tasks.withType(Jar::class.java).configureEach {
387 if (name == target.artifactsTaskName) {
388 it.setupJarManifest(multiRelease = config.jvmVariant.toJvmVariant() == JvmVariant.BOTH)
389 }
390 }
391 }
392 // test should compile and run against original production binaries
393 if (compilationType == CompilationType.TEST) {
394 val mainCompilation =
395 compilation.target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
396 val originalMainClassesDirs = project.objects.fileCollection().from(
397 mainCompilation.compileTaskProvider.flatMap { (it as KotlinCompileTool).destinationDirectory }
398 )
399 // compilationTask.destinationDirectory was changed from build/classes/kotlin/main to build/classes/atomicfu-orig/main,
400 // so we need to update libraries
401 (tasks.findByName(compilation.compileKotlinTaskName) as? AbstractKotlinCompileTool<*>)
402 ?.libraries
403 ?.setFrom(
404 originalMainClassesDirs + compilation.compileDependencyFiles
405 )
406 if (transformTask != null) {
407 // if transform task was not created, then originalMainClassesDirs == mainCompilation.output.classesDirs
408 (tasks.findByName("${target.name}${compilation.name.capitalize()}") as? Test)?.classpath =
409 originalMainClassesDirs + (compilation as KotlinCompilationToRunnableFiles).runtimeDependencyFiles - mainCompilation.output.classesDirs
410 }
411 compilation.compileKotlinTask.setFriendPaths(originalMainClassesDirs)
412 }
413 }
414 }
415
Stringnull416 private fun String.toJvmVariant(): JvmVariant = enumValueOf(toUpperCase(Locale.US))
417
418 private fun Project.registerJvmTransformTask(compilation: KotlinCompilation<*>): TaskProvider<AtomicFUTransformTask> =
419 tasks.register(
420 "transform${compilation.target.name.capitalize()}${compilation.name.capitalize()}Atomicfu",
421 AtomicFUTransformTask::class.java
422 )
423
424 private fun Project.registerJsTransformTask(compilation: KotlinCompilation<*>): TaskProvider<AtomicFUTransformJsTask> =
425 tasks.register(
426 "transform${compilation.target.name.capitalize()}${compilation.name.capitalize()}Atomicfu",
427 AtomicFUTransformJsTask::class.java
428 )
429
430 private fun TaskProvider<AtomicFUTransformTask>.configureJvmTask(
431 classpath: FileCollection,
432 classesTaskName: String,
433 transformedClassesDir: Provider<Directory>,
434 originalClassesDir: FileCollection,
435 config: AtomicFUPluginExtension
436 ): TaskProvider<AtomicFUTransformTask> =
437 apply {
438 configure {
439 it.dependsOn(classesTaskName)
440 it.classPath = classpath
441 it.inputFiles = originalClassesDir
442 it.destinationDirectory.value(transformedClassesDir)
443 it.jvmVariant = config.jvmVariant
444 it.verbose = config.verbose
445 }
446 }
447
configureJsTasknull448 private fun TaskProvider<AtomicFUTransformJsTask>.configureJsTask(
449 classesTaskName: String,
450 transformedClassesDir: Provider<Directory>,
451 originalClassesDir: FileCollection,
452 config: AtomicFUPluginExtension
453 ): TaskProvider<AtomicFUTransformJsTask> =
454 apply {
455 configure {
456 it.dependsOn(classesTaskName)
457 it.inputFiles = originalClassesDir
458 it.destinationDirectory.value(transformedClassesDir)
459 it.verbose = config.verbose
460 }
461 }
462
setupJarManifestnull463 private fun Jar.setupJarManifest(multiRelease: Boolean) {
464 if (multiRelease) {
465 manifest.attributes.apply {
466 put("Multi-Release", "true")
467 }
468 }
469 }
470
471 class AtomicFUPluginExtension(pluginVersion: String?) {
472 var dependenciesVersion = pluginVersion
473 var transformJvm = true
474 var transformJs = true
475 var jvmVariant: String = "FU"
476 var verbose: Boolean = false
477 }
478
479 @CacheableTask
480 abstract class AtomicFUTransformTask : ConventionTask() {
481 @get:Inject
482 internal abstract val providerFactory: ProviderFactory
483
484 @get:Inject
485 internal abstract val projectLayout: ProjectLayout
486
487 @PathSensitive(PathSensitivity.RELATIVE)
488 @InputFiles
489 lateinit var inputFiles: FileCollection
490
491 @Suppress("unused")
492 @Deprecated(
493 message = "Replaced with 'destinationDirectory'",
494 replaceWith = ReplaceWith("destinationDirectory")
495 )
496 @get:Internal
497 var outputDir: File
498 get() = destinationDirectory.get().asFile
<lambda>null499 set(value) { destinationDirectory.value(projectLayout.dir(providerFactory.provider { value })) }
500
501 @get:OutputDirectory
502 abstract val destinationDirectory: DirectoryProperty
503
504 @Classpath
505 @InputFiles
506 lateinit var classPath: FileCollection
507
508 @Input
509 var jvmVariant = "FU"
510
511 @Input
512 var verbose = false
513
514 @TaskAction
transformnull515 fun transform() {
516 val cp = classPath.files.map { it.absolutePath }
517 inputFiles.files.forEach { inputDir ->
518 AtomicFUTransformer(cp, inputDir, destinationDirectory.get().asFile).let { t ->
519 t.jvmVariant = jvmVariant.toJvmVariant()
520 t.verbose = verbose
521 t.transform()
522 }
523 }
524 }
525 }
526
527 @CacheableTask
528 abstract class AtomicFUTransformJsTask : ConventionTask() {
529
530 @get:Inject
531 internal abstract val providerFactory: ProviderFactory
532
533 @get:Inject
534 internal abstract val projectLayout: ProjectLayout
535
536 @PathSensitive(PathSensitivity.RELATIVE)
537 @InputFiles
538 lateinit var inputFiles: FileCollection
539
540 @Suppress("unused")
541 @Deprecated(
542 message = "Replaced with 'destinationDirectory'",
543 replaceWith = ReplaceWith("destinationDirectory")
544 )
545 @get:Internal
546 var outputDir: File
547 get() = destinationDirectory.get().asFile
<lambda>null548 set(value) { destinationDirectory.value(projectLayout.dir(providerFactory.provider { value })) }
549
550 @get:OutputDirectory
551 abstract val destinationDirectory: DirectoryProperty
552
553 @Input
554 var verbose = false
555
556 @TaskAction
transformnull557 fun transform() {
558 inputFiles.files.forEach { inputDir ->
559 AtomicFUTransformerJS(inputDir, destinationDirectory.get().asFile).let { t ->
560 t.verbose = verbose
561 t.transform()
562 }
563 }
564 }
565 }
566