Move commonizer to gradle task and attach to lazy file collection.

This commit is contained in:
Konstantin Tskhovrebov
2020-06-01 16:09:01 +03:00
parent 048559c053
commit 94a4aacd9e
2 changed files with 134 additions and 86 deletions
@@ -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<Set<KonanTarget>>,
kotlinVersion: String
): List<File> {
internal data class CommonizerTaskParams(
@get:InputDirectory val distributionDir: File,
@get:InputDirectory val baseDestinationDir: File,
@Internal val targetGroups: List<Set<KonanTarget>>,
@get:Input val kotlinVersion: String
) {
@Internal
val commandLineArguments = mutableListOf<String>()
@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<File>
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<String>) {
@@ -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<NativePlatformDependency>) ->
sourceSetDeps.forEach { sourceSetDep: NativePlatformDependency ->
dependencyResolver.addForResolve(sourceSetDep) { resolvedFiles: Set<File> ->
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<NativePlatformDependency, MutableList<(Set<File>) -> Unit>>()
private val dependencies = mutableMapOf<NativePlatformDependency, MutableList<(FileCollection) -> Unit>>()
fun addForResolve(dependency: NativePlatformDependency, whenResolved: (Set<File>) -> 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<Pair<CommonizedCommon, Set<KonanTarget>>> = dependencies.keys.filterIsInstance<CommonizedCommon>()
.map { it to it.targets }
val targetGroups: List<Pair<CommonizedCommon, Set<KonanTarget>>> =
dependencies.keys
.filterIsInstance<CommonizedCommon>()
.map { it to it.targets }
val commonizedLibsDirs: Map<CommonizedCommon, File> =
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) }
}
}