[Gradle][MPP] Mark 'commonizeNativeDistribution' as UP-TO-DATE when cache hits
^KT-52632 Verification Pending
This commit is contained in:
committed by
Space
parent
02601745a9
commit
3c8a4c5896
+2
-1
@@ -39,7 +39,7 @@ class CommonizerIT : BaseGradleIT() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
build("commonize", "-Pkotlin.mpp.enableNativeDistributionCommonizationCache=true") {
|
build("commonize", "-Pkotlin.mpp.enableNativeDistributionCommonizationCache=true") {
|
||||||
assertTasksExecuted(":commonizeNativeDistribution")
|
assertTasksUpToDate(":commonizeNativeDistribution")
|
||||||
assertNativeDistributionCommonizationCacheHit()
|
assertNativeDistributionCommonizationCacheHit()
|
||||||
assertContains("Native Distribution Commonization: All available targets are commonized already")
|
assertContains("Native Distribution Commonization: All available targets are commonized already")
|
||||||
assertContains("Native Distribution Commonization: Lock acquired")
|
assertContains("Native Distribution Commonization: Lock acquired")
|
||||||
@@ -552,6 +552,7 @@ class CommonizerIT : BaseGradleIT() {
|
|||||||
build(":assemble", options = testSourceSetsDependingOnMainParameterOption) {
|
build(":assemble", options = testSourceSetsDependingOnMainParameterOption) {
|
||||||
assertTestSourceSetsDependingOnMainParameter()
|
assertTestSourceSetsDependingOnMainParameter()
|
||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
|
assertTasksUpToDate(":commonizeNativeDistribution")
|
||||||
assertContains("Native Distribution Commonization: Cache hit")
|
assertContains("Native Distribution Commonization: Cache hit")
|
||||||
assertTasksUpToDate(":commonizeCInterop")
|
assertTasksUpToDate(":commonizeCInterop")
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -129,7 +129,7 @@ internal val Project.cleanNativeDistributionCommonizerTask: TaskProvider<Delete>
|
|||||||
configureTask = {
|
configureTask = {
|
||||||
group = "interop"
|
group = "interop"
|
||||||
description = "Deletes all previously commonized klib's from the Kotlin/Native distribution"
|
description = "Deletes all previously commonized klib's from the Kotlin/Native distribution"
|
||||||
delete(commonizeNativeDistributionTask.map { it.getRootOutputDirectory() })
|
delete(commonizeNativeDistributionTask.map { it.rootOutputDirectory })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -81,7 +81,7 @@ private fun Project.getOriginalPlatformLibrariesFor(target: LeafCommonizerTarget
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun NativeDistributionCommonizerTask.getCommonizedPlatformLibrariesFor(target: SharedCommonizerTarget): FileCollection {
|
private fun NativeDistributionCommonizerTask.getCommonizedPlatformLibrariesFor(target: SharedCommonizerTarget): FileCollection {
|
||||||
val targetOutputDirectory = CommonizerOutputFileLayout.resolveCommonizedDirectory(getRootOutputDirectory(), target)
|
val targetOutputDirectory = CommonizerOutputFileLayout.resolveCommonizedDirectory(rootOutputDirectory, target)
|
||||||
return project.filesProvider { targetOutputDirectory.listLibraryFiles() }.builtBy(this)
|
return project.filesProvider { targetOutputDirectory.listLibraryFiles() }.builtBy(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+94
-52
@@ -7,59 +7,77 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.gradle.targets.native.internal
|
package org.jetbrains.kotlin.gradle.targets.native.internal
|
||||||
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.logging.Logger
|
||||||
import org.jetbrains.kotlin.commonizer.*
|
import org.jetbrains.kotlin.commonizer.*
|
||||||
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.resolveCommonizedDirectory
|
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.resolveCommonizedDirectory
|
||||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
internal val Project.isNativeDistributionCommonizationCacheEnabled: Boolean
|
import kotlin.concurrent.withLock
|
||||||
get() = PropertiesProvider(this).enableNativeDistributionCommonizationCache
|
|
||||||
|
|
||||||
internal class NativeDistributionCommonizationCache(
|
internal class NativeDistributionCommonizationCache(
|
||||||
private val project: Project,
|
private val logger: Logger,
|
||||||
|
private val isCachingEnabled: Boolean,
|
||||||
private val commonizer: NativeDistributionCommonizer
|
private val commonizer: NativeDistributionCommonizer
|
||||||
) : NativeDistributionCommonizer {
|
) : NativeDistributionCommonizer {
|
||||||
|
|
||||||
|
fun isUpToDate(
|
||||||
|
konanHome: File, outputDirectory: File, outputTargets: Set<SharedCommonizerTarget>
|
||||||
|
): Boolean = lock.withLock(outputDirectory) {
|
||||||
|
todoTargets(konanHome, outputDirectory, outputTargets)
|
||||||
|
}.isEmpty()
|
||||||
|
|
||||||
override fun commonizeNativeDistribution(
|
override fun commonizeNativeDistribution(
|
||||||
konanHome: File,
|
konanHome: File,
|
||||||
outputDirectory: File,
|
outputDirectory: File,
|
||||||
outputTargets: Set<SharedCommonizerTarget>,
|
outputTargets: Set<SharedCommonizerTarget>,
|
||||||
logLevel: CommonizerLogLevel,
|
logLevel: CommonizerLogLevel,
|
||||||
additionalSettings: List<AdditionalCommonizerSetting<*>>,
|
additionalSettings: List<AdditionalCommonizerSetting<*>>,
|
||||||
) {
|
): Unit = lock.withLock(outputDirectory) {
|
||||||
if (!project.isNativeDistributionCommonizationCacheEnabled) {
|
val todoOutputTargets = todoTargets(konanHome, outputDirectory, outputTargets)
|
||||||
|
if (todoOutputTargets.isEmpty()) return@withLock
|
||||||
|
|
||||||
|
/* Invoke commonizer with only 'to do' targets */
|
||||||
|
commonizer.commonizeNativeDistribution(
|
||||||
|
konanHome, outputDirectory, todoOutputTargets, logLevel, additionalSettings
|
||||||
|
)
|
||||||
|
|
||||||
|
/* Mark targets as successfully commonized */
|
||||||
|
todoOutputTargets
|
||||||
|
.map { outputTarget -> resolveCommonizedDirectory(outputDirectory, outputTarget) }
|
||||||
|
.filter { commonizedDirectory -> commonizedDirectory.isDirectory }
|
||||||
|
.forEach { commonizedDirectory -> commonizedDirectory.resolve(".success").createNewFile() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun todoTargets(
|
||||||
|
konanHome: File, outputDirectory: File, outputTargets: Set<SharedCommonizerTarget>
|
||||||
|
): Set<SharedCommonizerTarget> {
|
||||||
|
lock.checkLocked(outputDirectory)
|
||||||
|
logInfo("Calculating cache state for $outputTargets")
|
||||||
|
|
||||||
|
if (!isCachingEnabled) {
|
||||||
logInfo("Cache disabled")
|
logInfo("Cache disabled")
|
||||||
|
return if (isMissingPlatformLibraries(konanHome, outputTargets)) return emptySet()
|
||||||
|
else outputTargets
|
||||||
}
|
}
|
||||||
|
|
||||||
withLock(outputDirectory) {
|
val cachedOutputTargets = outputTargets
|
||||||
val cachedOutputTargets = outputTargets
|
.filter { outputTarget -> isCached(resolveCommonizedDirectory(outputDirectory, outputTarget)) }
|
||||||
.filter { outputTarget -> isCached(resolveCommonizedDirectory(outputDirectory, outputTarget)) }
|
.onEach { outputTarget -> logInfo("Cache hit: $outputTarget already commonized") }
|
||||||
.onEach { outputTarget -> logInfo("Cache hit: $outputTarget already commonized") }
|
.toSet()
|
||||||
.toSet()
|
|
||||||
|
|
||||||
val enqueuedOutputTargets = if (project.isNativeDistributionCommonizationCacheEnabled) outputTargets - cachedOutputTargets
|
val todoOutputTargets = outputTargets - cachedOutputTargets
|
||||||
else outputTargets
|
|
||||||
|
|
||||||
if (canReturnFast(konanHome, enqueuedOutputTargets)) {
|
if (todoOutputTargets.isEmpty() || isMissingPlatformLibraries(konanHome, todoOutputTargets)) {
|
||||||
logInfo("All available targets are commonized already - Nothing to do")
|
logInfo("All available targets are commonized already - Nothing to do")
|
||||||
return
|
if (todoOutputTargets.isNotEmpty()) {
|
||||||
|
logInfo("Platforms cannot be commonized, because of missing platform libraries: $todoOutputTargets")
|
||||||
}
|
}
|
||||||
|
|
||||||
enqueuedOutputTargets
|
return emptySet()
|
||||||
.map { outputTarget -> resolveCommonizedDirectory(outputDirectory, outputTarget) }
|
|
||||||
.forEach { commonizedDirectory -> if (commonizedDirectory.exists()) commonizedDirectory.deleteRecursively() }
|
|
||||||
|
|
||||||
commonizer.commonizeNativeDistribution(
|
|
||||||
konanHome, outputDirectory, enqueuedOutputTargets, logLevel, additionalSettings
|
|
||||||
)
|
|
||||||
|
|
||||||
enqueuedOutputTargets
|
|
||||||
.map { outputTarget -> resolveCommonizedDirectory(outputDirectory, outputTarget) }
|
|
||||||
.filter { commonizedDirectory -> commonizedDirectory.isDirectory }
|
|
||||||
.forEach { commonizedDirectory -> commonizedDirectory.resolve(".success").createNewFile() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return todoOutputTargets
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isCached(directory: File): Boolean {
|
private fun isCached(directory: File): Boolean {
|
||||||
@@ -67,11 +85,9 @@ internal class NativeDistributionCommonizationCache(
|
|||||||
return successMarkerFile.isFile
|
return successMarkerFile.isFile
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun canReturnFast(
|
private fun isMissingPlatformLibraries(
|
||||||
konanHome: File, missingOutputTargets: Set<CommonizerTarget>
|
konanHome: File, missingOutputTargets: Set<CommonizerTarget>
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (missingOutputTargets.isEmpty()) return true
|
|
||||||
|
|
||||||
// If all platform lib dirs are missing, we can also return fast from the cache without invoking
|
// If all platform lib dirs are missing, we can also return fast from the cache without invoking
|
||||||
// the commonizer
|
// the commonizer
|
||||||
return missingOutputTargets.allLeaves()
|
return missingOutputTargets.allLeaves()
|
||||||
@@ -80,27 +96,53 @@ internal class NativeDistributionCommonizationCache(
|
|||||||
.none { platformLibsDir -> platformLibsDir.exists() }
|
.none { platformLibsDir -> platformLibsDir.exists() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <T> withLock(outputDirectory: File, action: () -> T): T {
|
/**
|
||||||
outputDirectory.mkdirs()
|
* Re-entrant lock implementation capable of locking a given output directory
|
||||||
val lockfile = outputDirectory.resolve(".lock")
|
* even between multiple process (Gradle Daemons)
|
||||||
logInfo("Acquire lock: ${lockfile.path} ...")
|
*/
|
||||||
FileOutputStream(outputDirectory.resolve(".lock")).use { stream ->
|
private val lock = object {
|
||||||
val lock = stream.channel.lock()
|
private val reentrantLock = ReentrantLock()
|
||||||
assert(lock.isValid)
|
private val lockedOutputDirectories = mutableSetOf<File>()
|
||||||
return try {
|
|
||||||
logInfo("Lock acquired: ${lockfile.path}")
|
fun <T> withLock(outputDirectory: File, action: () -> T): T {
|
||||||
action()
|
/* Enter intra-process wide lock */
|
||||||
} finally {
|
reentrantLock.withLock {
|
||||||
lock.release()
|
if (outputDirectory in lockedOutputDirectories) {
|
||||||
logInfo("Lock released: ${lockfile.path}")
|
/* Already acquired this directory and re-entered: We can just execute the action */
|
||||||
|
return action()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lock output directory inter-process wide */
|
||||||
|
outputDirectory.mkdirs()
|
||||||
|
val lockfile = outputDirectory.resolve(".lock")
|
||||||
|
logInfo("Acquire lock: ${lockfile.path} ...")
|
||||||
|
FileOutputStream(outputDirectory.resolve(".lock")).use { stream ->
|
||||||
|
val lock = stream.channel.lock()
|
||||||
|
assert(lock.isValid)
|
||||||
|
return try {
|
||||||
|
logInfo("Lock acquired: ${lockfile.path}")
|
||||||
|
lockedOutputDirectories.add(outputDirectory)
|
||||||
|
action()
|
||||||
|
} finally {
|
||||||
|
lockedOutputDirectories.remove(outputDirectory)
|
||||||
|
lock.release()
|
||||||
|
logInfo("Lock released: ${lockfile.path}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkLocked(outputDirectory: File) {
|
||||||
|
check(reentrantLock.isHeldByCurrentThread) {
|
||||||
|
"Expected lock to be held by current thread ${Thread.currentThread().name}"
|
||||||
|
}
|
||||||
|
|
||||||
|
check(outputDirectory in lockedOutputDirectories) {
|
||||||
|
"Expected $outputDirectory to be locked. Locked directories: $lockedOutputDirectories"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logInfo(message: String) = project.logger.info("${Logging.prefix}: $message")
|
private fun logInfo(message: String) =
|
||||||
|
logger.info("Native Distribution Commonization: $message")
|
||||||
private object Logging {
|
|
||||||
const val prefix = "Native Distribution Commonization"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-22
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.gradle.targets.native.internal
|
|||||||
|
|
||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.DefaultTask
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.tasks.Input
|
|
||||||
import org.gradle.api.tasks.Internal
|
import org.gradle.api.tasks.Internal
|
||||||
import org.gradle.api.tasks.TaskAction
|
import org.gradle.api.tasks.TaskAction
|
||||||
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
|
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
|
||||||
@@ -16,6 +15,7 @@ import org.jetbrains.kotlin.compilerRunner.KotlinNativeCommonizerToolRunner
|
|||||||
import org.jetbrains.kotlin.compilerRunner.konanHome
|
import org.jetbrains.kotlin.compilerRunner.konanHome
|
||||||
import org.jetbrains.kotlin.compilerRunner.registerCommonizerClasspathConfigurationIfNecessary
|
import org.jetbrains.kotlin.compilerRunner.registerCommonizerClasspathConfigurationIfNecessary
|
||||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
|
||||||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||||
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR
|
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR
|
||||||
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_KLIB_DIR
|
import org.jetbrains.kotlin.konan.library.KONAN_DISTRIBUTION_KLIB_DIR
|
||||||
@@ -26,34 +26,32 @@ internal open class NativeDistributionCommonizerTask : DefaultTask() {
|
|||||||
|
|
||||||
private val konanHome = project.file(project.konanHome)
|
private val konanHome = project.file(project.konanHome)
|
||||||
|
|
||||||
@get:Input
|
private val commonizerTargets: Set<SharedCommonizerTarget> by lazy {
|
||||||
internal val commonizerTargets: Set<SharedCommonizerTarget>
|
project.collectAllSharedCommonizerTargetsFromBuild()
|
||||||
get() = project.collectAllSharedCommonizerTargetsFromBuild()
|
}
|
||||||
|
|
||||||
|
private val commonizer by lazy {
|
||||||
|
NativeDistributionCommonizationCache(
|
||||||
|
logger = project.logger,
|
||||||
|
isCachingEnabled = project.kotlinPropertiesProvider.enableNativeDistributionCommonizationCache,
|
||||||
|
commonizer = GradleCliCommonizer(KotlinNativeCommonizerToolRunner(project))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@get:Internal
|
@get:Internal
|
||||||
internal val commonizerRunner = KotlinNativeCommonizerToolRunner(project)
|
internal val rootOutputDirectory: File by lazy {
|
||||||
|
project.file(konanHome)
|
||||||
@get:Input
|
|
||||||
@Suppress("unused") // Only for up-to-date checker.
|
|
||||||
internal val commonizerJvmArgs: List<String>
|
|
||||||
get() = commonizerRunner.getCustomJvmArgs()
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
internal fun getRootOutputDirectory(): File {
|
|
||||||
val kotlinVersion = project.getKotlinPluginVersion()
|
|
||||||
|
|
||||||
return project.file(konanHome)
|
|
||||||
.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
|
.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
|
||||||
.resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR)
|
.resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR)
|
||||||
.resolve(urlEncode(kotlinVersion))
|
.resolve(URLEncoder.encode(project.getKotlinPluginVersion(), Charsets.UTF_8.name()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
protected fun run() {
|
protected fun run() {
|
||||||
NativeDistributionCommonizationCache(project, GradleCliCommonizer(commonizerRunner)).commonizeNativeDistribution(
|
commonizer.commonizeNativeDistribution(
|
||||||
konanHome = konanHome,
|
konanHome = konanHome,
|
||||||
outputDirectory = getRootOutputDirectory(),
|
outputDirectory = rootOutputDirectory,
|
||||||
outputTargets = project.collectAllSharedCommonizerTargetsFromBuild(),
|
outputTargets = commonizerTargets,
|
||||||
logLevel = project.commonizerLogLevel,
|
logLevel = project.commonizerLogLevel,
|
||||||
additionalSettings = project.additionalCommonizerSettings,
|
additionalSettings = project.additionalCommonizerSettings,
|
||||||
)
|
)
|
||||||
@@ -61,6 +59,9 @@ internal open class NativeDistributionCommonizerTask : DefaultTask() {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
project.registerCommonizerClasspathConfigurationIfNecessary()
|
project.registerCommonizerClasspathConfigurationIfNecessary()
|
||||||
|
outputs.upToDateWhen {
|
||||||
|
commonizer.isUpToDate(konanHome, rootOutputDirectory, commonizerTargets)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,5 +75,3 @@ private fun Project.collectAllSharedCommonizerTargetsFromProject(): Set<SharedCo
|
|||||||
.filterIsInstance<SharedCommonizerTarget>()
|
.filterIsInstance<SharedCommonizerTarget>()
|
||||||
.toSet()
|
.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun urlEncode(value: String): String = URLEncoder.encode(value, Charsets.UTF_8.name())
|
|
||||||
|
|||||||
Reference in New Issue
Block a user