diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt index 6c35baf1ec5..2ef37a7bd47 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt @@ -6,9 +6,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import groovy.lang.Closure -import org.gradle.api.GradleException -import org.gradle.api.Project -import org.gradle.api.Task +import org.gradle.api.* import org.gradle.api.attributes.AttributeContainer import org.gradle.api.execution.TaskExecutionListener import org.gradle.api.file.FileCollection @@ -193,20 +191,37 @@ abstract class AbstractKotlinCompilation( override fun toString(): String = "compilation '$compilationName' ($target)" + internal val friendArtifactsTask: TaskProvider? by lazy { + if (associateWithTransitiveClosure.any { it.isMain() }) { + val archiveTasks = target.project.tasks.withType(AbstractArchiveTask::class.java) + if (!archiveTasks.isEmpty()) { + try { + archiveTasks.named(target.artifactsTaskName) + } catch (e: UnknownTaskException) { + // Native tasks does not extend AbstractArchiveTask + null + } + } else { + null + } + } else { + null + } + } + /** * If a compilation is aware of its associate compilations' outputs being added to the classpath in a transformed or packaged way, * it should point to those friend artifact files via this property. */ internal open val friendArtifacts: FileCollection get() = with(target.project) { - if (associateWithTransitiveClosure.any { it.isMain() }) { + val friendArtifactsTaskProvider = friendArtifactsTask + if (friendArtifactsTaskProvider != null) { // In case the main artifact is transitively added to the test classpath via a test dependency on another module // that depends on this module's production part, include the main artifact in the friend artifacts, lazily: files( - provider { - listOfNotNull( - tasks.withType(AbstractArchiveTask::class.java).findByName(target.artifactsTaskName)?.archivePathCompatible - ) + Callable { + friendArtifactsTaskProvider.get().archivePathCompatible } ) } else files() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt index a3b4fd5ff09..1aed8166b3c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLink.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.targets.js.ir import org.gradle.api.file.FileCollection import org.gradle.api.file.FileTree import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.* import org.gradle.api.tasks.incremental.IncrementalTaskInputs import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments @@ -19,9 +20,12 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode.PRODUCTION import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile import org.jetbrains.kotlin.gradle.utils.newFileProperty import java.io.File +import javax.inject.Inject @CacheableTask -open class KotlinJsIrLink : Kotlin2JsCompile() { +open class KotlinJsIrLink @Inject constructor( + objectFactory: ObjectFactory +): Kotlin2JsCompile(objectFactory) { // Link tasks are not affected by compiler plugin override val pluginClasspath: FileCollection = project.objects.fileCollection() diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLinkWithWorkers.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLinkWithWorkers.kt index eea2726b816..4668d1a8b55 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLinkWithWorkers.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrLinkWithWorkers.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.targets.js.ir +import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.CacheableTask import org.gradle.workers.WorkerExecutor import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers @@ -15,8 +16,9 @@ import javax.inject.Inject internal open class KotlinJsIrLinkWithWorkers @Inject constructor( + objectFactory: ObjectFactory, private val workerExecutor: WorkerExecutor -) : KotlinJsIrLink() { +) : KotlinJsIrLink(objectFactory) { override fun compilerRunner() = GradleCompilerRunnerWithWorkers( GradleCompileTaskProvider(this), diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index e688d0c5774..b0ac0b30a7c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -10,6 +10,7 @@ import org.gradle.api.Task import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.FileCollection import org.gradle.api.logging.Logger +import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.gradle.api.tasks.compile.AbstractCompile @@ -51,6 +52,7 @@ import org.jetbrains.kotlin.incremental.ChangedFiles import org.jetbrains.kotlin.library.impl.isKotlinLibrary import org.jetbrains.kotlin.utils.JsLibraryUtils import java.io.File +import java.util.zip.ZipFile import javax.inject.Inject const val KOTLIN_BUILD_DIR_NAME = "kotlin" @@ -304,6 +306,15 @@ abstract class AbstractKotlinCompile() : AbstractKo taskData.compilation.moduleName } + init { + if (taskData.compilation is AbstractKotlinCompilation<*> && + (taskData.compilation as AbstractKotlinCompilation<*>).friendArtifactsTask != null) { + this@AbstractKotlinCompile.dependsOn( + (taskData.compilation as AbstractKotlinCompilation<*>).friendArtifactsTask + ) + } + } + @get:Internal // takes part in the compiler arguments val friendPaths: FileCollection = project.files( project.provider { @@ -632,8 +643,9 @@ internal open class KotlinCompileWithWorkers @Inject constructor( @CacheableTask internal open class Kotlin2JsCompileWithWorkers @Inject constructor( + objectFactory: ObjectFactory, private val workerExecutor: WorkerExecutor -) : Kotlin2JsCompile() { +) : Kotlin2JsCompile(objectFactory) { override fun compilerRunner() = GradleCompilerRunnerWithWorkers( @@ -654,7 +666,9 @@ internal open class KotlinCompileCommonWithWorkers @Inject constructor( } @CacheableTask -open class Kotlin2JsCompile : AbstractKotlinCompile(), KotlinJsCompile { +open class Kotlin2JsCompile @Inject constructor( + objectFactory: ObjectFactory +) : AbstractKotlinCompile(), KotlinJsCompile { init { incremental = true @@ -718,6 +732,19 @@ open class Kotlin2JsCompile : AbstractKotlinCompile(), Ko override fun getSourceRoots() = SourceRoots.KotlinOnly.create(getSource(), sourceFilesExtensions) + @get:InputFiles + @get:Optional + @get:PathSensitive(PathSensitivity.RELATIVE) + internal val friendDependencies: FileCollection = objectFactory + .fileCollection() + .from(friendPaths) + .filter { + // .jar files are not required for js compilation as friend modules + // and, because of `@InputFiles` and different normalization strategy from `@Classpath`, + // they produce build cache misses + it.exists() && !it.name.endsWith(".jar") && libraryFilter(it) + } + @Suppress("unused") @get:InputFiles @get:Optional @@ -781,11 +808,7 @@ open class Kotlin2JsCompile : AbstractKotlinCompile(), Ko null } - val friendDependencies: List = friendPaths.files.filter { - it.exists() && libraryFilter(it) - }.map { it.absolutePath } - - args.friendModules = friendDependencies.joinToString(File.pathSeparator) + args.friendModules = friendDependencies.files.joinToString(File.pathSeparator) { it.absolutePath } if (args.sourceMapBaseDirs == null && !args.sourceMapPrefix.isNullOrEmpty()) { args.sourceMapBaseDirs = absolutePathProvider