KTIJ-25563: Add upToDateWhen condition to CInterop task outputs

This commit is contained in:
Konstantin Tskhovrebov
2023-09-05 17:28:25 +02:00
committed by Space Team
parent 503c1a2eb6
commit b672ba8eaf
3 changed files with 33 additions and 27 deletions
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.gradle.native
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.konan.file.File
import org.junit.jupiter.api.DisplayName
import kotlin.io.path.writeText
@@ -35,24 +34,10 @@ class CInteropIdeaSyncIT : KGPBaseTest() {
assertOutputContains(ideaSyncWarningMessage)
}
/*
The implementation before fixing KT-52243 considered the cinterop task as *not* up-to-date
when it was previously running in the IDE and therefore failing leniently. It would have always tried to re-run
this task to anticipate untracked environmental changes.
This cannot be easily implemented whilst also fixing KT-52243, which is more desirable.
A new mechanism for 'run tasks at import' leniency is proposed (using --continue), which is supposed to replace
the special cinterop mechanism.
https://youtrack.jetbrains.com/issue/KT-52243/
https://github.com/JetBrains/kotlin/pull/4812#issuecomment-1117287222
*/
runCatching {
/* Task is not considered up-to-date after lenient failure */
build("commonize", buildOptions = ideaSyncBuildOptions) {
assertTasksExecuted(interopTaskName)
assertOutputContains(ideaSyncWarningMessage)
}
/* Task is not considered up-to-date after lenient failure */
build("commonize", buildOptions = ideaSyncBuildOptions) {
assertTasksExecuted(interopTaskName)
assertOutputContains(ideaSyncWarningMessage)
}
/* Remove noise that causes failure */
@@ -5,48 +5,55 @@
package org.jetbrains.kotlin.gradle.targets.native.tasks
import org.gradle.api.Task
import org.jetbrains.kotlin.build.report.metrics.BuildMetrics
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
import org.jetbrains.kotlin.build.report.metrics.GradleBuildPerformanceMetric
import org.jetbrains.kotlin.build.report.metrics.GradleBuildTime
import org.jetbrains.kotlin.compilerRunner.KotlinNativeCInteropRunner
import org.jetbrains.kotlin.compilerRunner.KotlinNativeToolRunner
import org.jetbrains.kotlin.compilerRunner.KotlinToolRunner
import org.jetbrains.kotlin.gradle.report.GradleBuildMetricsReporter
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
import org.jetbrains.kotlin.gradle.utils.listFilesOrEmpty
internal fun KotlinNativeCInteropRunner.Companion.createExecutionContext(
task: Task,
task: CInteropProcess,
isInIdeaSync: Boolean,
runnerSettings: KotlinNativeToolRunner.Settings,
gradleExecutionContext: KotlinToolRunner.GradleExecutionContext,
metricsReporter: BuildMetricsReporter<GradleBuildTime, GradleBuildPerformanceMetric>
): KotlinNativeCInteropRunner.ExecutionContext {
return if (isInIdeaSync) IdeaSyncKotlinNativeCInteropRunnerExecutionContext(runnerSettings, gradleExecutionContext, task, metricsReporter)
else DefaultKotlinNativeCInteropRunnerExecutionContext(runnerSettings, gradleExecutionContext, metricsReporter)
else DefaultKotlinNativeCInteropRunnerExecutionContext(runnerSettings, gradleExecutionContext, task, metricsReporter)
}
private class DefaultKotlinNativeCInteropRunnerExecutionContext(
override val runnerSettings: KotlinNativeToolRunner.Settings,
override val gradleExecutionContext: KotlinToolRunner.GradleExecutionContext,
private val task: CInteropProcess,
override val metricsReporter: BuildMetricsReporter<GradleBuildTime, GradleBuildPerformanceMetric>
) : KotlinNativeCInteropRunner.ExecutionContext {
override fun runWithContext(action: () -> Unit) = action()
override fun runWithContext(action: () -> Unit) {
task.errorFileProvider.get().delete()
action()
}
}
private class IdeaSyncKotlinNativeCInteropRunnerExecutionContext(
override val runnerSettings: KotlinNativeToolRunner.Settings,
override val gradleExecutionContext: KotlinToolRunner.GradleExecutionContext,
private val task: Task,
private val task: CInteropProcess,
override val metricsReporter: BuildMetricsReporter<GradleBuildTime, GradleBuildPerformanceMetric>
) : KotlinNativeCInteropRunner.ExecutionContext {
override fun runWithContext(action: () -> Unit) {
val errorFile = task.errorFileProvider.get()
errorFile.delete()
try {
action()
} catch (t: Throwable) {
task.logger.warn("Warning: Failed to generate cinterop for ${task.path}: ${t.message ?: ""}", t)
val errorText = "Warning: Failed to generate cinterop for ${task.path}: ${t.message ?: ""}"
task.logger.warn(errorText, t)
task.outputs.files.forEach { file -> file.deleteRecursively() }
errorFile.writeText(errorText)
}
}
}
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.gradle.targets.native.tasks.*
import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.gradle.utils.GradleLoggerAdapter
import org.jetbrains.kotlin.gradle.utils.listFilesOrEmpty
import org.jetbrains.kotlin.incremental.deleteDirectoryContents
import org.jetbrains.kotlin.ir.linkage.partial.PartialLinkageMode
import org.jetbrains.kotlin.konan.library.KLIB_INTEROP_IR_PROVIDER_IDENTIFIER
import org.jetbrains.kotlin.konan.properties.saveToFile
@@ -1086,6 +1087,19 @@ abstract class CInteropProcess @Inject internal constructor(params: Params) :
@OutputFile
val outputFileProvider: Provider<File> = project.provider { destinationDir.get().resolve(outputFileName) }
//Error file will be written only for errors during a project sync because for the sync task mustn't fail
//see: org.jetbrains.kotlin.gradle.targets.native.tasks.IdeaSyncKotlinNativeCInteropRunnerExecutionContext
@get:OutputFile
internal val errorFileProvider: Provider<File> = project.provider { destinationDir.get().resolve("cinterop_error.out") }
init {
//KTIJ-25563:
//Failed CInterop task is successful if it was run during import to have properly imported project.
//But successful task is up-to-date for next invocations.
//We have to check up-to-date-ness only if CInterop didn't generate an error file
outputs.upToDateWhen { !errorFileProvider.get().exists() }
}
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
@get:NormalizeLineEndings