Move KotlinNativeLink task into separate file

Allows easier to work with class when code completion is slow.

^KT-27301 In Progress
This commit is contained in:
Yahor Berdnikau
2022-09-17 22:28:56 +02:00
parent cf3855a396
commit c7064a5edf
2 changed files with 261 additions and 235 deletions
@@ -0,0 +1,258 @@
/*
* Copyright 2010-2022 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.
*/
@file:Suppress("PackageDirectoryMismatch") // Old package for compatibility
package org.jetbrains.kotlin.gradle.tasks
import groovy.lang.Closure
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.artifacts.result.ResolvedDependencyResult
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.*
import org.gradle.process.ExecOperations
import org.jetbrains.kotlin.compilerRunner.KotlinNativeCompilerRunner
import org.jetbrains.kotlin.compilerRunner.KotlinToolRunner
import org.jetbrains.kotlin.compilerRunner.getKonanCacheKind
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonToolOptions
import org.jetbrains.kotlin.gradle.dsl.NativeCacheKind
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultLanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.targets.native.KonanPropertiesBuildService
import org.jetbrains.kotlin.gradle.targets.native.tasks.CompilerPluginData
import org.jetbrains.kotlin.gradle.targets.native.tasks.buildKotlinNativeBinaryLinkerArgs
import org.jetbrains.kotlin.gradle.utils.ResolvedDependencyGraph
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import javax.inject.Inject
/**
* A task producing a final binary from a compilation.
*/
@CacheableTask
abstract class KotlinNativeLink
@Inject
constructor(
@Internal
val binary: NativeBinary,
private val objectFactory: ObjectFactory,
private val providerFactory: ProviderFactory,
private val execOperations: ExecOperations
) : AbstractKotlinNativeCompile<KotlinCommonToolOptions, KotlinNativeCompilation, StubK2NativeCompilerArguments>(objectFactory) {
@get:Internal
final override val compilation: KotlinNativeCompilation
get() = binary.compilation
private val runnerSettings = KotlinNativeCompilerRunner.Settings.fromProject(project)
init {
dependsOn(project.provider { compilation.compileKotlinTaskProvider })
// Frameworks actively uses symlinks.
// Gradle build cache transforms symlinks into regular files https://guides.gradle.org/using-build-cache/#symbolic_links
outputs.cacheIf { outputKind != CompilerOutputKind.FRAMEWORK }
this.setSource(compilation.compileKotlinTask.outputFile)
includes.clear() // we need to include non '.kt' or '.kts' files
disallowSourceChanges()
}
override val destinationDirectory: DirectoryProperty = binary.outputDirectoryProperty
override val outputKind: CompilerOutputKind
@Input get() = binary.outputKind.compilerOutputKind
override val optimized: Boolean
@Input get() = binary.optimized
override val debuggable: Boolean
@Input get() = binary.debuggable
override val baseName: String
@Input get() = binary.baseName
@get:Input
protected val konanCacheKind: NativeCacheKind by lazy {
project.getKonanCacheKind(konanTarget)
}
inner class NativeLinkOptions : KotlinCommonToolOptions {
override var allWarningsAsErrors: Boolean = false
override var suppressWarnings: Boolean = false
override var verbose: Boolean = false
override var freeCompilerArgs: List<String> = listOf()
}
private val nativeLinkArgs = PropertiesProvider(project).nativeLinkArgs
// We propagate compilation free args to the link task for now (see KT-33717).
@get:Input
override val additionalCompilerOptions: Provider<Collection<String>> = providerFactory.provider {
kotlinOptions.freeCompilerArgs +
compilation.kotlinOptions.freeCompilerArgs +
((languageSettings as? DefaultLanguageSettingsBuilder)?.freeCompilerArgs ?: emptyList()) +
nativeLinkArgs
}
override val kotlinOptions: KotlinCommonToolOptions = NativeLinkOptions()
override fun kotlinOptions(fn: KotlinCommonToolOptions.() -> Unit) {
kotlinOptions.fn()
}
override fun kotlinOptions(fn: Closure<*>) {
fn.delegate = kotlinOptions
fn.call()
}
// Binary-specific options.
val entryPoint: String?
@Input
@Optional
get() = (binary as? Executable)?.entryPoint
val linkerOpts: List<String>
@Input get() = binary.linkerOpts
@get:Input
val binaryOptions: Map<String, String> by lazy { PropertiesProvider(project).nativeBinaryOptions + binary.binaryOptions }
val processTests: Boolean
@Input get() = binary is TestExecutable
@get:Classpath
val exportLibraries: FileCollection get() = exportLibrariesResolvedGraph?.files ?: objectFactory.fileCollection()
private val exportLibrariesResolvedGraph = if (binary is AbstractNativeLibrary) {
ResolvedDependencyGraph(project.configurations.getByName(binary.exportConfigurationName))
} else {
null
}
@get:Input
val isStaticFramework: Boolean
get() = binary.let { it is Framework && it.isStatic }
@get:Input
val embedBitcode: BitcodeEmbeddingMode
get() = (binary as? Framework)?.embedBitcode ?: BitcodeEmbeddingMode.DISABLE
@get:Internal
val apiFiles = project.files(project.configurations.getByName(compilation.apiConfigurationName)).filterKlibsPassedToCompiler()
private val localKotlinOptions get() =
object : KotlinCommonToolOptions {
override var allWarningsAsErrors = kotlinOptions.allWarningsAsErrors
override var suppressWarnings = kotlinOptions.suppressWarnings
override var verbose = kotlinOptions.verbose
override var freeCompilerArgs = additionalCompilerOptions.get().toList()
}
private val externalDependenciesArgs by lazy { ExternalDependenciesBuilder(project, compilation).buildCompilerArgs() }
private val cacheBuilderSettings by lazy {
CacheBuilder.Settings.createWithProject(project, binary, konanTarget, localKotlinOptions, externalDependenciesArgs)
}
override fun createCompilerArgs(): StubK2NativeCompilerArguments = StubK2NativeCompilerArguments()
override fun setupCompilerArgs(
args: StubK2NativeCompilerArguments,
defaultsOnly: Boolean,
ignoreClasspathResolutionErrors: Boolean
) = Unit
private fun validatedExportedLibraries() {
if (exportLibrariesResolvedGraph == null) return
val failed = mutableSetOf<ResolvedDependencyResult>()
exportLibrariesResolvedGraph
.allDependencies
.filterIsInstance<ResolvedDependencyResult>()
.forEach {
val dependencyFiles = exportLibrariesResolvedGraph.dependencyArtifacts(it).map { it.file }.filterKlibsPassedToCompiler()
if (!apiFiles.files.containsAll(dependencyFiles)) {
failed.add(it)
}
}
check(failed.isEmpty()) {
val failedDependenciesList = failed.joinToString(separator = "\n") {
val componentId = it.selected.id
when (componentId) {
is ModuleComponentIdentifier -> "|Files: ${exportLibrariesResolvedGraph.dependencyArtifacts(it).map { it.file }}"
is ProjectComponentIdentifier -> "|Project ${componentId.projectPath}"
else -> "|${componentId.displayName}"
}
}
"""
|Following dependencies exported in the ${binary.name} binary are not specified as API-dependencies of a corresponding source set:
|
$failedDependenciesList
|
|Please add them in the API-dependencies and rerun the build.
""".trimMargin()
}
}
@get:Internal
internal abstract val konanPropertiesService: Property<KonanPropertiesBuildService>
private val resolvedDependencyGraph = ResolvedDependencyGraph(
project.configurations.getByName(compilation.compileDependencyConfigurationName)
)
@TaskAction
fun compile() {
validatedExportedLibraries()
val output = outputFile.get()
output.parentFile.mkdirs()
val plugins = listOfNotNull(
compilerPluginClasspath?.let { CompilerPluginData(it, compilerPluginOptions) },
kotlinPluginData?.orNull?.let { CompilerPluginData(it.classpath, it.options) }
)
val executionContext = KotlinToolRunner.GradleExecutionContext.fromTaskContext(objectFactory, execOperations, logger)
val cacheArgs = CacheBuilder(
executionContext = executionContext,
settings = cacheBuilderSettings,
konanPropertiesService = konanPropertiesService.get()
).buildCompilerArgs(resolvedDependencyGraph)
val buildArgs = buildKotlinNativeBinaryLinkerArgs(
output,
optimized,
debuggable,
konanTarget,
outputKind,
libraries.files.filterKlibsPassedToCompiler(),
friendModule.files.toList(),
enableEndorsedLibs,
localKotlinOptions,
plugins,
processTests,
entryPoint,
embedBitcode,
linkerOpts,
binaryOptions,
isStaticFramework,
exportLibraries.files.filterKlibsPassedToCompiler(),
sources.asFileTree.files.toList(),
externalDependenciesArgs + cacheArgs
)
KotlinNativeCompilerRunner(
settings = runnerSettings,
executionContext = executionContext
).run(buildArgs)
}
}
@@ -91,12 +91,6 @@ internal fun MutableList<String>.addArgIfNotNull(parameter: String, value: Strin
}
}
internal fun MutableList<String>.addKey(key: String, enabled: Boolean) {
if (enabled) {
add(key)
}
}
internal fun MutableList<String>.addFileArgs(parameter: String, values: FileCollection) {
values.files.forEach {
addArg(parameter, it.canonicalPath)
@@ -140,10 +134,10 @@ private fun FileCollection.filterOutPublishableInteropLibs(libDirectories: List<
*/
private val File.canKlibBePassedToCompiler get() = (extension == "klib" || isDirectory) && exists()
private fun Collection<File>.filterKlibsPassedToCompiler(): List<File> = filter(File::canKlibBePassedToCompiler)
internal fun Collection<File>.filterKlibsPassedToCompiler(): List<File> = filter(File::canKlibBePassedToCompiler)
/* Returned FileCollection is lazy */
private fun FileCollection.filterKlibsPassedToCompiler(): FileCollection = filter(File::canKlibBePassedToCompiler)
internal fun FileCollection.filterKlibsPassedToCompiler(): FileCollection = filter(File::canKlibBePassedToCompiler)
// endregion
abstract class AbstractKotlinNativeCompile<
@@ -478,233 +472,7 @@ constructor(
}
}
/**
* A task producing a final binary from a compilation.
*/
@CacheableTask
abstract class KotlinNativeLink
@Inject
constructor(
@Internal
val binary: NativeBinary,
private val objectFactory: ObjectFactory,
private val providerFactory: ProviderFactory,
private val execOperations: ExecOperations
) : AbstractKotlinNativeCompile<KotlinCommonToolOptions, KotlinNativeCompilation, StubK2NativeCompilerArguments>(objectFactory) {
@get:Internal
final override val compilation: KotlinNativeCompilation
get() = binary.compilation
private val runnerSettings = KotlinNativeCompilerRunner.Settings.fromProject(project)
init {
dependsOn(project.provider { compilation.compileKotlinTaskProvider })
// Frameworks actively uses symlinks.
// Gradle build cache transforms symlinks into regular files https://guides.gradle.org/using-build-cache/#symbolic_links
outputs.cacheIf { outputKind != FRAMEWORK }
this.setSource(compilation.compileKotlinTask.outputFile)
includes.clear() // we need to include non '.kt' or '.kts' files
disallowSourceChanges()
}
override val destinationDirectory: DirectoryProperty = binary.outputDirectoryProperty
override val outputKind: CompilerOutputKind
@Input get() = binary.outputKind.compilerOutputKind
override val optimized: Boolean
@Input get() = binary.optimized
override val debuggable: Boolean
@Input get() = binary.debuggable
override val baseName: String
@Input get() = binary.baseName
@get:Input
protected val konanCacheKind: NativeCacheKind by lazy {
project.getKonanCacheKind(konanTarget)
}
inner class NativeLinkOptions : KotlinCommonToolOptions {
override var allWarningsAsErrors: Boolean = false
override var suppressWarnings: Boolean = false
override var verbose: Boolean = false
override var freeCompilerArgs: List<String> = listOf()
}
private val nativeLinkArgs = PropertiesProvider(project).nativeLinkArgs
// We propagate compilation free args to the link task for now (see KT-33717).
@get:Input
override val additionalCompilerOptions: Provider<Collection<String>> = providerFactory.provider {
kotlinOptions.freeCompilerArgs +
compilation.kotlinOptions.freeCompilerArgs +
((languageSettings as? DefaultLanguageSettingsBuilder)?.freeCompilerArgs ?: emptyList()) +
nativeLinkArgs
}
override val kotlinOptions: KotlinCommonToolOptions = NativeLinkOptions()
override fun kotlinOptions(fn: KotlinCommonToolOptions.() -> Unit) {
kotlinOptions.fn()
}
override fun kotlinOptions(fn: Closure<*>) {
fn.delegate = kotlinOptions
fn.call()
}
// Binary-specific options.
val entryPoint: String?
@Input
@Optional
get() = (binary as? Executable)?.entryPoint
val linkerOpts: List<String>
@Input get() = binary.linkerOpts
@get:Input
val binaryOptions: Map<String, String> by lazy { PropertiesProvider(project).nativeBinaryOptions + binary.binaryOptions }
val processTests: Boolean
@Input get() = binary is TestExecutable
@get:Classpath
val exportLibraries: FileCollection get() = exportLibrariesResolvedGraph?.files ?: objectFactory.fileCollection()
private val exportLibrariesResolvedGraph = if (binary is AbstractNativeLibrary) {
ResolvedDependencyGraph(project.configurations.getByName(binary.exportConfigurationName))
} else {
null
}
@get:Input
val isStaticFramework: Boolean by project.provider {
binary.let { it is Framework && it.isStatic }
}
@get:Input
val embedBitcode: BitcodeEmbeddingMode by project.provider {
(binary as? Framework)?.embedBitcode ?: BitcodeEmbeddingMode.DISABLE
}
@get:Internal
val apiFiles = project.files(project.configurations.getByName(compilation.apiConfigurationName)).filterKlibsPassedToCompiler()
private val localKotlinOptions get() =
object : KotlinCommonToolOptions {
override var allWarningsAsErrors = kotlinOptions.allWarningsAsErrors
override var suppressWarnings = kotlinOptions.suppressWarnings
override var verbose = kotlinOptions.verbose
override var freeCompilerArgs = additionalCompilerOptions.get().toList()
}
private val externalDependenciesArgs by lazy { ExternalDependenciesBuilder(project, compilation).buildCompilerArgs() }
private val cacheBuilderSettings by lazy {
CacheBuilder.Settings.createWithProject(project, binary, konanTarget, localKotlinOptions, externalDependenciesArgs)
}
override fun createCompilerArgs(): StubK2NativeCompilerArguments = StubK2NativeCompilerArguments()
override fun setupCompilerArgs(
args: StubK2NativeCompilerArguments,
defaultsOnly: Boolean,
ignoreClasspathResolutionErrors: Boolean
) = Unit
private fun validatedExportedLibraries() {
if (exportLibrariesResolvedGraph == null) return
val failed = mutableSetOf<ResolvedDependencyResult>()
exportLibrariesResolvedGraph
.allDependencies
.filterIsInstance<ResolvedDependencyResult>()
.forEach {
val dependencyFiles = exportLibrariesResolvedGraph.dependencyArtifacts(it).map { it.file }.filterKlibsPassedToCompiler()
if (!apiFiles.files.containsAll(dependencyFiles)) {
failed.add(it)
}
}
check(failed.isEmpty()) {
val failedDependenciesList = failed.joinToString(separator = "\n") {
val componentId = it.selected.id
when (componentId) {
is ModuleComponentIdentifier -> "|Files: ${exportLibrariesResolvedGraph.dependencyArtifacts(it).map { it.file }}"
is ProjectComponentIdentifier -> "|Project ${componentId.projectPath}"
else -> "|${componentId.displayName}"
}
}
"""
|Following dependencies exported in the ${binary.name} binary are not specified as API-dependencies of a corresponding source set:
|
$failedDependenciesList
|
|Please add them in the API-dependencies and rerun the build.
""".trimMargin()
}
}
@get:Internal
internal abstract val konanPropertiesService: Property<KonanPropertiesBuildService>
private val resolvedDependencyGraph = ResolvedDependencyGraph(
project.configurations.getByName(compilation.compileDependencyConfigurationName)
)
@TaskAction
fun compile() {
validatedExportedLibraries()
val output = outputFile.get()
output.parentFile.mkdirs()
val plugins = listOfNotNull(
compilerPluginClasspath?.let { CompilerPluginData(it, compilerPluginOptions) },
kotlinPluginData?.orNull?.let { CompilerPluginData(it.classpath, it.options) }
)
val executionContext = KotlinToolRunner.GradleExecutionContext.fromTaskContext(objectFactory, execOperations, logger)
val cacheArgs = CacheBuilder(
executionContext = executionContext,
settings = cacheBuilderSettings,
konanPropertiesService = konanPropertiesService.get()
).buildCompilerArgs(resolvedDependencyGraph)
val buildArgs = buildKotlinNativeBinaryLinkerArgs(
output,
optimized,
debuggable,
konanTarget,
outputKind,
libraries.files.filterKlibsPassedToCompiler(),
friendModule.files.toList(),
enableEndorsedLibs,
localKotlinOptions,
plugins,
processTests,
entryPoint,
embedBitcode,
linkerOpts,
binaryOptions,
isStaticFramework,
exportLibraries.files.filterKlibsPassedToCompiler(),
sources.asFileTree.files.toList(),
externalDependenciesArgs + cacheArgs
)
KotlinNativeCompilerRunner(
settings = runnerSettings,
executionContext = executionContext
).run(buildArgs)
}
}
private class ExternalDependenciesBuilder(
internal class ExternalDependenciesBuilder(
val project: Project,
val compilation: KotlinCompilation<*>,
intermediateLibraryName: String?