All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.compilerRunner.KotlinNativeToolRunner.kt Maven / Gradle / Ivy

There is a newer version: 2.0.20-RC
Show newest version
/*
 * Copyright 2010-2018 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.compilerRunner

import org.gradle.api.Named
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.KOTLIN_NATIVE_HOME
import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.DependencyDirectories
import java.util.*

/** Copied from Kotlin/Native repository. */

private val Project.jvmArgs
    get() = PropertiesProvider(this).nativeJvmArgs?.split("\\s+".toRegex()).orEmpty()

internal val Project.konanHome: String
    get() = PropertiesProvider(this).nativeHome?.let {
        file(it).absolutePath
    } ?: NativeCompilerDownloader(project).compilerDirectory.absolutePath

internal val Project.konanVersion: KonanVersion
    get() = PropertiesProvider(this).nativeVersion?.let {
        KonanVersion.fromString(it.toString())
    } ?: NativeCompilerDownloader.DEFAULT_KONAN_VERSION

internal interface KonanToolRunner : Named {
    val mainClass: String
    val classpath: FileCollection
    val jvmArgs: List
    val environment: Map
    val additionalSystemProperties: Map

    fun run(args: List)
    fun run(vararg args: String) = run(args.toList())
}

internal abstract class KonanCliRunner(
    val toolName: String,
    val fullName: String,
    val project: Project,
    private val additionalJvmArgs: List
) : KonanToolRunner {
    override val mainClass = "org.jetbrains.kotlin.cli.utilities.MainKt"

    override fun getName() = toolName

    // We need to unset some environment variables which are set by XCode and may potentially affect the tool executed.
    protected val blacklistEnvironment: List by lazy {
        KonanToolRunner::class.java.getResourceAsStream("/env_blacklist")?.let { stream ->
            stream.reader().use { it.readLines() }
        } ?: emptyList()
    }

    protected val blacklistProperties: Set =
        setOf("java.endorsed.dirs")

    override val classpath: FileCollection =
        project.fileTree("${project.konanHome}/konan/lib/")
            .apply { include("*.jar") }

    override val jvmArgs = mutableListOf("-ea").apply {
        if (additionalJvmArgs.none { it.startsWith("-Xmx") } &&
            project.jvmArgs.none { it.startsWith("-Xmx") }) {
            add("-Xmx3G")
        }
        // Disable C2 compiler for HotSpot VM to improve compilation speed.
        System.getProperty("java.vm.name")?.let {
            if (it.contains("HotSpot", true)) {
                add("-XX:TieredStopAtLevel=1")
            }
        }
        addAll(additionalJvmArgs)
        addAll(project.jvmArgs)
    }

    override val additionalSystemProperties = mutableMapOf(
        "konan.home" to project.konanHome,
        MessageRenderer.PROPERTY_KEY to MessageRenderer.GRADLE_STYLE.name,
        "java.library.path" to "${project.konanHome}/konan/nativelib"
    )

    override val environment = mutableMapOf("LIBCLANG_DISABLE_CRASH_RECOVERY" to "1")

    private fun String.escapeQuotes() = replace("\"", "\\\"")

    private fun Sequence>.escapeQuotesForWindows() =
        if (HostManager.hostIsMingw) {
            map { (key, value) -> key.escapeQuotes() to value.escapeQuotes() }
        } else {
            this
        }

    protected open fun transformArgs(args: List): List = args

    override fun run(args: List) {
        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 '$KOTLIN_NATIVE_HOME' project property contains an incorrect path.\n" +
                        "Please change it to the compiler root directory and rerun the build."
            )
        }

        project.javaexec { spec ->
            spec.main = mainClass
            spec.classpath = classpath
            spec.jvmArgs(jvmArgs)
            spec.systemProperties(
                System.getProperties().asSequence()
                    .map { (k, v) -> k.toString() to v.toString() }
                    .filter { (k, _) -> k !in blacklistProperties }
                    .escapeQuotesForWindows()
                    .toMap()
            )
            spec.systemProperties(additionalSystemProperties)
            spec.args(listOf(toolName) + transformArgs(args))
            blacklistEnvironment.forEach { spec.environment.remove(it) }
            spec.environment(environment)
        }
    }
}

internal class KonanInteropRunner(project: Project, additionalJvmArgs: List = emptyList()) :
    KonanCliRunner("cinterop", "Kotlin/Native cinterop tool", project, additionalJvmArgs) {
    init {
        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)
            }
            val toolchainDir = konanProperties.getProperty("llvmHome.mingw_x64")
            if (toolchainDir != null) {
                environment.put(
                    "PATH",
                    DependencyDirectories.defaultDependenciesRoot
                        .resolve("$toolchainDir/bin")
                        .absolutePath + ";${System.getenv("PATH")}"
                )
            }
        }
    }
}

internal class KonanCompilerRunner(
    project: Project,
    additionalJvmArgs: List = emptyList(),
    val useArgFile: Boolean = true
) : KonanCliRunner("konanc", "Kotlin/Native compiler", project, additionalJvmArgs) {
    override fun transformArgs(args: List): List {
        if (!useArgFile) {
            return args
        }

        val argFile = createTempFile(prefix = "konancArgs", suffix = ".lst").apply {
            deleteOnExit()
        }
        argFile.printWriter().use { writer ->
            args.forEach {
                writer.println(it)
            }
        }

        return listOf("@${argFile.absolutePath}")
    }
}

internal class KonanKlibRunner(project: Project, additionalJvmArgs: List = emptyList()) :
    KonanCliRunner("klib", "Klib management tool", project, additionalJvmArgs)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy