From 329991217a5c411f19d545d3b46da356cc7f6a79 Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Fri, 16 Jul 2021 16:48:55 +0200 Subject: [PATCH] [Gradle] Prevent cinterop's from depending on compile tasks - KotlinNativeCompilation Change how associate compilations are connected. Instead of using the implementation configuration, it now uses the `compileDependencyFiles` directly. This will keep the implementation configuration from containing any main compilation's output. Therefore this configuration can still be forwarded to the cinterop tool. - KotlinNativeTargetConfigurator.kt CInteropConfigurations.kt Create special configurations for cinteropApiElements and cinterop dependencies. This configuration is tries to resolve only cinterop dependencies when such variants are offered by a project. ^KT-47641 Verification Pending --- .../gradle/plugin/KotlinPluginWrapper.kt | 5 +- .../targets/native/KotlinNativeCompilation.kt | 14 +-- .../native/KotlinNativeTargetConfigurator.kt | 61 +++++------- .../native/internal/CInteropConfigurations.kt | 95 +++++++++++++++++++ 4 files changed, 123 insertions(+), 52 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropConfigurations.kt diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt index 934f0c38f98..7d369ea01bb 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPluginWrapper.kt @@ -20,7 +20,6 @@ import org.gradle.api.GradleException import org.gradle.api.NamedDomainObjectFactory import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.internal.FeaturePreviews import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging import org.gradle.api.model.ObjectFactory @@ -38,12 +37,11 @@ import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService import org.jetbrains.kotlin.gradle.targets.js.KotlinJsCompilerAttribute import org.jetbrains.kotlin.gradle.targets.js.KotlinJsPlugin import org.jetbrains.kotlin.gradle.targets.js.npm.addNpmDependencyExtension +import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropKlibLibraryElements import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile import org.jetbrains.kotlin.gradle.tasks.KOTLIN_COMPILER_EMBEDDABLE -import org.jetbrains.kotlin.gradle.tasks.KOTLIN_KLIB_COMMONIZER_EMBEDDABLE import org.jetbrains.kotlin.gradle.tasks.KOTLIN_MODULE_GROUP import org.jetbrains.kotlin.gradle.testing.internal.KotlinTestsRegistry -import org.jetbrains.kotlin.gradle.tooling.BuildKotlinToolingMetadataTask import org.jetbrains.kotlin.gradle.tooling.buildKotlinToolingMetadataTask import org.jetbrains.kotlin.gradle.utils.checkGradleCompatibility import org.jetbrains.kotlin.gradle.utils.loadPropertyFromResources @@ -148,6 +146,7 @@ abstract class KotlinBasePluginWrapper : Plugin { KotlinUsages.setupAttributesMatchingStrategy(project, this) KotlinJsCompilerAttribute.setupAttributesMatchingStrategy(project.dependencies.attributesSchema) ProjectLocalConfigurations.setupAttributesMatchingStrategy(this) + CInteropKlibLibraryElements.setupAttributesMatchingStrategy(this) } internal abstract fun getPlugin( diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt index 639be84c8e1..978e2401a58 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeCompilation.kt @@ -20,8 +20,8 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinNativeFragmentMetadataC import org.jetbrains.kotlin.gradle.plugin.sources.getVisibleSourceSetsFromAssociateCompilations import org.jetbrains.kotlin.gradle.plugin.sources.resolveAllDependsOnSourceSets import org.jetbrains.kotlin.gradle.targets.metadata.getMetadataCompilationForSourceSet -import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile +import org.jetbrains.kotlin.gradle.utils.filesProvider import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.konan.target.KonanTarget import java.io.File @@ -122,16 +122,10 @@ class KotlinNativeCompilation( get() = lowerCamelCaseName(target.disambiguationClassifier, compilationPurpose, "binaries") override fun addAssociateCompilationDependencies(other: KotlinCompilation<*>) { - with(target.project) { - // Kotlin native does not support either 'compileOnly' or 'runtimeOnly' configurations - dependencies.add( - implementationConfigurationName, - project.files({ other.output.classesDirs }) - ) + compileDependencyFiles += other.output.classesDirs + project.filesProvider { other.compileDependencyFiles } - configurations.named(implementationConfigurationName).configure { - it.extendsFrom(configurations.findByName(other.implementationConfigurationName)) - } + target.project.configurations.named(implementationConfigurationName).configure { configuration -> + configuration.extendsFrom(target.project.configurations.findByName(other.implementationConfigurationName)) } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt index 982a830489e..a457ef42b74 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetConfigurator.kt @@ -11,7 +11,6 @@ import org.gradle.api.NamedDomainObjectCollection import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.Dependency import org.gradle.api.attributes.Attribute import org.gradle.api.attributes.AttributeContainer @@ -25,18 +24,20 @@ import org.gradle.api.tasks.Copy import org.gradle.api.tasks.Exec import org.gradle.api.tasks.TaskProvider import org.gradle.language.base.plugins.LifecycleBasePlugin -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.KOTLIN_NATIVE_IGNORE_INCORRECT_DEPENDENCIES import org.jetbrains.kotlin.gradle.plugin.mpp.* import org.jetbrains.kotlin.gradle.plugin.mpp.apple.registerEmbedAndSignAppleFrameworkTask -import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinGradleVariant +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinNativeCompilationData +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.isMainCompilationData import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled import org.jetbrains.kotlin.gradle.targets.native.* import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask -import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeHostTest -import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest -import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest +import org.jetbrains.kotlin.gradle.targets.native.internal.createCInteropApiElementsKlibArtifact +import org.jetbrains.kotlin.gradle.targets.native.internal.locateOrCreateCInteropDependencyConfiguration +import org.jetbrains.kotlin.gradle.targets.native.tasks.* import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.testing.internal.configureConventions import org.jetbrains.kotlin.gradle.testing.internal.kotlinTestRegistry @@ -51,17 +52,6 @@ open class KotlinNativeTargetConfigurator : AbstractKotl createTestCompilation = true ) { - private fun createCInteropKlibArtifact( - interop: DefaultCInteropSettings, - interopTask: TaskProvider - ) = createKlibArtifact( - interop.compilation, - interopTask.map { it.outputFile }, - "cinterop-${interop.name}", - interopTask, - copy = true - ) - // region Task creation. private fun Project.createLinkTask(binary: NativeBinary) { val result = registerTask( @@ -219,8 +209,20 @@ open class KotlinNativeTargetConfigurator : AbstractKotl if (isMain()) { // Register the interop library as an outgoing klib to allow depending on projects with cinterops. project.dependencies.add(target.apiElementsConfigurationName, interopOutput) + + // Add interop library to special CInteropApiElements configuration + createCInteropApiElementsKlibArtifact(interop, interopTask) + // Add the interop library in publication. - createCInteropKlibArtifact(interop, interopTask) + createKlibArtifact( + compilation = interop.compilation, + artifactFile = interopTask.map { it.outputFile }, + classifier = "cinterop-${interop.name}", + producingTask = interopTask, + copy = true + ) + + // We cannot add the interop library in an compilation output because in this case // IDE doesn't see this library in module dependencies. So we have to manually add // main interop libraries in dependencies of the default test compilation. @@ -278,8 +280,8 @@ open class KotlinNativeTargetConfigurator : AbstractKotl protected fun configureCInterops(target: KotlinNativeTarget): Unit = with(target.project) { target.compilations.all { compilation -> createCInteropTasks(compilation, compilation.cinterops) - compilation.cinterops.all { - defineConfigurationsForCInterop(compilation, it, target, configurations) + compilation.cinterops.all { cinterop -> + cinterop.dependencyFiles += locateOrCreateCInteropDependencyConfiguration(compilation, cinterop, target) } } } @@ -412,25 +414,6 @@ open class KotlinNativeTargetConfigurator : AbstractKotl const val INTEROP_GROUP = "interop" const val RUN_GROUP = "run" - protected fun defineConfigurationsForCInterop( - compilation: KotlinNativeCompilation, - cinterop: CInteropSettings, - target: KotlinTarget, - configurations: ConfigurationContainer - ) { - val compileOnlyConfiguration = configurations.getByName(compilation.compileOnlyConfigurationName) - val implementationConfiguration = configurations.getByName(compilation.implementationConfigurationName) - - cinterop.dependencyFiles = configurations.maybeCreate(cinterop.dependencyConfigurationName).apply { - extendsFrom(compileOnlyConfiguration, implementationConfiguration) - usesPlatformOf(target) - isVisible = false - isCanBeConsumed = false - attributes.attribute(USAGE_ATTRIBUTE, KotlinUsages.consumerApiUsage(target)) - description = "Dependencies for cinterop '${cinterop.name}' (compilation '${compilation.name}')." - } - } - internal fun createKlibCompilationTask( compilation: KotlinNativeCompilationData<*> ): TaskProvider { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropConfigurations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropConfigurations.kt new file mode 100644 index 00000000000..d4883d7beaa --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropConfigurations.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2010-2021 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.gradle.targets.native.internal + +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.attributes.* +import org.gradle.api.internal.artifacts.ArtifactAttributes +import org.gradle.api.tasks.TaskProvider +import org.jetbrains.kotlin.gradle.plugin.CInteropSettings +import org.jetbrains.kotlin.gradle.plugin.KotlinNativeTargetConfigurator.NativeArtifactFormat +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultCInteropSettings +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages +import org.jetbrains.kotlin.gradle.plugin.usesPlatformOf +import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropKlibLibraryElements.cinteropKlibLibraryElements +import org.jetbrains.kotlin.gradle.tasks.CInteropProcess + +internal fun createCInteropApiElementsKlibArtifact( + settings: DefaultCInteropSettings, + interopTask: TaskProvider +) { + val project = settings.compilation.project + val configuration = project.locateOrCreateCInteropApiElementsConfiguration(settings.target ?: return) + project.artifacts.add(configuration.name, interopTask.map { it.outputFile }) { artifact -> + artifact.extension = "klib" + artifact.type = "klib" + artifact.classifier = "cinterop-${settings.name}" + artifact.builtBy(interopTask) + } +} + +internal fun Project.locateOrCreateCInteropDependencyConfiguration( + compilation: KotlinNativeCompilation, + cinterop: CInteropSettings, + target: KotlinTarget +): Configuration { + val compileOnlyConfiguration = configurations.getByName(compilation.compileOnlyConfigurationName) + val implementationConfiguration = configurations.getByName(compilation.implementationConfigurationName) + + return configurations.maybeCreate(cinterop.dependencyConfigurationName).apply { + extendsFrom(compileOnlyConfiguration, implementationConfiguration) + isVisible = false + isCanBeResolved = true + isCanBeConsumed = false + + usesPlatformOf(target) + attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, cinteropKlibLibraryElements()) + attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_API)) + description = "Dependencies for cinterop '${cinterop.name}' (compilation '${compilation.name}')." + } +} + +private fun Project.locateOrCreateCInteropApiElementsConfiguration(target: KotlinTarget): Configuration { + val configurationName = target.name + "CInteropApiElements" + configurations.findByName(configurationName)?.let { return it } + + return configurations.create(configurationName).apply { + isCanBeResolved = false + isCanBeConsumed = true + + usesPlatformOf(target) + attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, cinteropKlibLibraryElements()) + attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_API)) + attributes.attribute(ArtifactAttributes.ARTIFACT_FORMAT, NativeArtifactFormat.KLIB) + } +} + + +internal object CInteropKlibLibraryElements { + const val CINTEROP_KLIB = "cinterop-klib" + + fun Project.cinteropKlibLibraryElements(): LibraryElements = objects.named(LibraryElements::class.java, CINTEROP_KLIB) + + fun setupAttributesMatchingStrategy(schema: AttributesSchema) { + schema.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE) { strategy -> + strategy.compatibilityRules.add(CInteropLibraryElementsCompatibilityRule::class.java) + } + } +} + +private class CInteropLibraryElementsCompatibilityRule : AttributeCompatibilityRule { + override fun execute(details: CompatibilityCheckDetails) { + if (details.consumerValue?.name == CInteropKlibLibraryElements.CINTEROP_KLIB) { + if (details.producerValue?.name == LibraryElements.JAR || details.producerValue?.name == LibraryElements.CLASSES) { + details.compatible() + } + } + } +} +