Get rid of FileCollectionInternal usage that breaks with Gradle 5.0+

Remove the implementation of the `FileCollectionInternal` interface in
`KotlinCompilationOutput`. This interface may change unexpectedly (and
does between Gradle 4.10 and 5.0) so we need to get rid of its
implementation.

On the other hand, we can't just implement `FileCollection` instead
because Gradle internally assumes that all `FileCollection` instances
are also `FileCollectionInternal`. So the way to fix this is to remove
`SourceSetOutput` from superinterfaces of `KotlinCompilationOutput`
altogether.

Instead, to work with `SourceSetOutput` and `KotlinCompilationOutput`
in a uniform way, provide a wrapper for `SourceSetOutput`:
`KotlinWithJavaCompilationOutput` that is basically a proxy
class.
This commit is contained in:
Sergey Igushkin
2018-10-11 18:58:15 +03:00
parent 174fd41564
commit a0f4d5a637
9 changed files with 121 additions and 125 deletions
@@ -7,8 +7,15 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Named
import org.gradle.api.attributes.HasAttributes
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.SourceSetOutput
interface KotlinCompilationOutput {
var resourcesDir: Any
val classesDirs: ConfigurableFileCollection
val allOutputs: FileCollection
}
interface KotlinCompilation: Named, HasAttributes, HasKotlinDependencies {
val target: KotlinTarget
@@ -21,7 +28,7 @@ interface KotlinCompilation: Named, HasAttributes, HasKotlinDependencies {
var compileDependencyFiles: FileCollection
val output: SourceSetOutput
val output: KotlinCompilationOutput
val platformType get() = target.platformType
@@ -57,8 +57,7 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
protected val logger = Logging.getLogger(this.javaClass)!!
protected val isSeparateClassesDirSupported: Boolean by lazy {
!CopyClassesToJavaOutputStatus.isEnabled(project) &&
kotlinCompilation.output.javaClass.methods.any { it.name == "getClassesDirs" }
!CopyClassesToJavaOutputStatus.isEnabled(project) && isGradleVersionAtLeast(4, 0)
}
protected val sourceSetName: String = kotlinCompilation.compilationName
@@ -78,7 +77,7 @@ internal abstract class KotlinSourceSetProcessor<T : AbstractKotlinCompile<*>>(
kotlinCompilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
File(project.buildDir, "classes/kotlin/$targetSubDirectory${kotlinCompilation.compilationName}")
} else {
kotlinCompilation.output.classesDir
kotlinCompilation.output.classesDirs.singleFile
}
}
@@ -228,18 +227,15 @@ internal class Kotlin2JvmSourceSetProcessor(
}
}
internal fun SourceSetOutput.tryAddClassesDir(
internal fun KotlinCompilationOutput.tryAddClassesDir(
classesDirProvider: () -> FileCollection
): Boolean {
val getClassesDirs = javaClass.methods.firstOrNull { it.name == "getClassesDirs" && it.parameterCount == 0 }
?: return false
val classesDirs = getClassesDirs(this) as? ConfigurableFileCollection
?: return false
classesDirs.from(Callable { classesDirProvider() })
return true
}
): Boolean =
if (isGradleVersionAtLeast(4, 0)) {
classesDirs.from(Callable { classesDirProvider() })
true
} else {
false
}
internal class Kotlin2JsSourceSetProcessor(
project: Project,
@@ -280,8 +276,8 @@ internal class Kotlin2JsSourceSetProcessor(
kotlinTask.destinationDir = outputDir
if (!isSeparateClassesDirSupported) {
kotlinCompilation.output.setClassesDirCompatible(kotlinTask.destinationDir)
if (!isSeparateClassesDirSupported && kotlinCompilation is KotlinWithJavaCompilation) {
kotlinCompilation.javaSourceSet.output.setClassesDirCompatible(kotlinTask.destinationDir)
}
appliedPlugins
@@ -7,11 +7,13 @@ package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.*
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.artifacts.type.ArtifactTypeDefinition
import org.gradle.api.attributes.Usage
import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.artifacts.ArtifactAttributes
import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact
@@ -20,9 +22,9 @@ import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.testing.Test
import org.gradle.internal.cleanup.BuildOutputCleanupRegistry
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.jvm.tasks.ProcessResources
import org.gradle.nativeplatform.test.tasks.RunTestExecutable
@@ -74,11 +76,17 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
if (createTestCompilation) {
platformTarget.compilations.create(KotlinCompilation.TEST_COMPILATION_NAME).apply {
compileDependencyFiles = project.files(main.output, project.configurations.maybeCreate(compileDependencyConfigurationName))
compileDependencyFiles = project.files(
main.output.allOutputs,
project.configurations.maybeCreate(compileDependencyConfigurationName)
)
if (this is KotlinCompilationToRunnableFiles) {
runtimeDependencyFiles =
project.files(output, main.output, project.configurations.maybeCreate(runtimeDependencyConfigurationName))
runtimeDependencyFiles = project.files(
output.allOutputs,
main.output.allOutputs,
project.configurations.maybeCreate(runtimeDependencyConfigurationName)
)
}
}
}
@@ -127,22 +135,19 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
val resourcesTask = project.tasks.maybeCreate(compilation.processResourcesTaskName, ProcessResources::class.java)
resourcesTask.description = "Processes $resourceSet."
DslObject(resourcesTask).conventionMapping.map("destinationDir") { compilation.output.resourcesDir }
DslObject(resourcesTask).conventionMapping.map("destinationDir") { project.file(compilation.output.resourcesDir) }
resourcesTask.from(resourceSet)
}
protected fun createLifecycleTask(compilation: KotlinCompilation) {
val project = compilation.target.project
(compilation.output.classesDirs as ConfigurableFileCollection).from(project.files().builtBy(compilation.compileAllTaskName))
compilation.output.classesDirs.from(project.files().builtBy(compilation.compileAllTaskName))
project.tasks.create(compilation.compileAllTaskName).apply {
group = LifecycleBasePlugin.BUILD_GROUP
description = "Assembles outputs for compilation '${compilation.name}' of target '${compilation.target.name}'"
dependsOn(
compilation.output.dirs,
compilation.compileKotlinTaskName
)
dependsOn(compilation.compileKotlinTaskName)
if (compilation is KotlinCompilationWithResources) {
dependsOn(compilation.processResourcesTaskName)
}
@@ -336,7 +341,7 @@ open class KotlinTargetConfigurator<KotlinCompilationType: KotlinCompilation>(
val jar = project.tasks.create(target.artifactsTaskName, Jar::class.java)
jar.description = "Assembles a jar archive containing the main classes."
jar.group = BasePlugin.BUILD_GROUP
jar.from(mainCompilation.output)
jar.from(mainCompilation.output.allOutputs)
val apiElementsConfiguration = project.configurations.getByName(target.apiElementsConfigurationName)
@@ -619,7 +624,7 @@ open class KotlinNativeTargetConfigurator(
val mainCompilation = target.compilations.getByName(MAIN_COMPILATION_NAME)
target.compilations.findByName(TEST_COMPILATION_NAME)?.apply {
cinterops.all {
it.dependencyFiles += mainCompilation.output
it.dependencyFiles += mainCompilation.output.allOutputs
}
}
}
@@ -9,16 +9,11 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
import java.util.concurrent.Callable
interface KotlinCompilationFactory<T: KotlinCompilation> : NamedDomainObjectFactory<T> {
val itemClass: Class<T>
}
private fun KotlinTarget.createCompilationOutput(name: String) =
KotlinCompilationOutput(project, Callable { project.buildDir.resolve("processedResources/$targetName/$name") })
class KotlinCommonCompilationFactory(
val target: KotlinOnlyTarget<KotlinCommonCompilation>
) : KotlinCompilationFactory<KotlinCommonCompilation> {
@@ -26,7 +21,7 @@ class KotlinCommonCompilationFactory(
get() = KotlinCommonCompilation::class.java
override fun create(name: String): KotlinCommonCompilation =
KotlinCommonCompilation(target, name, target.createCompilationOutput(name))
KotlinCommonCompilation(target, name)
}
class KotlinJvmCompilationFactory(
@@ -36,7 +31,7 @@ class KotlinJvmCompilationFactory(
get() = KotlinJvmCompilation::class.java
override fun create(name: String): KotlinJvmCompilation =
KotlinJvmCompilation(target, name, target.createCompilationOutput(name))
KotlinJvmCompilation(target, name)
}
class KotlinWithJavaCompilationFactory(
@@ -62,7 +57,7 @@ class KotlinJvmAndroidCompilationFactory(
get() = KotlinJvmAndroidCompilation::class.java
override fun create(name: String): KotlinJvmAndroidCompilation =
KotlinJvmAndroidCompilation(target, name, target.createCompilationOutput(name))
KotlinJvmAndroidCompilation(target, name)
}
class KotlinJsCompilationFactory(
@@ -73,7 +68,7 @@ class KotlinJsCompilationFactory(
get() = KotlinJsCompilation::class.java
override fun create(name: String): KotlinJsCompilation =
KotlinJsCompilation(target, name, target.createCompilationOutput(name))
KotlinJsCompilation(target, name)
}
class KotlinNativeCompilationFactory(
@@ -85,7 +80,7 @@ class KotlinNativeCompilationFactory(
get() = KotlinNativeCompilation::class.java
override fun create(name: String): KotlinNativeCompilation =
KotlinNativeCompilation(target, name, target.createCompilationOutput(name)).apply {
KotlinNativeCompilation(target, name).apply {
if (name == KotlinCompilation.TEST_COMPILATION_NAME) {
friendCompilationName = KotlinCompilation.MAIN_COMPILATION_NAME
outputKinds = mutableListOf(NativeOutputKind.EXECUTABLE)
@@ -1,67 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.file.FileCollectionInternal
import org.gradle.api.internal.file.FileCollectionVisitor
import org.gradle.api.internal.file.FileSystemSubset
import org.gradle.api.tasks.SourceSetOutput
import java.io.File
import java.util.concurrent.Callable
class KotlinCompilationOutput constructor(
private val project: Project,
private var resourcesDir: Any,
private var backingFileCollection: ConfigurableFileCollection = project.files() // must be a parameter since it is used in delegation
) : SourceSetOutput, ConfigurableFileCollection by backingFileCollection, FileCollectionInternal {
/* TODO report Gradle bug: cannot implement SourceSetOutput without also implementing FileCollectionInternal, there's a cast to the latter
inside the Gradle logic for resolving file collections */
override fun registerWatchPoints(p0: FileSystemSubset.Builder) = (backingFileCollection as FileCollectionInternal).registerWatchPoints(p0)
override fun visitRootElements(p0: FileCollectionVisitor) = (backingFileCollection as FileCollectionInternal).visitRootElements(p0)
private val classesDirs: FileCollection = project.files()
private val otherDirs: ConfigurableFileCollection = project.files()
init {
this.from(classesDirs)
this.from(Callable { resourcesDir })
}
override fun setResourcesDir(dirNotation: Any) {
resourcesDir = dirNotation
}
override fun setResourcesDir(dir: File): Unit = setResourcesDir(dir as Any)
override fun getDirs(): FileCollection = otherDirs
override fun isLegacyLayout(): Boolean = false
override fun getResourcesDir(): File = project.file(resourcesDir)
@Suppress("OverridingDeprecatedMember")
override fun setClassesDir(file: File?) = @Suppress("deprecation") setClassesDir(file as Any)
@Suppress("OverridingDeprecatedMember")
override fun setClassesDir(fileNotation: Any?) = throw UnsupportedOperationException("Setting classesDir is not supported, use classesDirs.")
@Suppress("OverridingDeprecatedMember")
override fun getClassesDir(): File = classesDirs.singleFile
override fun getClassesDirs(): FileCollection = classesDirs
override fun dir(args: Map<String, Any>, dir: Any?) {
val dirFileCollection = project.files(dir)
args["builtBy"]?.let { dirFileCollection.builtBy(it) }
otherDirs.from(dirFileCollection)
}
override fun dir(dirNotation: Any?): Unit = dir(emptyMap(), dirNotation)
}
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin.mpp
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationOutput
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
import java.util.concurrent.Callable
class DefaultKotlinCompilationOutput(
project: Project,
override var resourcesDir: Any
) : KotlinCompilationOutput, Callable<FileCollection> {
override val classesDirs: ConfigurableFileCollection = project.files()
override val allOutputs = project.files().apply {
from(classesDirs)
from(Callable { resourcesDir })
}
override fun call(): FileCollection = allOutputs
}
class KotlinWithJavaCompilationOutput(
internal val compilation: KotlinWithJavaCompilation
): KotlinCompilationOutput, Callable<FileCollection> {
private val javaSourceSetOutput
get() = compilation.javaSourceSet.output
override var resourcesDir: Any
get() = javaSourceSetOutput.resourcesDir
set(value) { javaSourceSetOutput.setResourcesDir(value) }
override val classesDirs: ConfigurableFileCollection =
if (isGradleVersionAtLeast(4, 0))
javaSourceSetOutput.classesDirs as ConfigurableFileCollection
else
// In Gradle < 4.0 `SourceSetOutput` does not have `classesDirs`
@Suppress("DEPRECATION")
compilation.target.project.files(Callable { javaSourceSetOutput.classesDir })
override val allOutputs: FileCollection
get() = javaSourceSetOutput
override fun call(): FileCollection = allOutputs
}
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import java.lang.IllegalArgumentException
import java.util.*
import java.util.concurrent.Callable
internal fun KotlinCompilation.composeName(prefix: String? = null, suffix: String? = null): String {
val compilationNamePart = compilationName.takeIf { it != KotlinCompilation.MAIN_COMPILATION_NAME }
@@ -71,6 +72,12 @@ abstract class AbstractKotlinCompilation(
override val kotlinSourceSets: MutableSet<KotlinSourceSet> = mutableSetOf()
override val output: KotlinCompilationOutput by lazy {
DefaultKotlinCompilationOutput(
target.project,
Callable { target.project.buildDir.resolve("processedResources/${target.targetName}/$name") })
}
open fun addSourcesToCompileTask(sourceSet: KotlinSourceSet, addAsCommonSources: Boolean) {
(target.project.tasks.getByName(compileKotlinTaskName) as AbstractKotlinCompile<*>).apply {
source(sourceSet.kotlin)
@@ -186,8 +193,7 @@ internal fun KotlinCompilation.disambiguateName(simpleName: String): String {
open class KotlinJvmCompilation(
target: KotlinTarget,
name: String,
override val output: SourceSetOutput
name: String
) : AbstractKotlinCompilationToRunnableFiles(target, name), KotlinCompilationWithResources {
override val processResourcesTaskName: String
get() = disambiguateName("processResources")
@@ -199,8 +205,7 @@ class KotlinWithJavaCompilation(
) : AbstractKotlinCompilationToRunnableFiles(target, name), KotlinCompilationWithResources {
lateinit var javaSourceSet: SourceSet
override val output: SourceSetOutput
get() = javaSourceSet.output
override val output: KotlinCompilationOutput by lazy { KotlinWithJavaCompilationOutput(this) }
override val processResourcesTaskName: String
get() = javaSourceSet.processResourcesTaskName
@@ -249,26 +254,22 @@ class KotlinWithJavaCompilation(
class KotlinJvmAndroidCompilation(
target: KotlinAndroidTarget,
name: String,
override val output: SourceSetOutput
name: String
) : AbstractKotlinCompilationToRunnableFiles(target, name)
class KotlinJsCompilation(
target: KotlinTarget,
name: String,
override val output: SourceSetOutput
name: String
) : AbstractKotlinCompilationToRunnableFiles(target, name)
class KotlinCommonCompilation(
target: KotlinTarget,
name: String,
override val output: SourceSetOutput
name: String
) : AbstractKotlinCompilation(target, name)
class KotlinNativeCompilation(
override val target: KotlinNativeTarget,
name: String,
override val output: SourceSetOutput
name: String
) : AbstractKotlinCompilation(target, name), KotlinCompilationWithResources {
private val project: Project
@@ -23,7 +23,6 @@ import org.jetbrains.kotlin.gradle.plugin.sources.applyLanguageSettingsToKotlinT
import org.jetbrains.kotlin.gradle.tasks.AndroidTasksProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -226,8 +225,15 @@ class KotlinJvmWithJavaTargetPreset(
target.compilations.getByName("test").run {
val main = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
compileDependencyFiles = project.files(main.output, project.configurations.maybeCreate(compileDependencyConfigurationName))
runtimeDependencyFiles = project.files(output, main.output, project.configurations.maybeCreate(runtimeDependencyConfigurationName))
compileDependencyFiles = project.files(
main.output.allOutputs,
project.configurations.maybeCreate(compileDependencyConfigurationName)
)
runtimeDependencyFiles = project.files(
output.allOutputs,
main.output.allOutputs,
project.configurations.maybeCreate(runtimeDependencyConfigurationName)
)
}
return target
@@ -112,7 +112,7 @@ open class KotlinNativeCompile : AbstractCompile() {
@InputFiles get() = compilation.compileDependencyFiles.filterOutPublishableInteropLibs(project)
private val friendModule: FileCollection?
get() = compilation.friendCompilation?.output
get() = compilation.friendCompilation?.output?.allOutputs
override fun getClasspath(): FileCollection = libraries
override fun setClasspath(configuration: FileCollection?) {