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 package org.jetbrains.kotlin.gradle.targets.native.internal
import org.gradle.api.DefaultTask
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.compilerRunner.KotlinNativeKlibCommonizerToolRunner 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.library.KONAN_DISTRIBUTION_KLIB_DIR
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File import java.io.File
@@ -16,73 +19,95 @@ import java.nio.file.*
import java.nio.file.attribute.* import java.nio.file.attribute.*
import java.time.* import java.time.*
import java.util.* import java.util.*
import javax.inject.Inject
internal fun runCommonizerInBulk( internal data class CommonizerTaskParams(
project: Project, @get:InputDirectory val distributionDir: File,
distributionDir: File, @get:InputDirectory val baseDestinationDir: File,
baseDestinationDir: File, @Internal val targetGroups: List<Set<KonanTarget>>,
targetGroups: List<Set<KonanTarget>>, @get:Input val kotlinVersion: String
kotlinVersion: String ) {
): List<File> { @Internal
val commandLineArguments = mutableListOf<String>() val commandLineArguments = mutableListOf<String>()
@Internal
val successPostActions = mutableListOf<() -> Unit>() val successPostActions = mutableListOf<() -> Unit>()
@Internal
val failurePostActions = mutableListOf<() -> Unit>() val failurePostActions = mutableListOf<() -> Unit>()
val destinationDirs = targetGroups.map { targets -> // It isn't best option for "up-to-date" checker (for multi project build it will be started few times)
if (targets.size == 1) { // but commonizer has inner check
// no need to commonize, just use the libraries from the distribution @get:OutputDirectories
distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR) val destinationDirs: List<File>
} else {
// need stable order of targets for consistency
val orderedTargets = targets.sortedBy { it.name }
val discriminator = buildString { // need stable order of targets for consistency
orderedTargets.joinTo(this, separator = "-") private val orderedTargetGroups = targetGroups.map { it.sortedBy { target -> target.name } }
append("-")
append(kotlinVersion.toLowerCase().base64) @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 internal const val COMMONIZER_TASK_NAME = "runCommonizer"
// that accidentally were not cleaned up before
cleanUp(baseDestinationDir, excludedDirectories = destinationDirs)
try { internal open class CommonizerTask @Inject constructor(
callCommonizerCLI(project, commandLineArguments) @Nested val params: CommonizerTaskParams
successPostActions.forEach { it() } ) : DefaultTask() {
} catch (e: Exception) {
failurePostActions.forEach { it() } @TaskAction
throw e 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>) { private fun callCommonizerCLI(project: Project, commandLineArguments: List<String>) {
@@ -6,6 +6,7 @@
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.Project
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.compilerRunner.konanHome import org.jetbrains.kotlin.compilerRunner.konanHome
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet 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.getMetadataCompilationForSourceSet
import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled
import org.jetbrains.kotlin.gradle.targets.native.internal.NativePlatformDependency.* 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.gradle.utils.SingleWarningPerBuild
import org.jetbrains.kotlin.konan.library.* import org.jetbrains.kotlin.konan.library.*
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
import java.io.File import java.io.File
import java.util.concurrent.Callable
private val Project.isNativeDependencyPropagationEnabled: Boolean private val Project.isNativeDependencyPropagationEnabled: Boolean
get() = (findProperty("kotlin.native.enableDependencyPropagation") as? String)?.toBoolean() ?: true 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() { internal fun Project.setUpKotlinNativePlatformDependencies() {
if (multiplatformExtensionOrNull == null) { if (multiplatformExtensionOrNull == null) {
// not a multiplatform project, nothing to set up // not a multiplatform project, nothing to set up
@@ -34,26 +51,19 @@ internal fun Project.setUpKotlinNativePlatformDependencies() {
} }
val kotlinVersion = getKotlinPluginVersion()!! val kotlinVersion = getKotlinPluginVersion()!!
val allowCommonizer = isAllowCommonizer(kotlinVersion)
// 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 dependencyResolver = NativePlatformDependencyResolver(this, kotlinVersion) val dependencyResolver = NativePlatformDependencyResolver(this, kotlinVersion)
findSourceSetsToAddDependencies(allowCommonizer).forEach { (sourceSet: KotlinSourceSet, sourceSetDeps: Set<NativePlatformDependency>) -> findSourceSetsToAddDependencies(allowCommonizer).forEach { (sourceSet: KotlinSourceSet, sourceSetDeps: Set<NativePlatformDependency>) ->
sourceSetDeps.forEach { sourceSetDep: NativePlatformDependency -> sourceSetDeps.forEach { sourceSetDep: NativePlatformDependency ->
dependencyResolver.addForResolve(sourceSetDep) { resolvedFiles: Set<File> -> dependencyResolver.addForResolve(sourceSetDep) { resolvedFiles: FileCollection ->
//add commonized klibs to metadata compilation //add commonized klibs to metadata compilation
if (sourceSetDep is CommonizedCommon) { if (sourceSetDep is CommonizedCommon) {
getMetadataCompilationForSourceSet(sourceSet)?.let { compilation -> getMetadataCompilationForSourceSet(sourceSet)?.let { compilation ->
compilation.compileDependencyFiles += project.files(resolvedFiles) compilation.compileDependencyFiles += resolvedFiles
} }
} }
resolvedFiles.forEach { resolvedFile -> dependencies.add(sourceSet.implementationMetadataConfigurationName, dependencies.create(resolvedFiles))
dependencies.add(sourceSet.implementationMetadataConfigurationName, dependencies.create(files(resolvedFile)))
}
} }
} }
} }
@@ -80,9 +90,9 @@ private class NativePlatformDependencyResolver(val project: Project, val kotlinV
private val distributionLibsDir = distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR) private val distributionLibsDir = distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
private var alreadyResolved = false 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) check(!alreadyResolved)
dependencies.computeIfAbsent(dependency) { mutableListOf() }.add(whenResolved) dependencies.computeIfAbsent(dependency) { mutableListOf() }.add(whenResolved)
} }
@@ -91,24 +101,32 @@ private class NativePlatformDependencyResolver(val project: Project, val kotlinV
check(!alreadyResolved) check(!alreadyResolved)
alreadyResolved = true alreadyResolved = true
// first, run commonization val targetGroups: List<Pair<CommonizedCommon, Set<KonanTarget>>> =
val targetGroups: List<Pair<CommonizedCommon, Set<KonanTarget>>> = dependencies.keys.filterIsInstance<CommonizedCommon>() dependencies.keys
.map { it to it.targets } .filterIsInstance<CommonizedCommon>()
.map { it to it.targets }
val commonizedLibsDirs: Map<CommonizedCommon, File> = val commonizerTaskParams = CommonizerTaskParams(
runCommonizerInBulk( distributionDir,
project = project, distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR).resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR),
distributionDir = distributionDir, targetGroups.map { it.second },
baseDestinationDir = distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR).resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR), kotlinVersion
targetGroups = targetGroups.map { it.second }, )
kotlinVersion = kotlinVersion
).mapIndexed { index: Int, commonizedLibsDir: File -> val commonizerTaskProvider = project.registerTask(
targetGroups[index].first to commonizedLibsDir COMMONIZER_TASK_NAME,
}.toMap() 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 // then, resolve dependencies one by one
dependencies.forEach { (dependency, actions) -> dependencies.forEach { (dependency, actions) ->
val libs = when (dependency) { val fileCollection = when (dependency) {
is OutOfDistributionCommon -> { is OutOfDistributionCommon -> {
/* stdlib, endorsed libs */ /* stdlib, endorsed libs */
var hasStdlib = false var hasStdlib = false
@@ -122,28 +140,33 @@ private class NativePlatformDependencyResolver(val project: Project, val kotlinV
if (!hasStdlib) warnAboutMissingNativeStdlib() if (!hasStdlib) warnAboutMissingNativeStdlib()
libs project.files(libs)
} }
is OutOfDistributionPlatform -> { is OutOfDistributionPlatform -> {
/* platform libs for a specific target */ /* platform libs for a specific target */
libsInPlatformDir(distributionLibsDir, dependency.target) project.files(libsInPlatformDir(distributionLibsDir, dependency.target))
} }
is CommonizedCommon -> { is CommonizedCommon -> {
/* commonized platform libs with expect declarations */ /* commonized platform libs with expect declarations */
val commonizedLibsDir = commonizedLibsDirs.getValue(dependency) val commonizedLibsDir = commonizedLibsDirs.getValue(dependency)
libsInCommonDir(commonizedLibsDir) project.files(Callable {
libsInCommonDir(commonizedLibsDir)
}).builtBy(commonizerTaskProvider)
} }
is CommonizedPlatform -> { is CommonizedPlatform -> {
/* commonized platform libs with actual declarations */ /* commonized platform libs with actual declarations */
val commonizedLibsDir = commonizedLibsDirs.getValue(dependency.common) 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) }
} }
} }