From 508235ce08b06b4e9d9e16255c84aa0d4d7c9a01 Mon Sep 17 00:00:00 2001 From: Anton Lakotka Date: Mon, 27 Jun 2022 12:45:05 +0200 Subject: [PATCH] [Gradle] Make Kotlin Native tool runners GCC friendly Remove project usage with either GradleExecutionContext or associated Settings class. Set of Setting classes are meant to be created upon configuration phase. Therefore, they have to be easily serializable without heavy payload. They also annotated for Input/Output checks. ^KT-43293 --- .../kotlin/compilerRunner/KotlinToolRunner.kt | 7 +- .../compilerRunner/nativeToolRunners.kt | 122 ++++++++++++++---- 2 files changed, 100 insertions(+), 29 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt index 41effab1823..2738fc889d6 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/KotlinToolRunner.kt @@ -34,6 +34,11 @@ abstract class KotlinToolRunner( val logger: Logger, ) { companion object { + /** + * Executing [KotlinToolRunner] during Gradle Configuration Phase is undesired behaviour. + * Currently only [KotlinNativeLibraryGenerationRunner] used in this way. + * It should be fixed as part of KT-51255 + */ @Deprecated( "Building execution context from Project object isn't compatible with Gradle Configuration Cache", ReplaceWith("fromTaskContext()"), @@ -41,7 +46,7 @@ abstract class KotlinToolRunner( ) fun fromProject(project: Project) = GradleExecutionContext( filesProvider = project::files, - javaexec = project::javaexec, + javaexec = { spec -> project.javaexec(spec) }, // project::javaexec won't work due to different Classloaders logger = project.logger ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt index d1b70a33ad4..a0f0867e285 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/nativeToolRunners.kt @@ -6,6 +6,10 @@ package org.jetbrains.kotlin.compilerRunner import org.gradle.api.Project +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Nested import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider @@ -18,6 +22,7 @@ import org.jetbrains.kotlin.konan.properties.resolvablePropertyString import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.util.DependencyDirectories +import java.io.File import java.nio.file.Files import java.util.* @@ -45,10 +50,46 @@ internal fun Project.getKonanCacheKind(target: KonanTarget): NativeCacheKind { } } +private val Project.kotlinNativeCompilerJar: String + get() = if (nativeUseEmbeddableCompilerJar) + "$konanHome/konan/lib/kotlin-native-compiler-embeddable.jar" + else + "$konanHome/konan/lib/kotlin-native.jar" + internal abstract class KotlinNativeToolRunner( protected val toolName: String, - project: Project -) : KotlinToolRunner(project) { + private val settings: Settings, + executionContext: GradleExecutionContext +) : KotlinToolRunner(executionContext) { + + class Settings( + @get:Input + val konanVersion: CompilerVersion, + @get:Input + val konanHome: String, + @get:InputFile + val konanPropertiesFile: File, + @get:Input + val jvmArgs: List, + classpathProvider: () -> Set, + ) { + @get:Classpath + val classpath by lazy(classpathProvider) + + constructor(project: Project) : this( + konanVersion = project.konanVersion, + konanHome = project.konanHome, + konanPropertiesFile = project.file("${project.konanHome}/konan/konan.properties"), + jvmArgs = project.jvmArgs, + classpathProvider = { + project.files( + project.kotlinNativeCompilerJar, + "${project.konanHome}/konan/lib/trove4j.jar" + ).files + } + ) + } + final override val displayName get() = toolName final override val mainClass get() = "org.jetbrains.kotlin.cli.utilities.MainKt" @@ -65,29 +106,18 @@ internal abstract class KotlinNativeToolRunner( final override val execSystemProperties by lazy { // Still set konan.home for versions prior to 1.4-M3. - val konanHomeRequired = project.konanVersion.let { + val konanHomeRequired = settings.konanVersion.let { !it.isAtLeast(1, 4, 0) || it.toString(showMeta = false, showBuild = false) in listOf("1.4-M1", "1.4-M2") } listOfNotNull( - if (konanHomeRequired) "konan.home" to project.konanHome else null, + if (konanHomeRequired) "konan.home" to settings.konanHome else null, MessageRenderer.PROPERTY_KEY to MessageRenderer.GRADLE_STYLE.name ).toMap() } - final override val classpath by lazy { - project.files( - project.kotlinNativeCompilerJar, - "${project.konanHome}/konan/lib/trove4j.jar" - ).files - } - - private val Project.kotlinNativeCompilerJar: String - get() = if (nativeUseEmbeddableCompilerJar) - "$konanHome/konan/lib/kotlin-native-compiler-embeddable.jar" - else - "$konanHome/konan/lib/kotlin-native.jar" + final override val classpath get() = settings.classpath final override fun checkClasspath() = check(classpath.isNotEmpty()) { @@ -98,18 +128,23 @@ internal abstract class KotlinNativeToolRunner( """.trimIndent() } - data class IsolatedClassLoaderCacheKey(val classpath: Set) + data class IsolatedClassLoaderCacheKey(val classpath: Set) // TODO: can't we use this for other implementations too? final override val isolatedClassLoaderCacheKey get() = IsolatedClassLoaderCacheKey(classpath) override fun transformArgs(args: List) = listOf(toolName) + args - final override fun getCustomJvmArgs() = project.jvmArgs + final override fun getCustomJvmArgs() = settings.jvmArgs } /** A common ancestor for all runners that run the cinterop tool. */ -internal abstract class AbstractKotlinNativeCInteropRunner(toolName: String, project: Project) : KotlinNativeToolRunner(toolName, project) { +internal abstract class AbstractKotlinNativeCInteropRunner( + toolName: String, + settings: Settings, + executionContext: GradleExecutionContext +) : KotlinNativeToolRunner(toolName, settings, executionContext) { + override val mustRunViaExec get() = true override val execEnvironment by lazy { @@ -126,7 +161,7 @@ internal abstract class AbstractKotlinNativeCInteropRunner(toolName: String, pro if (HostManager.host == KonanTarget.MINGW_X64) { // TODO: Read it from Platform properties when it is accessible. val konanProperties = Properties().apply { - project.file("${project.konanHome}/konan/konan.properties").inputStream().use(::load) + settings.konanPropertiesFile.inputStream().use(::load) } konanProperties.resolvablePropertyString("llvmHome.mingw_x64")?.let { toolchainDir -> @@ -140,25 +175,47 @@ internal abstract class AbstractKotlinNativeCInteropRunner(toolName: String, pro } /** Kotlin/Native C-interop tool runner */ -internal class KotlinNativeCInteropRunner private constructor(project: Project) : AbstractKotlinNativeCInteropRunner("cinterop", project) { +internal class KotlinNativeCInteropRunner +private constructor( + private val settings: Settings, + gradleExecutionContext: GradleExecutionContext, +) : AbstractKotlinNativeCInteropRunner("cinterop", settings, gradleExecutionContext) { + interface ExecutionContext { - val project: Project + val runnerSettings: Settings + val gradleExecutionContext: GradleExecutionContext fun runWithContext(action: () -> Unit) } companion object { fun ExecutionContext.run(args: List) { - val runner = KotlinNativeCInteropRunner(project) + val runner = KotlinNativeCInteropRunner(runnerSettings, gradleExecutionContext) runWithContext { runner.run(args) } } } } /** Kotlin/Native compiler runner */ -internal class KotlinNativeCompilerRunner(project: Project) : KotlinNativeToolRunner("konanc", project) { - private val useArgFile get() = project.disableKonanDaemon +internal class KotlinNativeCompilerRunner( + private val settings: Settings, + executionContext: GradleExecutionContext +) : KotlinNativeToolRunner("konanc", settings.parent, executionContext) { + class Settings( + @get:Nested + val parent: KotlinNativeToolRunner.Settings, - override val mustRunViaExec get() = project.disableKonanDaemon + @get:Input + val disableKonanDaemon: Boolean, + ) { + constructor(project: Project) : this( + parent = KotlinNativeToolRunner.Settings(project), + disableKonanDaemon = project.disableKonanDaemon, + ) + } + + private val useArgFile get() = settings.disableKonanDaemon + + override val mustRunViaExec get() = settings.disableKonanDaemon override fun transformArgs(args: List): List { if (!useArgFile) return super.transformArgs(args) @@ -178,8 +235,17 @@ internal class KotlinNativeCompilerRunner(project: Project) : KotlinNativeToolRu } /** Platform libraries generation tool. Runs the cinterop tool under the hood. */ -internal class KotlinNativeLibraryGenerationRunner(project: Project) : - AbstractKotlinNativeCInteropRunner("generatePlatformLibraries", project) { +internal class KotlinNativeLibraryGenerationRunner( + private val settings: Settings, + executionContext: GradleExecutionContext +) : + AbstractKotlinNativeCInteropRunner("generatePlatformLibraries", settings, executionContext) { + + constructor(project: Project): this( + settings = Settings(project), + executionContext = GradleExecutionContext.fromProject(project) + ) + // The library generator works for a long time so enabling C2 can improve performance. override val disableC2: Boolean = false }