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.plugins.*
12 import org.gradle.api.tasks.*
13 import org.gradle.api.tasks.compile.*
14 import org.gradle.api.tasks.testing.*
15 import org.gradle.jvm.tasks.*
16 import org.jetbrains.kotlin.gradle.dsl.*
17 import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
18 import org.jetbrains.kotlin.gradle.plugin.*
19 import java.io.*
20 import java.util.*
21 import java.util.concurrent.*
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
27 private const val EXTENSION_NAME = "atomicfu"
28 private const val ORIGINAL_DIR_NAME = "originalClassesDir"
29 private const val COMPILE_ONLY_CONFIGURATION = "compileOnly"
30 private const val IMPLEMENTATION_CONFIGURATION = "implementation"
31 private const val TEST_IMPLEMENTATION_CONFIGURATION = "testImplementation"
32 // If the project uses KGP <= 1.6.20, only JS IR compiler plugin is available, and it is turned on via setting this property.
33 // The property is supported for backwards compatibility.
34 private const val ENABLE_JS_IR_TRANSFORMATION_LEGACY = "kotlinx.atomicfu.enableIrTransformation"
35 private const val ENABLE_JS_IR_TRANSFORMATION = "kotlinx.atomicfu.enableJsIrTransformation"
36 private const val ENABLE_JVM_IR_TRANSFORMATION = "kotlinx.atomicfu.enableJvmIrTransformation"
37
38 open class AtomicFUGradlePlugin : Plugin<Project> {
39 override fun apply(project: Project) = project.run {
40 val pluginVersion = rootProject.buildscript.configurations.findByName("classpath")
41 ?.allDependencies?.find { it.name == "atomicfu-gradle-plugin" }?.version
42 extensions.add(EXTENSION_NAME, AtomicFUPluginExtension(pluginVersion))
43 applyAtomicfuCompilerPlugin()
44 configureDependencies()
45 configureTasks()
46 }
47 }
48
Projectnull49 private fun Project.configureDependencies() {
50 withPluginWhenEvaluatedDependencies("kotlin") { version ->
51 dependencies.add(
52 if (config.transformJvm) COMPILE_ONLY_CONFIGURATION else IMPLEMENTATION_CONFIGURATION,
53 getAtomicfuDependencyNotation(Platform.JVM, version)
54 )
55 dependencies.add(TEST_IMPLEMENTATION_CONFIGURATION, getAtomicfuDependencyNotation(Platform.JVM, version))
56 }
57 withPluginWhenEvaluatedDependencies("org.jetbrains.kotlin.js") { version ->
58 dependencies.add(
59 if (config.transformJs) COMPILE_ONLY_CONFIGURATION else IMPLEMENTATION_CONFIGURATION,
60 getAtomicfuDependencyNotation(Platform.JS, version)
61 )
62 dependencies.add(TEST_IMPLEMENTATION_CONFIGURATION, getAtomicfuDependencyNotation(Platform.JS, version))
63 addCompilerPluginDependency()
64 }
65 withPluginWhenEvaluatedDependencies("kotlin-multiplatform") { version ->
66 configureMultiplatformPluginDependencies(version)
67 }
68 }
69
configureTasksnull70 private fun Project.configureTasks() {
71 val config = config
72 withPluginWhenEvaluated("kotlin") {
73 if (config.transformJvm) {
74 // skip transformation task if ir transformation is enabled
75 if (rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION)) return@withPluginWhenEvaluated
76 configureJvmTransformation("compileTestKotlin") { sourceSet, transformedDir, originalDir ->
77 createJvmTransformTask(sourceSet).configureJvmTask(
78 sourceSet.compileClasspath,
79 sourceSet.classesTaskName,
80 transformedDir,
81 originalDir,
82 config
83 )
84 }
85 }
86 }
87 withPluginWhenEvaluated("org.jetbrains.kotlin.js") {
88 if (config.transformJs) configureJsTransformation()
89 }
90 withPluginWhenEvaluated("kotlin-multiplatform") {
91 configureMultiplatformTransformation()
92 }
93 }
94
95 private data class KotlinVersion(val major: Int, val minor: Int, val patch: Int)
96
Projectnull97 private fun Project.getKotlinVersion(): KotlinVersion {
98 val kotlinVersion = getKotlinPluginVersion()
99 val (major, minor) = kotlinVersion
100 .split('.')
101 .take(2)
102 .map { it.toInt() }
103 val patch = kotlinVersion.substringAfterLast('.').substringBefore('-').toInt()
104 return KotlinVersion(major, minor, patch)
105 }
106
KotlinVersionnull107 private fun KotlinVersion.atLeast(major: Int, minor: Int, patch: Int) =
108 this.major == major && (this.minor == minor && this.patch >= patch || this.minor > minor) || this.major > major
109
110 // kotlinx-atomicfu compiler plugin is available for KGP >= 1.6.20
111 private fun Project.isCompilerPluginAvailable() = getKotlinVersion().atLeast(1, 6, 20)
112
113 private fun Project.applyAtomicfuCompilerPlugin() {
114 val kotlinVersion = getKotlinVersion()
115 // for KGP >= 1.7.20:
116 // compiler plugin for JS IR is applied via the property `kotlinx.atomicfu.enableJsIrTransformation`
117 // compiler plugin for JVM IR is applied via the property `kotlinx.atomicfu.enableJvmIrTransformation`
118 if (kotlinVersion.atLeast(1, 7, 20)) {
119 plugins.apply(AtomicfuKotlinGradleSubplugin::class.java)
120 extensions.getByType(AtomicfuKotlinGradleSubplugin.AtomicfuKotlinGradleExtension::class.java).apply {
121 isJsIrTransformationEnabled = rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION)
122 isJvmIrTransformationEnabled = rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION)
123 }
124 } else {
125 // for KGP >= 1.6.20 && KGP <= 1.7.20:
126 // compiler plugin for JS IR is applied via the property `kotlinx.atomicfu.enableIrTransformation`
127 // compiler plugin for JVM IR is not supported yet
128 if (kotlinVersion.atLeast(1, 6, 20)) {
129 if (rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION_LEGACY)) {
130 plugins.apply(AtomicfuKotlinGradleSubplugin::class.java)
131 }
132 }
133 }
134 }
135
Projectnull136 private fun Project.getBooleanProperty(name: String) =
137 rootProject.findProperty(name)?.toString()?.toBooleanStrict() ?: false
138
139 private fun String.toBooleanStrict(): Boolean = when (this) {
140 "true" -> true
141 "false" -> false
142 else -> throw IllegalArgumentException("The string doesn't represent a boolean value: $this")
143 }
144
Projectnull145 private fun Project.needsJsIrTransformation(target: KotlinTarget): Boolean =
146 (rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION) || rootProject.getBooleanProperty(ENABLE_JS_IR_TRANSFORMATION_LEGACY))
147 && target.isJsIrTarget()
148
149 private fun KotlinTarget.isJsIrTarget() = (this is KotlinJsTarget && this.irTarget != null) || this is KotlinJsIrTarget
150
151 private fun Project.addCompilerPluginDependency() {
152 if (isCompilerPluginAvailable()) {
153 withKotlinTargets { target ->
154 if (needsJsIrTransformation(target)) {
155 target.compilations.forEach { kotlinCompilation ->
156 kotlinCompilation.dependencies {
157 if (getKotlinVersion().atLeast(1, 7, 10)) {
158 // since Kotlin 1.7.10 we can add `atomicfu-runtime` dependency directly
159 implementation("org.jetbrains.kotlin:kotlinx-atomicfu-runtime:${getKotlinPluginVersion()}")
160 } else {
161 // add atomicfu compiler plugin dependency
162 // to provide the `atomicfu-runtime` library used during compiler plugin transformation
163 implementation("org.jetbrains.kotlin:atomicfu:${getKotlinPluginVersion()}")
164 }
165 }
166 }
167 }
168 }
169 }
170 }
171
172 private enum class Platform(val suffix: String) {
173 JVM("-jvm"),
174 JS("-js"),
175 NATIVE(""),
176 MULTIPLATFORM("")
177 }
178
179 private enum class CompilationType { MAIN, TEST }
180
compilationNameToTypenull181 private fun String.compilationNameToType(): CompilationType? = when (this) {
182 KotlinCompilation.MAIN_COMPILATION_NAME -> CompilationType.MAIN
183 KotlinCompilation.TEST_COMPILATION_NAME -> CompilationType.TEST
184 else -> null
185 }
186
sourceSetNameToTypenull187 private fun String.sourceSetNameToType(): CompilationType? = when (this) {
188 SourceSet.MAIN_SOURCE_SET_NAME -> CompilationType.MAIN
189 SourceSet.TEST_SOURCE_SET_NAME -> CompilationType.TEST
190 else -> null
191 }
192
193 private val Project.config: AtomicFUPluginExtension
194 get() = extensions.findByName(EXTENSION_NAME) as? AtomicFUPluginExtension ?: AtomicFUPluginExtension(null)
195
getAtomicfuDependencyNotationnull196 private fun getAtomicfuDependencyNotation(platform: Platform, version: String): String =
197 "org.jetbrains.kotlinx:atomicfu${platform.suffix}:$version"
198
199 // Note "afterEvaluate" does nothing when the project is already in executed state, so we need
200 // a special check for this case
201 fun <T> Project.whenEvaluated(fn: Project.() -> T) {
202 if (state.executed) {
203 fn()
204 } else {
205 afterEvaluate { fn() }
206 }
207 }
208
Projectnull209 fun Project.withPluginWhenEvaluated(plugin: String, fn: Project.() -> Unit) {
210 pluginManager.withPlugin(plugin) { whenEvaluated(fn) }
211 }
212
Projectnull213 fun Project.withPluginWhenEvaluatedDependencies(plugin: String, fn: Project.(version: String) -> Unit) {
214 withPluginWhenEvaluated(plugin) {
215 config.dependenciesVersion?.let { fn(it) }
216 }
217 }
218
Projectnull219 fun Project.withKotlinTargets(fn: (KotlinTarget) -> Unit) {
220 extensions.findByType(KotlinTargetsContainer::class.java)?.let { kotlinExtension ->
221 // find all compilations given sourceSet belongs to
222 kotlinExtension.targets
223 .all { target -> fn(target) }
224 }
225 }
226
setFriendPathsnull227 private fun KotlinCompile<*>.setFriendPaths(friendPathsFileCollection: FileCollection) {
228 val (majorVersion, minorVersion) = project.getKotlinPluginVersion()
229 .split('.')
230 .take(2)
231 .map { it.toInt() }
232 if (majorVersion == 1 && minorVersion < 7) {
233 (this as? AbstractKotlinCompile<*>)?.friendPaths?.from(friendPathsFileCollection)
234 } else {
235 // See KT-KT-54167 (works only for KGP 1.7.0+)
236 (this as BaseKotlinCompile).friendPaths.from(friendPathsFileCollection)
237 }
238 }
239
Projectnull240 fun Project.configureJsTransformation() =
241 configureTransformationForTarget((kotlinExtension as KotlinJsProjectExtension).js())
242
243 fun Project.configureMultiplatformTransformation() =
244 withKotlinTargets { target ->
245 if (target.platformType == KotlinPlatformType.common || target.platformType == KotlinPlatformType.native) {
246 return@withKotlinTargets // skip the common & native targets -- no transformation for them
247 }
248 configureTransformationForTarget(target)
249 }
250
Projectnull251 private fun Project.configureTransformationForTarget(target: KotlinTarget) {
252 val originalDirsByCompilation = hashMapOf<KotlinCompilation<*>, FileCollection>()
253 val config = config
254 target.compilations.all compilations@{ compilation ->
255 val compilationType = compilation.name.compilationNameToType()
256 ?: return@compilations // skip unknown compilations
257 val classesDirs = compilation.output.classesDirs
258 // make copy of original classes directory
259 val originalClassesDirs: FileCollection =
260 project.files(classesDirs.from.toTypedArray()).filter { it.exists() }
261 originalDirsByCompilation[compilation] = originalClassesDirs
262 val transformedClassesDir =
263 project.buildDir.resolve("classes/atomicfu/${target.name}/${compilation.name}")
264 val transformTask = when (target.platformType) {
265 KotlinPlatformType.jvm, KotlinPlatformType.androidJvm -> {
266 // skip transformation task if transformation is turned off or ir transformation is enabled
267 if (!config.transformJvm || rootProject.getBooleanProperty(ENABLE_JVM_IR_TRANSFORMATION)) return@compilations
268 project.createJvmTransformTask(compilation).configureJvmTask(
269 compilation.compileDependencyFiles,
270 compilation.compileAllTaskName,
271 transformedClassesDir,
272 originalClassesDirs,
273 config
274 )
275 }
276 KotlinPlatformType.js -> {
277 // skip when js transformation is not needed or when IR is transformed
278 if (!config.transformJs || (needsJsIrTransformation(target))) {
279 return@compilations
280 }
281 project.createJsTransformTask(compilation).configureJsTask(
282 compilation.compileAllTaskName,
283 transformedClassesDir,
284 originalClassesDirs,
285 config
286 )
287 }
288 else -> error("Unsupported transformation platform '${target.platformType}'")
289 }
290 //now transformTask is responsible for compiling this source set into the classes directory
291 classesDirs.setFrom(transformedClassesDir)
292 classesDirs.builtBy(transformTask)
293 (tasks.findByName(target.artifactsTaskName) as? Jar)?.apply {
294 setupJarManifest(multiRelease = config.jvmVariant.toJvmVariant() == JvmVariant.BOTH)
295 }
296 // test should compile and run against original production binaries
297 if (compilationType == CompilationType.TEST) {
298 val mainCompilation =
299 compilation.target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
300 val originalMainClassesDirs = project.files(
301 // use Callable because there is no guarantee that main is configured before test
302 Callable { originalDirsByCompilation[mainCompilation]!! }
303 )
304
305 // KGP >= 1.7.0 has breaking changes in task hierarchy:
306 // https://youtrack.jetbrains.com/issue/KT-32805#focus=Comments-27-5915479.0-0
307 val (majorVersion, minorVersion) = getKotlinPluginVersion()
308 .split('.')
309 .take(2)
310 .map { it.toInt() }
311 if (majorVersion == 1 && minorVersion < 7) {
312 (tasks.findByName(compilation.compileKotlinTaskName) as? AbstractCompile)?.classpath =
313 originalMainClassesDirs + compilation.compileDependencyFiles - mainCompilation.output.classesDirs
314 } else {
315 (tasks.findByName(compilation.compileKotlinTaskName) as? AbstractKotlinCompileTool<*>)
316 ?.libraries
317 ?.setFrom(
318 originalMainClassesDirs + compilation.compileDependencyFiles - mainCompilation.output.classesDirs
319 )
320 }
321
322 (tasks.findByName("${target.name}${compilation.name.capitalize()}") as? Test)?.classpath =
323 originalMainClassesDirs + (compilation as KotlinCompilationToRunnableFiles).runtimeDependencyFiles - mainCompilation.output.classesDirs
324
325 compilation.compileKotlinTask.setFriendPaths(originalMainClassesDirs)
326 }
327 }
328 }
329
Projectnull330 fun Project.sourceSetsByCompilation(): Map<KotlinSourceSet, List<KotlinCompilation<*>>> {
331 val sourceSetsByCompilation = hashMapOf<KotlinSourceSet, MutableList<KotlinCompilation<*>>>()
332 withKotlinTargets { target ->
333 target.compilations.forEach { compilation ->
334 compilation.allKotlinSourceSets.forEach { sourceSet ->
335 sourceSetsByCompilation.getOrPut(sourceSet) { mutableListOf() }.add(compilation)
336 }
337 }
338 }
339 return sourceSetsByCompilation
340 }
341
Projectnull342 fun Project.configureMultiplatformPluginDependencies(version: String) {
343 if (rootProject.getBooleanProperty("kotlin.mpp.enableGranularSourceSetsMetadata")) {
344 addCompilerPluginDependency()
345 val mainConfigurationName = project.extensions.getByType(KotlinMultiplatformExtension::class.java).sourceSets
346 .getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME)
347 .compileOnlyConfigurationName
348 dependencies.add(mainConfigurationName, getAtomicfuDependencyNotation(Platform.MULTIPLATFORM, version))
349
350 val testConfigurationName = project.extensions.getByType(KotlinMultiplatformExtension::class.java).sourceSets
351 .getByName(KotlinSourceSet.COMMON_TEST_SOURCE_SET_NAME)
352 .implementationConfigurationName
353 dependencies.add(testConfigurationName, getAtomicfuDependencyNotation(Platform.MULTIPLATFORM, version))
354
355 // For each source set that is only used in Native compilations, add an implementation dependency so that it
356 // gets published and is properly consumed as a transitive dependency:
357 sourceSetsByCompilation().forEach { (sourceSet, compilations) ->
358 val isSharedNativeSourceSet = compilations.all {
359 it.platformType == KotlinPlatformType.common || it.platformType == KotlinPlatformType.native
360 }
361 if (isSharedNativeSourceSet) {
362 val configuration = sourceSet.implementationConfigurationName
363 dependencies.add(configuration, getAtomicfuDependencyNotation(Platform.MULTIPLATFORM, version))
364 }
365 }
366 } else {
367 sourceSetsByCompilation().forEach { (sourceSet, compilations) ->
368 addCompilerPluginDependency()
369 val platformTypes = compilations.map { it.platformType }.toSet()
370 val compilationNames = compilations.map { it.compilationName }.toSet()
371 if (compilationNames.size != 1)
372 error("Source set '${sourceSet.name}' of project '$name' is part of several compilations $compilationNames")
373 val compilationType = compilationNames.single().compilationNameToType()
374 ?: return@forEach // skip unknown compilations
375 val platform =
376 if (platformTypes.size > 1) Platform.MULTIPLATFORM else // mix of platform types -> "common"
377 when (platformTypes.single()) {
378 KotlinPlatformType.common -> Platform.MULTIPLATFORM
379 KotlinPlatformType.jvm, KotlinPlatformType.androidJvm -> Platform.JVM
380 KotlinPlatformType.js -> Platform.JS
381 KotlinPlatformType.native, KotlinPlatformType.wasm -> Platform.NATIVE
382 }
383 val configurationName = when {
384 // impl dependency for native (there is no transformation)
385 platform == Platform.NATIVE -> sourceSet.implementationConfigurationName
386 // compileOnly dependency for main compilation (commonMain, jvmMain, jsMain)
387 compilationType == CompilationType.MAIN -> sourceSet.compileOnlyConfigurationName
388 // impl dependency for tests
389 else -> sourceSet.implementationConfigurationName
390 }
391 dependencies.add(configurationName, getAtomicfuDependencyNotation(platform, version))
392 }
393 }
394 }
395
Projectnull396 fun Project.configureJvmTransformation(
397 testTaskName: String,
398 createTransformTask: (sourceSet: SourceSet, transformedDir: File, originalDir: FileCollection) -> Task
399 ) {
400 val config = config
401 sourceSets.all { sourceSet ->
402 val compilationType = sourceSet.name.sourceSetNameToType()
403 ?: return@all // skip unknown types
404 val classesDirs = (sourceSet.output.classesDirs as ConfigurableFileCollection).from as Collection<Any>
405 // make copy of original classes directory
406 val originalClassesDirs: FileCollection = project.files(classesDirs.toTypedArray()).filter { it.exists() }
407 (sourceSet as ExtensionAware).extensions.add(ORIGINAL_DIR_NAME, originalClassesDirs)
408 val transformedClassesDir =
409 project.buildDir.resolve("classes/atomicfu/${sourceSet.name}")
410 // make transformedClassesDir the source path for output.classesDirs
411 (sourceSet.output.classesDirs as ConfigurableFileCollection).setFrom(transformedClassesDir)
412 val transformTask = createTransformTask(sourceSet, transformedClassesDir, originalClassesDirs)
413 //now transformTask is responsible for compiling this source set into the classes directory
414 sourceSet.compiledBy(transformTask)
415 (tasks.findByName(sourceSet.jarTaskName) as? Jar)?.apply {
416 setupJarManifest(multiRelease = config.jvmVariant.toJvmVariant() == JvmVariant.BOTH)
417 }
418 // test should compile and run against original production binaries
419 if (compilationType == CompilationType.TEST) {
420 val mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
421 val originalMainClassesDirs = project.files(
422 // use Callable because there is no guarantee that main is configured before test
423 Callable { (mainSourceSet as ExtensionAware).extensions.getByName(ORIGINAL_DIR_NAME) as FileCollection }
424 )
425
426 (tasks.findByName(testTaskName) as? AbstractCompile)?.run {
427 classpath =
428 originalMainClassesDirs + sourceSet.compileClasspath - mainSourceSet.output.classesDirs
429
430 (this as? KotlinCompile<*>)?.setFriendPaths(originalMainClassesDirs)
431 }
432
433 // todo: fix test runtime classpath for JS?
434 (tasks.findByName(JavaPlugin.TEST_TASK_NAME) as? Test)?.classpath =
435 originalMainClassesDirs + sourceSet.runtimeClasspath - mainSourceSet.output.classesDirs
436 }
437 }
438 }
439
Stringnull440 fun String.toJvmVariant(): JvmVariant = enumValueOf(toUpperCase(Locale.US))
441
442 fun Project.createJvmTransformTask(compilation: KotlinCompilation<*>): AtomicFUTransformTask =
443 tasks.create(
444 "transform${compilation.target.name.capitalize()}${compilation.name.capitalize()}Atomicfu",
445 AtomicFUTransformTask::class.java
446 )
447
448 fun Project.createJsTransformTask(compilation: KotlinCompilation<*>): AtomicFUTransformJsTask =
449 tasks.create(
450 "transform${compilation.target.name.capitalize()}${compilation.name.capitalize()}Atomicfu",
451 AtomicFUTransformJsTask::class.java
452 )
453
454 fun Project.createJvmTransformTask(sourceSet: SourceSet): AtomicFUTransformTask =
455 tasks.create(sourceSet.getTaskName("transform", "atomicfuClasses"), AtomicFUTransformTask::class.java)
456
457 fun AtomicFUTransformTask.configureJvmTask(
458 classpath: FileCollection,
459 classesTaskName: String,
460 transformedClassesDir: File,
461 originalClassesDir: FileCollection,
462 config: AtomicFUPluginExtension
463 ): ConventionTask =
464 apply {
465 dependsOn(classesTaskName)
466 classPath = classpath
467 inputFiles = originalClassesDir
468 outputDir = transformedClassesDir
469 jvmVariant = config.jvmVariant
470 verbose = config.verbose
471 }
472
AtomicFUTransformJsTasknull473 fun AtomicFUTransformJsTask.configureJsTask(
474 classesTaskName: String,
475 transformedClassesDir: File,
476 originalClassesDir: FileCollection,
477 config: AtomicFUPluginExtension
478 ): ConventionTask =
479 apply {
480 dependsOn(classesTaskName)
481 inputFiles = originalClassesDir
482 outputDir = transformedClassesDir
483 verbose = config.verbose
484 }
485
setupJarManifestnull486 fun Jar.setupJarManifest(multiRelease: Boolean) {
487 if (multiRelease) {
488 manifest.attributes.apply {
489 put("Multi-Release", "true")
490 }
491 }
492 }
493
494 val Project.sourceSets: SourceSetContainer
495 get() = convention.getPlugin(JavaPluginConvention::class.java).sourceSets
496
497 class AtomicFUPluginExtension(pluginVersion: String?) {
498 var dependenciesVersion = pluginVersion
499 var transformJvm = true
500 var transformJs = true
501 var jvmVariant: String = "FU"
502 var verbose: Boolean = false
503 }
504
505 @CacheableTask
506 open class AtomicFUTransformTask : ConventionTask() {
507 @PathSensitive(PathSensitivity.RELATIVE)
508 @InputFiles
509 lateinit var inputFiles: FileCollection
510
511 @OutputDirectory
512 lateinit var outputDir: File
513
514 @Classpath
515 @InputFiles
516 lateinit var classPath: FileCollection
517
518 @Input
519 var jvmVariant = "FU"
520
521 @Input
522 var verbose = false
523
524 @TaskAction
transformnull525 fun transform() {
526 val cp = classPath.files.map { it.absolutePath }
527 inputFiles.files.forEach { inputDir ->
528 AtomicFUTransformer(cp, inputDir, outputDir).let { t ->
529 t.jvmVariant = jvmVariant.toJvmVariant()
530 t.verbose = verbose
531 t.transform()
532 }
533 }
534 }
535 }
536
537 @CacheableTask
538 open class AtomicFUTransformJsTask : ConventionTask() {
539 @PathSensitive(PathSensitivity.RELATIVE)
540 @InputFiles
541 lateinit var inputFiles: FileCollection
542
543 @OutputDirectory
544 lateinit var outputDir: File
545
546 @Input
547 var verbose = false
548
549 @TaskAction
transformnull550 fun transform() {
551 inputFiles.files.forEach { inputDir ->
552 AtomicFUTransformerJS(inputDir, outputDir).let { t ->
553 t.verbose = verbose
554 t.transform()
555 }
556 }
557 }
558 }
559