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

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

There is a newer version: 2.1.20-Beta1
Show newest version
/*
 * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.compilerRunner

import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Provider
import org.gradle.process.ExecOperations
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.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind
import org.jetbrains.kotlin.gradle.dsl.NativeCacheOrchestration
import org.jetbrains.kotlin.gradle.internal.properties.NativeProperties
import org.jetbrains.kotlin.gradle.internal.properties.nativeProperties
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.useXcodeMessageStyle
import org.jetbrains.kotlin.gradle.report.GradleBuildMetricsReporter
import org.jetbrains.kotlin.gradle.targets.native.KonanPropertiesBuildService
import org.jetbrains.kotlin.gradle.utils.newInstance
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.*
import javax.inject.Inject

internal fun Project.getKonanCacheKind(target: KonanTarget): NativeCacheKind =
    kotlinPropertiesProvider.getKonanCacheKind(target, KonanPropertiesBuildService.registerIfAbsent(this))

internal fun PropertiesProvider.getKonanCacheKind(
    target: KonanTarget,
    konanPropertiesBuildService: Provider
): NativeCacheKind {
    val commonCacheKind = nativeCacheKind
    val targetCacheKind = nativeCacheKindForTarget(target)
    return when {
        targetCacheKind != null -> targetCacheKind
        commonCacheKind != null -> commonCacheKind
        else -> konanPropertiesBuildService.get().defaultCacheKindForTarget(target)
    }
}

internal fun Project.getKonanCacheOrchestration(): NativeCacheOrchestration {
    return PropertiesProvider(this).nativeCacheOrchestration ?: NativeCacheOrchestration.Compiler
}

internal fun Project.isKonanIncrementalCompilationEnabled(): Boolean {
    return PropertiesProvider(this).incrementalNative ?: false
}

internal fun Project.getKonanParallelThreads(): Int {
    return PropertiesProvider(this).nativeParallelThreads ?: 4
}

private val Project.kotlinNativeCompilerJar: Provider
    get() = nativeProperties.isUseEmbeddableCompilerJar
        .zip(nativeProperties.actualNativeHomeDirectory) { useJar, nativeHomeDir ->
            if (useJar) {
                nativeHomeDir.resolve("konan/lib/kotlin-native-compiler-embeddable.jar")
            } else {
                nativeHomeDir.resolve("konan/lib/kotlin-native.jar")
            }
        }

internal abstract class KotlinNativeToolRunner(
    protected val toolName: String,
    private val settings: Settings,
    metricsReporter: BuildMetricsReporter,
    objectsFactory: ObjectFactory,
    execOperations: ExecOperations,
) : KotlinToolRunner(metricsReporter, objectsFactory, execOperations) {

    class Settings(
        val konanHome: String,
        val konanPropertiesFile: File,
        val useXcodeMessageStyle: Boolean,
        val jvmArgs: List,
        val classpath: FileCollection,
        val konanDataDir: String?,
        val kotlinCompilerArgumentsLogLevel: Provider,
    ) {
        companion object {
            fun of(konanHome: String, konanDataDir: String?, project: Project) = Settings(
                konanHome = konanHome,
                konanPropertiesFile = project.file("${konanHome}/konan/konan.properties"),
                useXcodeMessageStyle = project.useXcodeMessageStyle.get(),
                jvmArgs = project.nativeProperties.jvmArgs.get(),
                classpath = project.files(project.kotlinNativeCompilerJar, "${konanHome}/konan/lib/trove4j.jar"),
                konanDataDir = konanDataDir,
                kotlinCompilerArgumentsLogLevel = project.kotlinPropertiesProvider.kotlinCompilerArgumentsLogLevel
            )
        }
    }

    final override val displayName get() = toolName

    final override val mainClass get() = "org.jetbrains.kotlin.cli.utilities.MainKt"
    final override val daemonEntryPoint
        get() = if (!settings.useXcodeMessageStyle) "daemonMain" else "daemonMainWithXcodeRenderer"

    // We need to unset some environment variables which are set by XCode and may potentially affect the tool executed.
    final override val execEnvironmentBlacklist: Set by lazy {
        HashSet().also { collector ->
            KotlinNativeToolRunner::class.java.getResourceAsStream("/env_blacklist")?.let { stream ->
                stream.reader().use { r -> r.forEachLine { collector.add(it) } }
            }
        }
    }

    final override val execSystemProperties by lazy {
        val messageRenderer = if (settings.useXcodeMessageStyle) MessageRenderer.XCODE_STYLE else MessageRenderer.GRADLE_STYLE
        mapOf(MessageRenderer.PROPERTY_KEY to messageRenderer.name)
    }

    final override val classpath get() = settings.classpath.files

    final override fun checkClasspath() =
        check(classpath.isNotEmpty()) {
            """
                Classpath of the tool is empty: $toolName
                Probably the '${NativeProperties.NATIVE_HOME.name}' project property contains an incorrect path.
                Please change it to the compiler root directory and rerun the build.
            """.trimIndent()
        }

    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() = settings.jvmArgs

    final override fun run(args: List) {
        super.run(args + extractArgsFromSettings())
    }

    protected open fun extractArgsFromSettings(): List {
        return settings.konanDataDir?.let { listOf("-Xkonan-data-dir=$it") } ?: emptyList()
    }

    override val compilerArgumentsLogLevel: KotlinCompilerArgumentsLogLevel get() = settings.kotlinCompilerArgumentsLogLevel.get()
}

/** A common ancestor for all runners that run the cinterop tool. */
internal abstract class AbstractKotlinNativeCInteropRunner(
    toolName: String,
    private val settings: Settings,
    metricsReporter: BuildMetricsReporter,
    objectsFactory: ObjectFactory,
    execOperations: ExecOperations,
) : KotlinNativeToolRunner(toolName, settings, metricsReporter, objectsFactory, execOperations) {

    override val mustRunViaExec get() = true

    override val execEnvironment by lazy {
        val result = mutableMapOf()
        result.putAll(super.execEnvironment)
        result["LIBCLANG_DISABLE_CRASH_RECOVERY"] = "1"
        llvmExecutablesPath?.let {
            result["PATH"] = "$it;${System.getenv("PATH")}"
        }
        result
    }

    override fun extractArgsFromSettings(): List {
        return settings.konanDataDir?.let { listOf("-Xkonan-data-dir", it) } ?: emptyList()
    }

    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 {
                settings.konanPropertiesFile.inputStream().use(::load)
            }

            konanProperties.resolvablePropertyString("llvmHome.mingw_x64")?.let { toolchainDir ->
                DependencyDirectories.getDependenciesRoot(settings.konanDataDir)
                    .resolve("$toolchainDir/bin")
                    .absolutePath
            }
        } else
            null
    }
}

/** Kotlin/Native C-interop tool runner */
internal fun ObjectFactory.KotlinNativeCInteropRunner(
    settings: KotlinNativeToolRunner.Settings,
    metricsReporter: BuildMetricsReporter,
): KotlinNativeCInteropRunner = newInstance(settings, metricsReporter)

internal abstract class KotlinNativeCInteropRunner @Inject constructor(
    settings: Settings,
    metricsReporter: BuildMetricsReporter,
    objectsFactory: ObjectFactory,
    execOperations: ExecOperations,
) : AbstractKotlinNativeCInteropRunner("cinterop", settings, metricsReporter, objectsFactory, execOperations) {

    interface ExecutionContext {
        val runnerSettings: Settings
        val metricsReporter: BuildMetricsReporter
        fun runWithContext(action: () -> Unit)
    }

    companion object {
        fun ExecutionContext.run(objectsFactory: ObjectFactory, args: List) {
            val runner = objectsFactory.KotlinNativeCInteropRunner(runnerSettings, metricsReporter)
            runWithContext { runner.run(args) }
        }
    }
}

/** Kotlin/Native compiler runner */
internal fun ObjectFactory.KotlinNativeCompilerRunner(
    settings: KotlinNativeCompilerRunner.Settings,
    metricsReporter: BuildMetricsReporter
): KotlinNativeCompilerRunner = newInstance(settings, metricsReporter)

internal abstract class KotlinNativeCompilerRunner @Inject constructor(
    private val settings: Settings,
    metricsReporter: BuildMetricsReporter,
    objectsFactory: ObjectFactory,
    execOperations: ExecOperations,
) : KotlinNativeToolRunner("konanc", settings.parent, metricsReporter, objectsFactory, execOperations) {
    class Settings(
        val parent: KotlinNativeToolRunner.Settings,
        val disableKonanDaemon: Boolean,
    ) {
        companion object {
            fun of(konanHome: String, konanDataDir: String?, project: Project) = Settings(
                parent = KotlinNativeToolRunner.Settings.of(konanHome, konanDataDir, project),
                disableKonanDaemon = project.nativeProperties.forceDisableRunningInProcess.get(),
            )
        }
    }

    private val useArgFile get() = settings.disableKonanDaemon

    override val mustRunViaExec get() = settings.disableKonanDaemon

    override fun transformArgs(args: List): List {
        if (!useArgFile) return super.transformArgs(args)

        val argFile = Files.createTempFile(/* prefix = */ "kotlinc-native-args", /* suffix = */ ".lst").toFile().apply { deleteOnExit() }
        argFile.printWriter().use { w ->
            args.forEach { arg ->
                val escapedArg = arg
                    .replace("\\", "\\\\")
                    .replace("\"", "\\\"")
                w.println("\"$escapedArg\"")
            }
        }

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

/** Platform libraries generation tool. Runs the cinterop tool under the hood. */
internal fun ObjectFactory.KotlinNativeLibraryGenerationRunner(
    settings: KotlinNativeToolRunner.Settings,
    metricsReporter: BuildMetricsReporter
): KotlinNativeLibraryGenerationRunner = newInstance(settings, metricsReporter)

internal abstract class KotlinNativeLibraryGenerationRunner @Inject constructor(
    settings: Settings,
    metricsReporter: BuildMetricsReporter,
    objectsFactory: ObjectFactory,
    execOperations: ExecOperations
) : AbstractKotlinNativeCInteropRunner("generatePlatformLibraries", settings, metricsReporter, objectsFactory, execOperations) {

    companion object {
        fun fromProject(project: Project) = project.objects.KotlinNativeLibraryGenerationRunner(
            settings = Settings.of(
                project.nativeProperties.actualNativeHomeDirectory.get().absolutePath,
                project.nativeProperties.konanDataDir.orNull?.absolutePath,
                project
            ),
            metricsReporter = GradleBuildMetricsReporter()
        )
    }

    // The library generator works for a long time so enabling C2 can improve performance.
    override val disableC2: Boolean = false
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy