[K/N][build] Refactored K/N tool runner

This commit is contained in:
Igor Chevdar
2021-10-02 00:14:35 +05:00
parent d642cda8f0
commit fa587a61fd
5 changed files with 98 additions and 143 deletions
@@ -16,172 +16,125 @@
package org.jetbrains.kotlin.gradle.plugin.konan package org.jetbrains.kotlin.gradle.plugin.konan
import org.gradle.api.Named
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.Action
import org.gradle.api.GradleException
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.jvm.toolchain.JavaLauncher
import org.gradle.jvm.toolchain.JavaToolchainService
import org.gradle.jvm.toolchain.JavaToolchainSpec
import org.gradle.process.ExecSpec
import org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin.ProjectProperty.KONAN_HOME import org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin.ProjectProperty.KONAN_HOME
import org.jetbrains.kotlin.konan.target.Family
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.DependencyProcessor
import java.nio.file.Files import java.nio.file.Files
import org.jetbrains.kotlin.* import org.jetbrains.kotlin.*
import java.io.ByteArrayOutputStream import org.jetbrains.kotlin.konan.properties.resolvablePropertyString
import org.jetbrains.kotlin.konan.util.DependencyDirectories
internal interface KonanToolRunner : Named { import java.io.File
val mainClass: String import java.util.Properties
val classpath: FileCollection import org.jetbrains.kotlin.compilerRunner.KotlinToolRunner
val jvmArgs: List<String>
val environment: Map<String, Any>
internal interface KonanToolRunner {
fun run(args: List<String>) fun run(args: List<String>)
fun run(vararg args: String) = run(args.toList())
} }
internal fun KonanToolRunner.run(vararg args: String) = run(args.toList())
internal abstract class KonanCliRunner( internal abstract class KonanCliRunner(
val toolName: String, protected val toolName: String,
val fullName: String, project: Project,
val project: Project, val additionalJvmArgs: List<String> = emptyList(),
private val additionalJvmArgs: List<String>, val konanHome: String = project.konanHome
private val konanHome: String ) : KotlinToolRunner(project), KonanToolRunner {
) : KonanToolRunner { final override val displayName get() = toolName
override val mainClass = "org.jetbrains.kotlin.cli.utilities.MainKt"
override fun getName() = toolName final override val mainClass get() = "org.jetbrains.kotlin.cli.utilities.MainKt"
final override val daemonEntryPoint get() = "daemonMain"
// We need to unset some environment variables which are set by XCode and may potentially affect the tool executed. // We need to unset some environment variables which are set by XCode and may potentially affect the tool executed.
protected val blacklistEnvironment: List<String> by lazy { final override val execEnvironmentBlacklist: Set<String> by lazy {
KonanPlugin::class.java.getResourceAsStream("/env_blacklist")?.let { stream -> HashSet<String>().also { collector ->
stream.reader().use { it.readLines() } KonanPlugin::class.java.getResourceAsStream("/env_blacklist")?.let { stream ->
} ?: emptyList<String>() stream.reader().use { r -> r.forEachLine { collector.add(it) } }
}
}
} }
protected val blacklistProperties: Set<String> = final override val execSystemProperties by lazy { mapOf("konan.home" to konanHome) }
setOf(
"java.endorsed.dirs", // Fix for KT-25887
"user.dir" // Don't propagate the working dir of the current Gradle process
)
override val classpath: FileCollection = final override val classpath by lazy { project.fileTree("$konanHome/konan/lib/").apply { include("*.jar") }.files }
project.fileTree("$konanHome/konan/lib/")
.apply { include("*.jar") }
override val jvmArgs = HostManager.defaultJvmArgs.toMutableList().apply { final override fun checkClasspath() =
if (additionalJvmArgs.none { it.startsWith("-Xmx") } && check(classpath.isNotEmpty()) {
project.jvmArgs.none { it.startsWith("-Xmx") }) { """
add("-Xmx3G") Classpath of the tool is empty: $toolName
} Probably the '${KONAN_HOME.propertyName}' project property contains an incorrect path.
addAll(additionalJvmArgs) Please change it to the compiler root directory and rerun the build.
addAll(project.jvmArgs)
}
override val environment = mutableMapOf("LIBCLANG_DISABLE_CRASH_RECOVERY" to "1")
private fun String.escapeQuotes() = replace("\"", "\\\"")
private fun Sequence<Pair<String, String>>.escapeQuotesForWindows() =
if (HostManager.hostIsMingw) {
map { (key, value) -> key.escapeQuotes() to value.escapeQuotes() }
} else {
this
}
open protected fun transformArgs(args: List<String>): List<String> = args
override fun run(args: List<String>) {
project.logger.info("Run tool: $toolName with args: ${args.joinToString(separator = " ")}")
if (classpath.isEmpty) {
throw IllegalStateException("Classpath of the tool is empty: $toolName\n" +
"Probably the '${KONAN_HOME.propertyName}' project property contains an incorrect path.\n" +
"Please change it to the compiler root directory and rerun the build.")
}
@Suppress("UNCHECKED_CAST")
val launcher = project.getProperty(KonanPlugin.ProjectProperty.KONAN_JVM_LAUNCHER) as? Provider<JavaLauncher>
?: throw IllegalStateException("Missing property: ${KonanPlugin.ProjectProperty.KONAN_JVM_LAUNCHER}")
val out = ByteArrayOutputStream()
val err = ByteArrayOutputStream()
val execResult = project.exec(object : Action<ExecSpec> {
override fun execute(exec: ExecSpec) {
exec.executable = launcher.get().executablePath.toString()
val properties = System.getProperties().asSequence()
.map { (k, v) -> k.toString() to v.toString() }
.filter { (k, _) -> k !in this@KonanCliRunner.blacklistProperties }
.filter { (k, _) -> !k.startsWith("sun") && !k.startsWith("java") }
.escapeQuotesForWindows()
.toMap()
.toMutableMap()
properties.put("konan.home", project.kotlinNativeDist.absolutePath)
exec.args(mutableListOf<String>().apply {
addAll(jvmArgs)
addAll(properties.entries.map { "-D${it.key}=${it.value}" })
add("-cp")
add(classpath.joinToString(separator = System.getProperty("path.separator")))
add(mainClass)
addAll(listOf(toolName) + transformArgs(args))
})
blacklistEnvironment.forEach { environment.remove(it) }
exec.environment(environment)
exec.errorOutput = err
exec.standardOutput = out
exec.isIgnoreExitValue = true
}
})
check(execResult.exitValue == 0) {
"""
stdout:$out
stderr:$err
""".trimIndent() """.trimIndent()
} }
}
data class IsolatedClassLoaderCacheKey(val classpath: Set<java.io.File>)
// TODO: can't we use this for other implementations too?
final override val isolatedClassLoaderCacheKey get() = IsolatedClassLoaderCacheKey(classpath)
override fun transformArgs(args: List<String>) = listOf(toolName) + args
final override fun getCustomJvmArgs() = additionalJvmArgs
} }
internal class KonanInteropRunner( /** Kotlin/Native compiler runner */
project: Project, internal class KonanCliCompilerRunner(
additionalJvmArgs: List<String> = emptyList(),
konanHome: String = project.konanHome
) : KonanCliRunner("cinterop", "Kotlin/Native cinterop tool", project, additionalJvmArgs, konanHome) {
init {
if (HostManager.host == KonanTarget.MINGW_X64) {
//TODO: Oh-ho-ho fix it in more convinient way.
environment.put("PATH", DependencyProcessor.defaultDependenciesRoot.absolutePath +
"\\llvm-11.1.0-windows-x64" +
"\\bin;${environment.get("PATH")}")
}
}
}
internal class KonanCompilerRunner(
project: Project, project: Project,
additionalJvmArgs: List<String> = emptyList(), additionalJvmArgs: List<String> = emptyList(),
val useArgFile: Boolean = true, val useArgFile: Boolean = true,
konanHome: String = project.konanHome konanHome: String = project.konanHome
) : KonanCliRunner("konanc", "Kotlin/Native compiler", project, additionalJvmArgs, konanHome) { ) : KonanCliRunner("konanc", project, additionalJvmArgs, konanHome) {
override val mustRunViaExec get() = false
override fun transformArgs(args: List<String>): List<String> { override fun transformArgs(args: List<String>): List<String> {
if (!useArgFile) { if (!useArgFile) return super.transformArgs(args)
return args
val argFile = Files.createTempFile(/* prefix = */ "konancArgs", /* suffix = */ ".lst").toFile().apply { deleteOnExit() }
argFile.printWriter().use { w ->
for (arg in args) {
val escapedArg = arg
.replace("\\", "\\\\")
.replace("\"", "\\\"")
w.println("\"$escapedArg\"")
}
} }
val argFile = Files.createTempFile("konancArgs", ".lst").toAbsolutePath().apply { return listOf(toolName, "@${argFile.absolutePath}")
toFile().deleteOnExit() }
} }
Files.write(argFile, args)
return listOf("@${argFile}") /** Kotlin/Native C-interop tool runner */
internal class KonanCliInteropRunner(
project: Project,
additionalJvmArgs: List<String> = emptyList(),
konanHome: String = project.konanHome
) : KonanCliRunner("cinterop", project, additionalJvmArgs, konanHome) {
override val mustRunViaExec get() = false
override val execEnvironment by lazy {
val result = mutableMapOf<String, String>()
result.putAll(super.execEnvironment)
result["LIBCLANG_DISABLE_CRASH_RECOVERY"] = "1"
llvmExecutablesPath?.let {
result["PATH"] = "$it;${System.getenv("PATH")}"
}
result
}
private val llvmExecutablesPath: String? by lazy {
if (HostManager.host == KonanTarget.MINGW_X64) {
// TODO: Read it from Platform properties when it is accessible.
val konanProperties = Properties().apply {
project.file("$konanHome/konan/konan.properties").inputStream().use(::load)
}
konanProperties.resolvablePropertyString("llvmHome.mingw_x64")?.let { toolchainDir ->
DependencyDirectories.defaultDependenciesRoot
.resolve("$toolchainDir/bin")
.absolutePath
}
} else
null
} }
} }
@@ -189,4 +142,6 @@ internal class KonanKlibRunner(
project: Project, project: Project,
additionalJvmArgs: List<String> = emptyList(), additionalJvmArgs: List<String> = emptyList(),
konanHome: String = project.konanHome konanHome: String = project.konanHome
) : KonanCliRunner("klib", "Klib management tool", project, additionalJvmArgs, konanHome) ) : KonanCliRunner("klib", project, additionalJvmArgs, konanHome) {
override val mustRunViaExec get() = false
}
@@ -4,7 +4,7 @@ import org.gradle.api.DefaultTask
import org.gradle.api.file.Directory import org.gradle.api.file.Directory
import org.gradle.api.provider.Property import org.gradle.api.provider.Property
import org.gradle.api.tasks.* import org.gradle.api.tasks.*
import org.jetbrains.kotlin.gradle.plugin.konan.KonanCompilerRunner import org.jetbrains.kotlin.gradle.plugin.konan.KonanCliCompilerRunner
import org.jetbrains.kotlin.gradle.plugin.konan.konanHome import org.jetbrains.kotlin.gradle.plugin.konan.konanHome
import org.jetbrains.kotlin.konan.library.defaultResolver import org.jetbrains.kotlin.konan.library.defaultResolver
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
@@ -79,6 +79,6 @@ open class KonanCacheTask: DefaultTask() {
"-Xadd-cache=${originalKlib?.absolutePath}", "-Xadd-cache=${originalKlib?.absolutePath}",
"-Xcache-directory=${cacheDirectory.absolutePath}" "-Xcache-directory=${cacheDirectory.absolutePath}"
) + additionalCacheFlags + cachedLibraries.map { "-Xcached-library=${it.key},${it.value}" } ) + additionalCacheFlags + cachedLibraries.map { "-Xcached-library=${it.key},${it.value}" }
KonanCompilerRunner(project, konanHome = konanHome).run(args) KonanCliCompilerRunner(project, konanHome = konanHome).run(args)
} }
} }
@@ -36,7 +36,7 @@ import java.io.File
abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec { abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
@get:Internal @get:Internal
override val toolRunner = KonanCompilerRunner(project, project.konanExtension.jvmArgs) override val toolRunner = KonanCliCompilerRunner(project, project.konanExtension.jvmArgs)
abstract val produce: CompilerOutputKind abstract val produce: CompilerOutputKind
@Internal get @Internal get
@@ -70,7 +70,7 @@ open class KonanCompilerDownloadTask : DefaultTask() {
// Download dependencies if a user said so. // Download dependencies if a user said so.
if (downloadDependencies) { if (downloadDependencies) {
val runner = KonanCompilerRunner(project, project.konanExtension.jvmArgs) val runner = KonanCliCompilerRunner(project, project.konanExtension.jvmArgs)
project.konanTargets.forEach { project.konanTargets.forEach {
runner.run("-Xcheck_dependencies", "-target", it.visibleName) runner.run("-Xcheck_dependencies", "-target", it.visibleName)
} }
@@ -37,7 +37,7 @@ import javax.inject.Inject
open class KonanInteropTask @Inject constructor(@Internal val workerExecutor: WorkerExecutor) : KonanBuildingTask(), KonanInteropSpec { open class KonanInteropTask @Inject constructor(@Internal val workerExecutor: WorkerExecutor) : KonanBuildingTask(), KonanInteropSpec {
@get:Internal @get:Internal
override val toolRunner: KonanToolRunner = KonanInteropRunner(project, project.konanExtension.jvmArgs) override val toolRunner: KonanToolRunner = KonanCliInteropRunner(project, project.konanExtension.jvmArgs)
override fun init(config: KonanBuildingConfig<*>, destinationDir: File, artifactName: String, target: KonanTarget) { override fun init(config: KonanBuildingConfig<*>, destinationDir: File, artifactName: String, target: KonanTarget) {
super.init(config, destinationDir, artifactName, target) super.init(config, destinationDir, artifactName, target)