[Gradle] Evaluate commonizedOutputLibraries in a changing provider

Add `changing` provider for Configuration Cache compatibility.
This makes AbstractCInteropCommonizerTask be compatible
with Configuration Cache.

^KT-49933
This commit is contained in:
Anton Lakotka
2023-01-16 14:16:56 +01:00
committed by Space Team
parent 15f2179557
commit 458e69f698
2 changed files with 46 additions and 2 deletions
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.base64Hash import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.base64Hash
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.ensureMaxFileNameLength import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.ensureMaxFileNameLength
import org.jetbrains.kotlin.commonizer.identityString import org.jetbrains.kotlin.commonizer.identityString
import org.jetbrains.kotlin.gradle.utils.changing
import org.jetbrains.kotlin.gradle.utils.outputFilesProvider import org.jetbrains.kotlin.gradle.utils.outputFilesProvider
import java.io.File import java.io.File
@@ -34,8 +35,10 @@ internal fun AbstractCInteropCommonizerTask.outputDirectory(group: CInteropCommo
internal fun AbstractCInteropCommonizerTask.commonizedOutputLibraries(dependent: CInteropCommonizerDependent): FileCollection { internal fun AbstractCInteropCommonizerTask.commonizedOutputLibraries(dependent: CInteropCommonizerDependent): FileCollection {
return outputFilesProvider { return outputFilesProvider {
(commonizedOutputDirectory(dependent) ?: return@outputFilesProvider emptySet<File>()) val outputDirectory = commonizedOutputDirectory(dependent) ?: return@outputFilesProvider emptySet<File>()
.listFiles().orEmpty().toSet() project.providers.changing {
outputDirectory.listFiles().orEmpty().toSet()
}
} }
} }
@@ -13,7 +13,10 @@ import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.provider.SetProperty import org.gradle.api.provider.SetProperty
import org.gradle.api.provider.ValueSource
import org.gradle.api.provider.ValueSourceParameters
import java.io.File import java.io.File
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@@ -125,3 +128,41 @@ internal inline fun <reified T> Project.listProperty(noinline itemsProvider: ()
internal inline fun <reified T> Project.setProperty(noinline itemsProvider: () -> Iterable<T>): SetProperty<T> = internal inline fun <reified T> Project.setProperty(noinline itemsProvider: () -> Iterable<T>): SetProperty<T> =
objects.setProperty(T::class.java).apply { set(provider(itemsProvider)) } objects.setProperty(T::class.java).apply { set(provider(itemsProvider)) }
/**
* Changing Provider will be evaluated every time it accessed.
*
* And its producing [code] will be serilalised to Configuration Cache as is
* So that it still will be evaluated during Task Execution phase.
* It is very convenient for Configuration Cache compatibility.
*
* It is recommended to use Task Output's and map/flatMap them to other Task Inputs but in cases when TaskOutput's is not available
* as Gradle's Properties or Providers then this [changing] provider can be used.
*
* name `changing` and overall concept is borrowed from Gradle internal API [org.gradle.api.internal.provider.Providers.changing]
*
* @see org.gradle.api.internal.provider.ChangingProvider
*/
internal fun <T> ProviderFactory.changing(code: () -> T): Provider<T> {
@Suppress("UNCHECKED_CAST")
val adhocValueSourceClass = AdhocValueSource::class.java as Class<AdhocValueSource<T>>
return of(adhocValueSourceClass) { valueSourceSpec ->
valueSourceSpec.parameters {
it.producingLambda.set(code)
}
}
}
private abstract class AdhocValueSource<T> : ValueSource<T, AdhocValueSource.Parameters> {
// this interface can't be parameterized with T since it breaks internal Gradle logic
// This is why producingLambda is '() -> Any?' but not '() -> T'
interface Parameters : ValueSourceParameters {
val producingLambda: Property<() -> Any?>
}
override fun obtain(): T {
@Suppress("UNCHECKED_CAST")
return parameters.producingLambda.get().invoke() as T
}
}