diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativeKlibCommonizer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CommonizerTask.kt similarity index 57% rename from libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativeKlibCommonizer.kt rename to libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CommonizerTask.kt index 8008f841c29..b9b661912ab 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativeKlibCommonizer.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CommonizerTask.kt @@ -5,8 +5,11 @@ package org.jetbrains.kotlin.gradle.targets.native.internal +import org.gradle.api.DefaultTask import org.gradle.api.Project +import org.gradle.api.tasks.* import org.jetbrains.kotlin.compilerRunner.KotlinNativeKlibCommonizerToolRunner +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.target.KonanTarget import java.io.File @@ -16,73 +19,95 @@ import java.nio.file.* import java.nio.file.attribute.* import java.time.* import java.util.* +import javax.inject.Inject -internal fun runCommonizerInBulk( - project: Project, - distributionDir: File, - baseDestinationDir: File, - targetGroups: List>, - kotlinVersion: String -): List { +internal data class CommonizerTaskParams( + @get:InputDirectory val distributionDir: File, + @get:InputDirectory val baseDestinationDir: File, + @Internal val targetGroups: List>, + @get:Input val kotlinVersion: String +) { + @Internal val commandLineArguments = mutableListOf() + @Internal val successPostActions = mutableListOf<() -> Unit>() + + @Internal val failurePostActions = mutableListOf<() -> Unit>() - val destinationDirs = targetGroups.map { targets -> - if (targets.size == 1) { - // no need to commonize, just use the libraries from the distribution - distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR) - } else { - // need stable order of targets for consistency - val orderedTargets = targets.sortedBy { it.name } + // It isn't best option for "up-to-date" checker (for multi project build it will be started few times) + // but commonizer has inner check + @get:OutputDirectories + val destinationDirs: List - val discriminator = buildString { - orderedTargets.joinTo(this, separator = "-") - append("-") - append(kotlinVersion.toLowerCase().base64) + // need stable order of targets for consistency + private val orderedTargetGroups = targetGroups.map { it.sortedBy { target -> target.name } } + + @Input + fun getTargetNames() = orderedTargetGroups.map { it.map { target -> target.name } } + + init { + destinationDirs = orderedTargetGroups.map { orderedTargets -> + if (orderedTargets.size == 1) { + // no need to commonize, just use the libraries from the distribution + distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR) + } else { + val discriminator = buildString { + orderedTargets.joinTo(this, separator = "-") + append("-") + append(kotlinVersion.toLowerCase().base64) + } + + val destinationDir = baseDestinationDir.resolve(discriminator) + if (!destinationDir.isDirectory) { + val parentDir = destinationDir.parentFile + parentDir.mkdirs() + + val destinationTmpDir = createTempDir( + prefix = "tmp-" + destinationDir.name, + suffix = ".new", + directory = parentDir + ) + + commandLineArguments += "native-dist-commonize" + commandLineArguments += "-distribution-path" + commandLineArguments += distributionDir.toString() + commandLineArguments += "-output-path" + commandLineArguments += destinationTmpDir.toString() + commandLineArguments += "-targets" + commandLineArguments += orderedTargets.joinToString(separator = ",") + + successPostActions.add { renameDirectory(destinationTmpDir, destinationDir) } + failurePostActions.add { renameToTempAndDelete(destinationTmpDir) } + } + + destinationDir } - - val destinationDir = baseDestinationDir.resolve(discriminator) - if (!destinationDir.isDirectory) { - val parentDir = destinationDir.parentFile - parentDir.mkdirs() - - val destinationTmpDir = createTempDir( - prefix = "tmp-" + destinationDir.name, - suffix = ".new", - directory = parentDir - ) - - commandLineArguments += "native-dist-commonize" - commandLineArguments += "-distribution-path" - commandLineArguments += distributionDir.toString() - commandLineArguments += "-output-path" - commandLineArguments += destinationTmpDir.toString() - commandLineArguments += "-targets" - commandLineArguments += orderedTargets.joinToString(separator = ",") - - successPostActions.add { renameDirectory(destinationTmpDir, destinationDir) } - failurePostActions.add { renameToTempAndDelete(destinationTmpDir) } - } - - destinationDir } } +} - // first of all remove directories with unused commonized libraries plus temporary directories with commonized libraries - // that accidentally were not cleaned up before - cleanUp(baseDestinationDir, excludedDirectories = destinationDirs) +internal const val COMMONIZER_TASK_NAME = "runCommonizer" - try { - callCommonizerCLI(project, commandLineArguments) - successPostActions.forEach { it() } - } catch (e: Exception) { - failurePostActions.forEach { it() } - throw e +internal open class CommonizerTask @Inject constructor( + @Nested val params: CommonizerTaskParams +) : DefaultTask() { + + @TaskAction + fun run() { + // first of all remove directories with unused commonized libraries plus temporary directories with commonized libraries + // that accidentally were not cleaned up before + cleanUp(params.baseDestinationDir, excludedDirectories = params.destinationDirs) + + try { + callCommonizerCLI(project, params.commandLineArguments) + params.successPostActions.forEach { it() } + } catch (e: Exception) { + params.failurePostActions.forEach { it() } + throw e + } } - - return destinationDirs } private fun callCommonizerCLI(project: Project, commandLineArguments: List) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt index b13b2e16e0e..06761e4dd15 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/KotlinNativePlatformDependencies.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.gradle.targets.native.internal import org.gradle.api.Project +import org.gradle.api.file.FileCollection import org.jetbrains.kotlin.compilerRunner.konanHome import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet @@ -18,15 +19,31 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation import org.jetbrains.kotlin.gradle.targets.metadata.getMetadataCompilationForSourceSet import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import org.jetbrains.kotlin.gradle.targets.native.internal.NativePlatformDependency.* +import org.jetbrains.kotlin.gradle.tasks.registerTask import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild import org.jetbrains.kotlin.konan.library.* import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.utils.addToStdlib.flattenTo import java.io.File +import java.util.concurrent.Callable private val Project.isNativeDependencyPropagationEnabled: Boolean get() = (findProperty("kotlin.native.enableDependencyPropagation") as? String)?.toBoolean() ?: true +//for reflection call from KotlinCommonizerModelBuilder +@JvmOverloads +@JvmName("isAllowCommonizer") +internal fun Project.isAllowCommonizer( + kotlinVersion: String = getKotlinPluginVersion()!! +): Boolean { + multiplatformExtensionOrNull ?: return false + + //register commonizer only for 1.4+, only for HMPP projects + return compareVersionNumbers(kotlinVersion, "1.4") >= 0 + && isKotlinGranularMetadataEnabled + && !isNativeDependencyPropagationEnabled // temporary fix: turn on commonizer only when native deps propagation is disabled +} + internal fun Project.setUpKotlinNativePlatformDependencies() { if (multiplatformExtensionOrNull == null) { // not a multiplatform project, nothing to set up @@ -34,26 +51,19 @@ internal fun Project.setUpKotlinNativePlatformDependencies() { } val kotlinVersion = getKotlinPluginVersion()!! - - // run commonizer only for 1.4+, only for HMPP projects and only on IDE sync - val allowCommonizer = compareVersionNumbers(kotlinVersion, "1.4") >= 0 - && isKotlinGranularMetadataEnabled - && !isNativeDependencyPropagationEnabled // temporary fix: turn on commonizer only when native deps propagation is disabled - + val allowCommonizer = isAllowCommonizer(kotlinVersion) val dependencyResolver = NativePlatformDependencyResolver(this, kotlinVersion) findSourceSetsToAddDependencies(allowCommonizer).forEach { (sourceSet: KotlinSourceSet, sourceSetDeps: Set) -> sourceSetDeps.forEach { sourceSetDep: NativePlatformDependency -> - dependencyResolver.addForResolve(sourceSetDep) { resolvedFiles: Set -> + dependencyResolver.addForResolve(sourceSetDep) { resolvedFiles: FileCollection -> //add commonized klibs to metadata compilation if (sourceSetDep is CommonizedCommon) { getMetadataCompilationForSourceSet(sourceSet)?.let { compilation -> - compilation.compileDependencyFiles += project.files(resolvedFiles) + compilation.compileDependencyFiles += resolvedFiles } } - resolvedFiles.forEach { resolvedFile -> - dependencies.add(sourceSet.implementationMetadataConfigurationName, dependencies.create(files(resolvedFile))) - } + dependencies.add(sourceSet.implementationMetadataConfigurationName, dependencies.create(resolvedFiles)) } } } @@ -80,9 +90,9 @@ private class NativePlatformDependencyResolver(val project: Project, val kotlinV private val distributionLibsDir = distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR) private var alreadyResolved = false - private val dependencies = mutableMapOf) -> Unit>>() + private val dependencies = mutableMapOf Unit>>() - fun addForResolve(dependency: NativePlatformDependency, whenResolved: (Set) -> Unit) { + fun addForResolve(dependency: NativePlatformDependency, whenResolved: (FileCollection) -> Unit) { check(!alreadyResolved) dependencies.computeIfAbsent(dependency) { mutableListOf() }.add(whenResolved) } @@ -91,24 +101,32 @@ private class NativePlatformDependencyResolver(val project: Project, val kotlinV check(!alreadyResolved) alreadyResolved = true - // first, run commonization - val targetGroups: List>> = dependencies.keys.filterIsInstance() - .map { it to it.targets } + val targetGroups: List>> = + dependencies.keys + .filterIsInstance() + .map { it to it.targets } - val commonizedLibsDirs: Map = - runCommonizerInBulk( - project = project, - distributionDir = distributionDir, - baseDestinationDir = distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR).resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR), - targetGroups = targetGroups.map { it.second }, - kotlinVersion = kotlinVersion - ).mapIndexed { index: Int, commonizedLibsDir: File -> - targetGroups[index].first to commonizedLibsDir - }.toMap() + val commonizerTaskParams = CommonizerTaskParams( + distributionDir, + distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR).resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR), + targetGroups.map { it.second }, + kotlinVersion + ) + + val commonizerTaskProvider = project.registerTask( + COMMONIZER_TASK_NAME, + CommonizerTask::class.java, + listOf(commonizerTaskParams) + ) {} + + val commonizedLibsDirs = + commonizerTaskParams.destinationDirs + .mapIndexed { index: Int, commonizedLibsDir: File -> targetGroups[index].first to commonizedLibsDir } + .toMap() // then, resolve dependencies one by one dependencies.forEach { (dependency, actions) -> - val libs = when (dependency) { + val fileCollection = when (dependency) { is OutOfDistributionCommon -> { /* stdlib, endorsed libs */ var hasStdlib = false @@ -122,28 +140,33 @@ private class NativePlatformDependencyResolver(val project: Project, val kotlinV if (!hasStdlib) warnAboutMissingNativeStdlib() - libs + project.files(libs) } is OutOfDistributionPlatform -> { /* platform libs for a specific target */ - libsInPlatformDir(distributionLibsDir, dependency.target) + project.files(libsInPlatformDir(distributionLibsDir, dependency.target)) } is CommonizedCommon -> { /* commonized platform libs with expect declarations */ val commonizedLibsDir = commonizedLibsDirs.getValue(dependency) - libsInCommonDir(commonizedLibsDir) + project.files(Callable { + libsInCommonDir(commonizedLibsDir) + }).builtBy(commonizerTaskProvider) + } is CommonizedPlatform -> { /* commonized platform libs with actual declarations */ val commonizedLibsDir = commonizedLibsDirs.getValue(dependency.common) - libsInPlatformDir(commonizedLibsDir, dependency.target) + libsInCommonDir(commonizedLibsDir) + project.files(Callable { + libsInPlatformDir(commonizedLibsDir, dependency.target) + libsInCommonDir(commonizedLibsDir) + }).builtBy(commonizerTaskProvider) } } - actions.forEach { it(libs) } + actions.forEach { it(fileCollection) } } }