[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
This commit is contained in:
Anton Lakotka
2022-06-27 12:45:05 +02:00
committed by Space
parent c6d32f7a60
commit 508235ce08
2 changed files with 100 additions and 29 deletions
@@ -34,6 +34,11 @@ abstract class KotlinToolRunner(
val logger: Logger, val logger: Logger,
) { ) {
companion object { 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( @Deprecated(
"Building execution context from Project object isn't compatible with Gradle Configuration Cache", "Building execution context from Project object isn't compatible with Gradle Configuration Cache",
ReplaceWith("fromTaskContext()"), ReplaceWith("fromTaskContext()"),
@@ -41,7 +46,7 @@ abstract class KotlinToolRunner(
) )
fun fromProject(project: Project) = GradleExecutionContext( fun fromProject(project: Project) = GradleExecutionContext(
filesProvider = project::files, filesProvider = project::files,
javaexec = project::javaexec, javaexec = { spec -> project.javaexec(spec) }, // project::javaexec won't work due to different Classloaders
logger = project.logger logger = project.logger
) )
@@ -6,6 +6,10 @@
package org.jetbrains.kotlin.compilerRunner package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Project 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.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider 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.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.DependencyDirectories import org.jetbrains.kotlin.konan.util.DependencyDirectories
import java.io.File
import java.nio.file.Files import java.nio.file.Files
import java.util.* 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( internal abstract class KotlinNativeToolRunner(
protected val toolName: String, protected val toolName: String,
project: Project private val settings: Settings,
) : KotlinToolRunner(project) { 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<String>,
classpathProvider: () -> Set<File>,
) {
@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 displayName get() = toolName
final override val mainClass get() = "org.jetbrains.kotlin.cli.utilities.MainKt" 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 { final override val execSystemProperties by lazy {
// Still set konan.home for versions prior to 1.4-M3. // 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.isAtLeast(1, 4, 0) ||
it.toString(showMeta = false, showBuild = false) in listOf("1.4-M1", "1.4-M2") it.toString(showMeta = false, showBuild = false) in listOf("1.4-M1", "1.4-M2")
} }
listOfNotNull( 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 MessageRenderer.PROPERTY_KEY to MessageRenderer.GRADLE_STYLE.name
).toMap() ).toMap()
} }
final override val classpath by lazy { final override val classpath get() = settings.classpath
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 fun checkClasspath() = final override fun checkClasspath() =
check(classpath.isNotEmpty()) { check(classpath.isNotEmpty()) {
@@ -98,18 +128,23 @@ internal abstract class KotlinNativeToolRunner(
""".trimIndent() """.trimIndent()
} }
data class IsolatedClassLoaderCacheKey(val classpath: Set<java.io.File>) data class IsolatedClassLoaderCacheKey(val classpath: Set<File>)
// TODO: can't we use this for other implementations too? // TODO: can't we use this for other implementations too?
final override val isolatedClassLoaderCacheKey get() = IsolatedClassLoaderCacheKey(classpath) final override val isolatedClassLoaderCacheKey get() = IsolatedClassLoaderCacheKey(classpath)
override fun transformArgs(args: List<String>) = listOf(toolName) + args override fun transformArgs(args: List<String>) = 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. */ /** 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 mustRunViaExec get() = true
override val execEnvironment by lazy { override val execEnvironment by lazy {
@@ -126,7 +161,7 @@ internal abstract class AbstractKotlinNativeCInteropRunner(toolName: String, pro
if (HostManager.host == KonanTarget.MINGW_X64) { if (HostManager.host == KonanTarget.MINGW_X64) {
// TODO: Read it from Platform properties when it is accessible. // TODO: Read it from Platform properties when it is accessible.
val konanProperties = Properties().apply { 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 -> konanProperties.resolvablePropertyString("llvmHome.mingw_x64")?.let { toolchainDir ->
@@ -140,25 +175,47 @@ internal abstract class AbstractKotlinNativeCInteropRunner(toolName: String, pro
} }
/** Kotlin/Native C-interop tool runner */ /** 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 { interface ExecutionContext {
val project: Project val runnerSettings: Settings
val gradleExecutionContext: GradleExecutionContext
fun runWithContext(action: () -> Unit) fun runWithContext(action: () -> Unit)
} }
companion object { companion object {
fun ExecutionContext.run(args: List<String>) { fun ExecutionContext.run(args: List<String>) {
val runner = KotlinNativeCInteropRunner(project) val runner = KotlinNativeCInteropRunner(runnerSettings, gradleExecutionContext)
runWithContext { runner.run(args) } runWithContext { runner.run(args) }
} }
} }
} }
/** Kotlin/Native compiler runner */ /** Kotlin/Native compiler runner */
internal class KotlinNativeCompilerRunner(project: Project) : KotlinNativeToolRunner("konanc", project) { internal class KotlinNativeCompilerRunner(
private val useArgFile get() = project.disableKonanDaemon 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<String>): List<String> { override fun transformArgs(args: List<String>): List<String> {
if (!useArgFile) return super.transformArgs(args) 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. */ /** Platform libraries generation tool. Runs the cinterop tool under the hood. */
internal class KotlinNativeLibraryGenerationRunner(project: Project) : internal class KotlinNativeLibraryGenerationRunner(
AbstractKotlinNativeCInteropRunner("generatePlatformLibraries", project) { 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. // The library generator works for a long time so enabling C2 can improve performance.
override val disableC2: Boolean = false override val disableC2: Boolean = false
} }