【问题标题】:Gradle forcefully brings you the incorrect version of Kotlin dependenciesGradle 强行给你带来不正确的 Kotlin 依赖版本
【发布时间】:2020-11-13 17:26:16
【问题描述】:

当不同版本的 Kotlin jar 混合在类路径中时,Stackoverflow 上有几个关于 Kotlin 编译器警告的问题。

这个问题是一个不同的问题,它涉及您使用 Kotlin 开发 Gradle 插件时的情况。当您至少满足以下条件之一时:

  • kotlin-dsl 插件已应用;
  • java-gradle-plugin 插件已应用;
  • 增加gradleApi()依赖;
  • 增加gradleKotlinDsl()依赖;

你有像kotlin("jvm") version "1.4.10" 这样的 Kotlin 插件,例如:

plugins {
    java
    kotlin("jvm") version "1.4.10"
    `kotlin-dsl`
    `java-gradle-plugin`
}

你会成名的:

w: Runtime JAR files in the classpath should have the same version. These files were found in the classpath:

bla-bla-bla/gradle-6.7/lib/kotlin-stdlib-1.3.72.jar (version 1.3)
bla-bla-bla/modules-2/files-2.1/.../kotlin-stdlib-jdk8-1.4.10.jar (version 1.4)
and so on for every Kotlin jar

w: Consider providing an explicit dependency on kotlin-reflect 1.4 to prevent strange errors
w: Some runtime JAR files in the classpath have an incompatible version. Consider removing them from the classpath

这里 Kotlin 1.3.72 是 Gradle 6.7 的嵌入式版本,而 Kotlin 1.4.10 是您手动添加的版本。 问题在于gradleApi()gradleKotlinDsl(),因为它们直接将 Kotlin jar 添加为本地文件系统文件,从而绕过了 Gradle 依赖版本解析。

【问题讨论】:

    标签: kotlin gradle gradle-plugin gradle-kotlin-dsl


    【解决方案1】:

    我发现解决此问题的唯一方法是在解决不正确的配置之前删除不正确的依赖项。这是我的 build.gradle.kts 的一部分:

    import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency
    import org.gradle.api.internal.artifacts.dsl.dependencies.DependencyFactory.ClassPathNotation
    import org.gradle.internal.component.local.model.OpaqueComponentIdentifier
    
    plugins {
        java
        kotlin("jvm") version "1.4.10"
        `kotlin-dsl`
        `java-gradle-plugin`
    }
    
    project.afterEvaluate {
        // Stupid `kotlin-dsl` and `java-gradle-plugin` plugins adds gradle embedded Kotlin dependencies
        //  directly as files providing you with incorrect classpath with mixed versions of Kotlin jars.
        // This code removes such dependencies.
    
        // First create a copy of embedded Kotlin dependencies - we only need to remove Kotlin jars from them.
        configurations.create("gradlefix").also { cfg ->
            dependencies {
                add(cfg.name, gradleApi())
                add(cfg.name, gradleKotlinDsl())
            }
        }
    
        // Here are all non-Kotlin jars from gradleKotlinDsl and gradleApi.
        val neededEmbeddedDependencies = configurations["gradlefix"].files
            .filterIsInstance<File>()
            .filterNot {
                it.name.startsWith("kotlin-")
                        && it.name.endsWith(".jar")
            }
    
        // Now remove embedded Kotlin from all configuration, but keep needed embedded jars.
        // It is expected that configurations are not yet resolved.
        configurations
            .filterNot { it.name == "gradlefix" }
            .forEach { cfg ->
                cfg.resolutionStrategy.eachDependency {
                    // Needed if you use chain of `includeBuild`s
                    if (requested.group == "org.jetbrains.kotlin") {
                        useVersion("1.4.10")
                    }
                }
    
                val removed = cfg.dependencies.removeIf {
                    val notation = ((it as? DefaultSelfResolvingDependency)
                        ?.targetComponentId as? OpaqueComponentIdentifier)
                        ?.classPathNotation
    
                    notation == ClassPathNotation.GRADLE_API
                            || notation == ClassPathNotation.GRADLE_KOTLIN_DSL
                }
    
                if (removed) {
                    dependencies {
                        add(cfg.name, project.files(*neededEmbeddedDependencies.toTypedArray()))
                    }
                }
            }
    }
    
    dependencies {
        implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
        implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
        implementation("org.jetbrains.kotlin:kotlin-gradle-plugin")
        implementation("org.jetbrains.kotlin:kotlin-reflect")
        // implementation(gradleApi()) // already added by java-gradle-plugin plugin
        // implementation(gradleKotlinDsl()) // already added by kotlin-dsl plugin
    }
    

    【讨论】:

    • 什么不是简单地省略kotlin-dsl 插件?它说here 带有嵌入式标准库,所以你应该知道你正在进入什么。
    • 查看我的build-time-tracker 插件。
    • @AbhijitSarkar,将您的kotlin("jvm") 更改为kotlin("jvm") version "1.4.10",您将在构建时间跟踪器中遇到同样的问题。你没有这个问题的唯一原因是你使用的是 Kotlin 1.3.72,嵌入到你拥有的 Gradle 6.5.1 中。我当然可以省略kotlin-dsl,但是我们都使用java-gradle-plugin呢?
    • 如果您必须支持 1.4,请使用 Gradle 6.8。就像您使用的任何依赖项一样,Gradle 也有它的包袱。
    【解决方案2】:

    我得到了一个类似的解决方案,但仍然非常不同:

    我发现我可以撤消添加默认命名的 Gradle 依赖项(gradleApi()gradleKotlinDsl()gradleTestKit()),并提供了我自己的不包含嵌入式 Kotlin 依赖项的版本。

    You can find it in real context here

    fun Project.replaceGradlePluginAutoDependenciesWithoutKotlin() {
        plugins.withId("org.gradle.java-gradle-plugin") {
            @Suppress("DEPRECATION")
            dependencies {
                // Undo org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin.applyDependencies
                if (configurations[JavaPlugin.COMPILE_CONFIGURATION_NAME].dependencies.remove(gradleApi())) {
                    add(JavaPlugin.COMPILE_CONFIGURATION_NAME, gradleApiWithoutKotlin())
                }
    
                // Undo org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin.TestKitAndPluginClasspathDependenciesAction
                afterEvaluate {
                    gradlePlugin.testSourceSets.forEach {
                        if (configurations[it.compileConfigurationName].dependencies.remove(gradleTestKit())) {
                            add(it.compileConfigurationName, gradleTestKitWithoutKotlin())
                        }
                    }
                }
            }
        }
    
        plugins.withId("org.gradle.kotlin.kotlin-dsl.base") { // applied from org.gradle.kotlin.kotlin-dsl
            dependencies {
                // based on org.gradle.kotlin.dsl.plugins.embedded.kotlinArtifactConfigurationNames (in EmbeddedKotlinPlugin.kt)
                val kotlinArtifactConfigurationNames = listOf(
                    JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME,
                    JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME
                )
                kotlinArtifactConfigurationNames.forEach {
                    // Undo org.gradle.kotlin.dsl.plugins.base.KotlinDslBasePlugin.addGradleKotlinDslDependencyTo
                    if (configurations[it].dependencies.remove(gradleKotlinDsl())) {
                        add(it, gradleKotlinDslWithoutKotlin())
                    }
                }
            }
        }
    }
    
    fun DependencyHandler.gradleApiWithoutKotlin(): Dependency =
        withoutKotlin(ClassPathNotation.GRADLE_API)
    
    fun DependencyHandler.gradleKotlinDslWithoutKotlin(): Dependency =
        withoutKotlin(ClassPathNotation.GRADLE_KOTLIN_DSL)
    
    fun DependencyHandler.gradleTestKitWithoutKotlin(): Dependency =
        withoutKotlin(ClassPathNotation.GRADLE_TEST_KIT)
    
    private fun DependencyHandler.withoutKotlin(notation: ClassPathNotation): Dependency {
        // Originally created in org.gradle.api.internal.notations.DependencyClassPathNotationConverter.create
        val gradleApi = create(notation) as FileCollectionDependency
        val filteredSource = gradleApi.files.filter { !it.name.startsWith("kotlin-") }
        val displayName = OpaqueComponentIdentifier("${notation.displayName} (without Kotlin)")
        return DefaultSelfResolvingDependency(displayName, filteredSource as FileCollectionInternal)
    }
    

    【讨论】:

      猜你喜欢
      • 2016-11-30
      • 1970-01-01
      • 1970-01-01
      • 2019-12-29
      • 2015-03-29
      • 2017-08-21
      • 1970-01-01
      • 2023-04-01
      • 2018-06-02
      相关资源
      最近更新 更多