[Commonizer] More precise up-to-date checks in Gradle task

Don't annotate the whole 'distributionDir' with @InputDirectory.
The contents of the distribution directory may grow over time due to
compilation cache and new commonized libraries.

Don't annotate 'baseDestinationDir' with @InputDirectory. The base
destination directory may contain numerous subdirectories with the
libraries produced for different combinations of commonized targets.
This means that the contents of the base directory will grow over time.

Use more precise directory up-to-dateness checks:
* Annotate the directory with the original common libraries
  (i.e. 'stdlib') as @InputDirectory.
* Annotate the directory with the original platform libraries
  (all targets altogether) as @InputDirectory.
* For each combination of commonized targets mark the list of the
  directories with resulting libraries as @OutputDirectories.

^KT-40120
This commit is contained in:
Dmitriy Dolovov
2020-07-10 22:13:47 +07:00
parent d3f9f4f3e8
commit 178b5db8de
2 changed files with 130 additions and 65 deletions
@@ -9,8 +9,9 @@ import org.gradle.api.DefaultTask
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.tasks.* 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_COMMON_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.library.KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@@ -21,84 +22,152 @@ import java.time.*
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
/**
* Note: Using [resultingLibsDirs] isn't the safest option for up-to-date checker, as in multi-project build
* this may cause re-running the commonizer for the same groups several times. Hopefully, the commonizer has
* inner up-to-date check that prevents doing extra work.
*/
internal data class CommonizerSubtaskParams(
// The ordered list of unique targets.
@get:Input val orderedTargetNames: List<String>,
// Only for up-to-date checker. The directories with the resulting libs
// (common first, then platforms in the same order as in 'orderedTargetNames').
@get:OutputDirectories val resultingLibsDirs: List<File>,
@get:Internal val destinationDir: File
)
internal data class CommonizerTaskParams( internal data class CommonizerTaskParams(
@get:InputDirectory val distributionDir: File, @get:Input val kotlinVersion: String,
@get:InputDirectory val baseDestinationDir: File,
@Internal val targetGroups: List<Set<KonanTarget>>, // Only for up-to-date checker. The directory with the original common libs.
@get:Input val kotlinVersion: String @get:InputDirectory val originalCommonLibsDir: File,
// Only for up-to-date checker. The directory with the original platform libs.
@get:InputDirectory val originalPlatformLibsDir: File,
@get:Internal val baseDestinationDir: File,
@get:Nested val subtasks: List<CommonizerSubtaskParams>
) { ) {
@Internal @get:Internal
val commandLineArguments = mutableListOf<String>() lateinit var commandLineArguments: List<String>
@Internal @get:Internal
val successPostActions = mutableListOf<() -> Unit>() lateinit var successPostActions: List<() -> Unit>
@Internal @get:Internal
val failurePostActions = mutableListOf<() -> Unit>() lateinit var failurePostActions: List<() -> Unit>
// It isn't best option for "up-to-date" checker (for multi project build it will be started few times) companion object {
// but commonizer has inner check fun build(
@get:OutputDirectories kotlinVersion: String,
val destinationDirs: List<File> targetGroups: List<Set<KonanTarget>>,
distributionDir: File,
baseDestinationDir: File
): CommonizerTaskParams {
val distributionLibsDir = distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR)
// need stable order of targets for consistency val commandLineArguments = mutableListOf<String>()
private val orderedTargetGroups = targetGroups.map { it.sortedBy { target -> target.name } } val successPostActions = mutableListOf<() -> Unit>()
val failurePostActions = mutableListOf<() -> Unit>()
@Input val subtasks = targetGroups.map { targets ->
fun getTargetNames() = orderedTargetGroups.map { it.map { target -> target.name } } val orderedTargetNames = targets.map { it.name }.sorted()
if (orderedTargetNames.size == 1) {
init { // no need to commonize, just use the libraries from the distribution
destinationDirs = orderedTargetGroups.map { orderedTargets -> buildSubtask(
if (orderedTargets.size == 1) { destinationDir = distributionLibsDir,
// no need to commonize, just use the libraries from the distribution orderedTargetNames = orderedTargetNames
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
) )
} else {
val discriminator = buildString {
orderedTargetNames.joinTo(this, separator = "-")
append("-")
append(kotlinVersion.toLowerCase().base64)
}
commandLineArguments += "native-dist-commonize" val destinationDir = baseDestinationDir.resolve(discriminator)
commandLineArguments += "-distribution-path" if (!destinationDir.isDirectory) {
commandLineArguments += distributionDir.toString() val parentDir = destinationDir.parentFile
commandLineArguments += "-output-path" parentDir.mkdirs()
commandLineArguments += destinationTmpDir.toString()
commandLineArguments += "-targets"
commandLineArguments += orderedTargets.joinToString(separator = ",")
successPostActions.add { renameDirectory(destinationTmpDir, destinationDir) } val destinationTmpDir = createTempDir(
failurePostActions.add { renameToTempAndDelete(destinationTmpDir) } 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 += orderedTargetNames.joinToString(separator = ",")
successPostActions.add { renameDirectory(destinationTmpDir, destinationDir) }
failurePostActions.add { renameToTempAndDelete(destinationTmpDir) }
}
buildSubtask(
destinationDir = destinationDir,
orderedTargetNames = orderedTargetNames
)
} }
}
destinationDir return CommonizerTaskParams(
kotlinVersion = kotlinVersion,
originalCommonLibsDir = commonLibsDir(distributionLibsDir),
originalPlatformLibsDir = platformLibsDir(distributionLibsDir),
baseDestinationDir = baseDestinationDir,
subtasks = subtasks
).also {
it.commandLineArguments = commandLineArguments
it.successPostActions = successPostActions
it.failurePostActions = failurePostActions
} }
} }
private fun commonLibsDir(baseDir: File): File = baseDir.resolve(KONAN_DISTRIBUTION_COMMON_LIBS_DIR)
private fun platformLibsDir(baseDir: File): File = baseDir.resolve(KONAN_DISTRIBUTION_PLATFORM_LIBS_DIR)
private fun platformLibsDirs(baseDir: File, orderedTargetNames: List<String>): List<File> {
val platformLibsDir = platformLibsDir(baseDir)
return orderedTargetNames.map(platformLibsDir::resolve)
}
private fun resultingLibsDirs(baseDir: File, orderedTargetNames: List<String>): List<File> {
return mutableListOf<File>().apply {
this += commonLibsDir(baseDir)
this += platformLibsDirs(baseDir, orderedTargetNames)
}
}
private fun buildSubtask(destinationDir: File, orderedTargetNames: List<String>) = CommonizerSubtaskParams(
orderedTargetNames = orderedTargetNames,
resultingLibsDirs = resultingLibsDirs(destinationDir, orderedTargetNames),
destinationDir = destinationDir
)
} }
} }
internal const val COMMONIZER_TASK_NAME = "runCommonizer" internal const val COMMONIZER_TASK_NAME = "runCommonizer"
internal open class CommonizerTask @Inject constructor( internal open class CommonizerTask @Inject constructor(
@Nested val params: CommonizerTaskParams @get:Nested val params: CommonizerTaskParams
) : DefaultTask() { ) : DefaultTask() {
@TaskAction @TaskAction
fun run() { fun run() {
// first of all remove directories with unused commonized libraries plus temporary directories with commonized libraries // first of all remove directories with unused commonized libraries plus temporary directories with commonized libraries
// that accidentally were not cleaned up before // that accidentally were not cleaned up before
cleanUp(params.baseDestinationDir, excludedDirectories = params.destinationDirs) cleanUp(
baseDirectory = params.baseDestinationDir,
excludedDirectories = params.subtasks.map { it.destinationDir }
)
try { try {
callCommonizerCLI(project, params.commandLineArguments) callCommonizerCLI(project, params.commandLineArguments)
@@ -104,16 +104,13 @@ private class NativePlatformDependencyResolver(val project: Project, val kotlinV
check(!alreadyResolved) check(!alreadyResolved)
alreadyResolved = true alreadyResolved = true
val targetGroups: List<Pair<CommonizedCommon, Set<KonanTarget>>> = val targetGroups: List<CommonizedCommon> = dependencies.keys.filterIsInstance<CommonizedCommon>()
dependencies.keys
.filterIsInstance<CommonizedCommon>()
.map { it to it.targets }
val commonizerTaskParams = CommonizerTaskParams( val commonizerTaskParams = CommonizerTaskParams.build(
kotlinVersion,
targetGroups.map { it.targets },
distributionDir, distributionDir,
distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR).resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR), distributionDir.resolve(KONAN_DISTRIBUTION_KLIB_DIR).resolve(KONAN_DISTRIBUTION_COMMONIZED_LIBS_DIR)
targetGroups.map { it.second },
kotlinVersion
) )
val commonizerTaskProvider = project.registerTask( val commonizerTaskProvider = project.registerTask(
@@ -122,10 +119,9 @@ private class NativePlatformDependencyResolver(val project: Project, val kotlinV
listOf(commonizerTaskParams) listOf(commonizerTaskParams)
) {} ) {}
val commonizedLibsDirs = val commonizedLibsDirs: Map<CommonizedCommon, File> = commonizerTaskParams.subtasks.mapIndexed { index, subtask ->
commonizerTaskParams.destinationDirs targetGroups[index] to subtask.destinationDir
.mapIndexed { index: Int, commonizedLibsDir: File -> targetGroups[index].first to commonizedLibsDir } }.toMap()
.toMap()
// then, resolve dependencies one by one // then, resolve dependencies one by one
dependencies.forEach { (dependency, actions) -> dependencies.forEach { (dependency, actions) ->